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 = malloc(sizeof(char));
1497 *m = CHAR_MAX;
1498 grouping = m;
1499 } else {
1500 size_t len;
1501 for (len = 0;
1502 lc->grouping[len] != '\0' && lc->grouping[len] != CHAR_MAX; len++) {
1503 /* Nothing */
1504 }
1505 len++;
1506 free(grouping);
1508 memcpy(grouping, lc->grouping, len);
1509 }
1511 grouping_sep = fc_strdup(lc->thousands_sep);
1512 }
1513
1515
1516#endif /* ENABLE_NLS */
1517}
1518
1519/************************************************************************/
1522void free_nls(void)
1523{
1524 free(grouping);
1525 grouping = nullptr;
1527 grouping_sep = nullptr;
1528}
1529
1530/************************************************************************/
1539void dont_run_as_root(const char *argv0, const char *fallback)
1540{
1541#if (defined(ALWAYS_ROOT) || defined(__EMX__) || defined(__BEOS__))
1542 return;
1543#else
1544 if (getuid() == 0 || geteuid() == 0) {
1546 _("%s: Fatal error: you're trying to run me as superuser!\n"),
1547 (argv0 ? argv0 : fallback ? fallback : "freeciv"));
1548 fc_fprintf(stderr, _("Use a non-privileged account instead.\n"));
1550 }
1551#endif /* ALWAYS_ROOT */
1552}
1553
1554/************************************************************************/
1561const char *m_pre_description(enum m_pre_result result)
1562{
1563 static const char * const descriptions[] = {
1564 N_("exact match"),
1565 N_("only match"),
1566 N_("ambiguous"),
1567 N_("empty"),
1568 N_("too long"),
1569 N_("non-match")
1570 };
1571
1572 fc_assert_ret_val(result >= 0 && result < ARRAY_SIZE(descriptions), nullptr);
1573
1574 return descriptions[result];
1575}
1576
1577/************************************************************************/
1581 size_t n_names,
1582 size_t max_len_name,
1585 const char *prefix,
1586 int *ind_result)
1587{
1589 len_fn, prefix, ind_result, nullptr, 0, nullptr);
1590}
1591
1592/************************************************************************/
1604 size_t n_names,
1605 size_t max_len_name,
1608 const char *prefix,
1609 int *ind_result,
1610 int *matches,
1611 int max_matches,
1612 int *pnum_matches)
1613{
1614 int i, len, nmatches;
1615
1616 if (len_fn == nullptr) {
1617 len = strlen(prefix);
1618 } else {
1619 len = len_fn(prefix);
1620 }
1621 if (len == 0) {
1622 return M_PRE_EMPTY;
1623 }
1624 if (len > max_len_name && max_len_name > 0) {
1625 return M_PRE_LONG;
1626 }
1627
1628 nmatches = 0;
1629 for (i = 0; i < n_names; i++) {
1630 const char *name = accessor_fn(i);
1631
1632 if (cmp_fn(name, prefix, len) == 0) {
1633 if (strlen(name) == len) {
1634 *ind_result = i;
1635 return M_PRE_EXACT;
1636 }
1637 if (nmatches == 0) {
1638 *ind_result = i; /* First match */
1639 }
1640 if (matches != nullptr && nmatches < max_matches) {
1641 matches[nmatches] = i;
1642 }
1643 nmatches++;
1644 }
1645 }
1646
1647 if (nmatches == 1) {
1648 return M_PRE_ONLY;
1649 } else if (nmatches > 1) {
1650 if (pnum_matches != nullptr) {
1652 }
1653 return M_PRE_AMBIGUOUS;
1654 } else {
1655 return M_PRE_FAIL;
1656 }
1657}
1658
1659/************************************************************************/
1665{
1666 static char *default_multicast_group_ipv4 = "225.1.1.1";
1667#ifdef FREECIV_IPV6_SUPPORT
1668 /* TODO: Get useful group (this is node local) */
1669 static char *default_multicast_group_ipv6 = "FF31::8000:15B4";
1670#endif /* IPv6 support */
1671
1672 if (mc_group == nullptr) {
1673 char *env = getenv("FREECIV_MULTICAST_GROUP");
1674
1675 if (env) {
1677 } else {
1678#ifdef FREECIV_IPV6_SUPPORT
1679 if (ipv6_preferred) {
1681 } else
1682#endif /* IPv6 support */
1683 {
1685 }
1686 }
1687 }
1688
1689 return mc_group;
1690}
1691
1692/************************************************************************/
1696{
1697 if (mc_group != nullptr) {
1698 free(mc_group);
1699 mc_group = nullptr;
1700 }
1701}
1702
1703/************************************************************************/
1710void interpret_tilde(char *buf, size_t buf_size, const char *filename)
1711{
1712 if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1713 fc_snprintf(buf, buf_size, "%s" DIR_SEPARATOR "%s", user_home_dir(), filename + 2);
1714 } else if (filename[0] == '~' && filename[1] == '\0') {
1716 } else {
1717 strncpy(buf, filename, buf_size);
1718 }
1719}
1720
1721/************************************************************************/
1727char *interpret_tilde_alloc(const char *filename)
1728{
1729 if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1730 const char *home = user_home_dir();
1731 size_t sz;
1732 char *buf;
1733
1734 filename += 2; /* Skip past "~/" */
1735 sz = strlen(home) + strlen(filename) + 2;
1736 buf = fc_malloc(sz);
1737 fc_snprintf(buf, sz, "%s/%s", home, filename);
1738 return buf;
1739 } else if (filename[0] == '~' && filename[1] == '\0') {
1740 return fc_strdup(user_home_dir());
1741 } else {
1742 return fc_strdup(filename);
1743 }
1744}
1745
1746/************************************************************************/
1750char *skip_to_basename(char *filepath)
1751{
1752 int j;
1753
1754 fc_assert_ret_val(filepath != nullptr, nullptr);
1755
1756 for (j = strlen(filepath); j >= 0; j--) {
1757 if (filepath[j] == DIR_SEPARATOR_CHAR) {
1758 return &filepath[j+1];
1759 }
1760 }
1761 return filepath;
1762}
1763
1764/************************************************************************/
1776bool make_dir(const char *pathname, int mode)
1777{
1778 char *dir;
1779 char *path = nullptr;
1780
1781 if (pathname[0] == '\0') {
1782 return FALSE;
1783 }
1784
1785 if (mode < 0) {
1786 mode = 0755;
1787 }
1788
1790 dir = path;
1791
1792 if (*dir == '/') {
1793 /* Don't consider root as directory separator, but skip it. */
1794 dir++;
1795 } else if (dir[0] != '\0' && dir[1] == ':' && dir[2] == '\\') {
1796 /* Don't consider Windows Drive a directory to create, but skip it. */
1797 dir += 3;
1798 }
1799
1800 do {
1801 dir = strchr(dir, DIR_SEPARATOR_CHAR);
1802 /* We set the current / with 0, and restore it afterwards */
1803 if (dir) {
1804 *dir = '\0';
1805 }
1806
1807#ifdef FREECIV_MSWINDOWS
1808#ifdef HAVE__MKDIR
1809 /* Prefer _mkdir() in Windows even if mkdir() would seem to be available -
1810 * chances are that it's wrong kind of mkdir().
1811 * TODO: Make a configure check for mkdir() that also makes sure that it
1812 * takes two parameters, and prefer such proper mkdir() here. */
1813 {
1815 bool failure = FALSE;
1816
1819 failure = TRUE;
1820 }
1821
1823
1824 if (failure) {
1825 free(path);
1826 return FALSE;
1827 }
1828 }
1829#else /* HAVE__MKDIR */
1830 if (mkdir(path, mode) == -1
1831 && fc_get_errno() != EEXIST) {
1832 free(path);
1833 return FALSE;
1834 }
1835#endif /* HAVE__MKDIR */
1836#else /* FREECIV_MSWINDOWS */
1837 if (mkdir(path, mode) == -1
1838 && fc_get_errno() != EEXIST) {
1839 free(path);
1840 return FALSE;
1841 }
1842#endif /* FREECIV_MSWINDOWS */
1843
1844 if (dir) {
1845 *dir = DIR_SEPARATOR_CHAR;
1846 dir++;
1847 }
1848 } while (dir);
1849
1850 free(path);
1851
1852 return TRUE;
1853}
1854
1855/************************************************************************/
1859bool make_dir_for_file(char *filename)
1860{
1861 int i;
1862
1863 for (i = strlen(filename) - 1 ; filename[i] != DIR_SEPARATOR_CHAR ; i--) {
1864 /* Nothing */
1865 }
1866
1867 filename[i] = '\0';
1868 log_debug("Create directory \"%s\"", filename);
1869
1870 if (!make_dir(filename, DIRMODE_DEFAULT)) {
1871 return FALSE;
1872 }
1873 filename[i] = DIR_SEPARATOR_CHAR;
1874
1875 return TRUE;
1876}
1877
1878/************************************************************************/
1881bool path_is_absolute(const char *filename)
1882{
1883 if (!filename) {
1884 return FALSE;
1885 }
1886
1887#ifdef FREECIV_MSWINDOWS
1888 if (strchr(filename, ':')) {
1889 return TRUE;
1890 }
1891#else /* FREECIV_MSWINDOWS */
1892 if (filename[0] == '/') {
1893 return TRUE;
1894 }
1895#endif /* FREECIV_MSWINDOWS */
1896
1897 return FALSE;
1898}
1899
1900/************************************************************************/
1920char scanin(const char **buf, char *delimiters, char *dest, int size)
1921{
1922 char *ptr, found = '?';
1923
1924 if (*buf == nullptr || strlen(*buf) == 0 || size == 0) {
1925 if (dest) {
1926 dest[0] = '\0';
1927 }
1928 *buf = nullptr;
1929 return '\0';
1930 }
1931
1932 if (dest) {
1933 strncpy(dest, *buf, size-1);
1934 dest[size-1] = '\0';
1936 ptr = strpbrk(dest, delimiters);
1937 } else {
1938 /* Just skip ahead. */
1939 ptr = strpbrk(*buf, delimiters);
1940 }
1941 if (ptr != nullptr) {
1942 found = *ptr;
1943 if (dest) {
1944 *ptr = '\0';
1945 }
1946 if (dest) {
1948 }
1949 *buf = strpbrk(*buf, delimiters);
1950 if (*buf != nullptr) {
1951 (*buf)++; /* Skip delimiter */
1952 } else {
1953 }
1954 } else {
1955 *buf = nullptr;
1956 }
1957
1958 return found;
1959}
1960
1961/************************************************************************/
1966{
1967 int seconds, minutes, hours, days;
1968 bool space = FALSE;
1969
1970 seconds = t % 60;
1971 minutes = (t / 60) % 60;
1972 hours = (t / (60 * 60)) % 24;
1973 days = t / (60 * 60 * 24);
1974
1975 if (maxlen <= 0) {
1976 return;
1977 }
1978
1979 buf[0] = '\0';
1980
1981 if (days > 0) {
1982 cat_snprintf(buf, maxlen, "%d %s", days, PL_("day", "days", days));
1983 space = TRUE;
1984 }
1985 if (hours > 0) {
1986 cat_snprintf(buf, maxlen, "%s%d %s",
1987 space ? " " : "", hours, PL_("hour", "hours", hours));
1988 space = TRUE;
1989 }
1990 if (minutes > 0) {
1991 cat_snprintf(buf, maxlen, "%s%d %s",
1992 space ? " " : "",
1993 minutes, PL_("minute", "minutes", minutes));
1994 space = TRUE;
1995 }
1996 if (seconds > 0) {
1997 cat_snprintf(buf, maxlen, "%s%d %s",
1998 space ? " " : "",
1999 seconds, PL_("second", "seconds", seconds));
2000 }
2001}
2002
2003/************************************************************************/
2008void array_shuffle(int *array, int n)
2009{
2010 if (n > 1 && array != nullptr) {
2011 int i, j, t;
2012
2013 for (i = 0; i < n - 1; i++) {
2014 j = i + fc_rand(n - i);
2015 t = array[j];
2016 array[j] = array[i];
2017 array[i] = t;
2018 }
2019 }
2020}
2021
2022/************************************************************************/
2027static bool wildcard_asterisk_fit(const char *pattern, const char *test)
2028{
2029 char jump_to;
2030
2031 /* Jump over the leading asterisks. */
2032 pattern++;
2033 while (TRUE) {
2034 switch (*pattern) {
2035 case '\0':
2036 /* It is a leading asterisk. */
2037 return TRUE;
2038 case '*':
2039 pattern++;
2040 continue;
2041 case '?':
2042 if ('\0' == *test) {
2043 return FALSE;
2044 }
2045 test++;
2046 pattern++;
2047 continue;
2048 }
2049
2050 break;
2051 }
2052
2053 if ('[' != *pattern) {
2054 if ('\\' == *pattern) {
2055 jump_to = *(pattern + 1);
2056 } else {
2057 jump_to = *pattern;
2058 }
2059 } else {
2060 jump_to = '\0';
2061 }
2062
2063 while ('\0' != *test) {
2064 if ('\0' != jump_to) {
2065 /* Jump to next matching charather. */
2066 test = strchr(test, jump_to);
2067 if (test == nullptr) {
2068 /* No match. */
2069 return FALSE;
2070 }
2071 }
2072
2074 return TRUE;
2075 }
2076
2077 (test)++;
2078 }
2079
2080 return FALSE;
2081}
2082
2083/************************************************************************/
2087static bool wildcard_range_fit(const char **pattern, const char **test)
2088{
2089 const char *start = (*pattern + 1);
2090 char testc;
2091 bool negation;
2092
2093 if ('\0' == **test) {
2094 /* Need one character. */
2095 return FALSE;
2096 }
2097
2098 /* Find the end of the pattern. */
2099 while (TRUE) {
2100 *pattern = strchr(*pattern, ']');
2101 if (*pattern == nullptr) {
2102 /* Wildcard format error. */
2103 return FALSE;
2104 } else if (*(*pattern - 1) != '\\') {
2105 /* This is the end. */
2106 break;
2107 } else {
2108 /* Try again. */
2109 (*pattern)++;
2110 }
2111 }
2112
2113 if ('!' == *start) {
2114 negation = TRUE;
2115 start++;
2116 } else {
2117 negation = FALSE;
2118 }
2119 testc = **test;
2120 (*test)++;
2121 (*pattern)++;
2122
2123 for (; start < *pattern; start++) {
2124 if ('-' == *start || '!' == *start) {
2125 /* Wildcard format error. */
2126 return FALSE;
2127 } else if (start < *pattern - 2 && '-' == *(start + 1)) {
2128 /* Case range. */
2129 if (*start <= testc && testc <= *(start + 2)) {
2130 return !negation;
2131 }
2132 start += 2;
2133 } else if (*start == testc) {
2134 /* Single character. */
2135 return !negation;
2136 }
2137 }
2138
2139 return negation;
2140}
2141
2142/************************************************************************/
2153bool wildcard_fit_string(const char *pattern, const char *test)
2154{
2155 while (TRUE) {
2156 switch (*pattern) {
2157 case '\0':
2158 /* '\0' != test. */
2159 return '\0' == *test;
2160 case '*':
2161 return wildcard_asterisk_fit(pattern, test); /* Maybe recursive. */
2162 case '[':
2163 if (!wildcard_range_fit(&pattern, &test)) {
2164 return FALSE;
2165 }
2166 continue;
2167 case '?':
2168 if ('\0' == *test) {
2169 return FALSE;
2170 }
2171 break;
2172 case '\\':
2173 pattern++;
2175 default:
2176 if (*pattern != *test) {
2177 return FALSE;
2178 }
2179 break;
2180 }
2181 pattern++;
2182 test++;
2183 }
2184
2185 return FALSE;
2186}
2187
2188/************************************************************************/
2202int fc_vsnprintcf(char *buf, size_t buf_len, const char *format,
2203 const struct cf_sequence *sequences, size_t sequences_num)
2204{
2205 const struct cf_sequence *pseq;
2206 char cformat[32];
2207 const char *f = format;
2208 char *const max = buf + buf_len - 1;
2209 char *b = buf, *c;
2210 const char *const cmax = cformat + sizeof(cformat) - 2;
2211 int i, j;
2212
2213 if ((size_t) -1 == sequences_num) {
2214 /* Find the number of sequences. */
2215 sequences_num = 0;
2216 for (pseq = sequences; CF_LAST != pseq->type; pseq++) {
2217 sequences_num++;
2218 }
2219 }
2220
2221 while ('\0' != *f) {
2222 if ('%' == *f) {
2223 /* Sequence. */
2224
2225 f++;
2226 if ('%' == *f) {
2227 /* Double '%'. */
2228 *b++ = '%';
2229 f++;
2230 continue;
2231 }
2232
2233 /* Make format. */
2234 c = cformat;
2235 *c++ = '%';
2236 for (; !fc_isalpha(*f) && '\0' != *f && '%' != *f && cmax > c; f++) {
2237 *c++ = *f;
2238 }
2239
2240 if (!fc_isalpha(*f)) {
2241 /* Beginning of a new sequence, end of the format, or too long
2242 * sequence. */
2243 *c = '\0';
2244 j = fc_snprintf(b, max - b + 1, "%s", cformat);
2245 if (-1 == j) {
2246 return -1;
2247 }
2248 b += j;
2249 continue;
2250 }
2251
2252 for (i = 0, pseq = sequences; i < sequences_num; i++, pseq++) {
2253 if (pseq->letter == *f) {
2254 j = -2;
2255 switch (pseq->type) {
2256 case CF_BOOLEAN:
2257 *c++ = 's';
2258 *c = '\0';
2259 j = fc_snprintf(b, max - b + 1, cformat,
2260 pseq->bool_value ? "TRUE" : "FALSE");
2261 break;
2262 case CF_TRANS_BOOLEAN:
2263 *c++ = 's';
2264 *c = '\0';
2265 j = fc_snprintf(b, max - b + 1, cformat,
2266 pseq->bool_value ? _("TRUE") : _("FALSE"));
2267 break;
2268 case CF_CHARACTER:
2269 *c++ = 'c';
2270 *c = '\0';
2271 j = fc_snprintf(b, max - b + 1, cformat, pseq->char_value);
2272 break;
2273 case CF_INTEGER:
2274 *c++ = 'd';
2275 *c = '\0';
2276 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2277 break;
2278 case CF_HEXA:
2279 *c++ = 'x';
2280 *c = '\0';
2281 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2282 break;
2283 case CF_FLOAT:
2284 *c++ = 'f';
2285 *c = '\0';
2286 j = fc_snprintf(b, max - b + 1, cformat, pseq->float_value);
2287 break;
2288 case CF_POINTER:
2289 *c++ = 'p';
2290 *c = '\0';
2291 j = fc_snprintf(b, max - b + 1, cformat, pseq->ptr_value);
2292 break;
2293 case CF_STRING:
2294 *c++ = 's';
2295 *c = '\0';
2296 j = fc_snprintf(b, max - b + 1, cformat, pseq->str_value);
2297 break;
2298 case CF_LAST:
2299 break;
2300 };
2301 if (-2 == j) {
2302 log_error("Error: unsupported sequence type: %d.", pseq->type);
2303 break;
2304 }
2305 if (-1 == j) {
2306 /* Full! */
2307 return -1;
2308 }
2309 f++;
2310 b += j;
2311 break;
2312 }
2313 }
2314 if (i >= sequences_num) {
2315 /* Format not supported. */
2316 *c = '\0';
2317 j = fc_snprintf(b, max - b + 1, "%s%c", cformat, *f);
2318 if (-1 == j) {
2319 return -1;
2320 }
2321 f++;
2322 b += j;
2323 }
2324 } else {
2325 /* Not a sequence. */
2326 *b++ = *f++;
2327 }
2328 if (max <= b) {
2329 /* Too long. */
2330 *max = '\0';
2331 return -1;
2332 }
2333 }
2334 *b = '\0';
2335 return b - buf;
2336}
2337
2338/************************************************************************/
2350int fc_snprintcf(char *buf, size_t buf_len, const char *format, ...)
2351{
2352 struct cf_sequence sequences[16];
2353 size_t sequences_num = 0;
2354 va_list args;
2355
2356 /* Collect sequence array. */
2357 va_start(args, format);
2358 do {
2359 sequences[sequences_num] = va_arg(args, struct cf_sequence);
2361 break;
2362 } else {
2363 sequences_num++;
2364 }
2365 } while (ARRAY_SIZE(sequences) > sequences_num);
2366
2368 && CF_LAST != va_arg(args, struct cf_sequence).type) {
2369 log_error("Too many custom sequences. Maybe did you forget cf_end() "
2370 "at the end of the arguments?");
2371 buf[0] = '\0';
2372 va_end(args);
2373 return -1;
2374 }
2375 va_end(args);
2376
2377 return fc_vsnprintcf(buf, buf_len, format, sequences, sequences_num);
2378}
2379
2380/************************************************************************/
2384static size_t extract_escapes(const char *format, char *escapes,
2385 size_t max_escapes)
2386{
2387 static const char format_escapes[] = {
2388 '*', 'd', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f',
2389 'F', 'g', 'G', 'a', 'A', 'c', 's', 'p', 'n', '\0'
2390 };
2391 bool reordered = FALSE;
2392 size_t num = 0;
2393 int idx = 0;
2394
2396 format = strchr(format, '%');
2397 while (format != nullptr) {
2398 format++;
2399 if ('%' == *format) {
2400 /* Double, not a sequence. */
2401 continue;
2402 } else if (fc_isdigit(*format)) {
2403 const char *start = format;
2404
2405 do {
2406 format++;
2407 } while (fc_isdigit(*format));
2408
2409 if ('$' == *format) {
2410 /* Strings are reordered. */
2411 sscanf(start, "%d", &idx);
2412 reordered = TRUE;
2413 }
2414 }
2415
2416 while ('\0' != *format
2417 && strchr(format_escapes, *format) == nullptr) {
2418 format++;
2419 }
2420 escapes[idx] = *format;
2421
2422 /* Increase the read count. */
2423 if (reordered) {
2424 if (idx > num) {
2425 num = idx;
2426 }
2427 } else {
2428 idx++;
2429 num++;
2430 }
2431
2432 if ('*' != *format) {
2433 format = strchr(format, '%');
2434 } /* Else we didn't have found the real sequence. */
2435 }
2436 return num;
2437}
2438
2439/************************************************************************/
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:74
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:109
#define fc_assert(condition)
Definition log.h:176
#define fc_assert_ret_val(condition, val)
Definition log.h:194
#define log_debug(message,...)
Definition log.h:115
#define log_normal(message,...)
Definition log.h:107
#define log_error(message,...)
Definition log.h:103
#define fc_assert_ret_val_msg(condition, val, message,...)
Definition log.h:208
#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:2384
void format_time_duration(time_t t, char *buf, int maxlen)
Definition shared.c:1965
static bool wildcard_asterisk_fit(const char *pattern, const char *test)
Definition shared.c:2027
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:2350
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:1539
bool wildcard_fit_string(const char *pattern, const char *test)
Definition shared.c:2153
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:2202
#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:1561
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:1603
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:1727
#define FREECIV_SAVE_PATH
Definition shared.c:90
bool make_dir_for_file(char *filename)
Definition shared.c:1859
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:1710
static struct astring realfile
Definition shared.c:114
bool make_dir(const char *pathname, int mode)
Definition shared.c:1776
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:1695
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:1881
void free_nls(void)
Definition shared.c:1522
static bool wildcard_range_fit(const char **pattern, const char **test)
Definition shared.c:2087
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:2443
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:1920
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:1750
#define FREECIV_DATA_PATH
Definition shared.c:87
#define default_scenario_path
void array_shuffle(int *array, int n)
Definition shared.c:2008
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:1580
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:1664
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