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)
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 ***********************************************************************/
15 #include <fc_config.h>
18 #include "fc_prehdrs.h"
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>
32 #include <sys/types.h>
46 #include <lmcons.h> /* UNLEN */
50 #endif /* HAVE_DIRECT_H */
51 #endif /* WIN32_NATIVE */
59 #include "string_vector.h"
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
69 #ifndef DEFAULT_SAVE_PATH
70 #define DEFAULT_SAVE_PATH "." PATH_SEPARATOR \
71 "~" DIR_SEPARATOR ".freeciv" DIR_SEPARATOR "saves"
73 #ifndef DEFAULT_SCENARIO_PATH
74 #define DEFAULT_SCENARIO_PATH \
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 */
83 #define FREECIV_PATH "FREECIV_PATH"
85 #ifndef FREECIV_DATA_PATH
86 #define FREECIV_DATA_PATH "FREECIV_DATA_PATH"
88 #ifndef FREECIV_SAVE_PATH
89 #define FREECIV_SAVE_PATH "FREECIV_SAVE_PATH"
91 #ifndef FREECIV_SCENARIO_PATH
92 #define FREECIV_SCENARIO_PATH "FREECIV_SCENARIO_PATH"
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
;
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-_";
105 static struct strvec
*data_dir_names
= NULL
;
106 static struct strvec
*save_dir_names
= NULL
;
107 static struct strvec
*scenario_dir_names
= NULL
;
109 static char *mc_group
= NULL
;
110 static char *home_dir
= NULL
;
112 static bool depr_freeciv_path_warned
= FALSE
;
114 static struct astring realfile
= ASTRING_INIT
;
116 static int compare_file_mtime_ptrs(const struct fileinfo
*const *ppa
,
117 const struct fileinfo
*const *ppb
);
120 /***************************************************************************
121 An AND function for fc_tristate.
122 ***************************************************************************/
123 enum fc_tristate
fc_tristate_and(enum fc_tristate one
, enum fc_tristate two
)
125 if (TRI_NO
== one
|| TRI_NO
== two
) {
129 if (TRI_MAYBE
== one
|| TRI_MAYBE
== two
) {
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
143 FIXME: This is only used in the Xaw client, and so probably does
144 not belong in common.
145 ***************************************************************/
146 char *create_centered_string(const char *s
)
148 /* Points to the part of the source that we're looking at. */
151 /* Points to the beginning of the line in the source that we're
155 /* Points to the result. */
158 /* Points to the part of the result that we're filling in right
168 for(cp
=s
; *cp
!= '\0'; cp
++) {
181 r
=rn
=fc_malloc(nlines
*(maxlen
+1));
184 for(cp0
=cp
=s
; *cp
!= '\0'; cp
++) {
188 for(i
=0; i
<(maxlen
-curlen
)/2; i
++)
190 memcpy(rn
, cp0
, curlen
);
197 for(i
=0; i
<(maxlen
-curlen
)/2; i
++)
204 /***************************************************************
205 Returns a statically allocated string containing a nicely-formatted
206 version of the given number according to the user's locale. (Only
207 works for numbers >= zero.) The number is given in scientific notation
208 as mantissa * 10^exponent.
209 ***************************************************************/
210 const char *big_int_to_text(unsigned int mantissa
, unsigned int exponent
)
212 static char buf
[64]; /* Note that we'll be filling this in right to left. */
213 char *grp
= grouping
;
215 unsigned int cnt
= 0;
219 /* We have to convert the encoding here (rather than when the locale
220 * is initialized) because it can't be done before the charsets are
222 local_to_internal_string_buffer(grouping_sep
, sep
, sizeof(sep
));
223 seplen
= strlen(sep
);
225 #if 0 /* Not needed while the values are unsigned. */
226 fc_assert_ret_val(0 <= mantissa
, NULL
);
227 fc_assert_ret_val(0 <= exponent
, NULL
);
234 /* We fill the string in backwards, starting from the right. So the first
235 * thing we do is terminate it. */
236 ptr
= &buf
[sizeof(buf
)];
239 while (mantissa
!= 0 && exponent
>= 0) {
242 if (ptr
<= buf
+ seplen
) {
243 /* Avoid a buffer overflow. */
244 fc_assert_ret_val(ptr
> buf
+ seplen
, NULL
);
248 /* Add on another character. */
256 *(--ptr
) = '0' + dig
;
259 if (mantissa
!= 0 && cnt
== *grp
) {
260 /* Reached count of digits in group: insert separator and reset count. */
262 if (*grp
== CHAR_MAX
) {
263 /* This test is unlikely to be necessary since we would need at
264 least 421-bit ints to break the 127 digit barrier, but why not. */
268 fc_assert_ret_val(ptr
>= buf
, NULL
);
269 memcpy(ptr
, sep
, seplen
);
270 if (*(grp
+ 1) != 0) {
271 /* Zero means to repeat the present group-size indefinitely. */
281 /****************************************************************************
282 Return a prettily formatted string containing the given number.
283 ****************************************************************************/
284 const char *int_to_text(unsigned int number
)
286 return big_int_to_text(number
, 0);
289 /****************************************************************************
290 Check whether or not the given char is a valid ascii character. The
291 character can be in any charset so long as it is a superset of ascii.
292 ****************************************************************************/
293 static bool is_ascii(char ch
)
295 /* this works with both signed and unsigned char's. */
296 return ch
>= ' ' && ch
<= '~';
299 /****************************************************************************
300 Check if the name is safe security-wise. This is intended to be used to
301 make sure an untrusted filename is safe to be used.
302 ****************************************************************************/
303 bool is_safe_filename(const char *name
)
307 /* must not be NULL or empty */
308 if (!name
|| *name
== '\0') {
312 for (; '\0' != name
[i
]; i
++) {
313 if ('.' != name
[i
] && NULL
== strchr(base64url
, name
[i
])) {
318 /* we don't allow the filename to ascend directories */
319 if (strstr(name
, PARENT_DIR_OPERATOR
)) {
323 /* Otherwise, it is okay... */
327 /***************************************************************
328 This is used in sundry places to make sure that names of cities,
329 players etc. do not contain yucky characters of various sorts.
330 Returns TRUE iff the name is acceptable.
331 FIXME: Not internationalised.
332 ***************************************************************/
333 bool is_ascii_name(const char *name
)
335 const char illegal_chars
[] = {'|', '%', '"', ',', '*', '<', '>', '\0'};
338 /* must not be NULL or empty */
339 if (!name
|| *name
== '\0') {
343 /* must begin and end with some non-space character */
344 if ((*name
== ' ') || (*(strchr(name
, '\0') - 1) == ' ')) {
348 /* must be composed entirely of printable ascii characters,
349 * and no illegal characters which can break ranking scripts. */
350 for (i
= 0; name
[i
]; i
++) {
351 if (!is_ascii(name
[i
])) {
354 for (j
= 0; illegal_chars
[j
]; j
++) {
355 if (name
[i
] == illegal_chars
[j
]) {
361 /* otherwise, it's okay... */
365 /*************************************************************************
366 Check for valid base64url.
367 *************************************************************************/
368 bool is_base64url(const char *s
)
372 /* must not be NULL or empty */
373 if (NULL
== s
|| '\0' == *s
) {
377 for (; '\0' != s
[i
]; i
++) {
378 if (NULL
== strchr(base64url
, s
[i
])) {
385 /*************************************************************************
386 generate a random string meeting criteria such as is_ascii_name(),
387 is_base64url(), and is_safe_filename().
388 *************************************************************************/
389 void randomize_base64url_string(char *s
, size_t n
)
393 /* must not be NULL or too short */
394 if (NULL
== s
|| 1 > n
) {
398 for (; i
< (n
- 1); i
++) {
399 s
[i
] = base64url
[fc_rand(sizeof(base64url
) - 1)];
404 /**************************************************************************
405 Compares two strings, in the collating order of the current locale,
406 given pointers to the two strings (i.e., given "char *"s).
407 Case-sensitive. Designed to be called from qsort().
408 **************************************************************************/
409 int compare_strings(const void *first
, const void *second
)
411 return fc_strcoll((const char *) first
, (const char *) second
);
414 /**************************************************************************
415 Compares two strings, in the collating order of the current locale,
416 given pointers to the two string pointers (i.e., given "char **"s).
417 Case-sensitive. Designed to be called from qsort().
418 **************************************************************************/
419 int compare_strings_ptrs(const void *first
, const void *second
)
421 return fc_strcoll(*((const char **) first
), *((const char **) second
));
424 /**************************************************************************
425 Compares two strings, in the collating order of the current locale,
426 given pointers to the two string pointers. Case-sensitive.
427 Designed to be called from strvec_sort().
428 **************************************************************************/
429 int compare_strings_strvec(const char *const *first
,
430 const char *const *second
)
432 return fc_strcoll(*first
, *second
);
435 /***************************************************************************
436 Returns 's' incremented to first non-space character.
437 ***************************************************************************/
438 char *skip_leading_spaces(char *s
)
440 fc_assert_ret_val(NULL
!= s
, NULL
);
441 while(*s
!= '\0' && fc_isspace(*s
)) {
447 /***************************************************************************
448 Removes leading spaces in string pointed to by 's'.
449 Note 's' must point to writeable memory!
450 ***************************************************************************/
451 void remove_leading_spaces(char *s
)
455 fc_assert_ret(NULL
!= s
);
456 t
= skip_leading_spaces(s
);
465 /***************************************************************************
466 Terminates string pointed to by 's' to remove traling spaces;
467 Note 's' must point to writeable memory!
468 ***************************************************************************/
469 void remove_trailing_spaces(char *s
)
474 fc_assert_ret(NULL
!= s
);
478 while(fc_isspace(*t
)) {
488 /***************************************************************************
489 Removes leading and trailing spaces in string pointed to by 's'.
490 Note 's' must point to writeable memory!
491 ***************************************************************************/
492 void remove_leading_trailing_spaces(char *s
)
494 remove_leading_spaces(s
);
495 remove_trailing_spaces(s
);
498 /***************************************************************************
499 As remove_trailing_spaces(), for specified char.
500 ***************************************************************************/
501 static void remove_trailing_char(char *s
, char trailing
)
505 fc_assert_ret(NULL
!= s
);
506 t
= s
+ strlen(s
) -1;
507 while(t
>=s
&& (*t
) == trailing
) {
513 /***************************************************************************
514 Returns pointer to '\0' at end of string 'str', and decrements
515 *nleft by the length of 'str'. This is intended to be useful to
516 allow strcat-ing without traversing the whole string each time,
517 while still keeping track of the buffer length.
523 fc_snprintf(p, n, "foo%p", p);
524 p = end_of_strn(p, &n);
525 fc_strlcpy(p, "yyy", n);
526 ***************************************************************************/
527 char *end_of_strn(char *str
, int *nleft
)
529 int len
= strlen(str
);
531 fc_assert_ret_val(0 < (*nleft
), NULL
); /* space for the terminating nul */
535 /**********************************************************************
536 Check the length of the given string. If the string is too long,
537 log errmsg, which should be a string in printf-format taking up to
538 two arguments: the string and the length.
539 **********************************************************************/
540 bool check_strlen(const char *str
, size_t len
, const char *errmsg
)
542 fc_assert_ret_val_msg(strlen(str
) < len
, TRUE
, errmsg
, str
, len
);
546 /**********************************************************************
547 Call check_strlen() on str and then strlcpy() it into buffer.
548 **********************************************************************/
549 size_t loud_strlcpy(char *buffer
, const char *str
, size_t len
,
552 (void) check_strlen(str
, len
, errmsg
);
553 return fc_strlcpy(buffer
, str
, len
);
556 /****************************************************************************
557 Convert 'str' to it's int reprentation if possible. 'pint' can be NULL,
558 then it will only test 'str' only contains an integer number.
559 ****************************************************************************/
560 bool str_to_int(const char *str
, int *pint
)
564 fc_assert_ret_val(NULL
!= str
, FALSE
);
566 while (fc_isspace(*str
)) {
567 /* Skip leading spaces. */
572 if ('-' == *str
|| '+' == *str
) {
576 while (fc_isdigit(*str
)) {
581 while (fc_isspace(*str
)) {
582 /* Ignore trailing spaces. */
586 return ('\0' == *str
&& (NULL
== pint
|| 1 == sscanf(start
, "%d", pint
)));
589 /****************************************************************************
590 Convert 'str' to it's float reprentation if possible. 'pfloat' can be NULL,
591 then it will only test 'str' only contains a floating point number.
592 ****************************************************************************/
593 bool str_to_float(const char *str
, float *pfloat
)
598 fc_assert_ret_val(NULL
!= str
, FALSE
);
600 while (fc_isspace(*str
)) {
601 /* Skip leading spaces. */
607 if ('-' == *str
|| '+' == *str
) {
611 while (fc_isdigit(*str
)) {
620 while (fc_isdigit(*str
)) {
628 while (fc_isspace(*str
)) {
629 /* Ignore trailing spaces. */
633 return ('\0' == *str
&& dot
634 && (NULL
== pfloat
|| 1 == sscanf(start
, "%f", pfloat
)));
637 /***************************************************************************
638 Returns string which gives users home dir, as specified by $HOME.
639 Gets value once, and then caches result.
640 If $HOME is not set, give a log message and returns NULL.
641 Note the caller should not mess with the returned string.
642 ***************************************************************************/
643 char *user_home_dir(void)
649 if (home_dir
== NULL
) {
650 #ifdef FREECIV_MSWINDOWS
652 /* some documentation at:
653 * http://justcheckingonall.wordpress.com/2008/05/16/find-shell-folders-win32/
654 * http://archives.seul.org/or/cvs/Oct-2004/msg00082.html */
659 if (SUCCEEDED(SHGetSpecialFolderLocation(NULL
, CSIDL_APPDATA
, &pidl
))) {
660 char *home_dir_in_local_encoding
= fc_malloc(PATH_MAX
);
662 if (SUCCEEDED(SHGetPathFromIDList(pidl
, home_dir_in_local_encoding
))) {
663 /* convert to internal encoding */
664 home_dir
= local_to_internal_string_malloc(home_dir_in_local_encoding
);
665 free(home_dir_in_local_encoding
);
667 #ifdef DIR_SEPARATOR_IS_DEFAULT
668 /* replace backslashes with forward slashes */
672 for (c
= home_dir
; *c
!= 0; c
++) {
674 *c
= DIR_SEPARATOR_CHAR
;
678 #endif /* DIR_SEPARATOR_IS_DEFAULT */
680 free(home_dir_in_local_encoding
);
682 log_error("Could not find home directory "
683 "(SHGetPathFromIDList() failed).");
686 SHGetMalloc(&pMalloc
);
688 pMalloc
->lpVtbl
->Free(pMalloc
, pidl
);
689 pMalloc
->lpVtbl
->Release(pMalloc
);
693 log_error("Could not find home directory "
694 "(SHGetSpecialFolderLocation() failed).");
697 if (home_dir
== NULL
)
698 #endif /* FREECIV_MSWINDOWS */
700 char *env
= getenv("HOME");
703 home_dir
= fc_strdup(env
);
704 log_verbose("HOME is %s", home_dir
);
706 log_error("Could not find home directory (HOME is not set).");
716 /***************************************************************************
717 Free user home directory information
718 ***************************************************************************/
719 void free_user_home_dir(void)
721 if (home_dir
!= NULL
) {
727 /***************************************************************************
728 Returns string which gives user's username, as specified by $USER or
729 as given in password file for this user's uid, or a made up name if
730 we can't get either of the above.
731 Gets value once, and then caches result.
732 Note the caller should not mess with returned string.
733 ***************************************************************************/
734 char *user_username(char *buf
, size_t bufsz
)
736 /* This function uses a number of different methods to try to find a
737 * username. This username then has to be truncated to bufsz
738 * characters (including terminator) and checked for sanity. Note that
739 * truncating a sane name can leave you with an insane name under some
742 /* If the environment variable $USER is present and sane, use it. */
744 char *env
= getenv("USER");
747 fc_strlcpy(buf
, env
, bufsz
);
748 if (is_ascii_name(buf
)) {
749 log_verbose("USER username is %s", buf
);
756 /* Otherwise if getpwuid() is available we can use it to find the true
759 struct passwd
*pwent
= getpwuid(getuid());
762 fc_strlcpy(buf
, pwent
->pw_name
, bufsz
);
763 if (is_ascii_name(buf
)) {
764 log_verbose("getpwuid username is %s", buf
);
769 #endif /* HAVE_GETPWUID */
772 /* On win32 the GetUserName function will give us the login name. */
774 char name
[UNLEN
+ 1];
775 DWORD length
= sizeof(name
);
777 if (GetUserName(name
, &length
)) {
778 fc_strlcpy(buf
, name
, bufsz
);
779 if (is_ascii_name(buf
)) {
780 log_verbose("GetUserName username is %s", buf
);
785 #endif /* WIN32_NATIVE */
788 fc_strlcpy(buf
, "name", bufsz
);
790 fc_snprintf(buf
, bufsz
, "name%d", (int) getuid());
792 log_verbose("fake username is %s", buf
);
793 fc_assert(is_ascii_name(buf
));
797 /***************************************************************************
798 Returns a list of directory paths, in the order in which they should
799 be searched. Base function for get_data_dirs(), get_save_dirs(),
801 ***************************************************************************/
802 static struct strvec
*base_get_dirs(const char *dir_list
)
804 struct strvec
*dirs
= strvec_new();
807 path
= fc_strdup(dir_list
); /* something we can strtok */
808 tok
= strtok(path
, PATH_SEPARATOR
);
810 int i
; /* strlen(tok), or -1 as flag */
812 tok
= skip_leading_spaces(tok
);
813 remove_trailing_spaces(tok
);
814 if (strcmp(tok
, DIR_SEPARATOR
) != 0) {
815 remove_trailing_char(tok
, DIR_SEPARATOR_CHAR
);
820 if (i
> 1 && tok
[1] != DIR_SEPARATOR_CHAR
) {
821 log_error("For \"%s\" in path cannot expand '~'"
822 " except as '~" DIR_SEPARATOR
"'; ignoring", tok
);
823 i
= 0; /* skip this one */
825 char *home
= user_home_dir();
828 log_verbose("No HOME, skipping path component %s", tok
);
831 int len
= strlen(home
) + i
; /* +1 -1 */
832 char *tmp
= fc_malloc(len
);
834 fc_snprintf(tmp
, len
, "%s%s", home
, tok
+ 1);
836 i
= -1; /* flag to free tok below */
842 /* We could check whether the directory exists and
843 * is readable etc? Don't currently. */
844 strvec_append(dirs
, tok
);
851 tok
= strtok(NULL
, PATH_SEPARATOR
);
858 /***************************************************************************
859 Free data dir name vectors.
860 ***************************************************************************/
861 void free_data_dir_names(void)
863 if (data_dir_names
!= NULL
) {
864 strvec_destroy(data_dir_names
);
865 data_dir_names
= NULL
;
867 if (save_dir_names
!= NULL
) {
868 strvec_destroy(save_dir_names
);
869 save_dir_names
= NULL
;
871 if (scenario_dir_names
!= NULL
) {
872 strvec_destroy(scenario_dir_names
);
873 scenario_dir_names
= NULL
;
877 /***************************************************************************
878 Returns a list of data directory paths, in the order in which they should
879 be searched. These paths are specified internally or may be set as the
880 environment variable $FREECIV_DATA PATH (a separated list of directories,
881 where the separator itself is specified internally, platform-dependent).
882 FREECIV_PATH may also be consulted for backward compatibility.
883 '~' at the start of a component (provided followed by '/' or '\0') is
886 The returned pointer is static and shouldn't be modified, nor destroyed
888 ***************************************************************************/
889 const struct strvec
*get_data_dirs(void)
891 /* The first time this function is called it will search and
892 * allocate the directory listing. Subsequently we will already
893 * know the list and can just return it. */
894 if (NULL
== data_dir_names
) {
897 if ((path
= getenv(FREECIV_DATA_PATH
)) && '\0' == path
[0]) {
898 /* TRANS: <FREECIV_DATA_PATH> configuration error */
899 log_error(_("\"%s\" is set but empty; trying \"%s\" instead."),
900 FREECIV_DATA_PATH
, FREECIV_PATH
);
903 if (NULL
== path
&& (path
= getenv(FREECIV_PATH
))) {
904 if (!depr_freeciv_path_warned
) {
905 log_error(_("FREECIV_PATH is deprecated, and won't work in future versions."));
906 depr_freeciv_path_warned
= TRUE
;
908 if ('\0' == path
[0]) {
909 /* TRANS: <FREECIV_PATH> configuration error */
910 log_error(_("\"%s\" is set but empty; using default \"%s\" "
911 "data directories instead."),
912 FREECIV_PATH
, DEFAULT_DATA_PATH
);
916 data_dir_names
= base_get_dirs(NULL
!= path
? path
: DEFAULT_DATA_PATH
);
917 strvec_remove_duplicate(data_dir_names
, strcmp
); /* Don't set a path both. */
918 strvec_iterate(data_dir_names
, dirname
) {
919 log_verbose("Data path component: %s", dirname
);
920 } strvec_iterate_end
;
923 return data_dir_names
;
926 /***************************************************************************
927 Returns a list of save 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_SAVE_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
935 The returned pointer is static and shouldn't be modified, nor destroyed
937 ***************************************************************************/
938 const struct strvec
*get_save_dirs(void)
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
== save_dir_names
) {
945 bool from_freeciv_path
= FALSE
;
947 if ((path
= getenv(FREECIV_SAVE_PATH
)) && '\0' == path
[0]) {
948 /* TRANS: <FREECIV_SAVE_PATH> configuration error */
949 log_error(_("\"%s\" is set but empty; trying \"%s\" instead."),
950 FREECIV_SAVE_PATH
, FREECIV_PATH
);
953 if (NULL
== path
&& (path
= getenv(FREECIV_PATH
))) {
954 if (!depr_freeciv_path_warned
) {
955 log_error(_("FREECIV_PATH is deprecated, and won't work in future versions."));
956 depr_freeciv_path_warned
= TRUE
;
958 if ('\0' == path
[0]) {
959 /* TRANS: <FREECIV_PATH> configuration error */
960 log_error(_("\"%s\" is set but empty; using default \"%s\" "
961 "save directories instead."),
962 FREECIV_PATH
, DEFAULT_SAVE_PATH
);
965 from_freeciv_path
= TRUE
;
968 save_dir_names
= base_get_dirs(NULL
!= path
? path
: DEFAULT_SAVE_PATH
);
969 if (from_freeciv_path
) {
970 /* Then also append a "/saves" suffix to every directory. */
974 for (i
= 0; i
< strvec_size(save_dir_names
); i
++) {
975 path
= strvec_get(save_dir_names
, i
);
976 fc_snprintf(buf
, sizeof(buf
), "%s/saves", path
);
977 strvec_insert(save_dir_names
, ++i
, buf
);
980 strvec_remove_duplicate(save_dir_names
, strcmp
); /* Don't set a path both. */
981 strvec_iterate(save_dir_names
, dirname
) {
982 log_verbose("Save path component: %s", dirname
);
983 } strvec_iterate_end
;
986 return save_dir_names
;
989 /***************************************************************************
990 Returns a list of scenario directory paths, in the order in which they
991 should be searched. These paths are specified internally or may be set
992 as the environment variable $FREECIV_SCENARIO_PATH (a separated list of
993 directories, where the separator itself is specified internally,
994 platform-dependent). FREECIV_PATH may also be consulted for backward
995 compatibility. '~' at the start of a component (provided followed
996 by '/' or '\0') is expanded as $HOME.
998 The returned pointer is static and shouldn't be modified, nor destroyed
1000 ***************************************************************************/
1001 const struct strvec
*get_scenario_dirs(void)
1003 /* The first time this function is called it will search and
1004 * allocate the directory listing. Subsequently we will already
1005 * know the list and can just return it. */
1006 if (NULL
== scenario_dir_names
) {
1008 bool from_freeciv_path
= FALSE
;
1010 if ((path
= getenv(FREECIV_SCENARIO_PATH
)) && '\0' == path
[0]) {
1011 /* TRANS: <FREECIV_SCENARIO_PATH> configuration error */
1012 log_error(_("\"%s\" is set but empty; trying \"%s\" instead."),
1013 FREECIV_SCENARIO_PATH
, FREECIV_PATH
);
1016 if (NULL
== path
&& (path
= getenv(FREECIV_PATH
))) {
1017 if (!depr_freeciv_path_warned
) {
1018 log_error(_("FREECIV_PATH is deprecated, and won't work in future versions."));
1019 depr_freeciv_path_warned
= TRUE
;
1021 if ('\0' == path
[0]) {
1022 /* TRANS: <FREECIV_PATH> configuration error */
1023 log_error( _("\"%s\" is set but empty; using default \"%s\" "
1024 "scenario directories instead."),
1025 FREECIV_PATH
, DEFAULT_SCENARIO_PATH
);
1028 from_freeciv_path
= TRUE
;
1031 scenario_dir_names
= base_get_dirs(NULL
!= path
? path
: DEFAULT_SCENARIO_PATH
);
1032 if (from_freeciv_path
) {
1033 /* Then also append subdirs every directory. */
1034 const char *subdirs
[] = {
1035 "scenarios", "scenario", NULL
1038 const char **subdir
;
1041 for (i
= 0; i
< strvec_size(scenario_dir_names
); i
++) {
1042 path
= strvec_get(scenario_dir_names
, i
);
1043 for (subdir
= subdirs
; NULL
!= *subdir
; subdir
++) {
1044 fc_snprintf(buf
, sizeof(buf
), "%s/%s", path
, *subdir
);
1045 strvec_insert(scenario_dir_names
, ++i
, buf
);
1049 strvec_remove_duplicate(scenario_dir_names
, strcmp
); /* Don't set a path both. */
1050 strvec_iterate(scenario_dir_names
, dirname
) {
1051 log_verbose("Scenario path component: %s", dirname
);
1052 } strvec_iterate_end
;
1055 return scenario_dir_names
;
1058 /***************************************************************************
1059 Returns a string vector storing the filenames in the data directories
1060 matching the given suffix.
1062 The list is allocated when the function is called; it should either
1063 be stored permanently or destroyed (with strvec_destroy()).
1065 The suffixes are removed from the filenames before the list is
1067 ***************************************************************************/
1068 struct strvec
*fileinfolist(const struct strvec
*dirs
, const char *suffix
)
1070 struct strvec
*files
= strvec_new();
1071 size_t suffix_len
= strlen(suffix
);
1073 fc_assert_ret_val(!strchr(suffix
, DIR_SEPARATOR_CHAR
), NULL
);
1079 /* First assemble a full list of names. */
1080 strvec_iterate(dirs
, dirname
) {
1082 struct dirent
*entry
;
1084 /* Open the directory for reading. */
1085 dir
= fc_opendir(dirname
);
1087 if (errno
== ENOENT
) {
1088 log_verbose("Skipping non-existing data directory %s.",
1091 /* TRANS: "...: <externally translated error string>."*/
1092 log_error(_("Could not read data directory %s: %s."),
1093 dirname
, fc_strerror(fc_get_errno()));
1098 /* Scan all entries in the directory. */
1099 while ((entry
= readdir(dir
))) {
1100 size_t len
= strlen(entry
->d_name
);
1102 /* Make sure the file name matches. */
1103 if (len
> suffix_len
1104 && strcmp(suffix
, entry
->d_name
+ len
- suffix_len
) == 0) {
1105 /* Strdup the entry so we can safely write to it. */
1106 char *match
= fc_strdup(entry
->d_name
);
1108 /* Clip the suffix. */
1109 match
[len
- suffix_len
] = '\0';
1111 strvec_append(files
, match
);
1117 } strvec_iterate_end
;
1119 /* Sort the list and remove duplications. */
1120 strvec_remove_duplicate(files
, strcmp
);
1121 strvec_sort(files
, compare_strings_strvec
);
1126 /***************************************************************************
1127 Returns a filename to access the specified file from a
1128 directory by searching all specified directories for the file.
1130 If the specified 'filename' is NULL, the returned string contains
1131 the effective path. (But this should probably only be used for
1134 Returns NULL if the specified filename cannot be found in any of the
1135 data directories. (A file is considered "found" if it can be
1136 read-opened.) The returned pointer points to static memory, so this
1137 function can only supply one filename at a time. Don't free that
1140 TODO: Make this re-entrant
1141 ***************************************************************************/
1142 const char *fileinfoname(const struct strvec
*dirs
, const char *filename
)
1145 char fnbuf
[strlen(filename
) + 1];
1147 #else /* WIN32_NATIVE */
1148 const char *fnbuf
= filename
;
1149 #endif /* WIN32_NATIVE */
1158 astr_clear(&realfile
);
1159 strvec_iterate(dirs
, dirname
) {
1161 astr_add(&realfile
, "%s%s", PATH_SEPARATOR
, dirname
);
1164 astr_add(&realfile
, "%s", dirname
);
1166 } strvec_iterate_end
;
1168 return astr_str(&realfile
);
1171 #ifndef DIR_SEPARATOR_IS_DEFAULT
1172 for (i
= 0; filename
[i
] != '\0'; i
++) {
1173 if (filename
[i
] == '/') {
1174 fnbuf
[i
] = DIR_SEPARATOR_CHAR
;
1176 fnbuf
[i
] = filename
[i
];
1180 #endif /* DIR_SEPARATOR_IS_DEFAULT */
1182 strvec_iterate(dirs
, dirname
) {
1183 struct stat buf
; /* see if we can open the file or directory */
1185 astr_set(&realfile
, "%s" DIR_SEPARATOR
"%s", dirname
, fnbuf
);
1186 if (fc_stat(astr_str(&realfile
), &buf
) == 0) {
1187 return astr_str(&realfile
);
1189 } strvec_iterate_end
;
1191 log_verbose("Could not find readable file \"%s\" in data path.", filename
);
1196 /**************************************************************************
1197 Free resources allocated for fileinfoname service
1198 **************************************************************************/
1199 void free_fileinfo_data(void)
1201 astr_free(&realfile
);
1204 /**************************************************************************
1205 Destroys the file info structure.
1206 **************************************************************************/
1207 static void fileinfo_destroy(struct fileinfo
*pfile
)
1210 free(pfile
->fullname
);
1214 /**************************************************************************
1215 Compare modification times.
1216 **************************************************************************/
1217 static int compare_file_mtime_ptrs(const struct fileinfo
*const *ppa
,
1218 const struct fileinfo
*const *ppb
)
1220 time_t a
= (*ppa
)->mtime
;
1221 time_t b
= (*ppb
)->mtime
;
1223 return ((a
< b
) ? 1 : (a
> b
) ? -1 : 0);
1226 /**************************************************************************
1228 **************************************************************************/
1229 static int compare_file_name_ptrs(const struct fileinfo
*const *ppa
,
1230 const struct fileinfo
*const *ppb
)
1232 return fc_strcoll((*ppa
)->name
, (*ppb
)->name
);
1235 /**************************************************************************
1237 **************************************************************************/
1238 static bool compare_fileinfo_name(const struct fileinfo
*pa
,
1239 const struct fileinfo
*pb
)
1241 return 0 == fc_strcoll(pa
->name
, pb
->name
);
1244 /**************************************************************************
1245 Search for filenames with the "infix" substring in the "subpath"
1246 subdirectory of the data path.
1247 "nodups" removes duplicate names.
1248 The returned list will be sorted by name first and modification time
1249 second. Returned "name"s will be truncated starting at the "infix"
1250 substring. The returned list must be freed with fileinfo_list_destroy().
1251 **************************************************************************/
1252 struct fileinfo_list
*fileinfolist_infix(const struct strvec
*dirs
,
1253 const char *infix
, bool nodups
)
1255 struct fileinfo_list
*res
;
1261 res
= fileinfo_list_new_full(fileinfo_destroy
);
1263 /* First assemble a full list of names. */
1264 strvec_iterate(dirs
, dirname
) {
1266 struct dirent
*entry
;
1268 /* Open the directory for reading. */
1269 dir
= fc_opendir(dirname
);
1274 /* Scan all entries in the directory. */
1275 while ((entry
= readdir(dir
))) {
1276 struct fileinfo
*file
;
1278 /* Strdup the entry so we can safely write to it. */
1279 char *filename
= fc_strdup(entry
->d_name
);
1281 /* Make sure the file name matches. */
1282 if ((ptr
= strstr(filename
, infix
))) {
1285 size_t len
= strlen(dirname
) + strlen(filename
) + 2;
1287 fullname
= fc_malloc(len
);
1288 fc_snprintf(fullname
, len
, "%s" DIR_SEPARATOR
"%s", dirname
, filename
);
1290 if (fc_stat(fullname
, &buf
) == 0) {
1291 file
= fc_malloc(sizeof(*file
));
1293 /* Clip the suffix. */
1296 file
->name
= filename
;
1297 file
->fullname
= fullname
;
1298 file
->mtime
= buf
.st_mtime
;
1300 fileinfo_list_append(res
, file
);
1311 } strvec_iterate_end
;
1313 /* Sort the list by name. */
1314 fileinfo_list_sort(res
, compare_file_name_ptrs
);
1317 fileinfo_list_unique_full(res
, compare_fileinfo_name
);
1320 /* Sort the list by last modification time. */
1321 fileinfo_list_sort(res
, compare_file_mtime_ptrs
);
1326 /***************************************************************************
1327 Language environmental variable (with emulation).
1328 ***************************************************************************/
1329 char *setup_langname(void)
1331 char *langname
= NULL
;
1334 langname
= getenv("LANG");
1337 /* set LANG by hand if it is not set */
1339 switch (PRIMARYLANGID(GetUserDefaultLangID())) {
1359 switch (SUBLANGID(GetUserDefaultLangID())) {
1360 case SUBLANG_ENGLISH_UK
:
1386 case LANG_HUNGARIAN
:
1398 case LANG_LITHUANIAN
:
1404 case LANG_NORWEGIAN
:
1410 case LANG_PORTUGUESE
:
1411 switch (SUBLANGID(GetUserDefaultLangID())) {
1412 case SUBLANG_PORTUGUESE_BRAZILIAN
:
1432 case LANG_UKRAINIAN
:
1440 if (langname
!= NULL
) {
1441 static char envstr
[40];
1443 fc_snprintf(envstr
, sizeof(envstr
), "LANG=%s", langname
);
1447 #endif /* WIN32_NATIVE */
1448 #endif /* ENABLE_NLS */
1453 /***************************************************************************
1454 Setup for Native Language Support, if configured to use it.
1455 (Call this only once, or it may leak memory.)
1456 ***************************************************************************/
1460 * Setup the cached locale numeric formatting information. Defaults
1461 * are as appropriate for the US.
1463 grouping
= fc_strdup("\3");
1464 grouping_sep
= fc_strdup(",");
1469 setup_langname(); /* Makes sure LANG env variable has been set */
1470 #endif /* WIN32_NATIVE */
1472 (void) setlocale(LC_ALL
, "");
1473 (void) bindtextdomain(PACKAGE
, get_locale_dir());
1474 (void) textdomain(PACKAGE
);
1476 /* Don't touch the defaults when LC_NUMERIC == "C".
1477 This is intended to cater to the common case where:
1478 1) The user is from North America. ;-)
1479 2) The user has not set the proper environment variables.
1480 (Most applications are (unfortunately) US-centric
1481 by default, so why bother?)
1482 This would result in the "C" locale being used, with grouping ""
1483 and thousands_sep "", where we really want "\3" and ",". */
1485 if (strcmp(setlocale(LC_NUMERIC
, NULL
), "C") != 0) {
1486 struct lconv
*lc
= localeconv();
1488 if (lc
->grouping
[0] == '\0') {
1489 /* This actually indicates no grouping at all. */
1490 char *m
= malloc(sizeof(char));
1496 lc
->grouping
[len
] != '\0' && lc
->grouping
[len
] != CHAR_MAX
; len
++) {
1501 grouping
= fc_malloc(len
);
1502 memcpy(grouping
, lc
->grouping
, len
);
1505 grouping_sep
= fc_strdup(lc
->thousands_sep
);
1509 char *autocap_opt_in
[] = { "fi", NULL
};
1511 bool ac_enabled
= FALSE
;
1513 char *lang
= getenv("LANG");
1515 if (lang
!= NULL
&& lang
[0] != '\0' && lang
[1] != '\0') {
1516 for (i
= 0; autocap_opt_in
[i
] != NULL
&& !ac_enabled
; i
++) {
1517 if (lang
[0] == autocap_opt_in
[i
][0]
1518 && lang
[1] == autocap_opt_in
[i
][1]) {
1520 capitalization_opt_in();
1526 #endif /* ENABLE_NLS */
1529 /***************************************************************************
1530 Free memory allocated by Native Language Support
1531 ***************************************************************************/
1537 grouping_sep
= NULL
;
1540 /***************************************************************************
1541 If we have root privileges, die with an error.
1542 (Eg, for security reasons.)
1543 Param argv0 should be argv[0] or similar; fallback is
1544 used instead if argv0 is NULL.
1545 But don't die on systems where the user is always root...
1546 (a general test for this would be better).
1547 Doesn't use log_*() because gets called before logging is setup.
1548 ***************************************************************************/
1549 void dont_run_as_root(const char *argv0
, const char *fallback
)
1551 #if (defined(ALWAYS_ROOT) || defined(__EMX__) || defined(__BEOS__))
1554 if (getuid()==0 || geteuid()==0) {
1556 _("%s: Fatal error: you're trying to run me as superuser!\n"),
1557 (argv0
? argv0
: fallback
? fallback
: "freeciv"));
1558 fc_fprintf(stderr
, _("Use a non-privileged account instead.\n"));
1561 #endif /* ALWAYS_ROOT */
1564 /***************************************************************************
1565 Return a description string of the result.
1566 In English, form of description is suitable to substitute in, eg:
1567 prefix is <description>
1568 (N.B.: The description is always in English, but they have all been marked
1569 for translation. If you want a localized version, use _() on the return.)
1570 ***************************************************************************/
1571 const char *m_pre_description(enum m_pre_result result
)
1573 static const char * const descriptions
[] = {
1581 fc_assert_ret_val(result
>= 0 && result
< ARRAY_SIZE(descriptions
), NULL
);
1582 return descriptions
[result
];
1585 /***************************************************************************
1586 See match_prefix_full().
1587 ***************************************************************************/
1588 enum m_pre_result
match_prefix(m_pre_accessor_fn_t accessor_fn
,
1590 size_t max_len_name
,
1591 m_pre_strncmp_fn_t cmp_fn
,
1592 m_strlen_fn_t len_fn
,
1596 return match_prefix_full(accessor_fn
, n_names
, max_len_name
, cmp_fn
,
1597 len_fn
, prefix
, ind_result
, NULL
, 0, NULL
);
1600 /***************************************************************************
1601 Given n names, with maximum length max_len_name, accessed by
1602 accessor_fn(0) to accessor_fn(n-1), look for matching prefix
1603 according to given comparison function.
1604 Returns type of match or fail, and for return <= M_PRE_AMBIGUOUS
1605 sets *ind_result with matching index (or for ambiguous, first match).
1606 If max_len_name==0, treat as no maximum.
1607 If the int array 'matches' is non-NULL, up to 'max_matches' ambiguous
1608 matching names indices will be inserted into it. If 'pnum_matches' is
1609 non-NULL, it will be set to the number of indices inserted into 'matches'.
1610 ***************************************************************************/
1611 enum m_pre_result
match_prefix_full(m_pre_accessor_fn_t accessor_fn
,
1613 size_t max_len_name
,
1614 m_pre_strncmp_fn_t cmp_fn
,
1615 m_strlen_fn_t len_fn
,
1622 int i
, len
, nmatches
;
1624 if (len_fn
== NULL
) {
1625 len
= strlen(prefix
);
1627 len
= len_fn(prefix
);
1632 if (len
> max_len_name
&& max_len_name
> 0) {
1637 for(i
=0; i
<n_names
; i
++) {
1638 const char *name
= accessor_fn(i
);
1639 if (cmp_fn(name
, prefix
, len
)==0) {
1640 if (strlen(name
) == len
) {
1645 *ind_result
= i
; /* first match */
1647 if (matches
!= NULL
&& nmatches
< max_matches
) {
1648 matches
[nmatches
] = i
;
1654 if (nmatches
== 1) {
1656 } else if (nmatches
> 1) {
1657 if (pnum_matches
!= NULL
) {
1658 *pnum_matches
= MIN(max_matches
, nmatches
);
1660 return M_PRE_AMBIGUOUS
;
1666 /***************************************************************************
1667 Returns string which gives the multicast group IP address for finding
1668 servers on the LAN, as specified by $FREECIV_MULTICAST_GROUP.
1669 Gets value once, and then caches result.
1670 ***************************************************************************/
1671 char *get_multicast_group(bool ipv6_preferred
)
1673 static char *default_multicast_group_ipv4
= "225.1.1.1";
1674 #ifdef FREECIV_IPV6_SUPPORT
1675 /* TODO: Get useful group (this is node local) */
1676 static char *default_multicast_group_ipv6
= "FF31::8000:15B4";
1677 #endif /* IPv6 support */
1679 if (mc_group
== NULL
) {
1680 char *env
= getenv("FREECIV_MULTICAST_GROUP");
1683 mc_group
= fc_strdup(env
);
1685 #ifdef FREECIV_IPV6_SUPPORT
1686 if (ipv6_preferred
) {
1687 mc_group
= fc_strdup(default_multicast_group_ipv6
);
1689 #endif /* IPv6 support */
1691 mc_group
= fc_strdup(default_multicast_group_ipv4
);
1699 /***************************************************************************
1700 Free multicast group resources
1701 ***************************************************************************/
1702 void free_multicast_group(void)
1704 if (mc_group
!= NULL
) {
1710 /***************************************************************************
1711 Interpret ~/ in filename as home dir
1712 New path is returned in buf of size buf_size
1714 This may fail if the path is too long. It is better to use
1715 interpret_tilde_alloc.
1716 ***************************************************************************/
1717 void interpret_tilde(char* buf
, size_t buf_size
, const char* filename
)
1719 if (filename
[0] == '~' && filename
[1] == DIR_SEPARATOR_CHAR
) {
1720 fc_snprintf(buf
, buf_size
, "%s" DIR_SEPARATOR
"%s", user_home_dir(), filename
+ 2);
1721 } else if (filename
[0] == '~' && filename
[1] == '\0') {
1722 strncpy(buf
, user_home_dir(), buf_size
);
1724 strncpy(buf
, filename
, buf_size
);
1728 /***************************************************************************
1729 Interpret ~/ in filename as home dir
1731 The new path is returned in buf, as a newly allocated buffer. The new
1732 path will always be allocated and written, even if there is no ~ present.
1733 ***************************************************************************/
1734 char *interpret_tilde_alloc(const char* filename
)
1736 if (filename
[0] == '~' && filename
[1] == DIR_SEPARATOR_CHAR
) {
1737 const char *home
= user_home_dir();
1741 filename
+= 2; /* Skip past "~/" */
1742 sz
= strlen(home
) + strlen(filename
) + 2;
1743 buf
= fc_malloc(sz
);
1744 fc_snprintf(buf
, sz
, "%s/%s", home
, filename
);
1746 } else if (filename
[0] == '~' && filename
[1] == '\0') {
1747 return fc_strdup(user_home_dir());
1749 return fc_strdup(filename
);
1753 /**************************************************************************
1754 Return a pointer to the start of the file basename in filepath.
1755 If the string contains no dir separator, it is returned itself.
1756 **************************************************************************/
1757 char *skip_to_basename(char *filepath
)
1760 fc_assert_ret_val(NULL
!= filepath
, NULL
);
1762 for (j
= strlen(filepath
); j
>= 0; j
--) {
1763 if (filepath
[j
] == DIR_SEPARATOR_CHAR
) {
1764 return &filepath
[j
+1];
1770 /**************************************************************************
1771 If the directory "pathname" does not exist, recursively create all
1772 directories until it does.
1773 **************************************************************************/
1774 bool make_dir(const char *pathname
)
1779 path
= interpret_tilde_alloc(pathname
);
1782 dir
= strchr(dir
, DIR_SEPARATOR_CHAR
);
1783 /* We set the current / with 0, and restore it afterwards */
1790 /* Prefer _mkdir() in Windows even if mkdir() would seem to be available -
1791 * chances are that it's wrong kind of mkdir().
1792 * TODO: Make a configure check for mkdir() that also makes sure that it
1793 * takes two parameters, and prefer such proper mkdir() here. */
1795 char *path_in_local_encoding
= internal_to_local_string_malloc(path
);
1797 _mkdir(path_in_local_encoding
);
1798 free(path_in_local_encoding
);
1800 #else /* HAVE__MKDIR */
1802 #endif /* HAVE__MKDIR */
1803 #else /* WIN32_NATIVE */
1805 #endif /* WIN32_NATIVE */
1808 *dir
= DIR_SEPARATOR_CHAR
;
1817 /**************************************************************************
1818 Returns TRUE if the filename's path is absolute.
1819 **************************************************************************/
1820 bool path_is_absolute(const char *filename
)
1827 if (strchr(filename
, ':')) {
1830 #else /* WIN32_NATIVE */
1831 if (filename
[0] == '/') {
1834 #endif /* WIN32_NATIVE */
1839 /**************************************************************************
1840 Scan in a word or set of words from start to but not including
1841 any of the given delimiters. The buf pointer will point past delimiter,
1842 or be set to NULL if there is nothing there. Removes excess white
1845 This function should be safe, and dest will contain "\0" and
1846 *buf == NULL on failure. We always fail gently.
1848 Due to the way the scanning is performed, looking for a space
1849 will give you the first word even if it comes before multiple
1852 Returns delimiter found.
1854 Pass in NULL for dest and -1 for size to just skip ahead. Note that if
1855 nothing is found, dest will contain the whole string, minus leading and
1856 trailing whitespace. You can scan for "" to conveniently grab the
1857 remainder of a string.
1858 **************************************************************************/
1859 char scanin(const char **buf
, char *delimiters
, char *dest
, int size
)
1861 char *ptr
, found
= '?';
1863 if (*buf
== NULL
|| strlen(*buf
) == 0 || size
== 0) {
1872 strncpy(dest
, *buf
, size
-1);
1873 dest
[size
-1] = '\0';
1874 remove_leading_trailing_spaces(dest
);
1875 ptr
= strpbrk(dest
, delimiters
);
1877 /* Just skip ahead. */
1878 ptr
= strpbrk(*buf
, delimiters
);
1886 remove_leading_trailing_spaces(dest
);
1888 *buf
= strpbrk(*buf
, delimiters
);
1890 (*buf
)++; /* skip delimiter */
1900 /**************************************************************************
1901 Convenience function to nicely format a time_t seconds value in to a
1902 string with hours, minutes, etc.
1903 **************************************************************************/
1904 void format_time_duration(time_t t
, char *buf
, int maxlen
)
1906 int seconds
, minutes
, hours
, days
;
1910 minutes
= (t
/ 60) % 60;
1911 hours
= (t
/ (60 * 60)) % 24;
1912 days
= t
/ (60 * 60 * 24);
1921 cat_snprintf(buf
, maxlen
, "%d %s", days
, PL_("day", "days", days
));
1925 cat_snprintf(buf
, maxlen
, "%s%d %s",
1926 space
? " " : "", hours
, PL_("hour", "hours", hours
));
1930 cat_snprintf(buf
, maxlen
, "%s%d %s",
1932 minutes
, PL_("minute", "minutes", minutes
));
1936 cat_snprintf(buf
, maxlen
, "%s%d %s",
1938 seconds
, PL_("second", "seconds", seconds
));
1942 /************************************************************************
1943 Randomize the elements of an array using the Fisher-Yates shuffle.
1945 see: http://benpfaff.org/writings/clc/shuffle.html
1946 ************************************************************************/
1947 void array_shuffle(int *array
, int n
)
1949 if (n
> 1 && array
!= NULL
) {
1951 for (i
= 0; i
< n
- 1; i
++) {
1952 j
= i
+ fc_rand(n
- i
);
1954 array
[j
] = array
[i
];
1960 /****************************************************************************
1961 Test an asterisk in the pattern against test. Returns TRUE if test fit the
1962 pattern. May be recursive, as it calls wildcard_fit_string() itself (if
1964 ****************************************************************************/
1965 static bool wildcard_asterisk_fit(const char *pattern
, const char *test
)
1969 /* Jump over the leading asterisks. */
1974 /* It is a leading asterisk. */
1980 if ('\0' == *test
) {
1991 if ('[' != *pattern
) {
1992 if ('\\' == *pattern
) {
1993 jump_to
= *(pattern
+ 1);
2001 while ('\0' != *test
) {
2002 if ('\0' != jump_to
) {
2003 /* Jump to next matching charather. */
2004 test
= strchr(test
, jump_to
);
2011 if (wildcard_fit_string(pattern
, test
)) {
2021 /****************************************************************************
2022 Test a range in the pattern against test. Returns TRUE if **test fit the
2023 first range in *pattern.
2024 ****************************************************************************/
2025 static bool wildcard_range_fit(const char **pattern
, const char **test
)
2027 const char *start
= (*pattern
+ 1);
2031 if ('\0' == **test
) {
2032 /* Need one character. */
2036 /* Find the end of the pattern. */
2038 *pattern
= strchr(*pattern
, ']');
2039 if (NULL
== *pattern
) {
2040 /* Wildcard format error. */
2042 } else if (*(*pattern
- 1) != '\\') {
2043 /* This is the end. */
2051 if ('!' == *start
) {
2061 for (; start
< *pattern
; start
++) {
2062 if ('-' == *start
|| '!' == *start
) {
2063 /* Wildcard format error. */
2065 } else if (start
< *pattern
- 2 && '-' == *(start
+ 1)) {
2067 if (*start
<= testc
&& testc
<= *(start
+ 2)) {
2071 } else if (*start
== testc
) {
2072 /* Single character. */
2080 /****************************************************************************
2081 Returns TRUE if test fit the pattern. The pattern can contain special
2083 * '*': to specify a substitute for any zero or more characters.
2084 * '?': to specify a substitute for any one character.
2085 * '[...]': to specify a range of characters:
2086 * '!': at the begenning of the range means that the matching result
2088 * 'A-Z': means any character between 'A' and 'Z'.
2089 * 'agr': means 'a', 'g' or 'r'.
2090 ****************************************************************************/
2091 bool wildcard_fit_string(const char *pattern
, const char *test
)
2097 return '\0' == *test
;
2099 return wildcard_asterisk_fit(pattern
, test
); /* Maybe recursive. */
2101 if (!wildcard_range_fit(&pattern
, &test
)) {
2106 if ('\0' == *test
) {
2112 /* break; not missing. */
2114 if (*pattern
!= *test
) {
2126 /****************************************************************************
2127 Print a string with a custom format. sequences is a pointer to an array of
2128 sequences, probably defined with CF_*_SEQ(). sequences_num is the number of
2129 the sequences, or -1 in the case the array is terminated with CF_END.
2132 static const struct cf_sequence sequences[] = {
2133 CF_INT_SEQ('y', 2010)
2137 fc_vsnprintcf(buf, sizeof(buf), "%y %+06y", sequences, 1);
2138 // This will print "2010 +02010" into buf.
2139 ****************************************************************************/
2140 int fc_vsnprintcf(char *buf
, size_t buf_len
, const char *format
,
2141 const struct cf_sequence
*sequences
, size_t sequences_num
)
2143 const struct cf_sequence
*pseq
;
2145 const char *f
= format
;
2146 char *const max
= buf
+ buf_len
- 1;
2148 const char *const cmax
= cformat
+ sizeof(cformat
) - 2;
2151 if ((size_t) -1 == sequences_num
) {
2152 /* Find the number of sequences. */
2154 for (pseq
= sequences
; CF_LAST
!= pseq
->type
; pseq
++) {
2159 while ('\0' != *f
) {
2174 for (; !fc_isalpha(*f
) && '\0' != *f
&& '%' != *f
&& cmax
> c
; f
++) {
2178 if (!fc_isalpha(*f
)) {
2179 /* Beginning of a new sequence, end of the format, or too long
2182 j
= fc_snprintf(b
, max
- b
+ 1, "%s", cformat
);
2190 for (i
= 0, pseq
= sequences
; i
< sequences_num
; i
++, pseq
++) {
2191 if (pseq
->letter
== *f
) {
2193 switch (pseq
->type
) {
2197 j
= fc_snprintf(b
, max
- b
+ 1, cformat
,
2198 pseq
->bool_value
? "TRUE" : "FALSE");
2200 case CF_TRANS_BOOLEAN
:
2203 j
= fc_snprintf(b
, max
- b
+ 1, cformat
,
2204 pseq
->bool_value
? _("TRUE") : _("FALSE"));
2209 j
= fc_snprintf(b
, max
- b
+ 1, cformat
, pseq
->char_value
);
2214 j
= fc_snprintf(b
, max
- b
+ 1, cformat
, pseq
->int_value
);
2219 j
= fc_snprintf(b
, max
- b
+ 1, cformat
, pseq
->int_value
);
2224 j
= fc_snprintf(b
, max
- b
+ 1, cformat
, pseq
->float_value
);
2229 j
= fc_snprintf(b
, max
- b
+ 1, cformat
, pseq
->ptr_value
);
2234 j
= fc_snprintf(b
, max
- b
+ 1, cformat
, pseq
->str_value
);
2240 log_error("Error: unsupported sequence type: %d.", pseq
->type
);
2252 if (i
>= sequences_num
) {
2253 /* Format not supported. */
2255 j
= fc_snprintf(b
, max
- b
+ 1, "%s%c", cformat
, *f
);
2263 /* Not a sequence. */
2276 /****************************************************************************
2277 Print a string with a custom format. The additional arguments are a suite
2278 of cf_*_seq() finished by cf_end(). This return the number of printed
2279 characters (excluding the last '\0') or -1 if the buffer is full.
2284 fc_snprintcf(buf, sizeof(buf), "%y %+06y",
2285 cf_int_seq('y', 2010), cf_end());
2286 // This will print "2010 +02010" into buf.
2287 ****************************************************************************/
2288 int fc_snprintcf(char *buf
, size_t buf_len
, const char *format
, ...)
2290 struct cf_sequence sequences
[16];
2291 size_t sequences_num
= 0;
2294 /* Collect sequence array. */
2295 va_start(args
, format
);
2297 sequences
[sequences_num
] = va_arg(args
, struct cf_sequence
);
2298 if (CF_LAST
== sequences
[sequences_num
].type
) {
2303 } while (ARRAY_SIZE(sequences
) > sequences_num
);
2305 if (ARRAY_SIZE(sequences
) <= sequences_num
2306 && CF_LAST
!= va_arg(args
, struct cf_sequence
).type
) {
2307 log_error("Too many custom sequences. Maybe did you forget cf_end() "
2308 "at the end of the arguments?");
2315 return fc_vsnprintcf(buf
, buf_len
, format
, sequences
, sequences_num
);
2318 /****************************************************************************
2319 Extract the sequences of a format. Returns the number of extracted
2321 ****************************************************************************/
2322 static size_t extract_escapes(const char *format
, char *escapes
,
2325 static const char format_escapes
[] = {
2326 '*', 'd', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f',
2327 'F', 'g', 'G', 'a', 'A', 'c', 's', 'p', 'n',
2329 bool reordered
= FALSE
;
2333 memset(escapes
, 0, max_escapes
);
2334 format
= strchr(format
, '%');
2335 while (NULL
!= format
) {
2337 if ('%' == *format
) {
2338 /* Double, not a sequence. */
2340 } else if (fc_isdigit(*format
)) {
2341 const char *start
= format
;
2345 } while (fc_isdigit(*format
));
2346 if ('$' == *format
) {
2347 /* Strings are reordered. */
2348 sscanf(start
, "%d", &idx
);
2353 while ('\0' != *format
2354 && NULL
== strchr(format_escapes
, *format
)) {
2357 escapes
[idx
] = *format
;
2359 /* Increase the read count. */
2369 if ('*' != *format
) {
2370 format
= strchr(format
, '%');
2371 } /* else we didn't have found the real sequence. */
2376 /****************************************************************************
2377 Returns TRUE iff both formats are compatible (if 'format1' can be used
2378 instead 'format2' and reciprocally).
2379 ****************************************************************************/
2380 bool formats_match(const char *format1
, const char *format2
)
2382 char format1_escapes
[256], format2_escapes
[256];
2383 size_t format1_escapes_num
= extract_escapes(format1
, format1_escapes
,
2384 sizeof(format1_escapes
));
2385 size_t format2_escapes_num
= extract_escapes(format2
, format2_escapes
,
2386 sizeof(format2_escapes
));
2388 return (format1_escapes_num
== format2_escapes_num
2389 && 0 == memcmp(format1_escapes
, format2_escapes
,
2390 format1_escapes_num
));