Freeciv-3.3
Loading...
Searching...
No Matches
shared.c
Go to the documentation of this file.
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#ifdef FREECIV_HAVE_DIRENT_H
26#include <dirent.h>
27#endif
28
29#include <errno.h>
30#include <limits.h>
31#include <stdarg.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <sys/stat.h>
35#include <sys/types.h>
36
37#ifdef HAVE_LOCALE_H
38#include <locale.h>
39#endif
40
41#ifdef HAVE_PWD_H
42#include <pwd.h>
43#endif
44#ifdef HAVE_UNISTD_H
45#include <unistd.h>
46#endif
47#ifdef FREECIV_MSWINDOWS
48#include <windows.h>
49#include <lmcons.h> /* UNLEN */
50#include <shlobj.h>
51#ifdef HAVE_DIRECT_H
52#include <direct.h>
53#endif /* HAVE_DIRECT_H */
54#endif /* FREECIV_MSWINDOWS */
55
56/* utility */
57#include "astring.h"
58#include "fc_dirent.h"
59#include "fciconv.h"
60#include "fcintl.h"
61#include "mem.h"
62#include "rand.h"
63#include "string_vector.h"
64
65#include "shared.h"
66
67/* If no default data path is defined use the default default one */
68#ifndef DEFAULT_DATA_PATH
69#define DEFAULT_DATA_PATH "." PATH_SEPARATOR \
70 "data" PATH_SEPARATOR \
71 FREECIV_STORAGE_DIR DIR_SEPARATOR DATASUBDIR
72#endif
73#ifndef DEFAULT_SAVE_PATH
74#define DEFAULT_SAVE_PATH "." PATH_SEPARATOR \
75 FREECIV_STORAGE_DIR DIR_SEPARATOR "saves"
76#endif
77#ifndef DEFAULT_SCENARIO_PATH
78#define DEFAULT_SCENARIO_PATH \
79 "." PATH_SEPARATOR \
80 "data" DIR_SEPARATOR "scenarios" PATH_SEPARATOR \
81 FREECIV_STORAGE_DIR DATASUBDIR DIR_SEPARATOR "scenarios" PATH_SEPARATOR \
82 FREECIV_STORAGE_DIR DIR_SEPARATOR "scenarios"
83#endif /* DEFAULT_SCENARIO_PATH */
84
85/* Environment */
86#ifndef FREECIV_DATA_PATH
87#define FREECIV_DATA_PATH "FREECIV_DATA_PATH"
88#endif
89#ifndef FREECIV_SAVE_PATH
90#define FREECIV_SAVE_PATH "FREECIV_SAVE_PATH"
91#endif
92#ifndef FREECIV_SCENARIO_PATH
93#define FREECIV_SCENARIO_PATH "FREECIV_SCENARIO_PATH"
94#endif
95
96/* Both of these are stored in the local encoding. The grouping_sep must
97 * be converted to the internal encoding when it's used. */
98static char *grouping = nullptr;
99static char *grouping_sep = nullptr;
100
101/* As well as base64 functions, this string is used for checking for
102 * 'safe' filenames, so should not contain / \ . */
103static const char base64url[] =
104 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
105
106static struct strvec *data_dir_names = nullptr;
107static struct strvec *save_dir_names = nullptr;
108static struct strvec *scenario_dir_names = nullptr;
109
110static char *mc_group = nullptr;
111static char *home_dir_user = nullptr;
112static char *storage_dir_freeciv = nullptr;
113
115
116static void remove_trailing_char(char *s, char trailing)
117 fc__attribute((nonnull (1)));
118
119static int compare_file_mtime_ptrs(const struct fileinfo *const *ppa,
120 const struct fileinfo *const *ppb);
121
122static char *expand_dir(char *tok_in, bool ok_to_free);
123
124/************************************************************************/
128{
129 if (TRI_NO == one || TRI_NO == two) {
130 return TRI_NO;
131 }
132
133 if (TRI_MAYBE == one || TRI_MAYBE == two) {
134 return TRI_MAYBE;
135 }
136
137 return TRI_YES;
138}
139
140/************************************************************************/
144{
145 if (TRI_YES == one || TRI_YES == two) {
146 return TRI_YES;
147 }
148
149 if (TRI_MAYBE == one || TRI_MAYBE == two) {
150 return TRI_MAYBE;
151 }
152
153 return TRI_NO;
154}
155
156/************************************************************************/
162const char *big_int_to_text(unsigned int mantissa, unsigned int exponent)
163{
164 static char buf[64]; /* Note that we'll be filling this in right to left. */
165 char *grp = grouping;
166 char *ptr;
167 unsigned int cnt = 0;
168 char sep[64];
169 size_t seplen;
170
171 /* We have to convert the encoding here (rather than when the locale
172 * is initialized) because it can't be done before the charsets are
173 * initialized. */
175 seplen = strlen(sep);
176
177#if 0 /* Not needed while the values are unsigned. */
178 fc_assert_ret_val(0 <= mantissa, nullptr);
179 fc_assert_ret_val(0 <= exponent, nullptr);
180#endif
181
182 if (mantissa == 0) {
183 return "0";
184 }
185
186 /* We fill the string in backwards, starting from the right. So the first
187 * thing we do is terminate it. */
188 ptr = &buf[sizeof(buf)];
189 *(--ptr) = '\0';
190
191 while (mantissa != 0) {
192 int dig;
193
194 if (ptr <= buf + seplen) {
195 /* Avoid a buffer overflow. */
196 fc_assert_ret_val(ptr > buf + seplen, nullptr);
197 return ptr;
198 }
199
200 /* Add on another character. */
201 if (exponent > 0) {
202 dig = 0;
203 exponent--;
204 } else {
205 dig = mantissa % 10;
206 mantissa /= 10;
207 }
208 *(--ptr) = '0' + dig;
209
210 cnt++;
211 if (mantissa != 0 && cnt == *grp) {
212 /* Reached count of digits in group: insert separator and reset count. */
213 cnt = 0;
214 if (*grp == CHAR_MAX) {
215 /* This test is unlikely to be necessary since we would need at
216 least 421-bit ints to break the 127 digit barrier, but why not. */
217 break;
218 }
219 ptr -= seplen;
220
221 fc_assert_ret_val(ptr >= buf, nullptr);
222
223 memcpy(ptr, sep, seplen);
224 if (*(grp + 1) != 0) {
225 /* Zero means to repeat the present group-size indefinitely. */
226 grp++;
227 }
228 }
229 }
230
231 return ptr;
232}
233
234/************************************************************************/
237const char *int_to_text(unsigned int number)
238{
239 return big_int_to_text(number, 0);
240}
241
242/************************************************************************/
246static bool is_ascii(char ch)
247{
248 /* This works with both signed and unsigned char's. */
249 return ch >= ' ' && ch <= '~';
250}
251
252/************************************************************************/
256bool is_safe_filename(const char *name)
257{
258 int i = 0;
259
260 /* Must not be nullptr or empty */
261 if (name == nullptr || *name == '\0') {
262 return FALSE;
263 }
264
265 for (; '\0' != name[i]; i++) {
266 if ('.' != name[i] && strchr(base64url, name[i]) == nullptr) {
267 return FALSE;
268 }
269 }
270
271 /* We don't allow the filename to ascend directories */
273 return FALSE;
274 }
275
276 /* Otherwise, it is okay... */
277 return TRUE;
278}
279
280/************************************************************************/
286bool is_ascii_name(const char *name)
287{
288 const char illegal_chars[] = {'|', '%', '"', ',', '*', '<', '>', '\0'};
289 int i, j;
290
291 /* Must not be nullptr or empty */
292 if (name == nullptr || *name == '\0') {
293 return FALSE;
294 }
295
296 /* Must begin and end with some non-space character */
297 if ((*name == ' ') || (*(strchr(name, '\0') - 1) == ' ')) {
298 return FALSE;
299 }
300
301 /* Must be composed entirely of printable ascii characters,
302 * and no illegal characters which can break ranking scripts. */
303 for (i = 0; name[i]; i++) {
304 if (!is_ascii(name[i])) {
305 return FALSE;
306 }
307 for (j = 0; illegal_chars[j]; j++) {
308 if (name[i] == illegal_chars[j]) {
309 return FALSE;
310 }
311 }
312 }
313
314 /* Otherwise, it's okay... */
315 return TRUE;
316}
317
318/************************************************************************/
321bool is_base64url(const char *s)
322{
323 size_t i = 0;
324
325 /* Must not be nullptr or empty */
326 if (s == nullptr || '\0' == *s) {
327 return FALSE;
328 }
329
330 for (; '\0' != s[i]; i++) {
331 if (strchr(base64url, s[i]) == nullptr) {
332 return FALSE;
333 }
334 }
335
336 return TRUE;
337}
338
339/************************************************************************/
343void randomize_base64url_string(char *s, size_t n)
344{
345 size_t i = 0;
346
347 /* Must not be nullptr or too short */
348 if (s == nullptr || 1 > n) {
349 return;
350 }
351
352 for (; i < (n - 1); i++) {
353 s[i] = base64url[fc_rand(sizeof(base64url) - 1)];
354 }
355 s[i] = '\0';
356}
357
358/************************************************************************/
363int compare_strings(const void *first, const void *second)
364{
365 return fc_strcoll((const char *) first, (const char *) second);
366}
367
368/************************************************************************/
373int compare_strings_ptrs(const void *first, const void *second)
374{
375 return fc_strcoll(*((const char **) first), *((const char **) second));
376}
377
378/************************************************************************/
383int compare_strings_strvec(const char *const *first,
384 const char *const *second)
385{
386 return fc_strcoll(*first, *second);
387}
388
389/************************************************************************/
393{
394 while (*s != '\0' && fc_isspace(*s)) {
395 s++;
396 }
397
398 return s;
399}
400
401/************************************************************************/
406{
407 char *t;
408
409 t = skip_leading_spaces(s);
410 if (t != s) {
411 while (*t != '\0') {
412 *s++ = *t++;
413 }
414 *s = '\0';
415 }
416}
417
418/************************************************************************/
423{
424 char *t;
425 size_t len;
426
427 len = strlen(s);
428 if (len > 0) {
429 t = s + len -1;
430 while (fc_isspace(*t)) {
431 *t = '\0';
432 if (t == s) {
433 break;
434 }
435 t--;
436 }
437 }
438}
439
440/************************************************************************/
449
450/************************************************************************/
453static void remove_trailing_char(char *s, char trailing)
454{
455 char *t;
456
457 t = s + strlen(s) -1;
458 while (t>=s && (*t) == trailing) {
459 *t = '\0';
460 t--;
461 }
462}
463
464/************************************************************************/
478char *end_of_strn(char *str, int *nleft)
479{
480 int len = strlen(str);
481
482 *nleft -= len;
483 fc_assert_ret_val(0 < (*nleft), nullptr); /* Space for the terminating nul */
484
485 return str + len;
486}
487
488/************************************************************************/
493bool check_strlen(const char *str, size_t len, const char *errmsg)
494{
496
497 return FALSE;
498}
499
500/************************************************************************/
503size_t loud_strlcpy(char *buffer, const char *str, size_t len,
504 const char *errmsg)
505{
507
508 return fc_strlcpy(buffer, str, len);
509}
510
511/************************************************************************/
515bool str_to_int(const char *str, int *pint)
516{
517 const char *start;
518
519 while (fc_isspace(*str)) {
520 /* Skip leading spaces. */
521 str++;
522 }
523
524 start = str;
525 if ('-' == *str || '+' == *str) {
526 /* Handle sign. */
527 str++;
528 }
529 while (fc_isdigit(*str)) {
530 /* Digits. */
531 str++;
532 }
533
534 while (fc_isspace(*str)) {
535 /* Ignore trailing spaces. */
536 str++;
537 }
538
539 return ('\0' == *str && (pint == nullptr || 1 == sscanf(start, "%d", pint)));
540}
541
542/************************************************************************/
547bool str_to_uint(const char *str, unsigned int *pint)
548{
549 const char *start;
550
551 while (fc_isspace(*str)) {
552 /* Skip leading spaces. */
553 str++;
554 }
555
556 start = str;
557 if ('+' == *str) {
558 /* Handle sign. */
559 str++;
560 }
561 while (fc_isdigit(*str)) {
562 /* Digits. */
563 str++;
564 }
565
566 while (fc_isspace(*str)) {
567 /* Ignore trailing spaces. */
568 str++;
569 }
570
571 return ('\0' == *str && (pint == nullptr || 1 == sscanf(start, "%u", pint)));
572}
573
574/************************************************************************/
579bool str_to_float(const char *str, float *pfloat)
580{
581 bool dot;
582 const char *start;
583
584 while (fc_isspace(*str)) {
585 /* Skip leading spaces. */
586 str++;
587 }
588
589 start = str;
590
591 if ('-' == *str || '+' == *str) {
592 /* Handle sign. */
593 str++;
594 }
595 while (fc_isdigit(*str)) {
596 /* Digits. */
597 str++;
598 }
599
600 if (*str == '.') {
601 dot = TRUE;
602 str++;
603
604 while (fc_isdigit(*str)) {
605 /* Digits. */
606 str++;
607 }
608 } else {
609 dot = FALSE;
610 }
611
612 while (fc_isspace(*str)) {
613 /* Ignore trailing spaces. */
614 str++;
615 }
616
617 return ('\0' == *str && dot
618 && (nullptr == pfloat || 1 == sscanf(start, "%f", pfloat)));
619}
620
621/************************************************************************/
627char *user_home_dir(void)
628{
629#ifdef AMIGA
630 return "PROGDIR:";
631#else /* AMIGA */
632
633#ifdef FREECIV_MSWINDOWS
634#define HOMEVAR "APPDATA"
635#else
636#define HOMEVAR "HOME"
637#endif
638
639 if (home_dir_user == nullptr) {
640 char *env = getenv(HOMEVAR);
641
642 if (env) {
645 } else {
646 log_error("Could not find home directory (" HOMEVAR " is not set).");
647 home_dir_user = nullptr;
648 }
649 }
650
651 return home_dir_user;
652#endif /* AMIGA */
653}
654
655/************************************************************************/
659{
660 if (home_dir_user != nullptr) {
662 home_dir_user = nullptr;
663 }
664}
665
666/************************************************************************/
685
686/************************************************************************/
690{
691 if (storage_dir_freeciv != nullptr) {
693 storage_dir_freeciv = nullptr;
694 }
695}
696
697/************************************************************************/
704char *user_username(char *buf, size_t bufsz)
705{
706 /* This function uses a number of different methods to try to find a
707 * username. This username then has to be truncated to bufsz
708 * characters (including terminator) and checked for sanity. Note that
709 * truncating a sane name can leave you with an insane name under some
710 * charsets. */
711
712 /* If the environment variable $USER is present and sane, use it. */
713 {
714 char *env = getenv("USER");
715
716 if (env) {
718 if (is_ascii_name(buf)) {
719 log_verbose("USER username is %s", buf);
720 return buf;
721 }
722 }
723 }
724
725#ifdef HAVE_GETPWUID
726 /* Otherwise if getpwuid() is available we can use it to find the true
727 * username. */
728 {
729 struct passwd *pwent = getpwuid(getuid());
730
731 if (pwent) {
732 fc_strlcpy(buf, pwent->pw_name, bufsz);
733 if (is_ascii_name(buf)) {
734 log_verbose("getpwuid username is %s", buf);
735 return buf;
736 }
737 }
738 }
739#endif /* HAVE_GETPWUID */
740
741#ifdef FREECIV_MSWINDOWS
742 /* On windows the GetUserName function will give us the login name. */
743 {
744 char name[UNLEN + 1];
745 DWORD length = sizeof(name);
746
747 if (GetUserName(name, &length)) {
749 if (is_ascii_name(buf)) {
750 log_verbose("GetUserName username is %s", buf);
751 return buf;
752 }
753 }
754 }
755#endif /* FREECIV_MSWINDOWS */
756
757#ifdef ALWAYS_ROOT
758 fc_strlcpy(buf, "name", bufsz);
759#else
760 fc_snprintf(buf, bufsz, "name%d", (int) getuid());
761#endif
762 log_verbose("fake username is %s", buf);
764
765 return buf;
766}
767
768/************************************************************************/
778static char *expand_dir(char *tok_in, bool ok_to_free)
779{
780 int i; /* strlen(tok), or -1 as flag */
781 char *tok;
782 char **ret = &tok; /* Return tok by default */
783 char *allocated;
784
787 if (strcmp(tok, DIR_SEPARATOR) != 0) {
789 }
790
791 i = strlen(tok);
792 if (tok[0] == '~') {
793 if (i > 1 && tok[1] != DIR_SEPARATOR_CHAR) {
794 log_error("For \"%s\" in path cannot expand '~'"
795 " except as '~" DIR_SEPARATOR "'; ignoring", tok);
796 i = 0; /* Skip this one */
797 } else {
798 char *home = user_home_dir();
799
800 if (!home) {
801 log_verbose("No HOME, skipping path component %s", tok);
802 i = 0;
803 } else {
804 int len = strlen(home) + i; /* +1 -1 */
805
806 allocated = fc_malloc(len);
807 ret = &allocated;
808
809 fc_snprintf(allocated, len, "%s%s", home, tok + 1);
810 i = -1; /* Flag to free tok below */
811 }
812 }
813 }
814
815 if (i != 0) {
816 /* We could check whether the directory exists and
817 * is readable etc? Don't currently. */
818 if (i == -1 && ok_to_free) {
819 free(tok);
820 tok = nullptr;
821 }
822 }
823
824 return *ret;
825}
826
827/************************************************************************/
832static struct strvec *base_get_dirs(const char *dir_list)
833{
834 struct strvec *dirs = strvec_new();
835 char *path, *tok;
836
837 path = fc_strdup(dir_list); /* Something we can strtok() */
838 tok = strtok(path, PATH_SEPARATOR);
839 do {
840 char *dir = expand_dir(tok, FALSE);
841
842 if (dir != nullptr) {
843 strvec_append(dirs, dir);
844 if (dir != tok) {
845 free(dir);
846 }
847 }
848
849 tok = strtok(nullptr, PATH_SEPARATOR);
850 } while (tok);
851
852 free(path);
853 return dirs;
854}
855
856/************************************************************************/
860{
861 if (data_dir_names != nullptr) {
863 data_dir_names = nullptr;
864 }
865 if (save_dir_names != nullptr) {
867 save_dir_names = nullptr;
868 }
869 if (scenario_dir_names != nullptr) {
871 scenario_dir_names = nullptr;
872 }
873}
874
875/************************************************************************/
886const struct strvec *get_data_dirs(void)
887{
888 /* The first time this function is called it will search and
889 * allocate the directory listing. Subsequently we will already
890 * know the list and can just return it. */
891 if (data_dir_names == nullptr) {
892 const char *path;
893
894#ifdef FREECIV_APPIMAGE
895 char default_data_path[5000];
896
900 "%s/usr/share/freeciv",
901 getenv("APPDIR"));
902#else /* FREECIV_APPIMAGE */
903#define default_data_path DEFAULT_DATA_PATH
904#endif /* FREECIV_APPIMAGE */
905
906 if ((path = getenv(FREECIV_DATA_PATH)) && '\0' == path[0]) {
907 /* TRANS: <FREECIV_DATA_PATH> configuration error */
908 log_error(_("\"%s\" is set but empty; using default \"%s\" "
909 "data directories instead."),
911 path = nullptr;
912 }
913 data_dir_names = base_get_dirs(path != nullptr ? path : default_data_path);
914 strvec_remove_duplicate(data_dir_names, strcmp); /* Don't set a path both. */
916 log_verbose("Data path component: %s", dirname);
918 }
919
920 return data_dir_names;
921}
922
923/************************************************************************/
934const struct strvec *get_save_dirs(void)
935{
936 /* The first time this function is called it will search and
937 * allocate the directory listing. Subsequently we will already
938 * know the list and can just return it. */
939 if (save_dir_names == nullptr) {
940 const char *path;
941
942 if ((path = getenv(FREECIV_SAVE_PATH)) && '\0' == path[0]) {
943 /* TRANS: <FREECIV_SAVE_PATH> configuration error */
944 log_error(_("\"%s\" is set but empty; using default \"%s\" "
945 "save directories instead."),
947 path = nullptr;
948 }
949 save_dir_names = base_get_dirs(path != nullptr ? path : DEFAULT_SAVE_PATH);
950 strvec_remove_duplicate(save_dir_names, strcmp); /* Don't set a path both. */
952 log_verbose("Save path component: %s", dirname);
954 }
955
956 return save_dir_names;
957}
958
959/************************************************************************/
971const struct strvec *get_scenario_dirs(void)
972{
973 /* The first time this function is called it will search and
974 * allocate the directory listing. Subsequently we will already
975 * know the list and can just return it. */
976 if (scenario_dir_names == nullptr) {
977 const char *path;
978
979#ifdef FREECIV_APPIMAGE
980 char default_scenario_path[5000];
981
987 "%s/usr/share/freeciv/scenarios",
988 getenv("APPDIR"));
989#else /* FREECIV_APPIMAGE */
990#define default_scenario_path DEFAULT_SCENARIO_PATH
991#endif /* FREECIV_APPIMAGE */
992
993 if ((path = getenv(FREECIV_SCENARIO_PATH)) && '\0' == path[0]) {
994 /* TRANS: <FREECIV_SCENARIO_PATH> configuration error */
995 log_error( _("\"%s\" is set but empty; using default \"%s\" "
996 "scenario directories instead."),
998 path = nullptr;
999 }
1000 scenario_dir_names = base_get_dirs(path != nullptr ? path : default_scenario_path);
1001 strvec_remove_duplicate(scenario_dir_names, strcmp); /* Don't set a path both. */
1003 log_verbose("Scenario path component: %s", dirname);
1005 }
1006
1007 return scenario_dir_names;
1008}
1009
1010/************************************************************************/
1020struct strvec *fileinfolist(const struct strvec *dirs, const char *suffix)
1021{
1022 struct strvec *files = strvec_new();
1023 size_t suffix_len = strlen(suffix);
1024
1026
1027 if (dirs == nullptr) {
1028 return files;
1029 }
1030
1031 /* First assemble a full list of names. */
1032 strvec_iterate(dirs, dirname) {
1033 DIR *dir;
1034 struct dirent *entry;
1035
1036 /* Open the directory for reading. */
1037 dir = fc_opendir(dirname);
1038 if (!dir) {
1039 if (errno == ENOENT) {
1040 log_verbose("Skipping non-existing data directory %s.",
1041 dirname);
1042 } else {
1043 /* TRANS: "...: <externally translated error string>."*/
1044 log_error(_("Could not read data directory %s: %s."),
1046 }
1047 continue;
1048 }
1049
1050 /* Scan all entries in the directory. */
1051 while ((entry = readdir(dir))) {
1052 size_t len = strlen(entry->d_name);
1053
1054 /* Make sure the file name matches. */
1055 if (len > suffix_len
1056 && strcmp(suffix, entry->d_name + len - suffix_len) == 0) {
1057 /* Strdup the entry so we can safely write to it. */
1058 char *match = fc_strdup(entry->d_name);
1059
1060 /* Clip the suffix. */
1061 match[len - suffix_len] = '\0';
1062
1063 strvec_append(files, match);
1064 free(match);
1065 }
1066 }
1067
1068 closedir(dir);
1070
1071 /* Sort the list and remove duplications. */
1074
1075 return files;
1076}
1077
1078/************************************************************************/
1094const char *fileinfoname(const struct strvec *dirs, const char *filename)
1095{
1096#ifndef DIR_SEPARATOR_IS_DEFAULT
1097 char fnbuf[filename != nullptr ? strlen(filename) + 1 : 1];
1098 int i;
1099#else /* DIR_SEPARATOR_IS_DEFAULT */
1100 const char *fnbuf = filename;
1101#endif /* DIR_SEPARATOR_IS_DEFAULT */
1102
1103 if (dirs == nullptr) {
1104 return nullptr;
1105 }
1106
1107 if (!filename) {
1108 bool first = TRUE;
1109
1111 strvec_iterate(dirs, dirname) {
1112 if (first) {
1114 first = FALSE;
1115 } else {
1116 astr_add(&realfile, "%s", dirname);
1117 }
1119
1120 return astr_str(&realfile);
1121 }
1122
1123#ifndef DIR_SEPARATOR_IS_DEFAULT
1124 for (i = 0; filename[i] != '\0'; i++) {
1125 if (filename[i] == '/') {
1127 } else {
1128 fnbuf[i] = filename[i];
1129 }
1130 }
1131 fnbuf[i] = '\0';
1132#endif /* DIR_SEPARATOR_IS_DEFAULT */
1133
1134 strvec_iterate(dirs, dirname) {
1135 struct stat buf; /* See if we can open the file or directory */
1136
1138 if (fc_stat(astr_str(&realfile), &buf) == 0) {
1139 return astr_str(&realfile);
1140 }
1142
1143 log_verbose("Could not find readable file \"%s\" in data path.", filename);
1144
1145 return nullptr;
1146}
1147
1148/************************************************************************/
1152{
1154}
1155
1156/************************************************************************/
1159static void fileinfo_destroy(struct fileinfo *pfile)
1160{
1161 free(pfile->name);
1162 free(pfile->fullname);
1163 free(pfile);
1164}
1165
1166/************************************************************************/
1169static int compare_file_mtime_ptrs(const struct fileinfo *const *ppa,
1170 const struct fileinfo *const *ppb)
1171{
1172 time_t a = (*ppa)->mtime;
1173 time_t b = (*ppb)->mtime;
1174
1175 return ((a < b) ? 1 : (a > b) ? -1 : 0);
1176}
1177
1178/************************************************************************/
1181static int compare_file_name_ptrs(const struct fileinfo *const *ppa,
1182 const struct fileinfo *const *ppb)
1183{
1184 return fc_strcoll((*ppa)->name, (*ppb)->name);
1185}
1186
1187/************************************************************************/
1190static bool compare_fileinfo_name(const struct fileinfo *pa,
1191 const struct fileinfo *pb)
1192{
1193 return 0 == fc_strcoll(pa->name, pb->name);
1194}
1195
1196/************************************************************************/
1204struct fileinfo_list *fileinfolist_infix(const struct strvec *dirs,
1205 const char *infix, bool nodups)
1206{
1207 struct fileinfo_list *res;
1208
1209 if (dirs == nullptr) {
1210 return nullptr;
1211 }
1212
1214
1215 /* First assemble a full list of names. */
1216 strvec_iterate(dirs, dirname) {
1217 DIR *dir;
1218 struct dirent *entry;
1219
1220 /* Open the directory for reading. */
1221 dir = fc_opendir(dirname);
1222 if (!dir) {
1223 continue;
1224 }
1225
1226 /* Scan all entries in the directory. */
1227 while ((entry = readdir(dir))) {
1228 struct fileinfo *file;
1229 char *ptr;
1230 /* Strdup the entry so we can safely write to it. */
1231 char *filename = fc_strdup(entry->d_name);
1232
1233 /* Make sure the file name matches. */
1234 if ((ptr = strstr(filename, infix))) {
1235 struct stat buf;
1236 char *fullname;
1237 size_t len = strlen(dirname) + strlen(filename) + 2;
1238
1239 fullname = fc_malloc(len);
1240 fc_snprintf(fullname, len, "%s" DIR_SEPARATOR "%s", dirname, filename);
1241
1242 if (fc_stat(fullname, &buf) == 0) {
1243 file = fc_malloc(sizeof(*file));
1244
1245 /* Clip the suffix. */
1246 *ptr = '\0';
1247
1248 file->name = filename;
1249 file->fullname = fullname;
1250 file->mtime = buf.st_mtime;
1251
1253 } else {
1254 free(fullname);
1255 free(filename);
1256 }
1257 } else {
1258 free(filename);
1259 }
1260 }
1261
1262 closedir(dir);
1264
1265 /* Sort the list by name. */
1267
1268 if (nodups) {
1270 }
1271
1272 /* Sort the list by last modification time. */
1274
1275 return res;
1276}
1277
1278/************************************************************************/
1281const char *setup_langname(void)
1282{
1283 const char *langname = nullptr;
1284
1285#ifdef ENABLE_NLS
1286 langname = getenv("LANG");
1287
1288#ifdef FREECIV_MSWINDOWS
1289 /* Set LANG by hand if it is not set */
1290 if (!langname) {
1292 case LANG_ARABIC:
1293 langname = "ar";
1294 break;
1295 case LANG_CATALAN:
1296 langname = "ca";
1297 break;
1298 case LANG_CZECH:
1299 langname = "cs";
1300 break;
1301 case LANG_DANISH:
1302 langname = "da";
1303 break;
1304 case LANG_GERMAN:
1305 langname = "de";
1306 break;
1307 case LANG_GREEK:
1308 langname = "el";
1309 break;
1310 case LANG_ENGLISH:
1311 switch (SUBLANGID(GetUserDefaultLangID())) {
1312 case SUBLANG_ENGLISH_UK:
1313 langname = "en_GB";
1314 break;
1315 default:
1316 langname = "en";
1317 break;
1318 }
1319 break;
1320 case LANG_SPANISH:
1321 langname = "es";
1322 break;
1323 case LANG_ESTONIAN:
1324 langname = "et";
1325 break;
1326 case LANG_FARSI:
1327 langname = "fa";
1328 break;
1329 case LANG_FINNISH:
1330 langname = "fi";
1331 break;
1332 case LANG_FRENCH:
1333 langname = "fr";
1334 break;
1335 case LANG_HEBREW:
1336 langname = "he";
1337 break;
1338 case LANG_HUNGARIAN:
1339 langname = "hu";
1340 break;
1341 case LANG_ITALIAN:
1342 langname = "it";
1343 break;
1344 case LANG_JAPANESE:
1345 langname = "ja";
1346 break;
1347 case LANG_KOREAN:
1348 langname = "ko";
1349 break;
1350 case LANG_LITHUANIAN:
1351 langname = "lt";
1352 break;
1353 case LANG_DUTCH:
1354 langname = "nl";
1355 break;
1356 case LANG_NORWEGIAN:
1357 langname = "nb";
1358 break;
1359 case LANG_POLISH:
1360 langname = "pl";
1361 break;
1362 case LANG_PORTUGUESE:
1363 switch (SUBLANGID(GetUserDefaultLangID())) {
1365 langname = "pt_BR";
1366 break;
1367 default:
1368 langname = "pt";
1369 break;
1370 }
1371 break;
1372 case LANG_ROMANIAN:
1373 langname = "ro";
1374 break;
1375 case LANG_RUSSIAN:
1376 langname = "ru";
1377 break;
1378 case LANG_SWEDISH:
1379 langname = "sv";
1380 break;
1381 case LANG_TURKISH:
1382 langname = "tr";
1383 break;
1384 case LANG_UKRAINIAN:
1385 langname = "uk";
1386 break;
1387 case LANG_CHINESE:
1388 langname = "zh_CN";
1389 break;
1390 }
1391
1392 if (langname != nullptr) {
1393 static char envstr[40];
1394
1395 fc_snprintf(envstr, sizeof(envstr), "LANG=%s", langname);
1396 putenv(envstr);
1397 }
1398 }
1399#endif /* FREECIV_MSWINDOWS */
1400#endif /* ENABLE_NLS */
1401
1402 return langname;
1403}
1404
1405#ifdef FREECIV_ENABLE_NLS
1406/************************************************************************/
1409static void autocap_update(void)
1410{
1411 char *autocap_opt_in[] = { "fi", nullptr };
1412 int i;
1413 bool ac_enabled = FALSE;
1414
1415 char *lang = getenv("LANG");
1416
1417 if (lang != nullptr && lang[0] != '\0' && lang[1] != '\0') {
1418 for (i = 0; autocap_opt_in[i] != nullptr && !ac_enabled; i++) {
1419 if (lang[0] == autocap_opt_in[i][0]
1420 && lang[1] == autocap_opt_in[i][1]) {
1421 ac_enabled = TRUE;
1422 break;
1423 }
1424 }
1425 }
1426
1428}
1429#endif /* FREECIV_ENABLE_NLS */
1430
1431/************************************************************************/
1434void switch_lang(const char *lang)
1435{
1436#ifdef FREECIV_ENABLE_NLS
1437#ifdef HAVE_SETENV
1438 setenv("LANG", lang, TRUE);
1439#else /* HAVE_SETENV */
1440 if (lang != nullptr) {
1441 static char envstr[40];
1442
1443 fc_snprintf(envstr, sizeof(envstr), "LANG=%s", lang);
1444 putenv(envstr);
1445 }
1446#endif /* HAVE_SETENV */
1447
1448 (void) setlocale(LC_ALL, "");
1449 (void) bindtextdomain("freeciv-core", get_locale_dir());
1450
1452
1453 log_normal("LANG set to %s", lang);
1454#else /* FREECIV_ENABLE_NLS */
1456#endif /* FREECIV_ENABLE_NLS */
1457}
1458
1459/************************************************************************/
1463void init_nls(void)
1464{
1465 /*
1466 * Setup the cached locale numeric formatting information. Defaults
1467 * are as appropriate for the US.
1468 */
1469 grouping = fc_strdup("\3");
1470 grouping_sep = fc_strdup(",");
1471
1472#ifdef ENABLE_NLS
1473
1474#ifdef FREECIV_MSWINDOWS
1475 setup_langname(); /* Makes sure LANG env variable has been set */
1476#endif /* FREECIV_MSWINDOWS */
1477
1478 (void) setlocale(LC_ALL, "");
1479 (void) bindtextdomain("freeciv-core", get_locale_dir());
1480 (void) textdomain("freeciv-core");
1481
1482 /* Don't touch the defaults when LC_NUMERIC == "C".
1483 This is intended to cater to the common case where:
1484 1) The user is from North America. ;-)
1485 2) The user has not set the proper environment variables.
1486 (Most applications are (unfortunately) US-centric
1487 by default, so why bother?)
1488 This would result in the "C" locale being used, with grouping ""
1489 and thousands_sep "", where we really want "\3" and ",". */
1490
1491 if (strcmp(setlocale(LC_NUMERIC, nullptr), "C") != 0) {
1492 struct lconv *lc = localeconv();
1493
1494 if (lc->grouping[0] == '\0') {
1495 /* This actually indicates no grouping at all. */
1496 char *m = fc_malloc(sizeof(char));
1497
1498 free(grouping);
1499 *m = CHAR_MAX;
1500 grouping = m;
1501 } else {
1502 size_t len;
1503
1504 for (len = 0;
1505 lc->grouping[len] != '\0' && lc->grouping[len] != CHAR_MAX; len++) {
1506 /* Nothing */
1507 }
1508 len++;
1509 free(grouping);
1511 memcpy(grouping, lc->grouping, len);
1512 }
1514 grouping_sep = fc_strdup(lc->thousands_sep);
1515 }
1516
1518
1519#endif /* ENABLE_NLS */
1520}
1521
1522/************************************************************************/
1525void free_nls(void)
1526{
1527 free(grouping);
1528 grouping = nullptr;
1530 grouping_sep = nullptr;
1531}
1532
1533/************************************************************************/
1542void dont_run_as_root(const char *argv0, const char *fallback)
1543{
1544#if (defined(ALWAYS_ROOT) || defined(__EMX__) || defined(__BEOS__))
1545 return;
1546#else
1547 if (getuid() == 0 || geteuid() == 0) {
1549 _("%s: Fatal error: you're trying to run me as superuser!\n"),
1550 (argv0 ? argv0 : fallback ? fallback : "freeciv"));
1551 fc_fprintf(stderr, _("Use a non-privileged account instead.\n"));
1553 }
1554#endif /* ALWAYS_ROOT */
1555}
1556
1557/************************************************************************/
1564const char *m_pre_description(enum m_pre_result result)
1565{
1566 static const char * const descriptions[] = {
1567 N_("exact match"),
1568 N_("only match"),
1569 N_("ambiguous"),
1570 N_("empty"),
1571 N_("too long"),
1572 N_("non-match")
1573 };
1574
1575 fc_assert_ret_val(result >= 0 && result < ARRAY_SIZE(descriptions), nullptr);
1576
1577 return descriptions[result];
1578}
1579
1580/************************************************************************/
1584 size_t n_names,
1585 size_t max_len_name,
1588 const char *prefix,
1589 int *ind_result)
1590{
1592 len_fn, prefix, ind_result, nullptr, 0, nullptr);
1593}
1594
1595/************************************************************************/
1607 size_t n_names,
1608 size_t max_len_name,
1611 const char *prefix,
1612 int *ind_result,
1613 int *matches,
1614 int max_matches,
1615 int *pnum_matches)
1616{
1617 int i, len, nmatches;
1618
1619 if (len_fn == nullptr) {
1620 len = strlen(prefix);
1621 } else {
1622 len = len_fn(prefix);
1623 }
1624 if (len == 0) {
1625 return M_PRE_EMPTY;
1626 }
1627 if (len > max_len_name && max_len_name > 0) {
1628 return M_PRE_LONG;
1629 }
1630
1631 nmatches = 0;
1632 for (i = 0; i < n_names; i++) {
1633 const char *name = accessor_fn(i);
1634
1635 if (cmp_fn(name, prefix, len) == 0) {
1636 if (strlen(name) == len) {
1637 *ind_result = i;
1638 return M_PRE_EXACT;
1639 }
1640 if (nmatches == 0) {
1641 *ind_result = i; /* First match */
1642 }
1643 if (matches != nullptr && nmatches < max_matches) {
1644 matches[nmatches] = i;
1645 }
1646 nmatches++;
1647 }
1648 }
1649
1650 if (nmatches == 1) {
1651 return M_PRE_ONLY;
1652 } else if (nmatches > 1) {
1653 if (pnum_matches != nullptr) {
1655 }
1656 return M_PRE_AMBIGUOUS;
1657 } else {
1658 return M_PRE_FAIL;
1659 }
1660}
1661
1662/************************************************************************/
1668{
1669 static char *default_multicast_group_ipv4 = "225.1.1.1";
1670#ifdef FREECIV_IPV6_SUPPORT
1671 /* TODO: Get useful group (this is node local) */
1672 static char *default_multicast_group_ipv6 = "FF31::8000:15B4";
1673#endif /* IPv6 support */
1674
1675 if (mc_group == nullptr) {
1676 char *env = getenv("FREECIV_MULTICAST_GROUP");
1677
1678 if (env) {
1680 } else {
1681#ifdef FREECIV_IPV6_SUPPORT
1682 if (ipv6_preferred) {
1684 } else
1685#endif /* IPv6 support */
1686 {
1688 }
1689 }
1690 }
1691
1692 return mc_group;
1693}
1694
1695/************************************************************************/
1699{
1700 if (mc_group != nullptr) {
1701 free(mc_group);
1702 mc_group = nullptr;
1703 }
1704}
1705
1706/************************************************************************/
1713void interpret_tilde(char *buf, size_t buf_size, const char *filename)
1714{
1715 if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1716 fc_snprintf(buf, buf_size, "%s" DIR_SEPARATOR "%s", user_home_dir(), filename + 2);
1717 } else if (filename[0] == '~' && filename[1] == '\0') {
1719 } else {
1720 strncpy(buf, filename, buf_size);
1721 }
1722}
1723
1724/************************************************************************/
1730char *interpret_tilde_alloc(const char *filename)
1731{
1732 if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1733 const char *home = user_home_dir();
1734 size_t sz;
1735 char *buf;
1736
1737 filename += 2; /* Skip past "~/" */
1738 sz = strlen(home) + strlen(filename) + 2;
1739 buf = fc_malloc(sz);
1740 fc_snprintf(buf, sz, "%s/%s", home, filename);
1741 return buf;
1742 } else if (filename[0] == '~' && filename[1] == '\0') {
1743 return fc_strdup(user_home_dir());
1744 } else {
1745 return fc_strdup(filename);
1746 }
1747}
1748
1749/************************************************************************/
1753char *skip_to_basename(char *filepath)
1754{
1755 int j;
1756
1757 fc_assert_ret_val(filepath != nullptr, nullptr);
1758
1759 for (j = strlen(filepath); j >= 0; j--) {
1760 if (filepath[j] == DIR_SEPARATOR_CHAR) {
1761 return &filepath[j+1];
1762 }
1763 }
1764 return filepath;
1765}
1766
1767/************************************************************************/
1779bool make_dir(const char *pathname, int mode)
1780{
1781 char *dir;
1782 char *path = nullptr;
1783
1784 if (pathname[0] == '\0') {
1785 return FALSE;
1786 }
1787
1788 if (mode < 0) {
1789 mode = 0755;
1790 }
1791
1793 dir = path;
1794
1795 if (*dir == '/') {
1796 /* Don't consider root as directory separator, but skip it. */
1797 dir++;
1798 } else if (dir[0] != '\0' && dir[1] == ':' && dir[2] == '\\') {
1799 /* Don't consider Windows Drive a directory to create, but skip it. */
1800 dir += 3;
1801 }
1802
1803 do {
1804 dir = strchr(dir, DIR_SEPARATOR_CHAR);
1805 /* We set the current / with 0, and restore it afterwards */
1806 if (dir) {
1807 *dir = '\0';
1808 }
1809
1810#ifdef FREECIV_MSWINDOWS
1811#ifdef HAVE__MKDIR
1812 /* Prefer _mkdir() in Windows even if mkdir() would seem to be available -
1813 * chances are that it's wrong kind of mkdir().
1814 * TODO: Make a configure check for mkdir() that also makes sure that it
1815 * takes two parameters, and prefer such proper mkdir() here. */
1816 {
1818 bool failure = FALSE;
1819
1822 failure = TRUE;
1823 }
1824
1826
1827 if (failure) {
1828 free(path);
1829 return FALSE;
1830 }
1831 }
1832#else /* HAVE__MKDIR */
1833 if (mkdir(path, mode) == -1
1834 && fc_get_errno() != EEXIST) {
1835 free(path);
1836 return FALSE;
1837 }
1838#endif /* HAVE__MKDIR */
1839#else /* FREECIV_MSWINDOWS */
1840 if (mkdir(path, mode) == -1
1841 && fc_get_errno() != EEXIST) {
1842 free(path);
1843 return FALSE;
1844 }
1845#endif /* FREECIV_MSWINDOWS */
1846
1847 if (dir) {
1848 *dir = DIR_SEPARATOR_CHAR;
1849 dir++;
1850 }
1851 } while (dir);
1852
1853 free(path);
1854
1855 return TRUE;
1856}
1857
1858/************************************************************************/
1862bool make_dir_for_file(char *filename)
1863{
1864 int i;
1865
1866 for (i = strlen(filename) - 1 ; filename[i] != DIR_SEPARATOR_CHAR ; i--) {
1867 /* Nothing */
1868 }
1869
1870 filename[i] = '\0';
1871 log_debug("Create directory \"%s\"", filename);
1872
1873 if (!make_dir(filename, DIRMODE_DEFAULT)) {
1874 return FALSE;
1875 }
1876 filename[i] = DIR_SEPARATOR_CHAR;
1877
1878 return TRUE;
1879}
1880
1881/************************************************************************/
1884bool path_is_absolute(const char *filename)
1885{
1886 if (!filename) {
1887 return FALSE;
1888 }
1889
1890#ifdef FREECIV_MSWINDOWS
1891 if (strchr(filename, ':')) {
1892 return TRUE;
1893 }
1894#else /* FREECIV_MSWINDOWS */
1895 if (filename[0] == '/') {
1896 return TRUE;
1897 }
1898#endif /* FREECIV_MSWINDOWS */
1899
1900 return FALSE;
1901}
1902
1903/************************************************************************/
1923char scanin(const char **buf, char *delimiters, char *dest, int size)
1924{
1925 char *ptr, found = '?';
1926
1927 if (*buf == nullptr || strlen(*buf) == 0 || size == 0) {
1928 if (dest) {
1929 dest[0] = '\0';
1930 }
1931 *buf = nullptr;
1932 return '\0';
1933 }
1934
1935 if (dest) {
1936 strncpy(dest, *buf, size-1);
1937 dest[size-1] = '\0';
1939 ptr = strpbrk(dest, delimiters);
1940 } else {
1941 /* Just skip ahead. */
1942 ptr = strpbrk(*buf, delimiters);
1943 }
1944 if (ptr != nullptr) {
1945 found = *ptr;
1946 if (dest) {
1947 *ptr = '\0';
1948 }
1949 if (dest) {
1951 }
1952 *buf = strpbrk(*buf, delimiters);
1953 if (*buf != nullptr) {
1954 (*buf)++; /* Skip delimiter */
1955 } else {
1956 }
1957 } else {
1958 *buf = nullptr;
1959 }
1960
1961 return found;
1962}
1963
1964/************************************************************************/
1969{
1970 int seconds, minutes, hours, days;
1971 bool space = FALSE;
1972
1973 seconds = t % 60;
1974 minutes = (t / 60) % 60;
1975 hours = (t / (60 * 60)) % 24;
1976 days = t / (60 * 60 * 24);
1977
1978 if (maxlen <= 0) {
1979 return;
1980 }
1981
1982 buf[0] = '\0';
1983
1984 if (days > 0) {
1985 cat_snprintf(buf, maxlen, "%d %s", days, PL_("day", "days", days));
1986 space = TRUE;
1987 }
1988 if (hours > 0) {
1989 cat_snprintf(buf, maxlen, "%s%d %s",
1990 space ? " " : "", hours, PL_("hour", "hours", hours));
1991 space = TRUE;
1992 }
1993 if (minutes > 0) {
1994 cat_snprintf(buf, maxlen, "%s%d %s",
1995 space ? " " : "",
1996 minutes, PL_("minute", "minutes", minutes));
1997 space = TRUE;
1998 }
1999 if (seconds > 0) {
2000 cat_snprintf(buf, maxlen, "%s%d %s",
2001 space ? " " : "",
2002 seconds, PL_("second", "seconds", seconds));
2003 }
2004}
2005
2006/************************************************************************/
2011void array_shuffle(int *array, int n)
2012{
2013 if (n > 1 && array != nullptr) {
2014 int i, j, t;
2015
2016 for (i = 0; i < n - 1; i++) {
2017 j = i + fc_rand(n - i);
2018 t = array[j];
2019 array[j] = array[i];
2020 array[i] = t;
2021 }
2022 }
2023}
2024
2025/************************************************************************/
2030static bool wildcard_asterisk_fit(const char *pattern, const char *test)
2031{
2032 char jump_to;
2033
2034 /* Jump over the leading asterisks. */
2035 pattern++;
2036 while (TRUE) {
2037 switch (*pattern) {
2038 case '\0':
2039 /* It is a leading asterisk. */
2040 return TRUE;
2041 case '*':
2042 pattern++;
2043 continue;
2044 case '?':
2045 if ('\0' == *test) {
2046 return FALSE;
2047 }
2048 test++;
2049 pattern++;
2050 continue;
2051 }
2052
2053 break;
2054 }
2055
2056 if ('[' != *pattern) {
2057 if ('\\' == *pattern) {
2058 jump_to = *(pattern + 1);
2059 } else {
2060 jump_to = *pattern;
2061 }
2062 } else {
2063 jump_to = '\0';
2064 }
2065
2066 while ('\0' != *test) {
2067 if ('\0' != jump_to) {
2068 /* Jump to next matching charather. */
2069 test = strchr(test, jump_to);
2070 if (test == nullptr) {
2071 /* No match. */
2072 return FALSE;
2073 }
2074 }
2075
2077 return TRUE;
2078 }
2079
2080 (test)++;
2081 }
2082
2083 return FALSE;
2084}
2085
2086/************************************************************************/
2090static bool wildcard_range_fit(const char **pattern, const char **test)
2091{
2092 const char *start = (*pattern + 1);
2093 char testc;
2094 bool negation;
2095
2096 if ('\0' == **test) {
2097 /* Need one character. */
2098 return FALSE;
2099 }
2100
2101 /* Find the end of the pattern. */
2102 while (TRUE) {
2103 *pattern = strchr(*pattern, ']');
2104 if (*pattern == nullptr) {
2105 /* Wildcard format error. */
2106 return FALSE;
2107 } else if (*(*pattern - 1) != '\\') {
2108 /* This is the end. */
2109 break;
2110 } else {
2111 /* Try again. */
2112 (*pattern)++;
2113 }
2114 }
2115
2116 if ('!' == *start) {
2117 negation = TRUE;
2118 start++;
2119 } else {
2120 negation = FALSE;
2121 }
2122 testc = **test;
2123 (*test)++;
2124 (*pattern)++;
2125
2126 for (; start < *pattern; start++) {
2127 if ('-' == *start || '!' == *start) {
2128 /* Wildcard format error. */
2129 return FALSE;
2130 } else if (start < *pattern - 2 && '-' == *(start + 1)) {
2131 /* Case range. */
2132 if (*start <= testc && testc <= *(start + 2)) {
2133 return !negation;
2134 }
2135 start += 2;
2136 } else if (*start == testc) {
2137 /* Single character. */
2138 return !negation;
2139 }
2140 }
2141
2142 return negation;
2143}
2144
2145/************************************************************************/
2156bool wildcard_fit_string(const char *pattern, const char *test)
2157{
2158 while (TRUE) {
2159 switch (*pattern) {
2160 case '\0':
2161 /* '\0' != test. */
2162 return '\0' == *test;
2163 case '*':
2164 return wildcard_asterisk_fit(pattern, test); /* Maybe recursive. */
2165 case '[':
2166 if (!wildcard_range_fit(&pattern, &test)) {
2167 return FALSE;
2168 }
2169 continue;
2170 case '?':
2171 if ('\0' == *test) {
2172 return FALSE;
2173 }
2174 break;
2175 case '\\':
2176 pattern++;
2178 default:
2179 if (*pattern != *test) {
2180 return FALSE;
2181 }
2182 break;
2183 }
2184 pattern++;
2185 test++;
2186 }
2187
2188 return FALSE;
2189}
2190
2191/************************************************************************/
2205int fc_vsnprintcf(char *buf, size_t buf_len, const char *format,
2206 const struct cf_sequence *sequences, size_t sequences_num)
2207{
2208 const struct cf_sequence *pseq;
2209 char cformat[32];
2210 const char *f = format;
2211 char *const max = buf + buf_len - 1;
2212 char *b = buf, *c;
2213 const char *const cmax = cformat + sizeof(cformat) - 2;
2214 int i, j;
2215
2216 if ((size_t) -1 == sequences_num) {
2217 /* Find the number of sequences. */
2218 sequences_num = 0;
2219 for (pseq = sequences; CF_LAST != pseq->type; pseq++) {
2220 sequences_num++;
2221 }
2222 }
2223
2224 while ('\0' != *f) {
2225 if ('%' == *f) {
2226 /* Sequence. */
2227
2228 f++;
2229 if ('%' == *f) {
2230 /* Double '%'. */
2231 *b++ = '%';
2232 f++;
2233 continue;
2234 }
2235
2236 /* Make format. */
2237 c = cformat;
2238 *c++ = '%';
2239 for (; !fc_isalpha(*f) && '\0' != *f && '%' != *f && cmax > c; f++) {
2240 *c++ = *f;
2241 }
2242
2243 if (!fc_isalpha(*f)) {
2244 /* Beginning of a new sequence, end of the format, or too long
2245 * sequence. */
2246 *c = '\0';
2247 j = fc_snprintf(b, max - b + 1, "%s", cformat);
2248 if (-1 == j) {
2249 return -1;
2250 }
2251 b += j;
2252 continue;
2253 }
2254
2255 for (i = 0, pseq = sequences; i < sequences_num; i++, pseq++) {
2256 if (pseq->letter == *f) {
2257 j = -2;
2258 switch (pseq->type) {
2259 case CF_BOOLEAN:
2260 *c++ = 's';
2261 *c = '\0';
2262 j = fc_snprintf(b, max - b + 1, cformat,
2263 pseq->bool_value ? "TRUE" : "FALSE");
2264 break;
2265 case CF_TRANS_BOOLEAN:
2266 *c++ = 's';
2267 *c = '\0';
2268 j = fc_snprintf(b, max - b + 1, cformat,
2269 pseq->bool_value ? _("TRUE") : _("FALSE"));
2270 break;
2271 case CF_CHARACTER:
2272 *c++ = 'c';
2273 *c = '\0';
2274 j = fc_snprintf(b, max - b + 1, cformat, pseq->char_value);
2275 break;
2276 case CF_INTEGER:
2277 *c++ = 'd';
2278 *c = '\0';
2279 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2280 break;
2281 case CF_HEXA:
2282 *c++ = 'x';
2283 *c = '\0';
2284 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2285 break;
2286 case CF_FLOAT:
2287 *c++ = 'f';
2288 *c = '\0';
2289 j = fc_snprintf(b, max - b + 1, cformat, pseq->float_value);
2290 break;
2291 case CF_POINTER:
2292 *c++ = 'p';
2293 *c = '\0';
2294 j = fc_snprintf(b, max - b + 1, cformat, pseq->ptr_value);
2295 break;
2296 case CF_STRING:
2297 *c++ = 's';
2298 *c = '\0';
2299 j = fc_snprintf(b, max - b + 1, cformat, pseq->str_value);
2300 break;
2301 case CF_LAST:
2302 break;
2303 };
2304 if (-2 == j) {
2305 log_error("Error: unsupported sequence type: %d.", pseq->type);
2306 break;
2307 }
2308 if (-1 == j) {
2309 /* Full! */
2310 return -1;
2311 }
2312 f++;
2313 b += j;
2314 break;
2315 }
2316 }
2317 if (i >= sequences_num) {
2318 /* Format not supported. */
2319 *c = '\0';
2320 j = fc_snprintf(b, max - b + 1, "%s%c", cformat, *f);
2321 if (-1 == j) {
2322 return -1;
2323 }
2324 f++;
2325 b += j;
2326 }
2327 } else {
2328 /* Not a sequence. */
2329 *b++ = *f++;
2330 }
2331 if (max <= b) {
2332 /* Too long. */
2333 *max = '\0';
2334 return -1;
2335 }
2336 }
2337 *b = '\0';
2338 return b - buf;
2339}
2340
2341/************************************************************************/
2353int fc_snprintcf(char *buf, size_t buf_len, const char *format, ...)
2354{
2355 struct cf_sequence sequences[16];
2356 size_t sequences_num = 0;
2357 va_list args;
2358
2359 /* Collect sequence array. */
2360 va_start(args, format);
2361 do {
2362 sequences[sequences_num] = va_arg(args, struct cf_sequence);
2364 break;
2365 } else {
2366 sequences_num++;
2367 }
2368 } while (ARRAY_SIZE(sequences) > sequences_num);
2369
2371 && CF_LAST != va_arg(args, struct cf_sequence).type) {
2372 log_error("Too many custom sequences. Maybe did you forget cf_end() "
2373 "at the end of the arguments?");
2374 buf[0] = '\0';
2375 va_end(args);
2376 return -1;
2377 }
2378 va_end(args);
2379
2380 return fc_vsnprintcf(buf, buf_len, format, sequences, sequences_num);
2381}
2382
2383/************************************************************************/
2387static size_t extract_escapes(const char *format, char *escapes,
2388 size_t max_escapes)
2389{
2390 static const char format_escapes[] = {
2391 '*', 'd', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f',
2392 'F', 'g', 'G', 'a', 'A', 'c', 's', 'p', 'n', '\0'
2393 };
2394 bool reordered = FALSE;
2395 size_t num = 0;
2396 int idx = 0;
2397
2399 format = strchr(format, '%');
2400 while (format != nullptr) {
2401 format++;
2402 if ('%' == *format) {
2403 /* Double, not a sequence. */
2404 continue;
2405 } else if (fc_isdigit(*format)) {
2406 const char *start = format;
2407
2408 do {
2409 format++;
2410 } while (fc_isdigit(*format));
2411
2412 if ('$' == *format) {
2413 /* Strings are reordered. */
2414 sscanf(start, "%d", &idx);
2415 reordered = TRUE;
2416 }
2417 }
2418
2419 while ('\0' != *format
2420 && strchr(format_escapes, *format) == nullptr) {
2421 format++;
2422 }
2423 escapes[idx] = *format;
2424
2425 /* Increase the read count. */
2426 if (reordered) {
2427 if (idx > num) {
2428 num = idx;
2429 }
2430 } else {
2431 idx++;
2432 num++;
2433 }
2434
2435 if ('*' != *format) {
2436 format = strchr(format, '%');
2437 } /* Else we didn't have found the real sequence. */
2438 }
2439 return num;
2440}
2441
2442/************************************************************************/
void astr_free(struct astring *astr)
Definition astring.c:148
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:251
void astr_clear(struct astring *astr)
Definition astring.c:201
void astr_add(struct astring *astr, const char *format,...)
Definition astring.c:271
#define str
Definition astring.c:76
#define n
Definition astring.c:77
static const char * astr_str(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:93
#define ASTRING_INIT
Definition astring.h:44
const size_t buf_size
Definition audio_sdl.c:53
char * incite_cost
Definition comments.c:76
DIR * fc_opendir(const char *dir_to_open)
Definition fc_dirent.c:29
char * internal_to_local_string_malloc(const char *text)
char * local_to_internal_string_buffer(const char *text, char *buf, size_t bufsz)
void fc_fprintf(FILE *stream, const char *format,...) fc__attribute((__format__(__printf__
const char * get_locale_dir(void)
Definition fcintl.c:111
void capitalization_opt_in(bool opt_in)
Definition fcintl.c:93
#define bindtextdomain(Package, Directory)
Definition fcintl.h:82
#define PL_(String1, String2, n)
Definition fcintl.h:71
#define _(String)
Definition fcintl.h:67
#define textdomain(Domain)
Definition fcintl.h:81
#define N_(String)
Definition fcintl.h:69
GType type
Definition repodlgs.c:1313
static const int bufsz
Definition helpdlg.c:70
const char * name
Definition inputfile.c:127
#define log_verbose(message,...)
Definition log.h:110
#define fc_assert(condition)
Definition log.h:177
#define fc_assert_ret_val(condition, val)
Definition log.h:195
#define log_debug(message,...)
Definition log.h:116
#define log_normal(message,...)
Definition log.h:108
#define log_error(message,...)
Definition log.h:104
#define fc_assert_ret_val_msg(condition, val, message,...)
Definition log.h:209
#define fc_strdup(str)
Definition mem.h:43
#define fc_malloc(sz)
Definition mem.h:34
int len
Definition packhand.c:127
#define fc_rand(_size)
Definition rand.h:56
int compare_strings(const void *first, const void *second)
Definition shared.c:363
static size_t extract_escapes(const char *format, char *escapes, size_t max_escapes)
Definition shared.c:2387
void format_time_duration(time_t t, char *buf, int maxlen)
Definition shared.c:1968
static bool wildcard_asterisk_fit(const char *pattern, const char *test)
Definition shared.c:2030
int compare_strings_ptrs(const void *first, const void *second)
Definition shared.c:373
char * user_username(char *buf, size_t bufsz)
Definition shared.c:704
int fc_snprintcf(char *buf, size_t buf_len, const char *format,...)
Definition shared.c:2353
static char * grouping_sep
Definition shared.c:99
enum fc_tristate fc_tristate_and(enum fc_tristate one, enum fc_tristate two)
Definition shared.c:127
static char * grouping
Definition shared.c:98
void dont_run_as_root(const char *argv0, const char *fallback)
Definition shared.c:1542
bool wildcard_fit_string(const char *pattern, const char *test)
Definition shared.c:2156
bool check_strlen(const char *str, size_t len, const char *errmsg)
Definition shared.c:493
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1094
static bool compare_fileinfo_name(const struct fileinfo *pa, const struct fileinfo *pb)
Definition shared.c:1190
char * user_home_dir(void)
Definition shared.c:627
void remove_trailing_spaces(char *s)
Definition shared.c:422
void free_user_home_dir(void)
Definition shared.c:658
int fc_vsnprintcf(char *buf, size_t buf_len, const char *format, const struct cf_sequence *sequences, size_t sequences_num)
Definition shared.c:2205
#define HOMEVAR
static char * storage_dir_freeciv
Definition shared.c:112
void init_nls(void)
Definition shared.c:1463
bool str_to_int(const char *str, int *pint)
Definition shared.c:515
#define default_data_path
bool str_to_float(const char *str, float *pfloat)
Definition shared.c:579
const char * m_pre_description(enum m_pre_result result)
Definition shared.c:1564
static struct strvec * data_dir_names
Definition shared.c:106
#define FREECIV_SCENARIO_PATH
Definition shared.c:93
static const char base64url[]
Definition shared.c:103
struct strvec * fileinfolist(const struct strvec *dirs, const char *suffix)
Definition shared.c:1020
static char * mc_group
Definition shared.c:110
char * skip_leading_spaces(char *s)
Definition shared.c:392
bool str_to_uint(const char *str, unsigned int *pint)
Definition shared.c:547
void free_data_dir_names(void)
Definition shared.c:859
enum m_pre_result 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)
Definition shared.c:1606
const struct strvec * get_scenario_dirs(void)
Definition shared.c:971
static void remove_trailing_char(char *s, char trailing) fc__attribute((nonnull(1)))
Definition shared.c:453
size_t loud_strlcpy(char *buffer, const char *str, size_t len, const char *errmsg)
Definition shared.c:503
char * interpret_tilde_alloc(const char *filename)
Definition shared.c:1730
#define FREECIV_SAVE_PATH
Definition shared.c:90
bool make_dir_for_file(char *filename)
Definition shared.c:1862
static void fileinfo_destroy(struct fileinfo *pfile)
Definition shared.c:1159
void interpret_tilde(char *buf, size_t buf_size, const char *filename)
Definition shared.c:1713
static struct astring realfile
Definition shared.c:114
bool make_dir(const char *pathname, int mode)
Definition shared.c:1779
int compare_strings_strvec(const char *const *first, const char *const *second)
Definition shared.c:383
bool is_base64url(const char *s)
Definition shared.c:321
void free_multicast_group(void)
Definition shared.c:1698
static char * home_dir_user
Definition shared.c:111
void free_fileinfo_data(void)
Definition shared.c:1151
static bool is_ascii(char ch)
Definition shared.c:246
char * end_of_strn(char *str, int *nleft)
Definition shared.c:478
bool path_is_absolute(const char *filename)
Definition shared.c:1884
void free_nls(void)
Definition shared.c:1525
static bool wildcard_range_fit(const char **pattern, const char **test)
Definition shared.c:2090
static struct strvec * scenario_dir_names
Definition shared.c:108
static int compare_file_mtime_ptrs(const struct fileinfo *const *ppa, const struct fileinfo *const *ppb)
Definition shared.c:1169
bool formats_match(const char *format1, const char *format2)
Definition shared.c:2446
const char * int_to_text(unsigned int number)
Definition shared.c:237
char scanin(const char **buf, char *delimiters, char *dest, int size)
Definition shared.c:1923
static int compare_file_name_ptrs(const struct fileinfo *const *ppa, const struct fileinfo *const *ppb)
Definition shared.c:1181
const struct strvec * get_save_dirs(void)
Definition shared.c:934
static char * expand_dir(char *tok_in, bool ok_to_free)
Definition shared.c:778
void remove_leading_spaces(char *s)
Definition shared.c:405
char * skip_to_basename(char *filepath)
Definition shared.c:1753
#define FREECIV_DATA_PATH
Definition shared.c:87
#define default_scenario_path
void array_shuffle(int *array, int n)
Definition shared.c:2011
enum m_pre_result 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)
Definition shared.c:1583
bool is_ascii_name(const char *name)
Definition shared.c:286
#define DEFAULT_SAVE_PATH
Definition shared.c:74
enum fc_tristate fc_tristate_or(enum fc_tristate one, enum fc_tristate two)
Definition shared.c:143
static struct strvec * base_get_dirs(const char *dir_list)
Definition shared.c:832
const char * setup_langname(void)
Definition shared.c:1281
void randomize_base64url_string(char *s, size_t n)
Definition shared.c:343
char * get_multicast_group(bool ipv6_preferred)
Definition shared.c:1667
void free_freeciv_storage_dir(void)
Definition shared.c:689
bool is_safe_filename(const char *name)
Definition shared.c:256
void remove_leading_trailing_spaces(char *s)
Definition shared.c:444
const char * big_int_to_text(unsigned int mantissa, unsigned int exponent)
Definition shared.c:162
const struct strvec * get_data_dirs(void)
Definition shared.c:886
char * freeciv_storage_dir(void)
Definition shared.c:671
void switch_lang(const char *lang)
Definition shared.c:1434
struct fileinfo_list * fileinfolist_infix(const struct strvec *dirs, const char *infix, bool nodups)
Definition shared.c:1204
static struct strvec * save_dir_names
Definition shared.c:107
fc_tristate
Definition shared.h:46
@ TRI_YES
Definition shared.h:46
@ TRI_NO
Definition shared.h:46
@ TRI_MAYBE
Definition shared.h:46
int(* m_pre_strncmp_fn_t)(const char *, const char *, size_t n)
Definition shared.h:229
#define DIR_SEPARATOR
Definition shared.h:127
#define DIR_SEPARATOR_CHAR
Definition shared.h:128
size_t() m_strlen_fn_t(const char *str)
Definition shared.h:232
#define PATH_SEPARATOR
Definition shared.h:116
#define PARENT_DIR_OPERATOR
Definition shared.h:131
#define ARRAY_SIZE(x)
Definition shared.h:85
#define MIN(x, y)
Definition shared.h:55
@ CF_POINTER
Definition shared.h:302
@ CF_HEXA
Definition shared.h:300
@ CF_CHARACTER
Definition shared.h:298
@ CF_INTEGER
Definition shared.h:299
@ CF_TRANS_BOOLEAN
Definition shared.h:297
@ CF_LAST
Definition shared.h:305
@ CF_BOOLEAN
Definition shared.h:296
@ CF_STRING
Definition shared.h:303
@ CF_FLOAT
Definition shared.h:301
#define DIRMODE_DEFAULT
Definition shared.h:258
m_pre_result
Definition shared.h:213
@ M_PRE_EXACT
Definition shared.h:214
@ M_PRE_ONLY
Definition shared.h:215
@ M_PRE_LONG
Definition shared.h:218
@ M_PRE_AMBIGUOUS
Definition shared.h:216
@ M_PRE_EMPTY
Definition shared.h:217
@ M_PRE_FAIL
Definition shared.h:219
const char *(* m_pre_accessor_fn_t)(int)
Definition shared.h:226
size_t size
Definition specvec.h:72
void strvec_destroy(struct strvec *psv)
void strvec_sort(struct strvec *psv, int(*sort_func)(const char *const *, const char *const *))
void strvec_append(struct strvec *psv, const char *string)
struct strvec * strvec_new(void)
void strvec_remove_duplicate(struct strvec *psv, int(*cmp_func)(const char *, const char *))
#define strvec_iterate(psv, str)
#define strvec_iterate_end
time_t mtime
Definition shared.h:176
char * fullname
Definition shared.h:175
char * name
Definition shared.h:174
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:960
bool fc_isalpha(char c)
Definition support.c:1207
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:777
bool fc_isspace(char c)
Definition support.c:1240
const char * fc_strerror(fc_errno err)
Definition support.c:609
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:986
fc_errno fc_get_errno(void)
Definition support.c:592
bool fc_isdigit(char c)
Definition support.c:1218
int fc_strcoll(const char *str0, const char *str1)
Definition support.c:471
int fc_stat(const char *filename, struct stat *buf)
Definition support.c:574
#define fc__attribute(x)
Definition support.h:99
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define fc__fallthrough
Definition support.h:119