a78429b7c40c50cf3a65279150304c7e00f97c2d
[rocksndiamonds.git] / src / libgame / misc.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2001 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * misc.c                                                   *
12 ***********************************************************/
13
14 #include <time.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <stdarg.h>
19 #include <ctype.h>
20 #include <string.h>
21 #include <unistd.h>
22
23 #include "platform.h"
24
25 #if !defined(PLATFORM_WIN32)
26 #include <pwd.h>
27 #include <sys/param.h>
28 #endif
29
30 #include "misc.h"
31 #include "random.h"
32
33
34 #if defined(PLATFORM_MSDOS)
35 volatile unsigned long counter = 0;
36
37 void increment_counter()
38 {
39   counter++;
40 }
41
42 END_OF_FUNCTION(increment_counter);
43 #endif
44
45
46 /* maximal allowed length of a command line option */
47 #define MAX_OPTION_LEN          256
48
49 #ifdef TARGET_SDL
50 static unsigned long mainCounter(int mode)
51 {
52   static unsigned long base_ms = 0;
53   unsigned long current_ms;
54   unsigned long counter_ms;
55
56   current_ms = SDL_GetTicks();
57
58   /* reset base time in case of counter initializing or wrap-around */
59   if (mode == INIT_COUNTER || current_ms < base_ms)
60     base_ms = current_ms;
61
62   counter_ms = current_ms - base_ms;
63
64   return counter_ms;            /* return milliseconds since last init */
65 }
66
67 #else /* !TARGET_SDL */
68
69 #if defined(PLATFORM_UNIX)
70 static unsigned long mainCounter(int mode)
71 {
72   static struct timeval base_time = { 0, 0 };
73   struct timeval current_time;
74   unsigned long counter_ms;
75
76   gettimeofday(&current_time, NULL);
77
78   /* reset base time in case of counter initializing or wrap-around */
79   if (mode == INIT_COUNTER || current_time.tv_sec < base_time.tv_sec)
80     base_time = current_time;
81
82   counter_ms = (current_time.tv_sec  - base_time.tv_sec)  * 1000
83              + (current_time.tv_usec - base_time.tv_usec) / 1000;
84
85   return counter_ms;            /* return milliseconds since last init */
86 }
87 #endif /* PLATFORM_UNIX */
88 #endif /* !TARGET_SDL */
89
90 void InitCounter()              /* set counter back to zero */
91 {
92 #if !defined(PLATFORM_MSDOS)
93   mainCounter(INIT_COUNTER);
94 #else
95   LOCK_VARIABLE(counter);
96   LOCK_FUNCTION(increment_counter);
97   install_int_ex(increment_counter, BPS_TO_TIMER(100));
98 #endif
99 }
100
101 unsigned long Counter() /* get milliseconds since last call of InitCounter() */
102 {
103 #if !defined(PLATFORM_MSDOS)
104   return mainCounter(READ_COUNTER);
105 #else
106   return (counter * 10);
107 #endif
108 }
109
110 static void sleep_milliseconds(unsigned long milliseconds_delay)
111 {
112   boolean do_busy_waiting = (milliseconds_delay < 5 ? TRUE : FALSE);
113
114 #if defined(PLATFORM_MSDOS)
115   /* don't use select() to perform waiting operations under DOS/Windows
116      environment; always use a busy loop for waiting instead */
117   do_busy_waiting = TRUE;
118 #endif
119
120   if (do_busy_waiting)
121   {
122     /* we want to wait only a few ms -- if we assume that we have a
123        kernel timer resolution of 10 ms, we would wait far to long;
124        therefore it's better to do a short interval of busy waiting
125        to get our sleeping time more accurate */
126
127     unsigned long base_counter = Counter(), actual_counter = Counter();
128
129     while (actual_counter < base_counter + milliseconds_delay &&
130            actual_counter >= base_counter)
131       actual_counter = Counter();
132   }
133   else
134   {
135 #if defined(TARGET_SDL)
136     SDL_Delay(milliseconds_delay);
137 #else
138     struct timeval delay;
139
140     delay.tv_sec  = milliseconds_delay / 1000;
141     delay.tv_usec = 1000 * (milliseconds_delay % 1000);
142
143     if (select(0, NULL, NULL, NULL, &delay) != 0)
144       Error(ERR_WARN, "sleep_milliseconds(): select() failed");
145 #endif
146   }
147 }
148
149 void Delay(unsigned long delay) /* Sleep specified number of milliseconds */
150 {
151   sleep_milliseconds(delay);
152 }
153
154 boolean FrameReached(unsigned long *frame_counter_var,
155                      unsigned long frame_delay)
156 {
157   unsigned long actual_frame_counter = FrameCounter;
158
159   if (actual_frame_counter < *frame_counter_var+frame_delay &&
160       actual_frame_counter >= *frame_counter_var)
161     return(FALSE);
162
163   *frame_counter_var = actual_frame_counter;
164   return(TRUE);
165 }
166
167 boolean DelayReached(unsigned long *counter_var,
168                      unsigned long delay)
169 {
170   unsigned long actual_counter = Counter();
171
172   if (actual_counter < *counter_var + delay &&
173       actual_counter >= *counter_var)
174     return(FALSE);
175
176   *counter_var = actual_counter;
177   return(TRUE);
178 }
179
180 void WaitUntilDelayReached(unsigned long *counter_var, unsigned long delay)
181 {
182   unsigned long actual_counter;
183
184   while(1)
185   {
186     actual_counter = Counter();
187
188     if (actual_counter < *counter_var + delay &&
189         actual_counter >= *counter_var)
190       sleep_milliseconds((*counter_var + delay - actual_counter) / 2);
191     else
192       break;
193   }
194
195   *counter_var = actual_counter;
196 }
197
198 /* int2str() returns a number converted to a string;
199    the used memory is static, but will be overwritten by later calls,
200    so if you want to save the result, copy it to a private string buffer;
201    there can be 10 local calls of int2str() without buffering the result --
202    the 11th call will then destroy the result from the first call and so on.
203 */
204
205 char *int2str(int number, int size)
206 {
207   static char shift_array[10][40];
208   static int shift_counter = 0;
209   char *s = shift_array[shift_counter];
210
211   shift_counter = (shift_counter + 1) % 10;
212
213   if (size > 20)
214     size = 20;
215
216   if (size)
217   {
218     sprintf(s, "                    %09d", number);
219     return &s[strlen(s) - size];
220   }
221   else
222   {
223     sprintf(s, "%d", number);
224     return s;
225   }
226 }
227
228 unsigned int SimpleRND(unsigned int max)
229 {
230 #if defined(TARGET_SDL)
231   static unsigned long root = 654321;
232   unsigned long current_ms;
233
234   current_ms = SDL_GetTicks();
235   root = root * 4253261 + current_ms;
236   return (root % max);
237 #else
238   static unsigned long root = 654321;
239   struct timeval current_time;
240
241   gettimeofday(&current_time, NULL);
242   root = root * 4253261 + current_time.tv_sec + current_time.tv_usec;
243   return (root % max);
244 #endif
245 }
246
247 #ifdef DEBUG
248 static unsigned int last_RND_value = 0;
249
250 unsigned int last_RND()
251 {
252   return last_RND_value;
253 }
254 #endif
255
256 unsigned int RND(unsigned int max)
257 {
258 #ifdef DEBUG
259   return (last_RND_value = random_linux_libc() % max);
260 #else
261   return (random_linux_libc() % max);
262 #endif
263 }
264
265 unsigned int InitRND(long seed)
266 {
267 #if defined(TARGET_SDL)
268   unsigned long current_ms;
269
270   if (seed == NEW_RANDOMIZE)
271   {
272     current_ms = SDL_GetTicks();
273     srandom_linux_libc((unsigned int) current_ms);
274     return (unsigned int) current_ms;
275   }
276   else
277   {
278     srandom_linux_libc((unsigned int) seed);
279     return (unsigned int) seed;
280   }
281 #else
282   struct timeval current_time;
283
284   if (seed == NEW_RANDOMIZE)
285   {
286     gettimeofday(&current_time, NULL);
287     srandom_linux_libc((unsigned int) current_time.tv_usec);
288     return (unsigned int) current_time.tv_usec;
289   }
290   else
291   {
292     srandom_linux_libc((unsigned int) seed);
293     return (unsigned int) seed;
294   }
295 #endif
296 }
297
298 char *getLoginName()
299 {
300 #if defined(PLATFORM_WIN32)
301   return ANONYMOUS_NAME;
302 #else
303   struct passwd *pwd;
304
305   if ((pwd = getpwuid(getuid())) == NULL)
306     return ANONYMOUS_NAME;
307   else
308     return pwd->pw_name;
309 #endif
310 }
311
312 char *getRealName()
313 {
314 #if defined(PLATFORM_UNIX)
315   struct passwd *pwd;
316
317   if ((pwd = getpwuid(getuid())) == NULL || strlen(pwd->pw_gecos) == 0)
318     return ANONYMOUS_NAME;
319   else
320   {
321     static char real_name[1024];
322     char *from_ptr = pwd->pw_gecos, *to_ptr = real_name;
323
324     if (strchr(pwd->pw_gecos, 'ß') == NULL)
325       return pwd->pw_gecos;
326
327     /* the user's real name contains a 'ß' character (german sharp s),
328        which has no equivalent in upper case letters (which our fonts use) */
329     while (*from_ptr != '\0' && (long)(to_ptr - real_name) < 1024 - 2)
330     {
331       if (*from_ptr != 'ß')
332         *to_ptr++ = *from_ptr++;
333       else
334       {
335         from_ptr++;
336         *to_ptr++ = 's';
337         *to_ptr++ = 's';
338       }
339     }
340     *to_ptr = '\0';
341
342     return real_name;
343   }
344 #else /* !PLATFORM_UNIX */
345   return ANONYMOUS_NAME;
346 #endif
347 }
348
349 char *getHomeDir()
350 {
351 #if defined(PLATFORM_UNIX)
352   static char *home_dir = NULL;
353
354   if (!home_dir)
355   {
356     if (!(home_dir = getenv("HOME")))
357     {
358       struct passwd *pwd;
359
360       if ((pwd = getpwuid(getuid())))
361         home_dir = pwd->pw_dir;
362       else
363         home_dir = ".";
364     }
365   }
366
367   return home_dir;
368 #else
369   return ".";
370 #endif
371 }
372
373 char *getPath2(char *path1, char *path2)
374 {
375   char *complete_path = checked_malloc(strlen(path1) + 1 +
376                                        strlen(path2) + 1);
377
378   sprintf(complete_path, "%s/%s", path1, path2);
379   return complete_path;
380 }
381
382 char *getPath3(char *path1, char *path2, char *path3)
383 {
384   char *complete_path = checked_malloc(strlen(path1) + 1 +
385                                        strlen(path2) + 1 +
386                                        strlen(path3) + 1);
387
388   sprintf(complete_path, "%s/%s/%s", path1, path2, path3);
389   return complete_path;
390 }
391
392 char *getStringCopy(char *s)
393 {
394   char *s_copy;
395
396   if (s == NULL)
397     return NULL;
398
399   s_copy = checked_malloc(strlen(s) + 1);
400
401   strcpy(s_copy, s);
402   return s_copy;
403 }
404
405 char *getStringToLower(char *s)
406 {
407   char *s_copy = checked_malloc(strlen(s) + 1);
408   char *s_ptr = s_copy;
409
410   while (*s)
411     *s_ptr++ = tolower(*s++);
412   *s_ptr = '\0';
413
414   return s_copy;
415 }
416
417 void GetOptions(char *argv[])
418 {
419   char **options_left = &argv[1];
420
421   /* initialize global program options */
422   options.display_name = NULL;
423   options.server_host = NULL;
424   options.server_port = 0;
425   options.ro_base_directory = RO_BASE_PATH;
426   options.rw_base_directory = RW_BASE_PATH;
427   options.level_directory = RO_BASE_PATH "/" LEVELS_DIRECTORY;
428   options.graphics_directory = RO_BASE_PATH "/" GRAPHICS_DIRECTORY;
429   options.sounds_directory = RO_BASE_PATH "/" SOUNDS_DIRECTORY;
430   options.music_directory = RO_BASE_PATH "/" MUSIC_DIRECTORY;
431   options.serveronly = FALSE;
432   options.network = FALSE;
433   options.verbose = FALSE;
434   options.debug = FALSE;
435
436   while (*options_left)
437   {
438     char option_str[MAX_OPTION_LEN];
439     char *option = options_left[0];
440     char *next_option = options_left[1];
441     char *option_arg = NULL;
442     int option_len = strlen(option);
443
444     if (option_len >= MAX_OPTION_LEN)
445       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
446
447     strcpy(option_str, option);                 /* copy argument into buffer */
448     option = option_str;
449
450     if (strcmp(option, "--") == 0)              /* stop scanning arguments */
451       break;
452
453     if (strncmp(option, "--", 2) == 0)          /* treat '--' like '-' */
454       option++;
455
456     option_arg = strchr(option, '=');
457     if (option_arg == NULL)                     /* no '=' in option */
458       option_arg = next_option;
459     else
460     {
461       *option_arg++ = '\0';                     /* cut argument from option */
462       if (*option_arg == '\0')                  /* no argument after '=' */
463         Error(ERR_EXIT_HELP, "option '%s' has invalid argument", option_str);
464     }
465
466     option_len = strlen(option);
467
468     if (strcmp(option, "-") == 0)
469       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
470     else if (strncmp(option, "-help", option_len) == 0)
471     {
472       printf("Usage: %s [options] [server.name [port]]\n"
473              "Options:\n"
474              "  -d, --display <host>[:<scr>]  X server display\n"
475              "  -b, --basepath <directory>    alternative base directory\n"
476              "  -l, --level <directory>       alternative level directory\n"
477              "  -g, --graphics <directory>    alternative graphics directory\n"
478              "  -s, --sounds <directory>      alternative graphics directory\n"
479              "  -m, --music <directory>       alternative graphics directory\n"
480              "  -n, --network                 network multiplayer game\n"
481              "      --serveronly              only start network server\n"
482              "  -v, --verbose                 verbose mode\n"
483              "      --debug                   display debugging information\n",
484              program.command_basename);
485       exit(0);
486     }
487     else if (strncmp(option, "-display", option_len) == 0)
488     {
489       if (option_arg == NULL)
490         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
491
492       options.display_name = option_arg;
493       if (option_arg == next_option)
494         options_left++;
495     }
496     else if (strncmp(option, "-basepath", option_len) == 0)
497     {
498       if (option_arg == NULL)
499         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
500
501       /* this should be extended to separate options for ro and rw data */
502       options.ro_base_directory = option_arg;
503       options.rw_base_directory = option_arg;
504       if (option_arg == next_option)
505         options_left++;
506
507       /* adjust path for level directory accordingly */
508       options.level_directory =
509         getPath2(options.ro_base_directory, LEVELS_DIRECTORY);
510     }
511     else if (strncmp(option, "-levels", option_len) == 0)
512     {
513       if (option_arg == NULL)
514         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
515
516       options.level_directory = option_arg;
517       if (option_arg == next_option)
518         options_left++;
519     }
520     else if (strncmp(option, "-graphics", option_len) == 0)
521     {
522       if (option_arg == NULL)
523         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
524
525       options.graphics_directory = option_arg;
526       if (option_arg == next_option)
527         options_left++;
528     }
529     else if (strncmp(option, "-sounds", option_len) == 0)
530     {
531       if (option_arg == NULL)
532         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
533
534       options.sounds_directory = option_arg;
535       if (option_arg == next_option)
536         options_left++;
537     }
538     else if (strncmp(option, "-music", option_len) == 0)
539     {
540       if (option_arg == NULL)
541         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
542
543       options.music_directory = option_arg;
544       if (option_arg == next_option)
545         options_left++;
546     }
547     else if (strncmp(option, "-network", option_len) == 0)
548     {
549       options.network = TRUE;
550     }
551     else if (strncmp(option, "-serveronly", option_len) == 0)
552     {
553       options.serveronly = TRUE;
554     }
555     else if (strncmp(option, "-verbose", option_len) == 0)
556     {
557       options.verbose = TRUE;
558     }
559     else if (strncmp(option, "-debug", option_len) == 0)
560     {
561       options.debug = TRUE;
562     }
563     else if (*option == '-')
564     {
565       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option_str);
566     }
567     else if (options.server_host == NULL)
568     {
569       options.server_host = *options_left;
570     }
571     else if (options.server_port == 0)
572     {
573       options.server_port = atoi(*options_left);
574       if (options.server_port < 1024)
575         Error(ERR_EXIT_HELP, "bad port number '%d'", options.server_port);
576     }
577     else
578       Error(ERR_EXIT_HELP, "too many arguments");
579
580     options_left++;
581   }
582 }
583
584 void Error(int mode, char *format, ...)
585 {
586   char *process_name = "";
587   FILE *error = stderr;
588   char *newline = "\n";
589
590   /* display warnings only when running in verbose mode */
591   if (mode & ERR_WARN && !options.verbose)
592     return;
593
594 #if !defined(PLATFORM_UNIX)
595   newline = "\r\n";
596
597   if ((error = openErrorFile()) == NULL)
598   {
599     printf("Cannot write to error output file!%s", newline);
600     program.exit_function(1);
601   }
602 #endif
603
604   if (mode & ERR_SOUND_SERVER)
605     process_name = " sound server";
606   else if (mode & ERR_NETWORK_SERVER)
607     process_name = " network server";
608   else if (mode & ERR_NETWORK_CLIENT)
609     process_name = " network client **";
610
611   if (format)
612   {
613     va_list ap;
614
615     fprintf(error, "%s%s: ", program.command_basename, process_name);
616
617     if (mode & ERR_WARN)
618       fprintf(error, "warning: ");
619
620     va_start(ap, format);
621     vfprintf(error, format, ap);
622     va_end(ap);
623   
624     fprintf(error, "%s", newline);
625   }
626   
627   if (mode & ERR_HELP)
628     fprintf(error, "%s: Try option '--help' for more information.%s",
629             program.command_basename, newline);
630
631   if (mode & ERR_EXIT)
632     fprintf(error, "%s%s: aborting%s",
633             program.command_basename, process_name, newline);
634
635   if (error != stderr)
636     fclose(error);
637
638   if (mode & ERR_EXIT)
639   {
640     if (mode & ERR_FROM_SERVER)
641       exit(1);                          /* child process: normal exit */
642     else
643       program.exit_function(1);         /* main process: clean up stuff */
644   }
645 }
646
647 void *checked_malloc(unsigned long size)
648 {
649   void *ptr;
650
651   ptr = malloc(size);
652
653   if (ptr == NULL)
654     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
655
656   return ptr;
657 }
658
659 void *checked_calloc(unsigned long size)
660 {
661   void *ptr;
662
663   ptr = calloc(1, size);
664
665   if (ptr == NULL)
666     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
667
668   return ptr;
669 }
670
671 void *checked_realloc(void *ptr, unsigned long size)
672 {
673   ptr = realloc(ptr, size);
674
675   if (ptr == NULL)
676     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
677
678   return ptr;
679 }
680
681 short getFile16BitInteger(FILE *file, int byte_order)
682 {
683   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
684     return ((fgetc(file) <<  8) |
685             (fgetc(file) <<  0));
686   else           /* BYTE_ORDER_LITTLE_ENDIAN */
687     return ((fgetc(file) <<  0) |
688             (fgetc(file) <<  8));
689 }
690
691 void putFile16BitInteger(FILE *file, short value, int byte_order)
692 {
693   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
694   {
695     fputc((value >>  8) & 0xff, file);
696     fputc((value >>  0) & 0xff, file);
697   }
698   else           /* BYTE_ORDER_LITTLE_ENDIAN */
699   {
700     fputc((value >>  0) & 0xff, file);
701     fputc((value >>  8) & 0xff, file);
702   }
703 }
704
705 int getFile32BitInteger(FILE *file, int byte_order)
706 {
707   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
708     return ((fgetc(file) << 24) |
709             (fgetc(file) << 16) |
710             (fgetc(file) <<  8) |
711             (fgetc(file) <<  0));
712   else           /* BYTE_ORDER_LITTLE_ENDIAN */
713     return ((fgetc(file) <<  0) |
714             (fgetc(file) <<  8) |
715             (fgetc(file) << 16) |
716             (fgetc(file) << 24));
717 }
718
719 void putFile32BitInteger(FILE *file, int value, int byte_order)
720 {
721   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
722   {
723     fputc((value >> 24) & 0xff, file);
724     fputc((value >> 16) & 0xff, file);
725     fputc((value >>  8) & 0xff, file);
726     fputc((value >>  0) & 0xff, file);
727   }
728   else           /* BYTE_ORDER_LITTLE_ENDIAN */
729   {
730     fputc((value >>  0) & 0xff, file);
731     fputc((value >>  8) & 0xff, file);
732     fputc((value >> 16) & 0xff, file);
733     fputc((value >> 24) & 0xff, file);
734   }
735 }
736
737 boolean getFileChunk(FILE *file, char *chunk_name, int *chunk_size,
738                      int byte_order)
739 {
740   const int chunk_name_length = 4;
741
742   /* read chunk name */
743   fgets(chunk_name, chunk_name_length + 1, file);
744
745   if (chunk_size != NULL)
746   {
747     /* read chunk size */
748     *chunk_size = getFile32BitInteger(file, byte_order);
749   }
750
751   return (feof(file) || ferror(file) ? FALSE : TRUE);
752 }
753
754 void putFileChunk(FILE *file, char *chunk_name, int chunk_size,
755                   int byte_order)
756 {
757   /* write chunk name */
758   fputs(chunk_name, file);
759
760   if (chunk_size >= 0)
761   {
762     /* write chunk size */
763     putFile32BitInteger(file, chunk_size, byte_order);
764   }
765 }
766
767 void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
768 {
769   while (bytes--)
770     fgetc(file);
771 }
772
773 void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
774 {
775   while (bytes--)
776     fputc(0, file);
777 }
778
779 #define TRANSLATE_KEYSYM_TO_KEYNAME     0
780 #define TRANSLATE_KEYSYM_TO_X11KEYNAME  1
781 #define TRANSLATE_X11KEYNAME_TO_KEYSYM  2
782
783 void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
784 {
785   static struct
786   {
787     Key key;
788     char *x11name;
789     char *name;
790   } translate_key[] =
791   {
792     /* normal cursor keys */
793     { KSYM_Left,        "XK_Left",              "cursor left" },
794     { KSYM_Right,       "XK_Right",             "cursor right" },
795     { KSYM_Up,          "XK_Up",                "cursor up" },
796     { KSYM_Down,        "XK_Down",              "cursor down" },
797
798     /* keypad cursor keys */
799 #ifdef KSYM_KP_Left
800     { KSYM_KP_Left,     "XK_KP_Left",           "keypad left" },
801     { KSYM_KP_Right,    "XK_KP_Right",          "keypad right" },
802     { KSYM_KP_Up,       "XK_KP_Up",             "keypad up" },
803     { KSYM_KP_Down,     "XK_KP_Down",           "keypad down" },
804 #endif
805
806     /* other keypad keys */
807 #ifdef KSYM_KP_Enter
808     { KSYM_KP_Enter,    "XK_KP_Enter",          "keypad enter" },
809     { KSYM_KP_Add,      "XK_KP_Add",            "keypad +" },
810     { KSYM_KP_Subtract, "XK_KP_Subtract",       "keypad -" },
811     { KSYM_KP_Multiply, "XK_KP_Multiply",       "keypad mltply" },
812     { KSYM_KP_Divide,   "XK_KP_Divide",         "keypad /" },
813     { KSYM_KP_Separator,"XK_KP_Separator",      "keypad ," },
814 #endif
815
816     /* modifier keys */
817     { KSYM_Shift_L,     "XK_Shift_L",           "left shift" },
818     { KSYM_Shift_R,     "XK_Shift_R",           "right shift" },
819     { KSYM_Control_L,   "XK_Control_L",         "left control" },
820     { KSYM_Control_R,   "XK_Control_R",         "right control" },
821     { KSYM_Meta_L,      "XK_Meta_L",            "left meta" },
822     { KSYM_Meta_R,      "XK_Meta_R",            "right meta" },
823     { KSYM_Alt_L,       "XK_Alt_L",             "left alt" },
824     { KSYM_Alt_R,       "XK_Alt_R",             "right alt" },
825     { KSYM_Super_L,     "XK_Super_L",           "left super" },  /* Win-L */
826     { KSYM_Super_R,     "XK_Super_R",           "right super" }, /* Win-R */
827     { KSYM_Mode_switch, "XK_Mode_switch",       "mode switch" }, /* Alt-R */
828     { KSYM_Multi_key,   "XK_Multi_key",         "multi key" },   /* Ctrl-R */
829
830     /* some special keys */
831     { KSYM_BackSpace,   "XK_BackSpace",         "backspace" },
832     { KSYM_Delete,      "XK_Delete",            "delete" },
833     { KSYM_Insert,      "XK_Insert",            "insert" },
834     { KSYM_Tab,         "XK_Tab",               "tab" },
835     { KSYM_Home,        "XK_Home",              "home" },
836     { KSYM_End,         "XK_End",               "end" },
837     { KSYM_Page_Up,     "XK_Page_Up",           "page up" },
838     { KSYM_Page_Down,   "XK_Page_Down",         "page down" },
839     { KSYM_Menu,        "XK_Menu",              "menu" },        /* Win-Menu */
840
841     /* ASCII 0x20 to 0x40 keys (except numbers) */
842     { KSYM_space,       "XK_space",             "space" },
843     { KSYM_exclam,      "XK_exclam",            "!" },
844     { KSYM_quotedbl,    "XK_quotedbl",          "\"" },
845     { KSYM_numbersign,  "XK_numbersign",        "#" },
846     { KSYM_dollar,      "XK_dollar",            "$" },
847     { KSYM_percent,     "XK_percent",           "%" },
848     { KSYM_ampersand,   "XK_ampersand",         "&" },
849     { KSYM_apostrophe,  "XK_apostrophe",        "'" },
850     { KSYM_parenleft,   "XK_parenleft",         "(" },
851     { KSYM_parenright,  "XK_parenright",        ")" },
852     { KSYM_asterisk,    "XK_asterisk",          "*" },
853     { KSYM_plus,        "XK_plus",              "+" },
854     { KSYM_comma,       "XK_comma",             "," },
855     { KSYM_minus,       "XK_minus",             "-" },
856     { KSYM_period,      "XK_period",            "." },
857     { KSYM_slash,       "XK_slash",             "/" },
858     { KSYM_colon,       "XK_colon",             ":" },
859     { KSYM_semicolon,   "XK_semicolon",         ";" },
860     { KSYM_less,        "XK_less",              "<" },
861     { KSYM_equal,       "XK_equal",             "=" },
862     { KSYM_greater,     "XK_greater",           ">" },
863     { KSYM_question,    "XK_question",          "?" },
864     { KSYM_at,          "XK_at",                "@" },
865
866     /* more ASCII keys */
867     { KSYM_bracketleft, "XK_bracketleft",       "[" },
868     { KSYM_backslash,   "XK_backslash",         "backslash" },
869     { KSYM_bracketright,"XK_bracketright",      "]" },
870     { KSYM_asciicircum, "XK_asciicircum",       "circumflex" },
871     { KSYM_underscore,  "XK_underscore",        "_" },
872     { KSYM_grave,       "XK_grave",             "grave" },
873     { KSYM_quoteleft,   "XK_quoteleft",         "quote left" },
874     { KSYM_braceleft,   "XK_braceleft",         "brace left" },
875     { KSYM_bar,         "XK_bar",               "bar" },
876     { KSYM_braceright,  "XK_braceright",        "brace right" },
877     { KSYM_asciitilde,  "XK_asciitilde",        "ascii tilde" },
878
879     /* special (non-ASCII) keys */
880     { KSYM_Adiaeresis,  "XK_Adiaeresis",        "Ä" },
881     { KSYM_Odiaeresis,  "XK_Odiaeresis",        "Ö" },
882     { KSYM_Udiaeresis,  "XK_Udiaeresis",        "Ãœ" },
883     { KSYM_adiaeresis,  "XK_adiaeresis",        "ä" },
884     { KSYM_odiaeresis,  "XK_odiaeresis",        "ö" },
885     { KSYM_udiaeresis,  "XK_udiaeresis",        "ü" },
886     { KSYM_ssharp,      "XK_ssharp",            "sharp s" },
887
888     /* end-of-array identifier */
889     { 0,                NULL,                   NULL }
890   };
891
892   int i;
893
894   if (mode == TRANSLATE_KEYSYM_TO_KEYNAME)
895   {
896     static char name_buffer[30];
897     Key key = *keysym;
898
899     if (key >= KSYM_A && key <= KSYM_Z)
900       sprintf(name_buffer, "%c", 'A' + (char)(key - KSYM_A));
901     else if (key >= KSYM_a && key <= KSYM_z)
902       sprintf(name_buffer, "%c", 'a' + (char)(key - KSYM_a));
903     else if (key >= KSYM_0 && key <= KSYM_9)
904       sprintf(name_buffer, "%c", '0' + (char)(key - KSYM_0));
905     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
906       sprintf(name_buffer, "keypad %c", '0' + (char)(key - KSYM_KP_0));
907     else if (key >= KSYM_F1 && key <= KSYM_F24)
908       sprintf(name_buffer, "function F%d", (int)(key - KSYM_F1 + 1));
909     else if (key == KSYM_UNDEFINED)
910       strcpy(name_buffer, "(undefined)");
911     else
912     {
913       i = 0;
914
915       do
916       {
917         if (key == translate_key[i].key)
918         {
919           strcpy(name_buffer, translate_key[i].name);
920           break;
921         }
922       }
923       while (translate_key[++i].name);
924
925       if (!translate_key[i].name)
926         strcpy(name_buffer, "(unknown)");
927     }
928
929     *name = name_buffer;
930   }
931   else if (mode == TRANSLATE_KEYSYM_TO_X11KEYNAME)
932   {
933     static char name_buffer[30];
934     Key key = *keysym;
935
936     if (key >= KSYM_A && key <= KSYM_Z)
937       sprintf(name_buffer, "XK_%c", 'A' + (char)(key - KSYM_A));
938     else if (key >= KSYM_a && key <= KSYM_z)
939       sprintf(name_buffer, "XK_%c", 'a' + (char)(key - KSYM_a));
940     else if (key >= KSYM_0 && key <= KSYM_9)
941       sprintf(name_buffer, "XK_%c", '0' + (char)(key - KSYM_0));
942     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
943       sprintf(name_buffer, "XK_KP_%c", '0' + (char)(key - KSYM_KP_0));
944     else if (key >= KSYM_F1 && key <= KSYM_F24)
945       sprintf(name_buffer, "XK_F%d", (int)(key - KSYM_F1 + 1));
946     else if (key == KSYM_UNDEFINED)
947       strcpy(name_buffer, "[undefined]");
948     else
949     {
950       i = 0;
951
952       do
953       {
954         if (key == translate_key[i].key)
955         {
956           strcpy(name_buffer, translate_key[i].x11name);
957           break;
958         }
959       }
960       while (translate_key[++i].x11name);
961
962       if (!translate_key[i].x11name)
963         sprintf(name_buffer, "0x%04lx", (unsigned long)key);
964     }
965
966     *x11name = name_buffer;
967   }
968   else if (mode == TRANSLATE_X11KEYNAME_TO_KEYSYM)
969   {
970     Key key = KSYM_UNDEFINED;
971     char *name_ptr = *x11name;
972
973     if (strncmp(name_ptr, "XK_", 3) == 0 && strlen(name_ptr) == 4)
974     {
975       char c = name_ptr[3];
976
977       if (c >= 'A' && c <= 'Z')
978         key = KSYM_A + (Key)(c - 'A');
979       else if (c >= 'a' && c <= 'z')
980         key = KSYM_a + (Key)(c - 'a');
981       else if (c >= '0' && c <= '9')
982         key = KSYM_0 + (Key)(c - '0');
983     }
984     else if (strncmp(name_ptr, "XK_KP_", 6) == 0 && strlen(name_ptr) == 7)
985     {
986       char c = name_ptr[6];
987
988       if (c >= '0' && c <= '9')
989         key = KSYM_0 + (Key)(c - '0');
990     }
991     else if (strncmp(name_ptr, "XK_F", 4) == 0 && strlen(name_ptr) <= 6)
992     {
993       char c1 = name_ptr[4];
994       char c2 = name_ptr[5];
995       int d = 0;
996
997       if ((c1 >= '0' && c1 <= '9') &&
998           ((c2 >= '0' && c1 <= '9') || c2 == '\0'))
999         d = atoi(&name_ptr[4]);
1000
1001       if (d >=1 && d <= 24)
1002         key = KSYM_F1 + (Key)(d - 1);
1003     }
1004     else if (strncmp(name_ptr, "XK_", 3) == 0)
1005     {
1006       i = 0;
1007
1008       do
1009       {
1010         if (strcmp(name_ptr, translate_key[i].x11name) == 0)
1011         {
1012           key = translate_key[i].key;
1013           break;
1014         }
1015       }
1016       while (translate_key[++i].x11name);
1017     }
1018     else if (strncmp(name_ptr, "0x", 2) == 0)
1019     {
1020       unsigned long value = 0;
1021
1022       name_ptr += 2;
1023
1024       while (name_ptr)
1025       {
1026         char c = *name_ptr++;
1027         int d = -1;
1028
1029         if (c >= '0' && c <= '9')
1030           d = (int)(c - '0');
1031         else if (c >= 'a' && c <= 'f')
1032           d = (int)(c - 'a' + 10);
1033         else if (c >= 'A' && c <= 'F')
1034           d = (int)(c - 'A' + 10);
1035
1036         if (d == -1)
1037         {
1038           value = -1;
1039           break;
1040         }
1041
1042         value = value * 16 + d;
1043       }
1044
1045       if (value != -1)
1046         key = (Key)value;
1047     }
1048
1049     *keysym = key;
1050   }
1051 }
1052
1053 char *getKeyNameFromKey(Key key)
1054 {
1055   char *name;
1056
1057   translate_keyname(&key, NULL, &name, TRANSLATE_KEYSYM_TO_KEYNAME);
1058   return name;
1059 }
1060
1061 char *getX11KeyNameFromKey(Key key)
1062 {
1063   char *x11name;
1064
1065   translate_keyname(&key, &x11name, NULL, TRANSLATE_KEYSYM_TO_X11KEYNAME);
1066   return x11name;
1067 }
1068
1069 Key getKeyFromX11KeyName(char *x11name)
1070 {
1071   Key key;
1072
1073   translate_keyname(&key, &x11name, NULL, TRANSLATE_X11KEYNAME_TO_KEYSYM);
1074   return key;
1075 }
1076
1077 char getCharFromKey(Key key)
1078 {
1079   char *keyname = getKeyNameFromKey(key);
1080   char letter = 0;
1081
1082   if (strlen(keyname) == 1)
1083     letter = keyname[0];
1084   else if (strcmp(keyname, "space") == 0)
1085     letter = ' ';
1086   else if (strcmp(keyname, "circumflex") == 0)
1087     letter = '^';
1088
1089   return letter;
1090 }
1091
1092 /* ------------------------------------------------------------------------- */
1093 /* some functions to handle lists of level directories                       */
1094 /* ------------------------------------------------------------------------- */
1095
1096 struct LevelDirInfo *newLevelDirInfo()
1097 {
1098   return checked_calloc(sizeof(struct LevelDirInfo));
1099 }
1100
1101 void pushLevelDirInfo(struct LevelDirInfo **node_first,
1102                       struct LevelDirInfo *node_new)
1103 {
1104   node_new->next = *node_first;
1105   *node_first = node_new;
1106 }
1107
1108 int numLevelDirInfo(struct LevelDirInfo *node)
1109 {
1110   int num = 0;
1111
1112   while (node)
1113   {
1114     num++;
1115     node = node->next;
1116   }
1117
1118   return num;
1119 }
1120
1121 boolean validLevelSeries(struct LevelDirInfo *node)
1122 {
1123   return (node != NULL && !node->node_group && !node->parent_link);
1124 }
1125
1126 struct LevelDirInfo *getFirstValidLevelSeries(struct LevelDirInfo *node)
1127 {
1128   if (node == NULL)
1129   {
1130     if (leveldir_first)         /* start with first level directory entry */
1131       return getFirstValidLevelSeries(leveldir_first);
1132     else
1133       return NULL;
1134   }
1135   else if (node->node_group)    /* enter level group (step down into tree) */
1136     return getFirstValidLevelSeries(node->node_group);
1137   else if (node->parent_link)   /* skip start entry of level group */
1138   {
1139     if (node->next)             /* get first real level series entry */
1140       return getFirstValidLevelSeries(node->next);
1141     else                        /* leave empty level group and go on */
1142       return getFirstValidLevelSeries(node->node_parent->next);
1143   }
1144   else                          /* this seems to be a regular level series */
1145     return node;
1146 }
1147
1148 struct LevelDirInfo *getLevelDirInfoFirstGroupEntry(struct LevelDirInfo *node)
1149 {
1150   if (node == NULL)
1151     return NULL;
1152
1153   if (node->node_parent == NULL)                /* top level group */
1154     return leveldir_first;
1155   else                                          /* sub level group */
1156     return node->node_parent->node_group;
1157 }
1158
1159 int numLevelDirInfoInGroup(struct LevelDirInfo *node)
1160 {
1161   return numLevelDirInfo(getLevelDirInfoFirstGroupEntry(node));
1162 }
1163
1164 int posLevelDirInfo(struct LevelDirInfo *node)
1165 {
1166   struct LevelDirInfo *node_cmp = getLevelDirInfoFirstGroupEntry(node);
1167   int pos = 0;
1168
1169   while (node_cmp)
1170   {
1171     if (node_cmp == node)
1172       return pos;
1173
1174     pos++;
1175     node_cmp = node_cmp->next;
1176   }
1177
1178   return 0;
1179 }
1180
1181 struct LevelDirInfo *getLevelDirInfoFromPos(struct LevelDirInfo *node, int pos)
1182 {
1183   struct LevelDirInfo *node_default = node;
1184   int pos_cmp = 0;
1185
1186   while (node)
1187   {
1188     if (pos_cmp == pos)
1189       return node;
1190
1191     pos_cmp++;
1192     node = node->next;
1193   }
1194
1195   return node_default;
1196 }
1197
1198 struct LevelDirInfo *getLevelDirInfoFromFilenameExt(struct LevelDirInfo *node,
1199                                                     char *filename)
1200 {
1201   if (filename == NULL)
1202     return NULL;
1203
1204   while (node)
1205   {
1206     if (node->node_group)
1207     {
1208       struct LevelDirInfo *node_group;
1209
1210       node_group = getLevelDirInfoFromFilenameExt(node->node_group, filename);
1211
1212       if (node_group)
1213         return node_group;
1214     }
1215     else if (!node->parent_link)
1216     {
1217       if (strcmp(filename, node->filename) == 0)
1218         return node;
1219     }
1220
1221     node = node->next;
1222   }
1223
1224   return NULL;
1225 }
1226
1227 struct LevelDirInfo *getLevelDirInfoFromFilename(char *filename)
1228 {
1229   return getLevelDirInfoFromFilenameExt(leveldir_first, filename);
1230 }
1231
1232 void dumpLevelDirInfo(struct LevelDirInfo *node, int depth)
1233 {
1234   int i;
1235
1236   while (node)
1237   {
1238     for (i=0; i<depth * 3; i++)
1239       printf(" ");
1240
1241     printf("filename == '%s'\n", node->filename);
1242
1243     if (node->node_group != NULL)
1244       dumpLevelDirInfo(node->node_group, depth + 1);
1245
1246     node = node->next;
1247   }
1248 }
1249
1250 void sortLevelDirInfo(struct LevelDirInfo **node_first,
1251                       int (*compare_function)(const void *, const void *))
1252 {
1253   int num_nodes = numLevelDirInfo(*node_first);
1254   struct LevelDirInfo **sort_array;
1255   struct LevelDirInfo *node = *node_first;
1256   int i = 0;
1257
1258   if (num_nodes == 0)
1259     return;
1260
1261   /* allocate array for sorting structure pointers */
1262   sort_array = checked_calloc(num_nodes * sizeof(struct LevelDirInfo *));
1263
1264   /* writing structure pointers to sorting array */
1265   while (i < num_nodes && node)         /* double boundary check... */
1266   {
1267     sort_array[i] = node;
1268
1269     i++;
1270     node = node->next;
1271   }
1272
1273   /* sorting the structure pointers in the sorting array */
1274   qsort(sort_array, num_nodes, sizeof(struct LevelDirInfo *),
1275         compare_function);
1276
1277   /* update the linkage of list elements with the sorted node array */
1278   for (i=0; i<num_nodes - 1; i++)
1279     sort_array[i]->next = sort_array[i + 1];
1280   sort_array[num_nodes - 1]->next = NULL;
1281
1282   /* update the linkage of the main list anchor pointer */
1283   *node_first = sort_array[0];
1284
1285   free(sort_array);
1286
1287   /* now recursively sort the level group structures */
1288   node = *node_first;
1289   while (node)
1290   {
1291     if (node->node_group != NULL)
1292       sortLevelDirInfo(&node->node_group, compare_function);
1293
1294     node = node->next;
1295   }
1296 }
1297
1298 inline void swap_numbers(int *i1, int *i2)
1299 {
1300   int help = *i1;
1301
1302   *i1 = *i2;
1303   *i2 = help;
1304 }
1305
1306 inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
1307 {
1308   int help_x = *x1;
1309   int help_y = *y1;
1310
1311   *x1 = *x2;
1312   *x2 = help_x;
1313
1314   *y1 = *y2;
1315   *y2 = help_y;
1316 }
1317
1318
1319 /* ========================================================================= */
1320 /* some stuff from "files.c"                                                 */
1321 /* ========================================================================= */
1322
1323 #if defined(PLATFORM_WIN32)
1324 #ifndef S_IRGRP
1325 #define S_IRGRP S_IRUSR
1326 #endif
1327 #ifndef S_IROTH
1328 #define S_IROTH S_IRUSR
1329 #endif
1330 #ifndef S_IWGRP
1331 #define S_IWGRP S_IWUSR
1332 #endif
1333 #ifndef S_IWOTH
1334 #define S_IWOTH S_IWUSR
1335 #endif
1336 #ifndef S_IXGRP
1337 #define S_IXGRP S_IXUSR
1338 #endif
1339 #ifndef S_IXOTH
1340 #define S_IXOTH S_IXUSR
1341 #endif
1342 #ifndef S_IRWXG
1343 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1344 #endif
1345 #ifndef S_ISGID
1346 #define S_ISGID 0
1347 #endif
1348 #endif  /* PLATFORM_WIN32 */
1349
1350 /* file permissions for newly written files */
1351 #define MODE_R_ALL              (S_IRUSR | S_IRGRP | S_IROTH)
1352 #define MODE_W_ALL              (S_IWUSR | S_IWGRP | S_IWOTH)
1353 #define MODE_X_ALL              (S_IXUSR | S_IXGRP | S_IXOTH)
1354
1355 #define MODE_W_PRIVATE          (S_IWUSR)
1356 #define MODE_W_PUBLIC           (S_IWUSR | S_IWGRP)
1357 #define MODE_W_PUBLIC_DIR       (S_IWUSR | S_IWGRP | S_ISGID)
1358
1359 #define DIR_PERMS_PRIVATE       (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1360 #define DIR_PERMS_PUBLIC        (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1361
1362 #define FILE_PERMS_PRIVATE      (MODE_R_ALL | MODE_W_PRIVATE)
1363 #define FILE_PERMS_PUBLIC       (MODE_R_ALL | MODE_W_PUBLIC)
1364
1365 char *getUserDataDir(void)
1366 {
1367   static char *userdata_dir = NULL;
1368
1369   if (!userdata_dir)
1370   {
1371     char *home_dir = getHomeDir();
1372     char *data_dir = program.userdata_directory;
1373
1374     userdata_dir = getPath2(home_dir, data_dir);
1375   }
1376
1377   return userdata_dir;
1378 }
1379
1380 char *getSetupDir()
1381 {
1382   return getUserDataDir();
1383 }
1384
1385 static mode_t posix_umask(mode_t mask)
1386 {
1387 #if defined(PLATFORM_UNIX)
1388   return umask(mask);
1389 #else
1390   return 0;
1391 #endif
1392 }
1393
1394 static int posix_mkdir(const char *pathname, mode_t mode)
1395 {
1396 #if defined(PLATFORM_WIN32)
1397   return mkdir(pathname);
1398 #else
1399   return mkdir(pathname, mode);
1400 #endif
1401 }
1402
1403 void createDirectory(char *dir, char *text, int permission_class)
1404 {
1405   /* leave "other" permissions in umask untouched, but ensure group parts
1406      of USERDATA_DIR_MODE are not masked */
1407   mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1408                      DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1409   mode_t normal_umask = posix_umask(0);
1410   mode_t group_umask = ~(dir_mode & S_IRWXG);
1411   posix_umask(normal_umask & group_umask);
1412
1413   if (access(dir, F_OK) != 0)
1414     if (posix_mkdir(dir, dir_mode) != 0)
1415       Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1416
1417   posix_umask(normal_umask);            /* reset normal umask */
1418 }
1419
1420 void InitUserDataDirectory()
1421 {
1422   createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1423 }
1424
1425 void SetFilePermissions(char *filename, int permission_class)
1426 {
1427   chmod(filename, (permission_class == PERMS_PRIVATE ?
1428                    FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1429 }
1430
1431 int getFileVersionFromCookieString(const char *cookie)
1432 {
1433   const char *ptr_cookie1, *ptr_cookie2;
1434   const char *pattern1 = "_FILE_VERSION_";
1435   const char *pattern2 = "?.?";
1436   const int len_cookie = strlen(cookie);
1437   const int len_pattern1 = strlen(pattern1);
1438   const int len_pattern2 = strlen(pattern2);
1439   const int len_pattern = len_pattern1 + len_pattern2;
1440   int version_major, version_minor;
1441
1442   if (len_cookie <= len_pattern)
1443     return -1;
1444
1445   ptr_cookie1 = &cookie[len_cookie - len_pattern];
1446   ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1447
1448   if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1449     return -1;
1450
1451   if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1452       ptr_cookie2[1] != '.' ||
1453       ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1454     return -1;
1455
1456   version_major = ptr_cookie2[0] - '0';
1457   version_minor = ptr_cookie2[2] - '0';
1458
1459   return VERSION_IDENT(version_major, version_minor, 0);
1460 }
1461
1462 boolean checkCookieString(const char *cookie, const char *template)
1463 {
1464   const char *pattern = "_FILE_VERSION_?.?";
1465   const int len_cookie = strlen(cookie);
1466   const int len_template = strlen(template);
1467   const int len_pattern = strlen(pattern);
1468
1469   if (len_cookie != len_template)
1470     return FALSE;
1471
1472   if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1473     return FALSE;
1474
1475   return TRUE;
1476 }
1477
1478 /* ------------------------------------------------------------------------- */
1479 /* setup file stuff                                                          */
1480 /* ------------------------------------------------------------------------- */
1481
1482 static char *string_tolower(char *s)
1483 {
1484   static char s_lower[100];
1485   int i;
1486
1487   if (strlen(s) >= 100)
1488     return s;
1489
1490   strcpy(s_lower, s);
1491
1492   for (i=0; i<strlen(s_lower); i++)
1493     s_lower[i] = tolower(s_lower[i]);
1494
1495   return s_lower;
1496 }
1497
1498 int get_string_integer_value(char *s)
1499 {
1500   static char *number_text[][3] =
1501   {
1502     { "0", "zero", "null", },
1503     { "1", "one", "first" },
1504     { "2", "two", "second" },
1505     { "3", "three", "third" },
1506     { "4", "four", "fourth" },
1507     { "5", "five", "fifth" },
1508     { "6", "six", "sixth" },
1509     { "7", "seven", "seventh" },
1510     { "8", "eight", "eighth" },
1511     { "9", "nine", "ninth" },
1512     { "10", "ten", "tenth" },
1513     { "11", "eleven", "eleventh" },
1514     { "12", "twelve", "twelfth" },
1515   };
1516
1517   int i, j;
1518
1519   for (i=0; i<13; i++)
1520     for (j=0; j<3; j++)
1521       if (strcmp(string_tolower(s), number_text[i][j]) == 0)
1522         return i;
1523
1524   return atoi(s);
1525 }
1526
1527 boolean get_string_boolean_value(char *s)
1528 {
1529   if (strcmp(string_tolower(s), "true") == 0 ||
1530       strcmp(string_tolower(s), "yes") == 0 ||
1531       strcmp(string_tolower(s), "on") == 0 ||
1532       get_string_integer_value(s) == 1)
1533     return TRUE;
1534   else
1535     return FALSE;
1536 }
1537
1538 char *getFormattedSetupEntry(char *token, char *value)
1539 {
1540   int i;
1541   static char entry[MAX_LINE_LEN];
1542
1543   sprintf(entry, "%s:", token);
1544   for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1545     entry[i] = ' ';
1546   entry[i] = '\0';
1547
1548   strcat(entry, value);
1549
1550   return entry;
1551 }
1552
1553 void freeSetupFileList(struct SetupFileList *setup_file_list)
1554 {
1555   if (!setup_file_list)
1556     return;
1557
1558   if (setup_file_list->token)
1559     free(setup_file_list->token);
1560   if (setup_file_list->value)
1561     free(setup_file_list->value);
1562   if (setup_file_list->next)
1563     freeSetupFileList(setup_file_list->next);
1564   free(setup_file_list);
1565 }
1566
1567 static struct SetupFileList *newSetupFileList(char *token, char *value)
1568 {
1569   struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1570
1571   new->token = checked_malloc(strlen(token) + 1);
1572   strcpy(new->token, token);
1573
1574   new->value = checked_malloc(strlen(value) + 1);
1575   strcpy(new->value, value);
1576
1577   new->next = NULL;
1578
1579   return new;
1580 }
1581
1582 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1583 {
1584   if (!setup_file_list)
1585     return NULL;
1586
1587   if (strcmp(setup_file_list->token, token) == 0)
1588     return setup_file_list->value;
1589   else
1590     return getTokenValue(setup_file_list->next, token);
1591 }
1592
1593 static void setTokenValue(struct SetupFileList *setup_file_list,
1594                           char *token, char *value)
1595 {
1596   if (!setup_file_list)
1597     return;
1598
1599   if (strcmp(setup_file_list->token, token) == 0)
1600   {
1601     free(setup_file_list->value);
1602     setup_file_list->value = checked_malloc(strlen(value) + 1);
1603     strcpy(setup_file_list->value, value);
1604   }
1605   else if (setup_file_list->next == NULL)
1606     setup_file_list->next = newSetupFileList(token, value);
1607   else
1608     setTokenValue(setup_file_list->next, token, value);
1609 }
1610
1611 #ifdef DEBUG
1612 static void printSetupFileList(struct SetupFileList *setup_file_list)
1613 {
1614   if (!setup_file_list)
1615     return;
1616
1617   printf("token: '%s'\n", setup_file_list->token);
1618   printf("value: '%s'\n", setup_file_list->value);
1619
1620   printSetupFileList(setup_file_list->next);
1621 }
1622 #endif
1623
1624 struct SetupFileList *loadSetupFileList(char *filename)
1625 {
1626   int line_len;
1627   char line[MAX_LINE_LEN];
1628   char *token, *value, *line_ptr;
1629   struct SetupFileList *setup_file_list = newSetupFileList("", "");
1630   struct SetupFileList *first_valid_list_entry;
1631
1632   FILE *file;
1633
1634   if (!(file = fopen(filename, MODE_READ)))
1635   {
1636     Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1637     return NULL;
1638   }
1639
1640   while(!feof(file))
1641   {
1642     /* read next line of input file */
1643     if (!fgets(line, MAX_LINE_LEN, file))
1644       break;
1645
1646     /* cut trailing comment or whitespace from input line */
1647     for (line_ptr = line; *line_ptr; line_ptr++)
1648     {
1649       if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1650       {
1651         *line_ptr = '\0';
1652         break;
1653       }
1654     }
1655
1656     /* cut trailing whitespaces from input line */
1657     for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1658       if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1659         *line_ptr = '\0';
1660
1661     /* ignore empty lines */
1662     if (*line == '\0')
1663       continue;
1664
1665     line_len = strlen(line);
1666
1667     /* cut leading whitespaces from token */
1668     for (token = line; *token; token++)
1669       if (*token != ' ' && *token != '\t')
1670         break;
1671
1672     /* find end of token */
1673     for (line_ptr = token; *line_ptr; line_ptr++)
1674     {
1675       if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1676       {
1677         *line_ptr = '\0';
1678         break;
1679       }
1680     }
1681
1682     if (line_ptr < line + line_len)
1683       value = line_ptr + 1;
1684     else
1685       value = "\0";
1686
1687     /* cut leading whitespaces from value */
1688     for (; *value; value++)
1689       if (*value != ' ' && *value != '\t')
1690         break;
1691
1692     if (*token && *value)
1693       setTokenValue(setup_file_list, token, value);
1694   }
1695
1696   fclose(file);
1697
1698   first_valid_list_entry = setup_file_list->next;
1699
1700   /* free empty list header */
1701   setup_file_list->next = NULL;
1702   freeSetupFileList(setup_file_list);
1703
1704   if (first_valid_list_entry == NULL)
1705     Error(ERR_WARN, "configuration file '%s' is empty", filename);
1706
1707   return first_valid_list_entry;
1708 }
1709
1710 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1711                                   char *identifier)
1712 {
1713   if (!setup_file_list)
1714     return;
1715
1716   if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1717   {
1718     if (strcmp(setup_file_list->value, identifier) != 0)
1719     {
1720       Error(ERR_WARN, "configuration file has wrong version");
1721       return;
1722     }
1723     else
1724       return;
1725   }
1726
1727   if (setup_file_list->next)
1728     checkSetupFileListIdentifier(setup_file_list->next, identifier);
1729   else
1730   {
1731     Error(ERR_WARN, "configuration file has no version information");
1732     return;
1733   }
1734 }
1735
1736
1737 /* ========================================================================= */
1738 /* functions only needed for non-Unix (non-command-line) systems */
1739 /* ========================================================================= */
1740
1741 #if !defined(PLATFORM_UNIX)
1742
1743 #define ERROR_FILENAME          "error.out"
1744
1745 void initErrorFile()
1746 {
1747   char *filename;
1748
1749   InitUserDataDirectory();
1750
1751   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
1752   unlink(filename);
1753   free(filename);
1754 }
1755
1756 FILE *openErrorFile()
1757 {
1758   char *filename;
1759   FILE *error_file;
1760
1761   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
1762   error_file = fopen(filename, MODE_APPEND);
1763   free(filename);
1764
1765   return error_file;
1766 }
1767
1768 void dumpErrorFile()
1769 {
1770   char *filename;
1771   FILE *error_file;
1772
1773   filename = getPath2(getUserDataDir(), ERROR_FILENAME);
1774   error_file = fopen(filename, MODE_READ);
1775   free(filename);
1776
1777   if (error_file != NULL)
1778   {
1779     while (!feof(error_file))
1780       fputc(fgetc(error_file), stderr);
1781
1782     fclose(error_file);
1783   }
1784 }
1785 #endif
1786
1787
1788 /* ========================================================================= */
1789 /* the following is only for debugging purpose and normally not used         */
1790 /* ========================================================================= */
1791
1792 #define DEBUG_NUM_TIMESTAMPS    3
1793
1794 void debug_print_timestamp(int counter_nr, char *message)
1795 {
1796   static long counter[DEBUG_NUM_TIMESTAMPS][2];
1797
1798   if (counter_nr >= DEBUG_NUM_TIMESTAMPS)
1799     Error(ERR_EXIT, "debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c");
1800
1801   counter[counter_nr][0] = Counter();
1802
1803   if (message)
1804     printf("%s %.2f seconds\n", message,
1805            (float)(counter[counter_nr][0] - counter[counter_nr][1]) / 1000);
1806
1807   counter[counter_nr][1] = Counter();
1808 }