1 /***********************************************************************
2  Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 ***********************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 #include "fc_prehdrs.h"
19 
20 #ifdef FREECIV_HAVE_SYS_TYPES_H
21 /* Under Mac OS X sys/types.h must be included before dirent.h */
22 #include <sys/types.h>
23 #endif
24 
25 #include <dirent.h>
26 #include <errno.h>
27 #include <limits.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 
34 #ifdef HAVE_LOCALE_H
35 #include <locale.h>
36 #endif
37 
38 #ifdef HAVE_PWD_H
39 #include <pwd.h>
40 #endif
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44 #ifdef WIN32_NATIVE
45 #include <windows.h>
46 #include <lmcons.h>	/* UNLEN */
47 #include <shlobj.h>
48 #ifdef HAVE_DIRECT_H
49 #include <direct.h>
50 #endif /* HAVE_DIRECT_H */
51 #endif /* WIN32_NATIVE */
52 
53 /* utility */
54 #include "astring.h"
55 #include "fciconv.h"
56 #include "fcintl.h"
57 #include "mem.h"
58 #include "rand.h"
59 #include "string_vector.h"
60 
61 #include "shared.h"
62 
63 /* If no default data path is defined use the default default one */
64 #ifndef DEFAULT_DATA_PATH
65 #define DEFAULT_DATA_PATH "." PATH_SEPARATOR \
66                           "data" PATH_SEPARATOR \
67                           "~" DIR_SEPARATOR ".freeciv" DIR_SEPARATOR DATASUBDIR
68 #endif
69 #ifndef DEFAULT_SAVE_PATH
70 #define DEFAULT_SAVE_PATH "." PATH_SEPARATOR \
71                           "~" DIR_SEPARATOR ".freeciv" DIR_SEPARATOR "saves"
72 #endif
73 #ifndef DEFAULT_SCENARIO_PATH
74 #define DEFAULT_SCENARIO_PATH                          \
75   "." PATH_SEPARATOR                                   \
76   "data" DIR_SEPARATOR "scenarios" PATH_SEPARATOR      \
77   "~" DIR_SEPARATOR ".freeciv" DIR_SEPARATOR DATASUBDIR DIR_SEPARATOR "scenarios" PATH_SEPARATOR \
78   "~" DIR_SEPARATOR ".freeciv" DIR_SEPARATOR "scenarios"
79 #endif /* DEFAULT_SCENARIO_PATH */
80 
81 /* environment */
82 #ifndef FREECIV_PATH
83 #define FREECIV_PATH "FREECIV_PATH"
84 #endif
85 #ifndef FREECIV_DATA_PATH
86 #define FREECIV_DATA_PATH "FREECIV_DATA_PATH"
87 #endif
88 #ifndef FREECIV_SAVE_PATH
89 #define FREECIV_SAVE_PATH "FREECIV_SAVE_PATH"
90 #endif
91 #ifndef FREECIV_SCENARIO_PATH
92 #define FREECIV_SCENARIO_PATH "FREECIV_SCENARIO_PATH"
93 #endif
94 
95 /* Both of these are stored in the local encoding.  The grouping_sep must
96  * be converted to the internal encoding when it's used. */
97 static char *grouping = NULL;
98 static char *grouping_sep = NULL;
99 
100 /* As well as base64 functions, this string is used for checking for
101  * 'safe' filenames, so should not contain / \ . */
102 static const char base64url[] =
103   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
104 
105 static struct strvec *data_dir_names = NULL;
106 static struct strvec *save_dir_names = NULL;
107 static struct strvec *scenario_dir_names = NULL;
108 
109 static char *mc_group = NULL;
110 static char *home_dir = NULL;
111 
112 static bool depr_freeciv_path_warned = FALSE;
113 
114 static struct astring realfile = ASTRING_INIT;
115 
116 static int compare_file_mtime_ptrs(const struct fileinfo *const *ppa,
117                                    const struct fileinfo *const *ppb);
118 
119 
120 /***************************************************************************
121   An AND function for fc_tristate.
122 ***************************************************************************/
fc_tristate_and(enum fc_tristate one,enum fc_tristate two)123 enum fc_tristate fc_tristate_and(enum fc_tristate one, enum fc_tristate two)
124 {
125   if (TRI_NO == one || TRI_NO == two) {
126     return TRI_NO;
127   }
128 
129   if (TRI_MAYBE == one || TRI_MAYBE == two) {
130     return TRI_MAYBE;
131   }
132 
133   return TRI_YES;
134 }
135 
136 /***************************************************************
137   Take a string containing multiple lines and create a copy where
138   each line is padded to the length of the longest line and centered.
139   We do not cope with tabs etc.  Note that we're assuming that the
140   last line does _not_ end with a newline.  The caller should
141   free() the result.
142 
143   FIXME: This is only used in the Xaw client, and so probably does
144   not belong in common.
145 ***************************************************************/
create_centered_string(const char * s)146 char *create_centered_string(const char *s)
147 {
148   /* Points to the part of the source that we're looking at. */
149   const char *cp;
150 
151   /* Points to the beginning of the line in the source that we're
152    * looking at. */
153   const char *cp0;
154 
155   /* Points to the result. */
156   char *r;
157 
158   /* Points to the part of the result that we're filling in right
159      now. */
160   char *rn;
161 
162   int i;
163 
164   int maxlen = 0;
165   int curlen = 0;
166   int nlines = 1;
167 
168   for(cp=s; *cp != '\0'; cp++) {
169     if(*cp!='\n')
170       curlen++;
171     else {
172       if(maxlen<curlen)
173 	maxlen=curlen;
174       curlen=0;
175       nlines++;
176     }
177   }
178   if(maxlen<curlen)
179     maxlen=curlen;
180 
181   r=rn=fc_malloc(nlines*(maxlen+1));
182 
183   curlen=0;
184   for(cp0=cp=s; *cp != '\0'; cp++) {
185     if(*cp!='\n')
186       curlen++;
187     else {
188       for(i=0; i<(maxlen-curlen)/2; i++)
189 	*rn++=' ';
190       memcpy(rn, cp0, curlen);
191       rn+=curlen;
192       *rn++='\n';
193       curlen=0;
194       cp0=cp+1;
195     }
196   }
197   for(i=0; i<(maxlen-curlen)/2; i++)
198     *rn++=' ';
199   strcpy(rn, cp0);
200 
201   return r;
202 }
203 
204 /************************************************************************//**
205   An OR function for fc_tristate.
206 ****************************************************************************/
fc_tristate_or(enum fc_tristate one,enum fc_tristate two)207 enum fc_tristate fc_tristate_or(enum fc_tristate one, enum fc_tristate two)
208 {
209   if (TRI_YES == one || TRI_YES == two) {
210     return TRI_YES;
211   }
212 
213   if (TRI_MAYBE == one || TRI_MAYBE == two) {
214     return TRI_MAYBE;
215   }
216 
217   return TRI_NO;
218 }
219 
220 /***************************************************************
221   Returns a statically allocated string containing a nicely-formatted
222   version of the given number according to the user's locale.  (Only
223   works for numbers >= zero.)  The number is given in scientific notation
224   as mantissa * 10^exponent.
225 ***************************************************************/
big_int_to_text(unsigned int mantissa,unsigned int exponent)226 const char *big_int_to_text(unsigned int mantissa, unsigned int exponent)
227 {
228   static char buf[64]; /* Note that we'll be filling this in right to left. */
229   char *grp = grouping;
230   char *ptr;
231   unsigned int cnt = 0;
232   char sep[64];
233   size_t seplen;
234 
235   /* We have to convert the encoding here (rather than when the locale
236    * is initialized) because it can't be done before the charsets are
237    * initialized. */
238   local_to_internal_string_buffer(grouping_sep, sep, sizeof(sep));
239   seplen = strlen(sep);
240 
241 #if 0 /* Not needed while the values are unsigned. */
242   fc_assert_ret_val(0 <= mantissa, NULL);
243   fc_assert_ret_val(0 <= exponent, NULL);
244 #endif
245 
246   if (mantissa == 0) {
247     return "0";
248   }
249 
250   /* We fill the string in backwards, starting from the right.  So the first
251    * thing we do is terminate it. */
252   ptr = &buf[sizeof(buf)];
253   *(--ptr) = '\0';
254 
255   while (mantissa != 0 && exponent >= 0) {
256     int dig;
257 
258     if (ptr <= buf + seplen) {
259       /* Avoid a buffer overflow. */
260       fc_assert_ret_val(ptr > buf + seplen, NULL);
261       return ptr;
262     }
263 
264     /* Add on another character. */
265     if (exponent > 0) {
266       dig = 0;
267       exponent--;
268     } else {
269       dig = mantissa % 10;
270       mantissa /= 10;
271     }
272     *(--ptr) = '0' + dig;
273 
274     cnt++;
275     if (mantissa != 0 && cnt == *grp) {
276       /* Reached count of digits in group: insert separator and reset count. */
277       cnt = 0;
278       if (*grp == CHAR_MAX) {
279 	/* This test is unlikely to be necessary since we would need at
280 	   least 421-bit ints to break the 127 digit barrier, but why not. */
281 	break;
282       }
283       ptr -= seplen;
284       fc_assert_ret_val(ptr >= buf, NULL);
285       memcpy(ptr, sep, seplen);
286       if (*(grp + 1) != 0) {
287 	/* Zero means to repeat the present group-size indefinitely. */
288         grp++;
289       }
290     }
291   }
292 
293   return ptr;
294 }
295 
296 
297 /****************************************************************************
298   Return a prettily formatted string containing the given number.
299 ****************************************************************************/
int_to_text(unsigned int number)300 const char *int_to_text(unsigned int number)
301 {
302   return big_int_to_text(number, 0);
303 }
304 
305 /****************************************************************************
306   Check whether or not the given char is a valid ascii character.  The
307   character can be in any charset so long as it is a superset of ascii.
308 ****************************************************************************/
is_ascii(char ch)309 static bool is_ascii(char ch)
310 {
311   /* this works with both signed and unsigned char's. */
312   return ch >= ' ' && ch <= '~';
313 }
314 
315 /****************************************************************************
316   Check if the name is safe security-wise.  This is intended to be used to
317   make sure an untrusted filename is safe to be used.
318 ****************************************************************************/
is_safe_filename(const char * name)319 bool is_safe_filename(const char *name)
320 {
321   int i = 0;
322 
323   /* must not be NULL or empty */
324   if (!name || *name == '\0') {
325     return FALSE;
326   }
327 
328   for (; '\0' != name[i]; i++) {
329     if ('.' != name[i] && NULL == strchr(base64url, name[i])) {
330       return FALSE;
331     }
332   }
333 
334   /* we don't allow the filename to ascend directories */
335   if (strstr(name, PARENT_DIR_OPERATOR)) {
336     return FALSE;
337   }
338 
339   /* Otherwise, it is okay... */
340   return TRUE;
341 }
342 
343 /***************************************************************
344   This is used in sundry places to make sure that names of cities,
345   players etc. do not contain yucky characters of various sorts.
346   Returns TRUE iff the name is acceptable.
347   FIXME:  Not internationalised.
348 ***************************************************************/
is_ascii_name(const char * name)349 bool is_ascii_name(const char *name)
350 {
351   const char illegal_chars[] = {'|', '%', '"', ',', '*', '<', '>', '\0'};
352   int i, j;
353 
354   /* must not be NULL or empty */
355   if (!name || *name == '\0') {
356     return FALSE;
357   }
358 
359   /* must begin and end with some non-space character */
360   if ((*name == ' ') || (*(strchr(name, '\0') - 1) == ' ')) {
361     return FALSE;
362   }
363 
364   /* must be composed entirely of printable ascii characters,
365    * and no illegal characters which can break ranking scripts. */
366   for (i = 0; name[i]; i++) {
367     if (!is_ascii(name[i])) {
368       return FALSE;
369     }
370     for (j = 0; illegal_chars[j]; j++) {
371       if (name[i] == illegal_chars[j]) {
372 	return FALSE;
373       }
374     }
375   }
376 
377   /* otherwise, it's okay... */
378   return TRUE;
379 }
380 
381 /*************************************************************************
382   Check for valid base64url.
383 *************************************************************************/
is_base64url(const char * s)384 bool is_base64url(const char *s)
385 {
386   size_t i = 0;
387 
388   /* must not be NULL or empty */
389   if (NULL == s || '\0' == *s) {
390     return FALSE;
391   }
392 
393   for (; '\0' != s[i]; i++) {
394     if (NULL == strchr(base64url, s[i])) {
395       return FALSE;
396     }
397   }
398   return TRUE;
399 }
400 
401 /*************************************************************************
402   generate a random string meeting criteria such as is_ascii_name(),
403   is_base64url(), and is_safe_filename().
404 *************************************************************************/
randomize_base64url_string(char * s,size_t n)405 void randomize_base64url_string(char *s, size_t n)
406 {
407   size_t i = 0;
408 
409   /* must not be NULL or too short */
410   if (NULL == s || 1 > n) {
411     return;
412   }
413 
414   for (; i < (n - 1); i++) {
415     s[i] = base64url[fc_rand(sizeof(base64url) - 1)];
416   }
417   s[i] = '\0';
418 }
419 
420 /**************************************************************************
421   Compares two strings, in the collating order of the current locale,
422   given pointers to the two strings (i.e., given "char *"s).
423   Case-sensitive.  Designed to be called from qsort().
424 **************************************************************************/
compare_strings(const void * first,const void * second)425 int compare_strings(const void *first, const void *second)
426 {
427   return fc_strcoll((const char *) first, (const char *) second);
428 }
429 
430 /**************************************************************************
431   Compares two strings, in the collating order of the current locale,
432   given pointers to the two string pointers (i.e., given "char **"s).
433   Case-sensitive.  Designed to be called from qsort().
434 **************************************************************************/
compare_strings_ptrs(const void * first,const void * second)435 int compare_strings_ptrs(const void *first, const void *second)
436 {
437   return fc_strcoll(*((const char **) first), *((const char **) second));
438 }
439 
440 /**************************************************************************
441   Compares two strings, in the collating order of the current locale,
442   given pointers to the two string pointers.  Case-sensitive.
443   Designed to be called from strvec_sort().
444 **************************************************************************/
compare_strings_strvec(const char * const * first,const char * const * second)445 int compare_strings_strvec(const char *const *first,
446                            const char *const *second)
447 {
448   return fc_strcoll(*first, *second);
449 }
450 
451 /***************************************************************************
452   Returns 's' incremented to first non-space character.
453 ***************************************************************************/
skip_leading_spaces(char * s)454 char *skip_leading_spaces(char *s)
455 {
456   fc_assert_ret_val(NULL != s, NULL);
457   while(*s != '\0' && fc_isspace(*s)) {
458     s++;
459   }
460   return s;
461 }
462 
463 /***************************************************************************
464   Removes leading spaces in string pointed to by 's'.
465   Note 's' must point to writeable memory!
466 ***************************************************************************/
remove_leading_spaces(char * s)467 void remove_leading_spaces(char *s)
468 {
469   char *t;
470 
471   fc_assert_ret(NULL != s);
472   t = skip_leading_spaces(s);
473   if (t != s) {
474     while (*t != '\0') {
475       *s++ = *t++;
476     }
477     *s = '\0';
478   }
479 }
480 
481 /***************************************************************************
482   Terminates string pointed to by 's' to remove traling spaces;
483   Note 's' must point to writeable memory!
484 ***************************************************************************/
remove_trailing_spaces(char * s)485 void remove_trailing_spaces(char *s)
486 {
487   char *t;
488   size_t len;
489 
490   fc_assert_ret(NULL != s);
491   len = strlen(s);
492   if (len > 0) {
493     t = s + len -1;
494     while(fc_isspace(*t)) {
495       *t = '\0';
496       if (t == s) {
497 	break;
498       }
499       t--;
500     }
501   }
502 }
503 
504 /***************************************************************************
505   Removes leading and trailing spaces in string pointed to by 's'.
506   Note 's' must point to writeable memory!
507 ***************************************************************************/
remove_leading_trailing_spaces(char * s)508 void remove_leading_trailing_spaces(char *s)
509 {
510   remove_leading_spaces(s);
511   remove_trailing_spaces(s);
512 }
513 
514 /***************************************************************************
515   As remove_trailing_spaces(), for specified char.
516 ***************************************************************************/
remove_trailing_char(char * s,char trailing)517 static void remove_trailing_char(char *s, char trailing)
518 {
519   char *t;
520 
521   fc_assert_ret(NULL != s);
522   t = s + strlen(s) -1;
523   while(t>=s && (*t) == trailing) {
524     *t = '\0';
525     t--;
526   }
527 }
528 
529 /***************************************************************************
530   Returns pointer to '\0' at end of string 'str', and decrements
531   *nleft by the length of 'str'.  This is intended to be useful to
532   allow strcat-ing without traversing the whole string each time,
533   while still keeping track of the buffer length.
534   Eg:
535      char buf[128];
536      int n = sizeof(buf);
537      char *p = buf;
538 
539      fc_snprintf(p, n, "foo%p", p);
540      p = end_of_strn(p, &n);
541      fc_strlcpy(p, "yyy", n);
542 ***************************************************************************/
end_of_strn(char * str,int * nleft)543 char *end_of_strn(char *str, int *nleft)
544 {
545   int len = strlen(str);
546   *nleft -= len;
547   fc_assert_ret_val(0 < (*nleft), NULL); /* space for the terminating nul */
548   return str + len;
549 }
550 
551 /**********************************************************************
552   Check the length of the given string.  If the string is too long,
553   log errmsg, which should be a string in printf-format taking up to
554   two arguments: the string and the length.
555 **********************************************************************/
check_strlen(const char * str,size_t len,const char * errmsg)556 bool check_strlen(const char *str, size_t len, const char *errmsg)
557 {
558   fc_assert_ret_val_msg(strlen(str) < len, TRUE, errmsg, str, len);
559   return FALSE;
560 }
561 
562 /**********************************************************************
563   Call check_strlen() on str and then strlcpy() it into buffer.
564 **********************************************************************/
loud_strlcpy(char * buffer,const char * str,size_t len,const char * errmsg)565 size_t loud_strlcpy(char *buffer, const char *str, size_t len,
566                     const char *errmsg)
567 {
568   (void) check_strlen(str, len, errmsg);
569   return fc_strlcpy(buffer, str, len);
570 }
571 
572 /****************************************************************************
573   Convert 'str' to its int reprentation if possible. 'pint' can be NULL,
574   then it will only test 'str' only contains an integer number.
575 ****************************************************************************/
str_to_int(const char * str,int * pint)576 bool str_to_int(const char *str, int *pint)
577 {
578   const char *start;
579 
580   fc_assert_ret_val(NULL != str, FALSE);
581 
582   while (fc_isspace(*str)) {
583     /* Skip leading spaces. */
584     str++;
585   }
586 
587   start = str;
588   if ('-' == *str || '+' == *str) {
589     /* Handle sign. */
590     str++;
591   }
592   while (fc_isdigit(*str)) {
593     /* Digits. */
594     str++;
595   }
596 
597   while (fc_isspace(*str)) {
598     /* Ignore trailing spaces. */
599     str++;
600   }
601 
602   return ('\0' == *str && (NULL == pint || 1 == sscanf(start, "%d", pint)));
603 }
604 
605 /****************************************************************************
606   Convert 'str' to it's unsigned int reprentation if possible. 'pint' can be NULL,
607   then it will only test 'str' only contains an unsigned integer number.
608 ****************************************************************************/
str_to_uint(const char * str,unsigned int * pint)609 bool str_to_uint(const char *str, unsigned int *pint)
610 {
611   const char *start;
612 
613   fc_assert_ret_val(NULL != str, FALSE);
614 
615   while (fc_isspace(*str)) {
616     /* Skip leading spaces. */
617     str++;
618   }
619 
620   start = str;
621   if ('+' == *str) {
622     /* Handle sign. */
623     str++;
624   }
625   while (fc_isdigit(*str)) {
626     /* Digits. */
627     str++;
628   }
629 
630   while (fc_isspace(*str)) {
631     /* Ignore trailing spaces. */
632     str++;
633   }
634 
635   return ('\0' == *str && (NULL == pint || 1 == sscanf(start, "%u", pint)));
636 }
637 
638 /****************************************************************************
639   Convert 'str' to it's float reprentation if possible. 'pfloat' can be NULL,
640   then it will only test 'str' only contains a floating point number.
641 ****************************************************************************/
str_to_float(const char * str,float * pfloat)642 bool str_to_float(const char *str, float *pfloat)
643 {
644   bool dot;
645   const char *start;
646 
647   fc_assert_ret_val(NULL != str, FALSE);
648 
649   while (fc_isspace(*str)) {
650     /* Skip leading spaces. */
651     str++;
652   }
653 
654   start = str;
655 
656   if ('-' == *str || '+' == *str) {
657     /* Handle sign. */
658     str++;
659   }
660   while (fc_isdigit(*str)) {
661     /* Digits. */
662     str++;
663   }
664 
665   if (*str == '.') {
666     dot = TRUE;
667     str++;
668 
669     while (fc_isdigit(*str)) {
670       /* Digits. */
671       str++;
672     }
673   } else {
674     dot = FALSE;
675   }
676 
677   while (fc_isspace(*str)) {
678     /* Ignore trailing spaces. */
679     str++;
680   }
681 
682   return ('\0' == *str && dot
683           && (NULL == pfloat || 1 == sscanf(start, "%f", pfloat)));
684 }
685 
686 /***************************************************************************
687   Returns string which gives users home dir, as specified by $HOME.
688   Gets value once, and then caches result.
689   If $HOME is not set, give a log message and returns NULL.
690   Note the caller should not mess with the returned string.
691 ***************************************************************************/
user_home_dir(void)692 char *user_home_dir(void)
693 {
694 #ifdef AMIGA
695   return "PROGDIR:";
696 #else  /* AMIGA */
697 
698   if (home_dir == NULL) {
699 #ifdef FREECIV_MSWINDOWS
700 
701     /* some documentation at:
702      * http://justcheckingonall.wordpress.com/2008/05/16/find-shell-folders-win32/
703      * http://archives.seul.org/or/cvs/Oct-2004/msg00082.html */
704 
705     LPITEMIDLIST pidl;
706     LPMALLOC pMalloc;
707 
708     if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidl))) {
709       char *home_dir_in_local_encoding = fc_malloc(PATH_MAX);
710 
711       if (SUCCEEDED(SHGetPathFromIDList(pidl, home_dir_in_local_encoding))) {
712         /* convert to internal encoding */
713         home_dir = local_to_internal_string_malloc(home_dir_in_local_encoding);
714         free(home_dir_in_local_encoding);
715 
716 #ifdef DIR_SEPARATOR_IS_DEFAULT
717         /* replace backslashes with forward slashes */
718         {
719           char *c;
720 
721           for (c = home_dir; *c != 0; c++) {
722             if (*c == '\\') {
723               *c = DIR_SEPARATOR_CHAR;
724             }
725           }
726         }
727 #endif /* DIR_SEPARATOR_IS_DEFAULT */
728       } else {
729         free(home_dir_in_local_encoding);
730         home_dir = NULL;
731         log_error("Could not find home directory "
732                   "(SHGetPathFromIDList() failed).");
733       }
734 
735       SHGetMalloc(&pMalloc);
736       if (pMalloc) {
737         pMalloc->lpVtbl->Free(pMalloc, pidl);
738         pMalloc->lpVtbl->Release(pMalloc);
739       }
740 
741     } else {
742       log_error("Could not find home directory "
743                 "(SHGetSpecialFolderLocation() failed).");
744     }
745 
746     if (home_dir == NULL)
747 #endif /* FREECIV_MSWINDOWS */
748     {
749       char *env = getenv("HOME");
750 
751       if (env) {
752         home_dir = fc_strdup(env);
753         log_verbose("HOME is %s", home_dir);
754       } else {
755         log_error("Could not find home directory (HOME is not set).");
756         home_dir = NULL;
757       }
758     }
759   }
760 
761   return home_dir;
762 #endif /* AMIGA */
763 }
764 
765 /***************************************************************************
766   Free user home directory information
767 ***************************************************************************/
free_user_home_dir(void)768 void free_user_home_dir(void)
769 {
770   if (home_dir != NULL) {
771     free(home_dir);
772     home_dir = NULL;
773   }
774 }
775 
776 /***************************************************************************
777   Returns string which gives user's username, as specified by $USER or
778   as given in password file for this user's uid, or a made up name if
779   we can't get either of the above.
780   Gets value once, and then caches result.
781   Note the caller should not mess with returned string.
782 ***************************************************************************/
user_username(char * buf,size_t bufsz)783 char *user_username(char *buf, size_t bufsz)
784 {
785   /* This function uses a number of different methods to try to find a
786    * username.  This username then has to be truncated to bufsz
787    * characters (including terminator) and checked for sanity.  Note that
788    * truncating a sane name can leave you with an insane name under some
789    * charsets. */
790 
791   /* If the environment variable $USER is present and sane, use it. */
792   {
793     char *env = getenv("USER");
794 
795     if (env) {
796       fc_strlcpy(buf, env, bufsz);
797       if (is_ascii_name(buf)) {
798         log_verbose("USER username is %s", buf);
799         return buf;
800       }
801     }
802   }
803 
804 #ifdef HAVE_GETPWUID
805   /* Otherwise if getpwuid() is available we can use it to find the true
806    * username. */
807   {
808     struct passwd *pwent = getpwuid(getuid());
809 
810     if (pwent) {
811       fc_strlcpy(buf, pwent->pw_name, bufsz);
812       if (is_ascii_name(buf)) {
813         log_verbose("getpwuid username is %s", buf);
814         return buf;
815       }
816     }
817   }
818 #endif /* HAVE_GETPWUID */
819 
820 #ifdef WIN32_NATIVE
821   /* On win32 the GetUserName function will give us the login name. */
822   {
823     char name[UNLEN + 1];
824     DWORD length = sizeof(name);
825 
826     if (GetUserName(name, &length)) {
827       fc_strlcpy(buf, name, bufsz);
828       if (is_ascii_name(buf)) {
829         log_verbose("GetUserName username is %s", buf);
830         return buf;
831       }
832     }
833   }
834 #endif /* WIN32_NATIVE */
835 
836 #ifdef ALWAYS_ROOT
837   fc_strlcpy(buf, "name", bufsz);
838 #else
839   fc_snprintf(buf, bufsz, "name%d", (int) getuid());
840 #endif
841   log_verbose("fake username is %s", buf);
842   fc_assert(is_ascii_name(buf));
843   return buf;
844 }
845 
846 /***************************************************************************
847   Returns a list of directory paths, in the order in which they should
848   be searched.  Base function for get_data_dirs(), get_save_dirs(),
849   get_scenario_dirs()
850 ***************************************************************************/
base_get_dirs(const char * dir_list)851 static struct strvec *base_get_dirs(const char *dir_list)
852 {
853   struct strvec *dirs = strvec_new();
854   char *path, *tok;
855 
856   path = fc_strdup(dir_list);   /* something we can strtok */
857   tok = strtok(path, PATH_SEPARATOR);
858   do {
859     int i;                      /* strlen(tok), or -1 as flag */
860 
861     tok = skip_leading_spaces(tok);
862     remove_trailing_spaces(tok);
863     if (strcmp(tok, DIR_SEPARATOR) != 0) {
864       remove_trailing_char(tok, DIR_SEPARATOR_CHAR);
865     }
866 
867     i = strlen(tok);
868     if (tok[0] == '~') {
869       if (i > 1 && tok[1] != DIR_SEPARATOR_CHAR) {
870         log_error("For \"%s\" in path cannot expand '~'"
871                   " except as '~" DIR_SEPARATOR "'; ignoring", tok);
872         i = 0;  /* skip this one */
873       } else {
874         char *home = user_home_dir();
875 
876         if (!home) {
877           log_verbose("No HOME, skipping path component %s", tok);
878           i = 0;
879         } else {
880           int len = strlen(home) + i;   /* +1 -1 */
881           char *tmp = fc_malloc(len);
882 
883           fc_snprintf(tmp, len, "%s%s", home, tok + 1);
884           tok = tmp;
885           i = -1;       /* flag to free tok below */
886         }
887       }
888     }
889 
890     if (i != 0) {
891       /* We could check whether the directory exists and
892        * is readable etc?  Don't currently. */
893       strvec_append(dirs, tok);
894       if (i == -1) {
895         free(tok);
896         tok = NULL;
897       }
898     }
899 
900     tok = strtok(NULL, PATH_SEPARATOR);
901   } while(tok);
902 
903   free(path);
904   return dirs;
905 }
906 
907 /***************************************************************************
908   Free data dir name vectors.
909 ***************************************************************************/
free_data_dir_names(void)910 void free_data_dir_names(void)
911 {
912   if (data_dir_names != NULL) {
913     strvec_destroy(data_dir_names);
914     data_dir_names = NULL;
915   }
916   if (save_dir_names != NULL) {
917     strvec_destroy(save_dir_names);
918     save_dir_names = NULL;
919   }
920   if (scenario_dir_names != NULL) {
921     strvec_destroy(scenario_dir_names);
922     scenario_dir_names = NULL;
923   }
924 }
925 
926 /***************************************************************************
927   Returns a list of data directory paths, in the order in which they should
928   be searched.  These paths are specified internally or may be set as the
929   environment variable $FREECIV_DATA PATH (a separated list of directories,
930   where the separator itself is specified internally, platform-dependent).
931   FREECIV_PATH may also be consulted for backward compatibility.
932   '~' at the start of a component (provided followed by '/' or '\0') is
933   expanded as $HOME.
934 
935   The returned pointer is static and shouldn't be modified, nor destroyed
936   by the user caller.
937 ***************************************************************************/
get_data_dirs(void)938 const struct strvec *get_data_dirs(void)
939 {
940   /* The first time this function is called it will search and
941    * allocate the directory listing.  Subsequently we will already
942    * know the list and can just return it. */
943   if (NULL == data_dir_names) {
944     const char *path;
945 
946     if ((path = getenv(FREECIV_DATA_PATH)) && '\0' == path[0]) {
947       /* TRANS: <FREECIV_DATA_PATH> configuration error */
948       log_error(_("\"%s\" is set but empty; trying \"%s\" instead."),
949                 FREECIV_DATA_PATH, FREECIV_PATH);
950       path = NULL;
951     }
952     if (NULL == path && (path = getenv(FREECIV_PATH))) {
953       if (!depr_freeciv_path_warned) {
954         log_error(_("FREECIV_PATH is deprecated, and won't work in future versions."));
955         depr_freeciv_path_warned = TRUE;
956       }
957       if ('\0' == path[0]) {
958         /* TRANS: <FREECIV_PATH> configuration error */
959         log_error(_("\"%s\" is set but empty; using default \"%s\" "
960                     "data directories instead."),
961                   FREECIV_PATH, DEFAULT_DATA_PATH);
962         path = NULL;
963       }
964     }
965     data_dir_names = base_get_dirs(NULL != path ? path : DEFAULT_DATA_PATH);
966     strvec_remove_duplicate(data_dir_names, strcmp); /* Don't set a path both. */
967     strvec_iterate(data_dir_names, dirname) {
968       log_verbose("Data path component: %s", dirname);
969     } strvec_iterate_end;
970   }
971 
972   return data_dir_names;
973 }
974 
975 /***************************************************************************
976   Returns a list of save directory paths, in the order in which they should
977   be searched.  These paths are specified internally or may be set as the
978   environment variable $FREECIV_SAVE_PATH (a separated list of directories,
979   where the separator itself is specified internally, platform-dependent).
980   FREECIV_PATH may also be consulted for backward compatibility.
981   '~' at the start of a component (provided followed by '/' or '\0') is
982   expanded as $HOME.
983 
984   The returned pointer is static and shouldn't be modified, nor destroyed
985   by the user caller.
986 ***************************************************************************/
get_save_dirs(void)987 const struct strvec *get_save_dirs(void)
988 {
989   /* The first time this function is called it will search and
990    * allocate the directory listing.  Subsequently we will already
991    * know the list and can just return it. */
992   if (NULL == save_dir_names) {
993     const char *path;
994     bool from_freeciv_path = FALSE;
995 
996     if ((path = getenv(FREECIV_SAVE_PATH)) && '\0' == path[0]) {
997       /* TRANS: <FREECIV_SAVE_PATH> configuration error */
998       log_error(_("\"%s\" is set but empty; trying \"%s\" instead."),
999                 FREECIV_SAVE_PATH, FREECIV_PATH);
1000       path = NULL;
1001     }
1002     if (NULL == path && (path = getenv(FREECIV_PATH))) {
1003       if (!depr_freeciv_path_warned) {
1004         log_error(_("FREECIV_PATH is deprecated, and won't work in future versions."));
1005         depr_freeciv_path_warned = TRUE;
1006       }
1007       if ('\0' == path[0]) {
1008         /* TRANS: <FREECIV_PATH> configuration error */
1009         log_error(_("\"%s\" is set but empty; using default \"%s\" "
1010                     "save directories instead."),
1011                   FREECIV_PATH, DEFAULT_SAVE_PATH);
1012         path = NULL;
1013       } else {
1014         from_freeciv_path = TRUE;
1015       }
1016     }
1017     save_dir_names = base_get_dirs(NULL != path ? path : DEFAULT_SAVE_PATH);
1018     if (from_freeciv_path) {
1019       /* Then also append a "/saves" suffix to every directory. */
1020       char buf[512];
1021       size_t i;
1022 
1023       for (i = 0; i < strvec_size(save_dir_names); i++) {
1024         path = strvec_get(save_dir_names, i);
1025         fc_snprintf(buf, sizeof(buf), "%s/saves", path);
1026         strvec_insert(save_dir_names, ++i, buf);
1027       }
1028     }
1029     strvec_remove_duplicate(save_dir_names, strcmp); /* Don't set a path both. */
1030     strvec_iterate(save_dir_names, dirname) {
1031       log_verbose("Save path component: %s", dirname);
1032     } strvec_iterate_end;
1033   }
1034 
1035   return save_dir_names;
1036 }
1037 
1038 /***************************************************************************
1039   Returns a list of scenario directory paths, in the order in which they
1040   should be searched.  These paths are specified internally or may be set
1041   as the environment variable $FREECIV_SCENARIO_PATH (a separated list of
1042   directories, where the separator itself is specified internally,
1043   platform-dependent).  FREECIV_PATH may also be consulted for backward
1044   compatibility.  '~' at the start of a component (provided followed
1045   by '/' or '\0') is expanded as $HOME.
1046 
1047   The returned pointer is static and shouldn't be modified, nor destroyed
1048   by the user caller.
1049 ***************************************************************************/
get_scenario_dirs(void)1050 const struct strvec *get_scenario_dirs(void)
1051 {
1052   /* The first time this function is called it will search and
1053    * allocate the directory listing.  Subsequently we will already
1054    * know the list and can just return it. */
1055   if (NULL == scenario_dir_names) {
1056     const char *path;
1057     bool from_freeciv_path = FALSE;
1058 
1059     if ((path = getenv(FREECIV_SCENARIO_PATH)) && '\0' == path[0]) {
1060       /* TRANS: <FREECIV_SCENARIO_PATH> configuration error */
1061       log_error(_("\"%s\" is set but empty; trying \"%s\" instead."),
1062                 FREECIV_SCENARIO_PATH, FREECIV_PATH);
1063       path = NULL;
1064     }
1065     if (NULL == path && (path = getenv(FREECIV_PATH))) {
1066       if (!depr_freeciv_path_warned) {
1067         log_error(_("FREECIV_PATH is deprecated, and won't work in future versions."));
1068         depr_freeciv_path_warned = TRUE;
1069       }
1070       if ('\0' == path[0]) {
1071         /* TRANS: <FREECIV_PATH> configuration error */
1072         log_error( _("\"%s\" is set but empty; using default \"%s\" "
1073                      "scenario directories instead."),
1074                   FREECIV_PATH, DEFAULT_SCENARIO_PATH);
1075         path = NULL;
1076       } else {
1077         from_freeciv_path = TRUE;
1078       }
1079     }
1080     scenario_dir_names = base_get_dirs(NULL != path ? path : DEFAULT_SCENARIO_PATH);
1081     if (from_freeciv_path) {
1082       /* Then also append subdirs every directory. */
1083       const char *subdirs[] = {
1084         "scenarios", "scenario", NULL
1085       };
1086       char buf[512];
1087       const char **subdir;
1088       size_t i;
1089 
1090       for (i = 0; i < strvec_size(scenario_dir_names); i++) {
1091         path = strvec_get(scenario_dir_names, i);
1092         for (subdir = subdirs; NULL != *subdir; subdir++) {
1093           fc_snprintf(buf, sizeof(buf), "%s/%s", path, *subdir);
1094           strvec_insert(scenario_dir_names, ++i, buf);
1095         }
1096       }
1097     }
1098     strvec_remove_duplicate(scenario_dir_names, strcmp);      /* Don't set a path both. */
1099     strvec_iterate(scenario_dir_names, dirname) {
1100       log_verbose("Scenario path component: %s", dirname);
1101     } strvec_iterate_end;
1102   }
1103 
1104   return scenario_dir_names;
1105 }
1106 
1107 /***************************************************************************
1108   Returns a string vector storing the filenames in the data directories
1109   matching the given suffix.
1110 
1111   The list is allocated when the function is called; it should either
1112   be stored permanently or destroyed (with strvec_destroy()).
1113 
1114   The suffixes are removed from the filenames before the list is
1115   returned.
1116 ***************************************************************************/
fileinfolist(const struct strvec * dirs,const char * suffix)1117 struct strvec *fileinfolist(const struct strvec *dirs, const char *suffix)
1118 {
1119   struct strvec *files = strvec_new();
1120   size_t suffix_len = strlen(suffix);
1121 
1122   fc_assert_ret_val(!strchr(suffix, DIR_SEPARATOR_CHAR), NULL);
1123 
1124   if (NULL == dirs) {
1125     return files;
1126   }
1127 
1128   /* First assemble a full list of names. */
1129   strvec_iterate(dirs, dirname) {
1130     DIR *dir;
1131     struct dirent *entry;
1132 
1133     /* Open the directory for reading. */
1134     dir = fc_opendir(dirname);
1135     if (!dir) {
1136       if (errno == ENOENT) {
1137         log_verbose("Skipping non-existing data directory %s.",
1138                     dirname);
1139       } else {
1140         /* TRANS: "...: <externally translated error string>."*/
1141         log_error(_("Could not read data directory %s: %s."),
1142                   dirname, fc_strerror(fc_get_errno()));
1143       }
1144       continue;
1145     }
1146 
1147     /* Scan all entries in the directory. */
1148     while ((entry = readdir(dir))) {
1149       size_t len = strlen(entry->d_name);
1150 
1151       /* Make sure the file name matches. */
1152       if (len > suffix_len
1153           && strcmp(suffix, entry->d_name + len - suffix_len) == 0) {
1154         /* Strdup the entry so we can safely write to it. */
1155         char *match = fc_strdup(entry->d_name);
1156 
1157         /* Clip the suffix. */
1158         match[len - suffix_len] = '\0';
1159 
1160         strvec_append(files, match);
1161         free(match);
1162       }
1163     }
1164 
1165     closedir(dir);
1166   } strvec_iterate_end;
1167 
1168   /* Sort the list and remove duplications. */
1169   strvec_remove_duplicate(files, strcmp);
1170   strvec_sort(files, compare_strings_strvec);
1171 
1172   return files;
1173 }
1174 
1175 /***************************************************************************
1176   Returns a filename to access the specified file from a
1177   directory by searching all specified directories for the file.
1178 
1179   If the specified 'filename' is NULL, the returned string contains
1180   the effective path.  (But this should probably only be used for
1181   debug output.)
1182 
1183   Returns NULL if the specified filename cannot be found in any of the
1184   data directories.  (A file is considered "found" if it can be
1185   read-opened.)  The returned pointer points to static memory, so this
1186   function can only supply one filename at a time.  Don't free that
1187   pointer.
1188 
1189   TODO: Make this re-entrant
1190 ***************************************************************************/
fileinfoname(const struct strvec * dirs,const char * filename)1191 const char *fileinfoname(const struct strvec *dirs, const char *filename)
1192 {
1193 #ifndef DIR_SEPARATOR_IS_DEFAULT
1194   char fnbuf[filename != NULL ? strlen(filename) + 1 : 1];
1195   int i;
1196 #else  /* DIR_SEPARATOR_IS_DEFAULT */
1197   const char *fnbuf = filename;
1198 #endif /* DIR_SEPARATOR_IS_DEFAULT */
1199 
1200   if (NULL == dirs) {
1201     return NULL;
1202   }
1203 
1204   if (!filename) {
1205     bool first = TRUE;
1206 
1207     astr_clear(&realfile);
1208     strvec_iterate(dirs, dirname) {
1209       if (first) {
1210         astr_add(&realfile, "%s%s", PATH_SEPARATOR, dirname);
1211         first = FALSE;
1212       } else {
1213         astr_add(&realfile, "%s", dirname);
1214       }
1215     } strvec_iterate_end;
1216 
1217     return astr_str(&realfile);
1218   }
1219 
1220 #ifndef DIR_SEPARATOR_IS_DEFAULT
1221   for (i = 0; filename[i] != '\0'; i++) {
1222     if (filename[i] == '/') {
1223       fnbuf[i] = DIR_SEPARATOR_CHAR;
1224     } else {
1225       fnbuf[i] = filename[i];
1226     }
1227   }
1228   fnbuf[i] = '\0';
1229 #endif /* DIR_SEPARATOR_IS_DEFAULT */
1230 
1231   strvec_iterate(dirs, dirname) {
1232     struct stat buf;    /* see if we can open the file or directory */
1233 
1234     astr_set(&realfile, "%s" DIR_SEPARATOR "%s", dirname, fnbuf);
1235     if (fc_stat(astr_str(&realfile), &buf) == 0) {
1236       return astr_str(&realfile);
1237     }
1238   } strvec_iterate_end;
1239 
1240   log_verbose("Could not find readable file \"%s\" in data path.", filename);
1241 
1242   return NULL;
1243 }
1244 
1245 /**************************************************************************
1246   Free resources allocated for fileinfoname service
1247 **************************************************************************/
free_fileinfo_data(void)1248 void free_fileinfo_data(void)
1249 {
1250   astr_free(&realfile);
1251 }
1252 
1253 /**************************************************************************
1254   Destroys the file info structure.
1255 **************************************************************************/
fileinfo_destroy(struct fileinfo * pfile)1256 static void fileinfo_destroy(struct fileinfo *pfile)
1257 {
1258   free(pfile->name);
1259   free(pfile->fullname);
1260   free(pfile);
1261 }
1262 
1263 /**************************************************************************
1264   Compare modification times.
1265 **************************************************************************/
compare_file_mtime_ptrs(const struct fileinfo * const * ppa,const struct fileinfo * const * ppb)1266 static int compare_file_mtime_ptrs(const struct fileinfo *const *ppa,
1267                                    const struct fileinfo *const *ppb)
1268 {
1269   time_t a = (*ppa)->mtime;
1270   time_t b = (*ppb)->mtime;
1271 
1272   return ((a < b) ? 1 : (a > b) ? -1 : 0);
1273 }
1274 
1275 /**************************************************************************
1276   Compare names.
1277 **************************************************************************/
compare_file_name_ptrs(const struct fileinfo * const * ppa,const struct fileinfo * const * ppb)1278 static int compare_file_name_ptrs(const struct fileinfo *const *ppa,
1279                                   const struct fileinfo *const *ppb)
1280 {
1281   return fc_strcoll((*ppa)->name, (*ppb)->name);
1282 }
1283 
1284 /**************************************************************************
1285   Compare names.
1286 **************************************************************************/
compare_fileinfo_name(const struct fileinfo * pa,const struct fileinfo * pb)1287 static bool compare_fileinfo_name(const struct fileinfo *pa,
1288                                   const struct fileinfo *pb)
1289 {
1290   return 0 == fc_strcoll(pa->name, pb->name);
1291 }
1292 
1293 /**************************************************************************
1294   Search for filenames with the "infix" substring in the "subpath"
1295   subdirectory of the data path.
1296   "nodups" removes duplicate names.
1297   The returned list will be sorted by name first and modification time
1298   second.  Returned "name"s will be truncated starting at the "infix"
1299   substring.  The returned list must be freed with fileinfo_list_destroy().
1300 **************************************************************************/
fileinfolist_infix(const struct strvec * dirs,const char * infix,bool nodups)1301 struct fileinfo_list *fileinfolist_infix(const struct strvec *dirs,
1302                                          const char *infix, bool nodups)
1303 {
1304   struct fileinfo_list *res;
1305 
1306   if (NULL == dirs) {
1307     return NULL;
1308   }
1309 
1310   res = fileinfo_list_new_full(fileinfo_destroy);
1311 
1312   /* First assemble a full list of names. */
1313   strvec_iterate(dirs, dirname) {
1314     DIR *dir;
1315     struct dirent *entry;
1316 
1317     /* Open the directory for reading. */
1318     dir = fc_opendir(dirname);
1319     if (!dir) {
1320       continue;
1321     }
1322 
1323     /* Scan all entries in the directory. */
1324     while ((entry = readdir(dir))) {
1325       struct fileinfo *file;
1326       char *ptr;
1327       /* Strdup the entry so we can safely write to it. */
1328       char *filename = fc_strdup(entry->d_name);
1329 
1330       /* Make sure the file name matches. */
1331       if ((ptr = strstr(filename, infix))) {
1332         struct stat buf;
1333         char *fullname;
1334         size_t len = strlen(dirname) + strlen(filename) + 2;
1335 
1336         fullname = fc_malloc(len);
1337         fc_snprintf(fullname, len, "%s" DIR_SEPARATOR "%s", dirname, filename);
1338 
1339         if (fc_stat(fullname, &buf) == 0) {
1340           file = fc_malloc(sizeof(*file));
1341 
1342           /* Clip the suffix. */
1343           *ptr = '\0';
1344 
1345           file->name = filename;
1346           file->fullname = fullname;
1347           file->mtime = buf.st_mtime;
1348 
1349           fileinfo_list_append(res, file);
1350         } else {
1351           free(fullname);
1352           free(filename);
1353         }
1354       } else {
1355         free(filename);
1356       }
1357     }
1358 
1359     closedir(dir);
1360   } strvec_iterate_end;
1361 
1362   /* Sort the list by name. */
1363   fileinfo_list_sort(res, compare_file_name_ptrs);
1364 
1365   if (nodups) {
1366     fileinfo_list_unique_full(res, compare_fileinfo_name);
1367   }
1368 
1369   /* Sort the list by last modification time. */
1370   fileinfo_list_sort(res, compare_file_mtime_ptrs);
1371 
1372   return res;
1373 }
1374 
1375 /***************************************************************************
1376   Language environmental variable (with emulation).
1377 ***************************************************************************/
setup_langname(void)1378 char *setup_langname(void)
1379 {
1380   char *langname = NULL;
1381 
1382 #ifdef ENABLE_NLS
1383   langname = getenv("LANG");
1384 
1385 #ifdef WIN32_NATIVE
1386   /* set LANG by hand if it is not set */
1387   if (!langname) {
1388     switch (PRIMARYLANGID(GetUserDefaultLangID())) {
1389       case LANG_ARABIC:
1390         langname = "ar";
1391         break;
1392       case LANG_CATALAN:
1393         langname = "ca";
1394         break;
1395       case LANG_CZECH:
1396         langname = "cs";
1397         break;
1398       case LANG_DANISH:
1399         langname = "da";
1400         break;
1401       case LANG_GERMAN:
1402         langname = "de";
1403         break;
1404       case LANG_GREEK:
1405         langname = "el";
1406         break;
1407       case LANG_ENGLISH:
1408         switch (SUBLANGID(GetUserDefaultLangID())) {
1409           case SUBLANG_ENGLISH_UK:
1410             langname = "en_GB";
1411             break;
1412           default:
1413             langname = "en";
1414             break;
1415         }
1416         break;
1417       case LANG_SPANISH:
1418         langname = "es";
1419         break;
1420       case LANG_ESTONIAN:
1421         langname = "et";
1422         break;
1423       case LANG_FARSI:
1424         langname = "fa";
1425         break;
1426       case LANG_FINNISH:
1427         langname = "fi";
1428         break;
1429       case LANG_FRENCH:
1430         langname = "fr";
1431         break;
1432       case LANG_HEBREW:
1433         langname = "he";
1434         break;
1435       case LANG_HUNGARIAN:
1436         langname = "hu";
1437         break;
1438       case LANG_ITALIAN:
1439         langname = "it";
1440         break;
1441       case LANG_JAPANESE:
1442         langname = "ja";
1443         break;
1444       case LANG_KOREAN:
1445         langname = "ko";
1446         break;
1447       case LANG_LITHUANIAN:
1448         langname = "lt";
1449         break;
1450       case LANG_DUTCH:
1451         langname = "nl";
1452         break;
1453       case LANG_NORWEGIAN:
1454         langname = "nb";
1455         break;
1456       case LANG_POLISH:
1457         langname = "pl";
1458         break;
1459       case LANG_PORTUGUESE:
1460         switch (SUBLANGID(GetUserDefaultLangID())) {
1461           case SUBLANG_PORTUGUESE_BRAZILIAN:
1462             langname = "pt_BR";
1463             break;
1464           default:
1465             langname = "pt";
1466             break;
1467         }
1468         break;
1469       case LANG_ROMANIAN:
1470         langname = "ro";
1471         break;
1472       case LANG_RUSSIAN:
1473         langname = "ru";
1474         break;
1475       case LANG_SWEDISH:
1476         langname = "sv";
1477         break;
1478       case LANG_TURKISH:
1479         langname = "tr";
1480         break;
1481       case LANG_UKRAINIAN:
1482         langname = "uk";
1483         break;
1484       case LANG_CHINESE:
1485         langname = "zh_CN";
1486         break;
1487     }
1488 
1489     if (langname != NULL) {
1490       static char envstr[40];
1491 
1492       fc_snprintf(envstr, sizeof(envstr), "LANG=%s", langname);
1493       putenv(envstr);
1494     }
1495   }
1496 #endif /* WIN32_NATIVE */
1497 #endif /* ENABLE_NLS */
1498 
1499   return langname;
1500 }
1501 
1502 /***************************************************************************
1503   Setup for Native Language Support, if configured to use it.
1504   (Call this only once, or it may leak memory.)
1505 ***************************************************************************/
init_nls(void)1506 void init_nls(void)
1507 {
1508   /*
1509    * Setup the cached locale numeric formatting information. Defaults
1510    * are as appropriate for the US.
1511    */
1512   grouping = fc_strdup("\3");
1513   grouping_sep = fc_strdup(",");
1514 
1515 #ifdef ENABLE_NLS
1516 
1517 #ifdef WIN32_NATIVE
1518   setup_langname(); /* Makes sure LANG env variable has been set */
1519 #endif /* WIN32_NATIVE */
1520 
1521   (void) setlocale(LC_ALL, "");
1522   (void) bindtextdomain(PACKAGE, get_locale_dir());
1523   (void) textdomain(PACKAGE);
1524 
1525   /* Don't touch the defaults when LC_NUMERIC == "C".
1526      This is intended to cater to the common case where:
1527        1) The user is from North America. ;-)
1528        2) The user has not set the proper environment variables.
1529 	  (Most applications are (unfortunately) US-centric
1530 	  by default, so why bother?)
1531      This would result in the "C" locale being used, with grouping ""
1532      and thousands_sep "", where we really want "\3" and ",". */
1533 
1534   if (strcmp(setlocale(LC_NUMERIC, NULL), "C") != 0) {
1535     struct lconv *lc = localeconv();
1536 
1537     if (lc->grouping[0] == '\0') {
1538       /* This actually indicates no grouping at all. */
1539       char *m = malloc(sizeof(char));
1540       *m = CHAR_MAX;
1541       grouping = m;
1542     } else {
1543       size_t len;
1544       for (len = 0;
1545 	   lc->grouping[len] != '\0' && lc->grouping[len] != CHAR_MAX; len++) {
1546 	/* nothing */
1547       }
1548       len++;
1549       free(grouping);
1550       grouping = fc_malloc(len);
1551       memcpy(grouping, lc->grouping, len);
1552     }
1553     free(grouping_sep);
1554     grouping_sep = fc_strdup(lc->thousands_sep);
1555   }
1556 
1557   {
1558     char *autocap_opt_in[] = { "fi", NULL };
1559     int i;
1560     bool ac_enabled = FALSE;
1561 
1562     char *lang = getenv("LANG");
1563 
1564     if (lang != NULL && lang[0] != '\0' && lang[1] != '\0') {
1565       for (i = 0; autocap_opt_in[i] != NULL && !ac_enabled; i++) {
1566         if (lang[0] == autocap_opt_in[i][0]
1567             && lang[1] == autocap_opt_in[i][1]) {
1568           ac_enabled = TRUE;
1569           capitalization_opt_in();
1570         }
1571       }
1572     }
1573   }
1574 
1575 #endif /* ENABLE_NLS */
1576 }
1577 
1578 /***************************************************************************
1579   Free memory allocated by Native Language Support
1580 ***************************************************************************/
free_nls(void)1581 void free_nls(void)
1582 {
1583   free(grouping);
1584   grouping = NULL;
1585   free(grouping_sep);
1586   grouping_sep = NULL;
1587 }
1588 
1589 /***************************************************************************
1590   If we have root privileges, die with an error.
1591   (Eg, for security reasons.)
1592   Param argv0 should be argv[0] or similar; fallback is
1593   used instead if argv0 is NULL.
1594   But don't die on systems where the user is always root...
1595   (a general test for this would be better).
1596   Doesn't use log_*() because gets called before logging is setup.
1597 ***************************************************************************/
dont_run_as_root(const char * argv0,const char * fallback)1598 void dont_run_as_root(const char *argv0, const char *fallback)
1599 {
1600 #if (defined(ALWAYS_ROOT) || defined(__EMX__) || defined(__BEOS__))
1601   return;
1602 #else
1603   if (getuid()==0 || geteuid()==0) {
1604     fc_fprintf(stderr,
1605 	       _("%s: Fatal error: you're trying to run me as superuser!\n"),
1606 	       (argv0 ? argv0 : fallback ? fallback : "freeciv"));
1607     fc_fprintf(stderr, _("Use a non-privileged account instead.\n"));
1608     exit(EXIT_FAILURE);
1609   }
1610 #endif /* ALWAYS_ROOT */
1611 }
1612 
1613 /***************************************************************************
1614   Return a description string of the result.
1615   In English, form of description is suitable to substitute in, eg:
1616      prefix is <description>
1617   (N.B.: The description is always in English, but they have all been marked
1618    for translation.  If you want a localized version, use _() on the return.)
1619 ***************************************************************************/
m_pre_description(enum m_pre_result result)1620 const char *m_pre_description(enum m_pre_result result)
1621 {
1622   static const char * const descriptions[] = {
1623     N_("exact match"),
1624     N_("only match"),
1625     N_("ambiguous"),
1626     N_("empty"),
1627     N_("too long"),
1628     N_("non-match")
1629   };
1630   fc_assert_ret_val(result >= 0 && result < ARRAY_SIZE(descriptions), NULL);
1631   return descriptions[result];
1632 }
1633 
1634 /***************************************************************************
1635   See match_prefix_full().
1636 ***************************************************************************/
match_prefix(m_pre_accessor_fn_t accessor_fn,size_t n_names,size_t max_len_name,m_pre_strncmp_fn_t cmp_fn,m_strlen_fn_t len_fn,const char * prefix,int * ind_result)1637 enum m_pre_result match_prefix(m_pre_accessor_fn_t accessor_fn,
1638                                size_t n_names,
1639                                size_t max_len_name,
1640                                m_pre_strncmp_fn_t cmp_fn,
1641                                m_strlen_fn_t len_fn,
1642                                const char *prefix,
1643                                int *ind_result)
1644 {
1645   return match_prefix_full(accessor_fn, n_names, max_len_name, cmp_fn,
1646                            len_fn, prefix, ind_result, NULL, 0, NULL);
1647 }
1648 
1649 /***************************************************************************
1650   Given n names, with maximum length max_len_name, accessed by
1651   accessor_fn(0) to accessor_fn(n-1), look for matching prefix
1652   according to given comparison function.
1653   Returns type of match or fail, and for return <= M_PRE_AMBIGUOUS
1654   sets *ind_result with matching index (or for ambiguous, first match).
1655   If max_len_name==0, treat as no maximum.
1656   If the int array 'matches' is non-NULL, up to 'max_matches' ambiguous
1657   matching names indices will be inserted into it. If 'pnum_matches' is
1658   non-NULL, it will be set to the number of indices inserted into 'matches'.
1659 ***************************************************************************/
match_prefix_full(m_pre_accessor_fn_t accessor_fn,size_t n_names,size_t max_len_name,m_pre_strncmp_fn_t cmp_fn,m_strlen_fn_t len_fn,const char * prefix,int * ind_result,int * matches,int max_matches,int * pnum_matches)1660 enum m_pre_result match_prefix_full(m_pre_accessor_fn_t accessor_fn,
1661                                     size_t n_names,
1662                                     size_t max_len_name,
1663                                     m_pre_strncmp_fn_t cmp_fn,
1664                                     m_strlen_fn_t len_fn,
1665                                     const char *prefix,
1666                                     int *ind_result,
1667                                     int *matches,
1668                                     int max_matches,
1669                                     int *pnum_matches)
1670 {
1671   int i, len, nmatches;
1672 
1673   if (len_fn == NULL) {
1674     len = strlen(prefix);
1675   } else {
1676     len = len_fn(prefix);
1677   }
1678   if (len == 0) {
1679     return M_PRE_EMPTY;
1680   }
1681   if (len > max_len_name && max_len_name > 0) {
1682     return M_PRE_LONG;
1683   }
1684 
1685   nmatches = 0;
1686   for(i=0; i<n_names; i++) {
1687     const char *name = accessor_fn(i);
1688     if (cmp_fn(name, prefix, len)==0) {
1689       if (strlen(name) == len) {
1690 	*ind_result = i;
1691 	return M_PRE_EXACT;
1692       }
1693       if (nmatches==0) {
1694 	*ind_result = i;	/* first match */
1695       }
1696       if (matches != NULL && nmatches < max_matches) {
1697         matches[nmatches] = i;
1698       }
1699       nmatches++;
1700     }
1701   }
1702 
1703   if (nmatches == 1) {
1704     return M_PRE_ONLY;
1705   } else if (nmatches > 1) {
1706     if (pnum_matches != NULL) {
1707       *pnum_matches = MIN(max_matches, nmatches);
1708     }
1709     return M_PRE_AMBIGUOUS;
1710   } else {
1711     return M_PRE_FAIL;
1712   }
1713 }
1714 
1715 /***************************************************************************
1716   Returns string which gives the multicast group IP address for finding
1717   servers on the LAN, as specified by $FREECIV_MULTICAST_GROUP.
1718   Gets value once, and then caches result.
1719 ***************************************************************************/
get_multicast_group(bool ipv6_preferred)1720 char *get_multicast_group(bool ipv6_preferred)
1721 {
1722   static char *default_multicast_group_ipv4 = "225.1.1.1";
1723 #ifdef FREECIV_IPV6_SUPPORT
1724   /* TODO: Get useful group (this is node local) */
1725   static char *default_multicast_group_ipv6 = "FF31::8000:15B4";
1726 #endif /* IPv6 support */
1727 
1728   if (mc_group == NULL) {
1729     char *env = getenv("FREECIV_MULTICAST_GROUP");
1730 
1731     if (env) {
1732       mc_group = fc_strdup(env);
1733     } else {
1734 #ifdef FREECIV_IPV6_SUPPORT
1735       if (ipv6_preferred) {
1736         mc_group = fc_strdup(default_multicast_group_ipv6);
1737       } else
1738 #endif /* IPv6 support */
1739       {
1740         mc_group = fc_strdup(default_multicast_group_ipv4);
1741       }
1742     }
1743   }
1744 
1745   return mc_group;
1746 }
1747 
1748 /***************************************************************************
1749   Free multicast group resources
1750 ***************************************************************************/
free_multicast_group(void)1751 void free_multicast_group(void)
1752 {
1753   if (mc_group != NULL) {
1754     free(mc_group);
1755     mc_group = NULL;
1756   }
1757 }
1758 
1759 /***************************************************************************
1760   Interpret ~/ in filename as home dir
1761   New path is returned in buf of size buf_size
1762 
1763   This may fail if the path is too long.  It is better to use
1764   interpret_tilde_alloc.
1765 ***************************************************************************/
interpret_tilde(char * buf,size_t buf_size,const char * filename)1766 void interpret_tilde(char* buf, size_t buf_size, const char* filename)
1767 {
1768   if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1769     fc_snprintf(buf, buf_size, "%s" DIR_SEPARATOR "%s", user_home_dir(), filename + 2);
1770   } else if (filename[0] == '~' && filename[1] == '\0') {
1771     strncpy(buf, user_home_dir(), buf_size);
1772   } else  {
1773     strncpy(buf, filename, buf_size);
1774   }
1775 }
1776 
1777 /***************************************************************************
1778   Interpret ~/ in filename as home dir
1779 
1780   The new path is returned in buf, as a newly allocated buffer.  The new
1781   path will always be allocated and written, even if there is no ~ present.
1782 ***************************************************************************/
interpret_tilde_alloc(const char * filename)1783 char *interpret_tilde_alloc(const char* filename)
1784 {
1785   if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1786     const char *home = user_home_dir();
1787     size_t sz;
1788     char *buf;
1789 
1790     filename += 2; /* Skip past "~/" */
1791     sz = strlen(home) + strlen(filename) + 2;
1792     buf = fc_malloc(sz);
1793     fc_snprintf(buf, sz, "%s/%s", home, filename);
1794     return buf;
1795   } else if (filename[0] == '~' && filename[1] == '\0') {
1796     return fc_strdup(user_home_dir());
1797   } else  {
1798     return fc_strdup(filename);
1799   }
1800 }
1801 
1802 /**************************************************************************
1803   Return a pointer to the start of the file basename in filepath.
1804   If the string contains no dir separator, it is returned itself.
1805 **************************************************************************/
skip_to_basename(char * filepath)1806 char *skip_to_basename(char *filepath)
1807 {
1808   int j;
1809   fc_assert_ret_val(NULL != filepath, NULL);
1810 
1811   for (j = strlen(filepath); j >= 0; j--) {
1812     if (filepath[j] == DIR_SEPARATOR_CHAR) {
1813       return &filepath[j+1];
1814     }
1815   }
1816   return filepath;
1817 }
1818 
1819 /**************************************************************************
1820   If the directory "pathname" does not exist, recursively create all
1821   directories until it does.
1822 **************************************************************************/
make_dir(const char * pathname)1823 bool make_dir(const char *pathname)
1824 {
1825   char *dir;
1826   char *path = NULL;
1827 
1828   path = interpret_tilde_alloc(pathname);
1829   dir = path;
1830   do {
1831     dir = strchr(dir, DIR_SEPARATOR_CHAR);
1832     /* We set the current / with 0, and restore it afterwards */
1833     if (dir) {
1834       *dir = '\0';
1835     }
1836 
1837 #ifdef WIN32_NATIVE
1838 #ifdef HAVE__MKDIR
1839     /* Prefer _mkdir() in Windows even if mkdir() would seem to be available -
1840      * chances are that it's wrong kind of mkdir().
1841      * TODO: Make a configure check for mkdir() that also makes sure that it
1842      *       takes two parameters, and prefer such proper mkdir() here. */
1843     {
1844       char *path_in_local_encoding = internal_to_local_string_malloc(path);
1845 
1846       _mkdir(path_in_local_encoding);
1847       free(path_in_local_encoding);
1848     }
1849 #else  /* HAVE__MKDIR */
1850     mkdir(path, 0755);
1851 #endif /* HAVE__MKDIR */
1852 #else  /* WIN32_NATIVE */
1853     mkdir(path, 0755);
1854 #endif /* WIN32_NATIVE */
1855 
1856     if (dir) {
1857       *dir = DIR_SEPARATOR_CHAR;
1858       dir++;
1859     }
1860   } while (dir);
1861 
1862   free(path);
1863   return TRUE;
1864 }
1865 
1866 /**************************************************************************
1867   Returns TRUE if the filename's path is absolute.
1868 **************************************************************************/
path_is_absolute(const char * filename)1869 bool path_is_absolute(const char *filename)
1870 {
1871   if (!filename) {
1872     return FALSE;
1873   }
1874 
1875 #ifdef WIN32_NATIVE
1876   if (strchr(filename, ':')) {
1877     return TRUE;
1878   }
1879 #else  /* WIN32_NATIVE */
1880   if (filename[0] == '/') {
1881     return TRUE;
1882   }
1883 #endif /* WIN32_NATIVE */
1884 
1885   return FALSE;
1886 }
1887 
1888 /**************************************************************************
1889   Scan in a word or set of words from start to but not including
1890   any of the given delimiters. The buf pointer will point past delimiter,
1891   or be set to NULL if there is nothing there. Removes excess white
1892   space.
1893 
1894   This function should be safe, and dest will contain "\0" and
1895   *buf == NULL on failure. We always fail gently.
1896 
1897   Due to the way the scanning is performed, looking for a space
1898   will give you the first word even if it comes before multiple
1899   spaces.
1900 
1901   Returns delimiter found.
1902 
1903   Pass in NULL for dest and -1 for size to just skip ahead.  Note that if
1904   nothing is found, dest will contain the whole string, minus leading and
1905   trailing whitespace.  You can scan for "" to conveniently grab the
1906   remainder of a string.
1907 **************************************************************************/
scanin(const char ** buf,char * delimiters,char * dest,int size)1908 char scanin(const char **buf, char *delimiters, char *dest, int size)
1909 {
1910   char *ptr, found = '?';
1911 
1912   if (*buf == NULL || strlen(*buf) == 0 || size == 0) {
1913     if (dest) {
1914       dest[0] = '\0';
1915     }
1916     *buf = NULL;
1917     return '\0';
1918   }
1919 
1920   if (dest) {
1921     strncpy(dest, *buf, size-1);
1922     dest[size-1] = '\0';
1923     remove_leading_trailing_spaces(dest);
1924     ptr = strpbrk(dest, delimiters);
1925   } else {
1926     /* Just skip ahead. */
1927     ptr = strpbrk(*buf, delimiters);
1928   }
1929   if (ptr != NULL) {
1930     found = *ptr;
1931     if (dest) {
1932       *ptr = '\0';
1933     }
1934     if (dest) {
1935       remove_leading_trailing_spaces(dest);
1936     }
1937     *buf = strpbrk(*buf, delimiters);
1938     if (*buf != NULL) {
1939       (*buf)++; /* skip delimiter */
1940     } else {
1941     }
1942   } else {
1943     *buf = NULL;
1944   }
1945 
1946   return found;
1947 }
1948 
1949 /**************************************************************************
1950   Convenience function to nicely format a time_t seconds value in to a
1951   string with hours, minutes, etc.
1952 **************************************************************************/
format_time_duration(time_t t,char * buf,int maxlen)1953 void format_time_duration(time_t t, char *buf, int maxlen)
1954 {
1955   int seconds, minutes, hours, days;
1956   bool space = FALSE;
1957 
1958   seconds = t % 60;
1959   minutes = (t / 60) % 60;
1960   hours = (t / (60 * 60)) % 24;
1961   days = t / (60 * 60 * 24);
1962 
1963   if (maxlen <= 0) {
1964     return;
1965   }
1966 
1967   buf[0] = '\0';
1968 
1969   if (days > 0) {
1970     cat_snprintf(buf, maxlen, "%d %s", days, PL_("day", "days", days));
1971     space = TRUE;
1972   }
1973   if (hours > 0) {
1974     cat_snprintf(buf, maxlen, "%s%d %s",
1975                  space ? " " : "", hours, PL_("hour", "hours", hours));
1976     space = TRUE;
1977   }
1978   if (minutes > 0) {
1979     cat_snprintf(buf, maxlen, "%s%d %s",
1980                  space ? " " : "",
1981                  minutes, PL_("minute", "minutes", minutes));
1982     space = TRUE;
1983   }
1984   if (seconds > 0) {
1985     cat_snprintf(buf, maxlen, "%s%d %s",
1986                  space ? " " : "",
1987                  seconds, PL_("second", "seconds", seconds));
1988   }
1989 }
1990 
1991 /************************************************************************
1992   Randomize the elements of an array using the Fisher-Yates shuffle.
1993 
1994   see: http://benpfaff.org/writings/clc/shuffle.html
1995 ************************************************************************/
array_shuffle(int * array,int n)1996 void array_shuffle(int *array, int n)
1997 {
1998   if (n > 1 && array != NULL) {
1999     int i, j, t;
2000     for (i = 0; i < n - 1; i++) {
2001       j = i + fc_rand(n - i);
2002       t = array[j];
2003       array[j] = array[i];
2004       array[i] = t;
2005     }
2006   }
2007 }
2008 
2009 /****************************************************************************
2010   Test an asterisk in the pattern against test. Returns TRUE if test fit the
2011   pattern. May be recursive, as it calls wildcard_fit_string() itself (if
2012   many asterisks).
2013 ****************************************************************************/
wildcard_asterisk_fit(const char * pattern,const char * test)2014 static bool wildcard_asterisk_fit(const char *pattern, const char *test)
2015 {
2016   char jump_to;
2017 
2018   /* Jump over the leading asterisks. */
2019   pattern++;
2020   while (TRUE) {
2021     switch (*pattern) {
2022     case '\0':
2023       /* It is a leading asterisk. */
2024       return TRUE;
2025     case '*':
2026       pattern++;
2027       continue;
2028     case '?':
2029       if ('\0' == *test) {
2030         return FALSE;
2031       }
2032       test++;
2033       pattern++;
2034       continue;
2035     }
2036 
2037     break;
2038   }
2039 
2040   if ('[' != *pattern) {
2041     if ('\\' == *pattern) {
2042       jump_to = *(pattern + 1);
2043     } else {
2044       jump_to = *pattern;
2045     }
2046   } else {
2047     jump_to = '\0';
2048   }
2049 
2050   while ('\0' != *test) {
2051     if ('\0' != jump_to) {
2052       /* Jump to next matching charather. */
2053       test = strchr(test, jump_to);
2054       if (NULL == test) {
2055         /* No match. */
2056         return FALSE;
2057       }
2058     }
2059 
2060     if (wildcard_fit_string(pattern, test)) {
2061       return TRUE;
2062     }
2063 
2064     (test)++;
2065   }
2066 
2067   return FALSE;
2068 }
2069 
2070 /****************************************************************************
2071   Test a range in the pattern against test. Returns TRUE if **test fit the
2072   first range in *pattern.
2073 ****************************************************************************/
wildcard_range_fit(const char ** pattern,const char ** test)2074 static bool wildcard_range_fit(const char **pattern, const char **test)
2075 {
2076   const char *start = (*pattern + 1);
2077   char testc;
2078   bool negation;
2079 
2080   if ('\0' == **test) {
2081     /* Need one character. */
2082     return FALSE;
2083   }
2084 
2085   /* Find the end of the pattern. */
2086   while (TRUE) {
2087     *pattern = strchr(*pattern, ']');
2088     if (NULL == *pattern) {
2089       /* Wildcard format error. */
2090       return FALSE;
2091     } else if (*(*pattern - 1) != '\\') {
2092       /* This is the end. */
2093       break;
2094     } else {
2095       /* Try again. */
2096       (*pattern)++;
2097     }
2098   }
2099 
2100   if ('!' == *start) {
2101     negation = TRUE;
2102     start++;
2103   } else {
2104     negation = FALSE;
2105   }
2106   testc = **test;
2107   (*test)++;
2108   (*pattern)++;
2109 
2110   for (; start < *pattern; start++) {
2111     if ('-' == *start || '!' == *start) {
2112       /* Wildcard format error. */
2113       return FALSE;
2114     } else if (start < *pattern - 2 && '-' == *(start + 1)) {
2115       /* Case range. */
2116       if (*start <= testc && testc <= *(start + 2)) {
2117         return !negation;
2118       }
2119       start += 2;
2120     } else if (*start == testc) {
2121       /* Single character. */
2122       return !negation;
2123     }
2124   }
2125 
2126   return negation;
2127 }
2128 
2129 /****************************************************************************
2130   Returns TRUE if test fit the pattern. The pattern can contain special
2131   characters:
2132   * '*': to specify a substitute for any zero or more characters.
2133   * '?': to specify a substitute for any one character.
2134   * '[...]': to specify a range of characters:
2135     * '!': at the begenning of the range means that the matching result
2136       will be inverted
2137     * 'A-Z': means any character between 'A' and 'Z'.
2138     * 'agr': means 'a', 'g' or 'r'.
2139 ****************************************************************************/
wildcard_fit_string(const char * pattern,const char * test)2140 bool wildcard_fit_string(const char *pattern, const char *test)
2141 {
2142   while (TRUE) {
2143     switch (*pattern) {
2144     case '\0':
2145       /* '\0' != test. */
2146       return '\0' == *test;
2147     case '*':
2148       return wildcard_asterisk_fit(pattern, test); /* Maybe recursive. */
2149     case '[':
2150       if (!wildcard_range_fit(&pattern, &test)) {
2151         return FALSE;
2152       }
2153       continue;
2154     case '?':
2155       if ('\0' == *test) {
2156         return FALSE;
2157       }
2158       break;
2159     case '\\':
2160       pattern++;
2161       fc__fallthrough; /* No break */
2162     default:
2163       if (*pattern != *test) {
2164         return FALSE;
2165       }
2166       break;
2167     }
2168     pattern++;
2169     test++;
2170   }
2171 
2172   return FALSE;
2173 }
2174 
2175 /****************************************************************************
2176   Print a string with a custom format. sequences is a pointer to an array of
2177   sequences, probably defined with CF_*_SEQ(). sequences_num is the number of
2178   the sequences, or -1 in the case the array is terminated with CF_END.
2179 
2180   Example:
2181   static const struct cf_sequence sequences[] = {
2182     CF_INT_SEQ('y', 2010)
2183   };
2184   char buf[256];
2185 
2186   fc_vsnprintcf(buf, sizeof(buf), "%y %+06y", sequences, 1);
2187   // This will print "2010 +02010" into buf.
2188 ****************************************************************************/
fc_vsnprintcf(char * buf,size_t buf_len,const char * format,const struct cf_sequence * sequences,size_t sequences_num)2189 int fc_vsnprintcf(char *buf, size_t buf_len, const char *format,
2190                   const struct cf_sequence *sequences, size_t sequences_num)
2191 {
2192   const struct cf_sequence *pseq;
2193   char cformat[32];
2194   const char *f = format;
2195   char *const max = buf + buf_len - 1;
2196   char *b = buf, *c;
2197   const char *const cmax = cformat + sizeof(cformat) - 2;
2198   int i, j;
2199 
2200   if ((size_t) -1 == sequences_num) {
2201     /* Find the number of sequences. */
2202     sequences_num = 0;
2203     for (pseq = sequences; CF_LAST != pseq->type; pseq++) {
2204       sequences_num++;
2205     }
2206   }
2207 
2208   while ('\0' != *f) {
2209     if ('%' == *f) {
2210       /* Sequence. */
2211 
2212       f++;
2213       if ('%' == *f) {
2214         /* Double '%'. */
2215         *b++ = '%';
2216         f++;
2217         continue;
2218       }
2219 
2220       /* Make format. */
2221       c = cformat;
2222       *c++ = '%';
2223       for (; !fc_isalpha(*f) && '\0' != *f && '%' != *f && cmax > c; f++) {
2224         *c++ = *f;
2225       }
2226 
2227       if (!fc_isalpha(*f)) {
2228         /* Beginning of a new sequence, end of the format, or too long
2229          * sequence. */
2230         *c = '\0';
2231         j = fc_snprintf(b, max - b + 1, "%s", cformat);
2232         if (-1 == j) {
2233           return -1;
2234         }
2235         b += j;
2236         continue;
2237       }
2238 
2239       for (i = 0, pseq = sequences; i < sequences_num; i++, pseq++) {
2240         if (pseq->letter == *f) {
2241           j = -2;
2242           switch (pseq->type) {
2243           case CF_BOOLEAN:
2244             *c++ = 's';
2245             *c = '\0';
2246             j = fc_snprintf(b, max - b + 1, cformat,
2247                             pseq->bool_value ? "TRUE" : "FALSE");
2248             break;
2249           case CF_TRANS_BOOLEAN:
2250             *c++ = 's';
2251             *c = '\0';
2252             j = fc_snprintf(b, max - b + 1, cformat,
2253                             pseq->bool_value ? _("TRUE") : _("FALSE"));
2254             break;
2255           case CF_CHARACTER:
2256             *c++ = 'c';
2257             *c = '\0';
2258             j = fc_snprintf(b, max - b + 1, cformat, pseq->char_value);
2259             break;
2260           case CF_INTEGER:
2261             *c++ = 'd';
2262             *c = '\0';
2263             j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2264             break;
2265           case CF_HEXA:
2266             *c++ = 'x';
2267             *c = '\0';
2268             j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2269             break;
2270           case CF_FLOAT:
2271             *c++ = 'f';
2272             *c = '\0';
2273             j = fc_snprintf(b, max - b + 1, cformat, pseq->float_value);
2274             break;
2275           case CF_POINTER:
2276             *c++ = 'p';
2277             *c = '\0';
2278             j = fc_snprintf(b, max - b + 1, cformat, pseq->ptr_value);
2279             break;
2280           case CF_STRING:
2281             *c++ = 's';
2282             *c = '\0';
2283             j = fc_snprintf(b, max - b + 1, cformat, pseq->str_value);
2284             break;
2285           case CF_LAST:
2286             break;
2287           };
2288           if (-2 == j) {
2289             log_error("Error: unsupported sequence type: %d.", pseq->type);
2290             break;
2291           }
2292           if (-1 == j) {
2293             /* Full! */
2294             return -1;
2295           }
2296           f++;
2297           b += j;
2298           break;
2299         }
2300       }
2301       if (i >= sequences_num) {
2302         /* Format not supported. */
2303         *c = '\0';
2304         j = fc_snprintf(b, max - b + 1, "%s%c", cformat, *f);
2305         if (-1 == j) {
2306           return -1;
2307         }
2308         f++;
2309         b += j;
2310       }
2311     } else {
2312       /* Not a sequence. */
2313       *b++ = *f++;
2314     }
2315     if (max <= b) {
2316       /* Too long. */
2317       *max = '\0';
2318       return -1;
2319     }
2320   }
2321   *b = '\0';
2322   return b - buf;
2323 }
2324 
2325 /****************************************************************************
2326   Print a string with a custom format. The additional arguments are a suite
2327   of cf_*_seq() finished by cf_end(). This return the number of printed
2328   characters (excluding the last '\0') or -1 if the buffer is full.
2329 
2330   Example:
2331   char buf[256];
2332 
2333   fc_snprintcf(buf, sizeof(buf), "%y %+06y",
2334                cf_int_seq('y', 2010), cf_end());
2335   // This will print "2010 +02010" into buf.
2336 ****************************************************************************/
fc_snprintcf(char * buf,size_t buf_len,const char * format,...)2337 int fc_snprintcf(char *buf, size_t buf_len, const char *format, ...)
2338 {
2339   struct cf_sequence sequences[16];
2340   size_t sequences_num = 0;
2341   va_list args;
2342 
2343   /* Collect sequence array. */
2344   va_start(args, format);
2345   do {
2346     sequences[sequences_num] = va_arg(args, struct cf_sequence);
2347     if (CF_LAST == sequences[sequences_num].type) {
2348       break;
2349     } else {
2350       sequences_num++;
2351     }
2352   } while (ARRAY_SIZE(sequences) > sequences_num);
2353 
2354   if (ARRAY_SIZE(sequences) <= sequences_num
2355       && CF_LAST != va_arg(args, struct cf_sequence).type) {
2356     log_error("Too many custom sequences. Maybe did you forget cf_end() "
2357               "at the end of the arguments?");
2358     buf[0] = '\0';
2359     va_end(args);
2360     return -1;
2361   }
2362   va_end(args);
2363 
2364   return fc_vsnprintcf(buf, buf_len, format, sequences, sequences_num);
2365 }
2366 
2367 /****************************************************************************
2368   Extract the sequences of a format. Returns the number of extracted
2369   escapes.
2370 ****************************************************************************/
extract_escapes(const char * format,char * escapes,size_t max_escapes)2371 static size_t extract_escapes(const char *format, char *escapes,
2372                               size_t max_escapes)
2373 {
2374   static const char format_escapes[] = {
2375     '*', 'd', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f',
2376     'F', 'g', 'G', 'a', 'A', 'c', 's', 'p', 'n', '\0'
2377   };
2378   bool reordered = FALSE;
2379   size_t num = 0;
2380   int idx = 0;
2381 
2382   memset(escapes, 0, max_escapes);
2383   format = strchr(format, '%');
2384   while (NULL != format) {
2385     format++;
2386     if ('%' == *format) {
2387       /* Double, not a sequence. */
2388       continue;
2389     } else if (fc_isdigit(*format)) {
2390       const char *start = format;
2391 
2392       do {
2393         format++;
2394       } while (fc_isdigit(*format));
2395       if ('$' == *format) {
2396         /* Strings are reordered. */
2397         sscanf(start, "%d", &idx);
2398         reordered = TRUE;
2399       }
2400     }
2401 
2402     while ('\0' != *format
2403            && NULL == strchr(format_escapes, *format)) {
2404       format++;
2405     }
2406     escapes[idx] = *format;
2407 
2408     /* Increase the read count. */
2409     if (reordered) {
2410       if (idx > num) {
2411         num = idx;
2412       }
2413     } else {
2414       idx++;
2415       num++;
2416     }
2417 
2418     if ('*' != *format) {
2419       format = strchr(format, '%');
2420     } /* else we didn't have found the real sequence. */
2421   }
2422   return num;
2423 }
2424 
2425 /****************************************************************************
2426   Returns TRUE iff both formats are compatible (if 'format1' can be used
2427   instead 'format2' and reciprocally).
2428 ****************************************************************************/
formats_match(const char * format1,const char * format2)2429 bool formats_match(const char *format1, const char *format2)
2430 {
2431   char format1_escapes[256], format2_escapes[256];
2432   size_t format1_escapes_num = extract_escapes(format1, format1_escapes,
2433                                                sizeof(format1_escapes));
2434   size_t format2_escapes_num = extract_escapes(format2, format2_escapes,
2435                                                sizeof(format2_escapes));
2436 
2437   return (format1_escapes_num == format2_escapes_num
2438           && 0 == memcmp(format1_escapes, format2_escapes,
2439                          format1_escapes_num));
2440 }
2441