Freeciv-3.2
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 = NULL;
99static char *grouping_sep = NULL;
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 = NULL;
107static struct strvec *save_dir_names = NULL;
109
110static char *mc_group = NULL;
111static char *home_dir_user = NULL;
113
115
116static int compare_file_mtime_ptrs(const struct fileinfo *const *ppa,
117 const struct fileinfo *const *ppb);
118
119static char *expand_dir(char *tok_in, bool ok_to_free);
120
121/************************************************************************/
125{
126 if (TRI_NO == one || TRI_NO == two) {
127 return TRI_NO;
128 }
129
130 if (TRI_MAYBE == one || TRI_MAYBE == two) {
131 return TRI_MAYBE;
132 }
133
134 return TRI_YES;
135}
136
137/************************************************************************/
141{
142 if (TRI_YES == one || TRI_YES == two) {
143 return TRI_YES;
144 }
145
146 if (TRI_MAYBE == one || TRI_MAYBE == two) {
147 return TRI_MAYBE;
148 }
149
150 return TRI_NO;
151}
152
153/************************************************************************/
159const char *big_int_to_text(unsigned int mantissa, unsigned int exponent)
160{
161 static char buf[64]; /* Note that we'll be filling this in right to left. */
162 char *grp = grouping;
163 char *ptr;
164 unsigned int cnt = 0;
165 char sep[64];
166 size_t seplen;
167
168 /* We have to convert the encoding here (rather than when the locale
169 * is initialized) because it can't be done before the charsets are
170 * initialized. */
172 seplen = strlen(sep);
173
174#if 0 /* Not needed while the values are unsigned. */
177#endif
178
179 if (mantissa == 0) {
180 return "0";
181 }
182
183 /* We fill the string in backwards, starting from the right. So the first
184 * thing we do is terminate it. */
185 ptr = &buf[sizeof(buf)];
186 *(--ptr) = '\0';
187
188 while (mantissa != 0) {
189 int dig;
190
191 if (ptr <= buf + seplen) {
192 /* Avoid a buffer overflow. */
194 return ptr;
195 }
196
197 /* Add on another character. */
198 if (exponent > 0) {
199 dig = 0;
200 exponent--;
201 } else {
202 dig = mantissa % 10;
203 mantissa /= 10;
204 }
205 *(--ptr) = '0' + dig;
206
207 cnt++;
208 if (mantissa != 0 && cnt == *grp) {
209 /* Reached count of digits in group: insert separator and reset count. */
210 cnt = 0;
211 if (*grp == CHAR_MAX) {
212 /* This test is unlikely to be necessary since we would need at
213 least 421-bit ints to break the 127 digit barrier, but why not. */
214 break;
215 }
216 ptr -= seplen;
217
218 fc_assert_ret_val(ptr >= buf, NULL);
219
220 memcpy(ptr, sep, seplen);
221 if (*(grp + 1) != 0) {
222 /* Zero means to repeat the present group-size indefinitely. */
223 grp++;
224 }
225 }
226 }
227
228 return ptr;
229}
230
231/************************************************************************/
234const char *int_to_text(unsigned int number)
235{
236 return big_int_to_text(number, 0);
237}
238
239/************************************************************************/
243static bool is_ascii(char ch)
244{
245 /* This works with both signed and unsigned char's. */
246 return ch >= ' ' && ch <= '~';
247}
248
249/************************************************************************/
253bool is_safe_filename(const char *name)
254{
255 int i = 0;
256
257 /* Must not be NULL or empty */
258 if (!name || *name == '\0') {
259 return FALSE;
260 }
261
262 for (; '\0' != name[i]; i++) {
263 if ('.' != name[i] && NULL == strchr(base64url, name[i])) {
264 return FALSE;
265 }
266 }
267
268 /* We don't allow the filename to ascend directories */
270 return FALSE;
271 }
272
273 /* Otherwise, it is okay... */
274 return TRUE;
275}
276
277/************************************************************************/
283bool is_ascii_name(const char *name)
284{
285 const char illegal_chars[] = {'|', '%', '"', ',', '*', '<', '>', '\0'};
286 int i, j;
287
288 /* Must not be NULL or empty */
289 if (!name || *name == '\0') {
290 return FALSE;
291 }
292
293 /* Must begin and end with some non-space character */
294 if ((*name == ' ') || (*(strchr(name, '\0') - 1) == ' ')) {
295 return FALSE;
296 }
297
298 /* Must be composed entirely of printable ascii characters,
299 * and no illegal characters which can break ranking scripts. */
300 for (i = 0; name[i]; i++) {
301 if (!is_ascii(name[i])) {
302 return FALSE;
303 }
304 for (j = 0; illegal_chars[j]; j++) {
305 if (name[i] == illegal_chars[j]) {
306 return FALSE;
307 }
308 }
309 }
310
311 /* Otherwise, it's okay... */
312 return TRUE;
313}
314
315/************************************************************************/
318bool is_base64url(const char *s)
319{
320 size_t i = 0;
321
322 /* Must not be NULL or empty */
323 if (NULL == s || '\0' == *s) {
324 return FALSE;
325 }
326
327 for (; '\0' != s[i]; i++) {
328 if (NULL == strchr(base64url, s[i])) {
329 return FALSE;
330 }
331 }
332 return TRUE;
333}
334
335/************************************************************************/
339void randomize_base64url_string(char *s, size_t n)
340{
341 size_t i = 0;
342
343 /* Must not be NULL or too short */
344 if (NULL == s || 1 > n) {
345 return;
346 }
347
348 for (; i < (n - 1); i++) {
349 s[i] = base64url[fc_rand(sizeof(base64url) - 1)];
350 }
351 s[i] = '\0';
352}
353
354/************************************************************************/
359int compare_strings(const void *first, const void *second)
360{
361 return fc_strcoll((const char *) first, (const char *) second);
362}
363
364/************************************************************************/
369int compare_strings_ptrs(const void *first, const void *second)
370{
371 return fc_strcoll(*((const char **) first), *((const char **) second));
372}
373
374/************************************************************************/
379int compare_strings_strvec(const char *const *first,
380 const char *const *second)
381{
382 return fc_strcoll(*first, *second);
383}
384
385/************************************************************************/
389{
391
392 while (*s != '\0' && fc_isspace(*s)) {
393 s++;
394 }
395
396 return s;
397}
398
399/************************************************************************/
404{
405 char *t;
406
407 fc_assert_ret(NULL != s);
408 t = skip_leading_spaces(s);
409 if (t != s) {
410 while (*t != '\0') {
411 *s++ = *t++;
412 }
413 *s = '\0';
414 }
415}
416
417/************************************************************************/
422{
423 char *t;
424 size_t len;
425
426 fc_assert_ret(NULL != s);
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 fc_assert_ret(NULL != s);
458
459 t = s + strlen(s) -1;
460 while (t>=s && (*t) == trailing) {
461 *t = '\0';
462 t--;
463 }
464}
465
466/************************************************************************/
480char *end_of_strn(char *str, int *nleft)
481{
482 int len = strlen(str);
483
484 *nleft -= len;
485 fc_assert_ret_val(0 < (*nleft), NULL); /* Space for the terminating nul */
486
487 return str + len;
488}
489
490/************************************************************************/
495bool check_strlen(const char *str, size_t len, const char *errmsg)
496{
498
499 return FALSE;
500}
501
502/************************************************************************/
505size_t loud_strlcpy(char *buffer, const char *str, size_t len,
506 const char *errmsg)
507{
509
510 return fc_strlcpy(buffer, str, len);
511}
512
513/************************************************************************/
517bool str_to_int(const char *str, int *pint)
518{
519 const char *start;
520
522
523 while (fc_isspace(*str)) {
524 /* Skip leading spaces. */
525 str++;
526 }
527
528 start = str;
529 if ('-' == *str || '+' == *str) {
530 /* Handle sign. */
531 str++;
532 }
533 while (fc_isdigit(*str)) {
534 /* Digits. */
535 str++;
536 }
537
538 while (fc_isspace(*str)) {
539 /* Ignore trailing spaces. */
540 str++;
541 }
542
543 return ('\0' == *str && (NULL == pint || 1 == sscanf(start, "%d", pint)));
544}
545
546/************************************************************************/
551bool str_to_uint(const char *str, unsigned int *pint)
552{
553 const char *start;
554
556
557 while (fc_isspace(*str)) {
558 /* Skip leading spaces. */
559 str++;
560 }
561
562 start = str;
563 if ('+' == *str) {
564 /* Handle sign. */
565 str++;
566 }
567 while (fc_isdigit(*str)) {
568 /* Digits. */
569 str++;
570 }
571
572 while (fc_isspace(*str)) {
573 /* Ignore trailing spaces. */
574 str++;
575 }
576
577 return ('\0' == *str && (NULL == pint || 1 == sscanf(start, "%u", pint)));
578}
579
580/************************************************************************/
584bool str_to_float(const char *str, float *pfloat)
585{
586 bool dot;
587 const char *start;
588
590
591 while (fc_isspace(*str)) {
592 /* Skip leading spaces. */
593 str++;
594 }
595
596 start = str;
597
598 if ('-' == *str || '+' == *str) {
599 /* Handle sign. */
600 str++;
601 }
602 while (fc_isdigit(*str)) {
603 /* Digits. */
604 str++;
605 }
606
607 if (*str == '.') {
608 dot = TRUE;
609 str++;
610
611 while (fc_isdigit(*str)) {
612 /* Digits. */
613 str++;
614 }
615 } else {
616 dot = FALSE;
617 }
618
619 while (fc_isspace(*str)) {
620 /* Ignore trailing spaces. */
621 str++;
622 }
623
624 return ('\0' == *str && dot
625 && (NULL == pfloat || 1 == sscanf(start, "%f", pfloat)));
626}
627
628/************************************************************************/
634char *user_home_dir(void)
635{
636#ifdef AMIGA
637 return "PROGDIR:";
638#else /* AMIGA */
639
640#ifdef FREECIV_MSWINDOWS
641#define HOMEVAR "APPDATA"
642#else
643#define HOMEVAR "HOME"
644#endif
645
646 if (home_dir_user == NULL) {
647 char *env = getenv(HOMEVAR);
648
649 if (env) {
652 } else {
653 log_error("Could not find home directory (" HOMEVAR " is not set).");
655 }
656 }
657
658 return home_dir_user;
659#endif /* AMIGA */
660}
661
662/************************************************************************/
666{
667 if (home_dir_user != NULL) {
670 }
671}
672
673/************************************************************************/
692
693/************************************************************************/
703
704/************************************************************************/
711char *user_username(char *buf, size_t bufsz)
712{
713 /* This function uses a number of different methods to try to find a
714 * username. This username then has to be truncated to bufsz
715 * characters (including terminator) and checked for sanity. Note that
716 * truncating a sane name can leave you with an insane name under some
717 * charsets. */
718
719 /* If the environment variable $USER is present and sane, use it. */
720 {
721 char *env = getenv("USER");
722
723 if (env) {
725 if (is_ascii_name(buf)) {
726 log_verbose("USER username is %s", buf);
727 return buf;
728 }
729 }
730 }
731
732#ifdef HAVE_GETPWUID
733 /* Otherwise if getpwuid() is available we can use it to find the true
734 * username. */
735 {
736 struct passwd *pwent = getpwuid(getuid());
737
738 if (pwent) {
739 fc_strlcpy(buf, pwent->pw_name, bufsz);
740 if (is_ascii_name(buf)) {
741 log_verbose("getpwuid username is %s", buf);
742 return buf;
743 }
744 }
745 }
746#endif /* HAVE_GETPWUID */
747
748#ifdef FREECIV_MSWINDOWS
749 /* On windows the GetUserName function will give us the login name. */
750 {
751 char name[UNLEN + 1];
752 DWORD length = sizeof(name);
753
754 if (GetUserName(name, &length)) {
756 if (is_ascii_name(buf)) {
757 log_verbose("GetUserName username is %s", buf);
758 return buf;
759 }
760 }
761 }
762#endif /* FREECIV_MSWINDOWS */
763
764#ifdef ALWAYS_ROOT
765 fc_strlcpy(buf, "name", bufsz);
766#else
767 fc_snprintf(buf, bufsz, "name%d", (int) getuid());
768#endif
769 log_verbose("fake username is %s", buf);
771
772 return buf;
773}
774
775/************************************************************************/
785static char *expand_dir(char *tok_in, bool ok_to_free)
786{
787 int i; /* strlen(tok), or -1 as flag */
788 char *tok;
789 char **ret = &tok; /* Return tok by default */
790 char *allocated;
791
794 if (strcmp(tok, DIR_SEPARATOR) != 0) {
796 }
797
798 i = strlen(tok);
799 if (tok[0] == '~') {
800 if (i > 1 && tok[1] != DIR_SEPARATOR_CHAR) {
801 log_error("For \"%s\" in path cannot expand '~'"
802 " except as '~" DIR_SEPARATOR "'; ignoring", tok);
803 i = 0; /* Skip this one */
804 } else {
805 char *home = user_home_dir();
806
807 if (!home) {
808 log_verbose("No HOME, skipping path component %s", tok);
809 i = 0;
810 } else {
811 int len = strlen(home) + i; /* +1 -1 */
812
813 allocated = fc_malloc(len);
814 ret = &allocated;
815
816 fc_snprintf(allocated, len, "%s%s", home, tok + 1);
817 i = -1; /* Flag to free tok below */
818 }
819 }
820 }
821
822 if (i != 0) {
823 /* We could check whether the directory exists and
824 * is readable etc? Don't currently. */
825 if (i == -1 && ok_to_free) {
826 free(tok);
827 tok = NULL;
828 }
829 }
830
831 return *ret;
832}
833
834/************************************************************************/
839static struct strvec *base_get_dirs(const char *dir_list)
840{
841 struct strvec *dirs = strvec_new();
842 char *path, *tok;
843
844 path = fc_strdup(dir_list); /* Something we can strtok() */
845 tok = strtok(path, PATH_SEPARATOR);
846 do {
847 char *dir = expand_dir(tok, FALSE);
848
849 if (dir != NULL) {
850 strvec_append(dirs, dir);
851 if (dir != tok) {
852 free(dir);
853 }
854 }
855
857 } while (tok);
858
859 free(path);
860 return dirs;
861}
862
863/************************************************************************/
881
882/************************************************************************/
893const struct strvec *get_data_dirs(void)
894{
895 /* The first time this function is called it will search and
896 * allocate the directory listing. Subsequently we will already
897 * know the list and can just return it. */
898 if (NULL == data_dir_names) {
899 const char *path;
900
901#ifdef FREECIV_APPIMAGE
902 char default_data_path[5000];
903
907 "%s/usr/share/freeciv",
908 getenv("APPDIR"));
909#else /* FREECIV_APPIMAGE */
910#define default_data_path DEFAULT_DATA_PATH
911#endif /* FREECIV_APPIMAGE */
912
913 if ((path = getenv(FREECIV_DATA_PATH)) && '\0' == path[0]) {
914 /* TRANS: <FREECIV_DATA_PATH> configuration error */
915 log_error(_("\"%s\" is set but empty; using default \"%s\" "
916 "data directories instead."),
918 path = NULL;
919 }
921 strvec_remove_duplicate(data_dir_names, strcmp); /* Don't set a path both. */
923 log_verbose("Data path component: %s", dirname);
925 }
926
927 return data_dir_names;
928}
929
930/************************************************************************/
941const struct strvec *get_save_dirs(void)
942{
943 /* The first time this function is called it will search and
944 * allocate the directory listing. Subsequently we will already
945 * know the list and can just return it. */
946 if (NULL == save_dir_names) {
947 const char *path;
948
949 if ((path = getenv(FREECIV_SAVE_PATH)) && '\0' == path[0]) {
950 /* TRANS: <FREECIV_SAVE_PATH> configuration error */
951 log_error(_("\"%s\" is set but empty; using default \"%s\" "
952 "save directories instead."),
954 path = NULL;
955 }
957 strvec_remove_duplicate(save_dir_names, strcmp); /* Don't set a path both. */
959 log_verbose("Save path component: %s", dirname);
961 }
962
963 return save_dir_names;
964}
965
966/************************************************************************/
978const struct strvec *get_scenario_dirs(void)
979{
980 /* The first time this function is called it will search and
981 * allocate the directory listing. Subsequently we will already
982 * know the list and can just return it. */
983 if (NULL == scenario_dir_names) {
984 const char *path;
985
986#ifdef FREECIV_APPIMAGE
987 char default_scenario_path[5000];
988
994 "%s/usr/share/freeciv/scenarios",
995 getenv("APPDIR"));
996#else /* FREECIV_APPIMAGE */
997#define default_scenario_path DEFAULT_SCENARIO_PATH
998#endif /* FREECIV_APPIMAGE */
999
1000 if ((path = getenv(FREECIV_SCENARIO_PATH)) && '\0' == path[0]) {
1001 /* TRANS: <FREECIV_SCENARIO_PATH> configuration error */
1002 log_error( _("\"%s\" is set but empty; using default \"%s\" "
1003 "scenario directories instead."),
1005 path = NULL;
1006 }
1008 strvec_remove_duplicate(scenario_dir_names, strcmp); /* Don't set a path both. */
1010 log_verbose("Scenario path component: %s", dirname);
1012 }
1013
1014 return scenario_dir_names;
1015}
1016
1017/************************************************************************/
1027struct strvec *fileinfolist(const struct strvec *dirs, const char *suffix)
1028{
1029 struct strvec *files = strvec_new();
1030 size_t suffix_len = strlen(suffix);
1031
1033
1034 if (NULL == dirs) {
1035 return files;
1036 }
1037
1038 /* First assemble a full list of names. */
1039 strvec_iterate(dirs, dirname) {
1040 DIR *dir;
1041 struct dirent *entry;
1042
1043 /* Open the directory for reading. */
1044 dir = fc_opendir(dirname);
1045 if (!dir) {
1046 if (errno == ENOENT) {
1047 log_verbose("Skipping non-existing data directory %s.",
1048 dirname);
1049 } else {
1050 /* TRANS: "...: <externally translated error string>."*/
1051 log_error(_("Could not read data directory %s: %s."),
1053 }
1054 continue;
1055 }
1056
1057 /* Scan all entries in the directory. */
1058 while ((entry = readdir(dir))) {
1059 size_t len = strlen(entry->d_name);
1060
1061 /* Make sure the file name matches. */
1062 if (len > suffix_len
1063 && strcmp(suffix, entry->d_name + len - suffix_len) == 0) {
1064 /* Strdup the entry so we can safely write to it. */
1065 char *match = fc_strdup(entry->d_name);
1066
1067 /* Clip the suffix. */
1068 match[len - suffix_len] = '\0';
1069
1070 strvec_append(files, match);
1071 free(match);
1072 }
1073 }
1074
1075 closedir(dir);
1077
1078 /* Sort the list and remove duplications. */
1081
1082 return files;
1083}
1084
1085/************************************************************************/
1101const char *fileinfoname(const struct strvec *dirs, const char *filename)
1102{
1103#ifndef DIR_SEPARATOR_IS_DEFAULT
1104 char fnbuf[filename != NULL ? strlen(filename) + 1 : 1];
1105 int i;
1106#else /* DIR_SEPARATOR_IS_DEFAULT */
1107 const char *fnbuf = filename;
1108#endif /* DIR_SEPARATOR_IS_DEFAULT */
1109
1110 if (NULL == dirs) {
1111 return NULL;
1112 }
1113
1114 if (!filename) {
1115 bool first = TRUE;
1116
1118 strvec_iterate(dirs, dirname) {
1119 if (first) {
1121 first = FALSE;
1122 } else {
1123 astr_add(&realfile, "%s", dirname);
1124 }
1126
1127 return astr_str(&realfile);
1128 }
1129
1130#ifndef DIR_SEPARATOR_IS_DEFAULT
1131 for (i = 0; filename[i] != '\0'; i++) {
1132 if (filename[i] == '/') {
1134 } else {
1135 fnbuf[i] = filename[i];
1136 }
1137 }
1138 fnbuf[i] = '\0';
1139#endif /* DIR_SEPARATOR_IS_DEFAULT */
1140
1141 strvec_iterate(dirs, dirname) {
1142 struct stat buf; /* See if we can open the file or directory */
1143
1145 if (fc_stat(astr_str(&realfile), &buf) == 0) {
1146 return astr_str(&realfile);
1147 }
1149
1150 log_verbose("Could not find readable file \"%s\" in data path.", filename);
1151
1152 return NULL;
1153}
1154
1155/************************************************************************/
1159{
1161}
1162
1163/************************************************************************/
1166static void fileinfo_destroy(struct fileinfo *pfile)
1167{
1168 free(pfile->name);
1169 free(pfile->fullname);
1170 free(pfile);
1171}
1172
1173/************************************************************************/
1176static int compare_file_mtime_ptrs(const struct fileinfo *const *ppa,
1177 const struct fileinfo *const *ppb)
1178{
1179 time_t a = (*ppa)->mtime;
1180 time_t b = (*ppb)->mtime;
1181
1182 return ((a < b) ? 1 : (a > b) ? -1 : 0);
1183}
1184
1185/************************************************************************/
1188static int compare_file_name_ptrs(const struct fileinfo *const *ppa,
1189 const struct fileinfo *const *ppb)
1190{
1191 return fc_strcoll((*ppa)->name, (*ppb)->name);
1192}
1193
1194/************************************************************************/
1197static bool compare_fileinfo_name(const struct fileinfo *pa,
1198 const struct fileinfo *pb)
1199{
1200 return 0 == fc_strcoll(pa->name, pb->name);
1201}
1202
1203/************************************************************************/
1211struct fileinfo_list *fileinfolist_infix(const struct strvec *dirs,
1212 const char *infix, bool nodups)
1213{
1214 struct fileinfo_list *res;
1215
1216 if (NULL == dirs) {
1217 return NULL;
1218 }
1219
1221
1222 /* First assemble a full list of names. */
1223 strvec_iterate(dirs, dirname) {
1224 DIR *dir;
1225 struct dirent *entry;
1226
1227 /* Open the directory for reading. */
1228 dir = fc_opendir(dirname);
1229 if (!dir) {
1230 continue;
1231 }
1232
1233 /* Scan all entries in the directory. */
1234 while ((entry = readdir(dir))) {
1235 struct fileinfo *file;
1236 char *ptr;
1237 /* Strdup the entry so we can safely write to it. */
1238 char *filename = fc_strdup(entry->d_name);
1239
1240 /* Make sure the file name matches. */
1241 if ((ptr = strstr(filename, infix))) {
1242 struct stat buf;
1243 char *fullname;
1244 size_t len = strlen(dirname) + strlen(filename) + 2;
1245
1246 fullname = fc_malloc(len);
1247 fc_snprintf(fullname, len, "%s" DIR_SEPARATOR "%s", dirname, filename);
1248
1249 if (fc_stat(fullname, &buf) == 0) {
1250 file = fc_malloc(sizeof(*file));
1251
1252 /* Clip the suffix. */
1253 *ptr = '\0';
1254
1255 file->name = filename;
1256 file->fullname = fullname;
1257 file->mtime = buf.st_mtime;
1258
1260 } else {
1261 free(fullname);
1262 free(filename);
1263 }
1264 } else {
1265 free(filename);
1266 }
1267 }
1268
1269 closedir(dir);
1271
1272 /* Sort the list by name. */
1274
1275 if (nodups) {
1277 }
1278
1279 /* Sort the list by last modification time. */
1281
1282 return res;
1283}
1284
1285/************************************************************************/
1288const char *setup_langname(void)
1289{
1290 const char *langname = NULL;
1291
1292#ifdef ENABLE_NLS
1293 langname = getenv("LANG");
1294
1295#ifdef FREECIV_MSWINDOWS
1296 /* Set LANG by hand if it is not set */
1297 if (!langname) {
1299 case LANG_ARABIC:
1300 langname = "ar";
1301 break;
1302 case LANG_CATALAN:
1303 langname = "ca";
1304 break;
1305 case LANG_CZECH:
1306 langname = "cs";
1307 break;
1308 case LANG_DANISH:
1309 langname = "da";
1310 break;
1311 case LANG_GERMAN:
1312 langname = "de";
1313 break;
1314 case LANG_GREEK:
1315 langname = "el";
1316 break;
1317 case LANG_ENGLISH:
1318 switch (SUBLANGID(GetUserDefaultLangID())) {
1319 case SUBLANG_ENGLISH_UK:
1320 langname = "en_GB";
1321 break;
1322 default:
1323 langname = "en";
1324 break;
1325 }
1326 break;
1327 case LANG_SPANISH:
1328 langname = "es";
1329 break;
1330 case LANG_ESTONIAN:
1331 langname = "et";
1332 break;
1333 case LANG_FARSI:
1334 langname = "fa";
1335 break;
1336 case LANG_FINNISH:
1337 langname = "fi";
1338 break;
1339 case LANG_FRENCH:
1340 langname = "fr";
1341 break;
1342 case LANG_HEBREW:
1343 langname = "he";
1344 break;
1345 case LANG_HUNGARIAN:
1346 langname = "hu";
1347 break;
1348 case LANG_ITALIAN:
1349 langname = "it";
1350 break;
1351 case LANG_JAPANESE:
1352 langname = "ja";
1353 break;
1354 case LANG_KOREAN:
1355 langname = "ko";
1356 break;
1357 case LANG_LITHUANIAN:
1358 langname = "lt";
1359 break;
1360 case LANG_DUTCH:
1361 langname = "nl";
1362 break;
1363 case LANG_NORWEGIAN:
1364 langname = "nb";
1365 break;
1366 case LANG_POLISH:
1367 langname = "pl";
1368 break;
1369 case LANG_PORTUGUESE:
1370 switch (SUBLANGID(GetUserDefaultLangID())) {
1372 langname = "pt_BR";
1373 break;
1374 default:
1375 langname = "pt";
1376 break;
1377 }
1378 break;
1379 case LANG_ROMANIAN:
1380 langname = "ro";
1381 break;
1382 case LANG_RUSSIAN:
1383 langname = "ru";
1384 break;
1385 case LANG_SWEDISH:
1386 langname = "sv";
1387 break;
1388 case LANG_TURKISH:
1389 langname = "tr";
1390 break;
1391 case LANG_UKRAINIAN:
1392 langname = "uk";
1393 break;
1394 case LANG_CHINESE:
1395 langname = "zh_CN";
1396 break;
1397 }
1398
1399 if (langname != NULL) {
1400 static char envstr[40];
1401
1402 fc_snprintf(envstr, sizeof(envstr), "LANG=%s", langname);
1403 putenv(envstr);
1404 }
1405 }
1406#endif /* FREECIV_MSWINDOWS */
1407#endif /* ENABLE_NLS */
1408
1409 return langname;
1410}
1411
1412#ifdef FREECIV_ENABLE_NLS
1413/************************************************************************/
1416static void autocap_update(void)
1417{
1418 char *autocap_opt_in[] = { "fi", NULL };
1419 int i;
1420 bool ac_enabled = FALSE;
1421
1422 char *lang = getenv("LANG");
1423
1424 if (lang != NULL && lang[0] != '\0' && lang[1] != '\0') {
1425 for (i = 0; autocap_opt_in[i] != NULL && !ac_enabled; i++) {
1426 if (lang[0] == autocap_opt_in[i][0]
1427 && lang[1] == autocap_opt_in[i][1]) {
1428 ac_enabled = TRUE;
1429 break;
1430 }
1431 }
1432 }
1433
1435}
1436#endif /* FREECIV_ENABLE_NLS */
1437
1438/************************************************************************/
1441void switch_lang(const char *lang)
1442{
1443#ifdef FREECIV_ENABLE_NLS
1444#ifdef HAVE_SETENV
1445 setenv("LANG", lang, TRUE);
1446#else /* HAVE_SETENV */
1447 if (lang != NULL) {
1448 static char envstr[40];
1449
1450 fc_snprintf(envstr, sizeof(envstr), "LANG=%s", lang);
1451 putenv(envstr);
1452 }
1453#endif /* HAVE_SETENV */
1454
1455 (void) setlocale(LC_ALL, "");
1456 (void) bindtextdomain("freeciv-core", get_locale_dir());
1457
1459
1460 log_normal("LANG set to %s", lang);
1461#else /* FREECIV_ENABLE_NLS */
1463#endif /* FREECIV_ENABLE_NLS */
1464}
1465
1466/************************************************************************/
1470void init_nls(void)
1471{
1472 /*
1473 * Setup the cached locale numeric formatting information. Defaults
1474 * are as appropriate for the US.
1475 */
1476 grouping = fc_strdup("\3");
1477 grouping_sep = fc_strdup(",");
1478
1479#ifdef ENABLE_NLS
1480
1481#ifdef FREECIV_MSWINDOWS
1482 setup_langname(); /* Makes sure LANG env variable has been set */
1483#endif /* FREECIV_MSWINDOWS */
1484
1485 (void) setlocale(LC_ALL, "");
1486 (void) bindtextdomain("freeciv-core", get_locale_dir());
1487 (void) textdomain("freeciv-core");
1488
1489 /* Don't touch the defaults when LC_NUMERIC == "C".
1490 This is intended to cater to the common case where:
1491 1) The user is from North America. ;-)
1492 2) The user has not set the proper environment variables.
1493 (Most applications are (unfortunately) US-centric
1494 by default, so why bother?)
1495 This would result in the "C" locale being used, with grouping ""
1496 and thousands_sep "", where we really want "\3" and ",". */
1497
1498 if (strcmp(setlocale(LC_NUMERIC, NULL), "C") != 0) {
1499 struct lconv *lc = localeconv();
1500
1501 if (lc->grouping[0] == '\0') {
1502 /* This actually indicates no grouping at all. */
1503 char *m = fc_malloc(sizeof(char));
1504
1505 free(grouping);
1506 *m = CHAR_MAX;
1507 grouping = m;
1508 } else {
1509 size_t len;
1510
1511 for (len = 0;
1512 lc->grouping[len] != '\0' && lc->grouping[len] != CHAR_MAX; len++) {
1513 /* Nothing */
1514 }
1515 len++;
1516 free(grouping);
1518 memcpy(grouping, lc->grouping, len);
1519 }
1521 grouping_sep = fc_strdup(lc->thousands_sep);
1522 }
1523
1525
1526#endif /* ENABLE_NLS */
1527}
1528
1529/************************************************************************/
1532void free_nls(void)
1533{
1534 free(grouping);
1535 grouping = NULL;
1538}
1539
1540/************************************************************************/
1549void dont_run_as_root(const char *argv0, const char *fallback)
1550{
1551#if (defined(ALWAYS_ROOT) || defined(__EMX__) || defined(__BEOS__))
1552 return;
1553#else
1554 if (getuid() == 0 || geteuid() == 0) {
1556 _("%s: Fatal error: you're trying to run me as superuser!\n"),
1557 (argv0 ? argv0 : fallback ? fallback : "freeciv"));
1558 fc_fprintf(stderr, _("Use a non-privileged account instead.\n"));
1560 }
1561#endif /* ALWAYS_ROOT */
1562}
1563
1564/************************************************************************/
1571const char *m_pre_description(enum m_pre_result result)
1572{
1573 static const char * const descriptions[] = {
1574 N_("exact match"),
1575 N_("only match"),
1576 N_("ambiguous"),
1577 N_("empty"),
1578 N_("too long"),
1579 N_("non-match")
1580 };
1581
1582 fc_assert_ret_val(result >= 0 && result < ARRAY_SIZE(descriptions), NULL);
1583
1584 return descriptions[result];
1585}
1586
1587/************************************************************************/
1591 size_t n_names,
1592 size_t max_len_name,
1595 const char *prefix,
1596 int *ind_result)
1597{
1599 len_fn, prefix, ind_result, NULL, 0, NULL);
1600}
1601
1602/************************************************************************/
1614 size_t n_names,
1615 size_t max_len_name,
1618 const char *prefix,
1619 int *ind_result,
1620 int *matches,
1621 int max_matches,
1622 int *pnum_matches)
1623{
1624 int i, len, nmatches;
1625
1626 if (len_fn == NULL) {
1627 len = strlen(prefix);
1628 } else {
1629 len = len_fn(prefix);
1630 }
1631 if (len == 0) {
1632 return M_PRE_EMPTY;
1633 }
1634 if (len > max_len_name && max_len_name > 0) {
1635 return M_PRE_LONG;
1636 }
1637
1638 nmatches = 0;
1639 for (i = 0; i < n_names; i++) {
1640 const char *name = accessor_fn(i);
1641
1642 if (cmp_fn(name, prefix, len) == 0) {
1643 if (strlen(name) == len) {
1644 *ind_result = i;
1645 return M_PRE_EXACT;
1646 }
1647 if (nmatches == 0) {
1648 *ind_result = i; /* First match */
1649 }
1650 if (matches != NULL && nmatches < max_matches) {
1651 matches[nmatches] = i;
1652 }
1653 nmatches++;
1654 }
1655 }
1656
1657 if (nmatches == 1) {
1658 return M_PRE_ONLY;
1659 } else if (nmatches > 1) {
1660 if (pnum_matches != NULL) {
1662 }
1663 return M_PRE_AMBIGUOUS;
1664 } else {
1665 return M_PRE_FAIL;
1666 }
1667}
1668
1669/************************************************************************/
1675{
1676 static char *default_multicast_group_ipv4 = "225.1.1.1";
1677#ifdef FREECIV_IPV6_SUPPORT
1678 /* TODO: Get useful group (this is node local) */
1679 static char *default_multicast_group_ipv6 = "FF31::8000:15B4";
1680#endif /* IPv6 support */
1681
1682 if (mc_group == NULL) {
1683 char *env = getenv("FREECIV_MULTICAST_GROUP");
1684
1685 if (env) {
1687 } else {
1688#ifdef FREECIV_IPV6_SUPPORT
1689 if (ipv6_preferred) {
1691 } else
1692#endif /* IPv6 support */
1693 {
1695 }
1696 }
1697 }
1698
1699 return mc_group;
1700}
1701
1702/************************************************************************/
1706{
1707 if (mc_group != NULL) {
1708 free(mc_group);
1709 mc_group = NULL;
1710 }
1711}
1712
1713/************************************************************************/
1720void interpret_tilde(char *buf, size_t buf_size, const char *filename)
1721{
1722 if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1723 fc_snprintf(buf, buf_size, "%s" DIR_SEPARATOR "%s", user_home_dir(), filename + 2);
1724 } else if (filename[0] == '~' && filename[1] == '\0') {
1726 } else {
1727 strncpy(buf, filename, buf_size);
1728 }
1729}
1730
1731/************************************************************************/
1737char *interpret_tilde_alloc(const char *filename)
1738{
1739 if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1740 const char *home = user_home_dir();
1741 size_t sz;
1742 char *buf;
1743
1744 filename += 2; /* Skip past "~/" */
1745 sz = strlen(home) + strlen(filename) + 2;
1746 buf = fc_malloc(sz);
1747 fc_snprintf(buf, sz, "%s/%s", home, filename);
1748 return buf;
1749 } else if (filename[0] == '~' && filename[1] == '\0') {
1750 return fc_strdup(user_home_dir());
1751 } else {
1752 return fc_strdup(filename);
1753 }
1754}
1755
1756/************************************************************************/
1760char *skip_to_basename(char *filepath)
1761{
1762 int j;
1763
1764 fc_assert_ret_val(NULL != filepath, NULL);
1765
1766 for (j = strlen(filepath); j >= 0; j--) {
1767 if (filepath[j] == DIR_SEPARATOR_CHAR) {
1768 return &filepath[j+1];
1769 }
1770 }
1771 return filepath;
1772}
1773
1774/************************************************************************/
1782bool make_dir(const char *pathname)
1783{
1784 char *dir;
1785 char *path = NULL;
1786
1787 if (pathname[0] == '\0') {
1788 return FALSE;
1789 }
1790
1792 dir = path;
1793
1794 if (*dir == '/') {
1795 /* Don't consider root as directory separator, but skip it. */
1796 dir++;
1797 } else if (dir[0] != '\0' && dir[1] == ':' && dir[2] == '\\') {
1798 /* Don't consider Windows Drive a directory to create, but skip it. */
1799 dir += 3;
1800 }
1801
1802 do {
1803 dir = strchr(dir, DIR_SEPARATOR_CHAR);
1804 /* We set the current / with 0, and restore it afterwards */
1805 if (dir) {
1806 *dir = '\0';
1807 }
1808
1809#ifdef FREECIV_MSWINDOWS
1810#ifdef HAVE__MKDIR
1811 /* Prefer _mkdir() in Windows even if mkdir() would seem to be available -
1812 * chances are that it's wrong kind of mkdir().
1813 * TODO: Make a configure check for mkdir() that also makes sure that it
1814 * takes two parameters, and prefer such proper mkdir() here. */
1815 {
1817 bool failure = FALSE;
1818
1821 failure = TRUE;
1822 }
1823
1825
1826 if (failure) {
1827 free(path);
1828 return FALSE;
1829 }
1830 }
1831#else /* HAVE__MKDIR */
1832 if (mkdir(path, 0755) == -1
1833 && fc_get_errno() != EEXIST) {
1834 free(path);
1835 return FALSE;
1836 }
1837#endif /* HAVE__MKDIR */
1838#else /* FREECIV_MSWINDOWS */
1839 if (mkdir(path, 0755) == -1
1840 && fc_get_errno() != EEXIST) {
1841 free(path);
1842 return FALSE;
1843 }
1844#endif /* FREECIV_MSWINDOWS */
1845
1846 if (dir) {
1847 *dir = DIR_SEPARATOR_CHAR;
1848 dir++;
1849 }
1850 } while (dir);
1851
1852 free(path);
1853
1854 return TRUE;
1855}
1856
1857/************************************************************************/
1861bool make_dir_for_file(char *filename)
1862{
1863 int i;
1864
1865 for (i = strlen(filename) - 1 ; filename[i] != DIR_SEPARATOR_CHAR ; i--) {
1866 /* Nothing */
1867 }
1868
1869 filename[i] = '\0';
1870 log_debug("Create directory \"%s\"", filename);
1871
1872 if (!make_dir(filename)) {
1873 return FALSE;
1874 }
1875 filename[i] = DIR_SEPARATOR_CHAR;
1876
1877 return TRUE;
1878}
1879
1880/************************************************************************/
1883bool path_is_absolute(const char *filename)
1884{
1885 if (!filename) {
1886 return FALSE;
1887 }
1888
1889#ifdef FREECIV_MSWINDOWS
1890 if (strchr(filename, ':')) {
1891 return TRUE;
1892 }
1893#else /* FREECIV_MSWINDOWS */
1894 if (filename[0] == '/') {
1895 return TRUE;
1896 }
1897#endif /* FREECIV_MSWINDOWS */
1898
1899 return FALSE;
1900}
1901
1902/************************************************************************/
1922char scanin(const char **buf, char *delimiters, char *dest, int size)
1923{
1924 char found = '?';
1925 bool sf = FALSE;
1926
1927 if (*buf == NULL || strlen(*buf) == 0 || size == 0) {
1928 if (dest) {
1929 dest[0] = '\0';
1930 }
1931 *buf = NULL;
1932 return '\0';
1933 }
1934
1935 if (dest) {
1936 char *ptr;
1937
1938 strncpy(dest, *buf, size-1);
1939 dest[size - 1] = '\0';
1941 ptr = strpbrk(dest, delimiters);
1942 if (ptr != NULL) {
1943 found = *ptr;
1944 *ptr = '\0';
1946 sf = TRUE;
1947 }
1948 } else {
1949 const char *ptr;
1950
1951 /* Just skip ahead. */
1952 ptr = strpbrk(*buf, delimiters);
1953 if (ptr != NULL) {
1954 found = *ptr;
1955 sf = TRUE;
1956 }
1957 }
1958
1959 if (sf) {
1960 *buf = strpbrk(*buf, delimiters);
1961 if (*buf != NULL) {
1962 (*buf)++; /* Skip delimiter */
1963 } else {
1964 }
1965 } else {
1966 *buf = NULL;
1967 }
1968
1969 return found;
1970}
1971
1972/************************************************************************/
1977{
1978 int seconds, minutes, hours, days;
1979 bool space = FALSE;
1980
1981 seconds = t % 60;
1982 minutes = (t / 60) % 60;
1983 hours = (t / (60 * 60)) % 24;
1984 days = t / (60 * 60 * 24);
1985
1986 if (maxlen <= 0) {
1987 return;
1988 }
1989
1990 buf[0] = '\0';
1991
1992 if (days > 0) {
1993 cat_snprintf(buf, maxlen, "%d %s", days, PL_("day", "days", days));
1994 space = TRUE;
1995 }
1996 if (hours > 0) {
1997 cat_snprintf(buf, maxlen, "%s%d %s",
1998 space ? " " : "", hours, PL_("hour", "hours", hours));
1999 space = TRUE;
2000 }
2001 if (minutes > 0) {
2002 cat_snprintf(buf, maxlen, "%s%d %s",
2003 space ? " " : "",
2004 minutes, PL_("minute", "minutes", minutes));
2005 space = TRUE;
2006 }
2007 if (seconds > 0) {
2008 cat_snprintf(buf, maxlen, "%s%d %s",
2009 space ? " " : "",
2010 seconds, PL_("second", "seconds", seconds));
2011 }
2012}
2013
2014/************************************************************************/
2019void array_shuffle(int *array, int n)
2020{
2021 if (n > 1 && array != NULL) {
2022 int i, j, t;
2023
2024 for (i = 0; i < n - 1; i++) {
2025 j = i + fc_rand(n - i);
2026 t = array[j];
2027 array[j] = array[i];
2028 array[i] = t;
2029 }
2030 }
2031}
2032
2033/************************************************************************/
2038static bool wildcard_asterisk_fit(const char *pattern, const char *test)
2039{
2040 char jump_to;
2041
2042 /* Jump over the leading asterisks. */
2043 pattern++;
2044 while (TRUE) {
2045 switch (*pattern) {
2046 case '\0':
2047 /* It is a leading asterisk. */
2048 return TRUE;
2049 case '*':
2050 pattern++;
2051 continue;
2052 case '?':
2053 if ('\0' == *test) {
2054 return FALSE;
2055 }
2056 test++;
2057 pattern++;
2058 continue;
2059 }
2060
2061 break;
2062 }
2063
2064 if ('[' != *pattern) {
2065 if ('\\' == *pattern) {
2066 jump_to = *(pattern + 1);
2067 } else {
2068 jump_to = *pattern;
2069 }
2070 } else {
2071 jump_to = '\0';
2072 }
2073
2074 while ('\0' != *test) {
2075 if ('\0' != jump_to) {
2076 /* Jump to next matching charather. */
2077 test = strchr(test, jump_to);
2078 if (NULL == test) {
2079 /* No match. */
2080 return FALSE;
2081 }
2082 }
2083
2085 return TRUE;
2086 }
2087
2088 (test)++;
2089 }
2090
2091 return FALSE;
2092}
2093
2094/************************************************************************/
2098static bool wildcard_range_fit(const char **pattern, const char **test)
2099{
2100 const char *start = (*pattern + 1);
2101 char testc;
2102 bool negation;
2103
2104 if ('\0' == **test) {
2105 /* Need one character. */
2106 return FALSE;
2107 }
2108
2109 /* Find the end of the pattern. */
2110 while (TRUE) {
2111 *pattern = strchr(*pattern, ']');
2112 if (NULL == *pattern) {
2113 /* Wildcard format error. */
2114 return FALSE;
2115 } else if (*(*pattern - 1) != '\\') {
2116 /* This is the end. */
2117 break;
2118 } else {
2119 /* Try again. */
2120 (*pattern)++;
2121 }
2122 }
2123
2124 if ('!' == *start) {
2125 negation = TRUE;
2126 start++;
2127 } else {
2128 negation = FALSE;
2129 }
2130 testc = **test;
2131 (*test)++;
2132 (*pattern)++;
2133
2134 for (; start < *pattern; start++) {
2135 if ('-' == *start || '!' == *start) {
2136 /* Wildcard format error. */
2137 return FALSE;
2138 } else if (start < *pattern - 2 && '-' == *(start + 1)) {
2139 /* Case range. */
2140 if (*start <= testc && testc <= *(start + 2)) {
2141 return !negation;
2142 }
2143 start += 2;
2144 } else if (*start == testc) {
2145 /* Single character. */
2146 return !negation;
2147 }
2148 }
2149
2150 return negation;
2151}
2152
2153/************************************************************************/
2164bool wildcard_fit_string(const char *pattern, const char *test)
2165{
2166 while (TRUE) {
2167 switch (*pattern) {
2168 case '\0':
2169 /* '\0' != test. */
2170 return '\0' == *test;
2171 case '*':
2172 return wildcard_asterisk_fit(pattern, test); /* Maybe recursive. */
2173 case '[':
2174 if (!wildcard_range_fit(&pattern, &test)) {
2175 return FALSE;
2176 }
2177 continue;
2178 case '?':
2179 if ('\0' == *test) {
2180 return FALSE;
2181 }
2182 break;
2183 case '\\':
2184 pattern++;
2186 default:
2187 if (*pattern != *test) {
2188 return FALSE;
2189 }
2190 break;
2191 }
2192 pattern++;
2193 test++;
2194 }
2195
2196 return FALSE;
2197}
2198
2199/************************************************************************/
2213int fc_vsnprintcf(char *buf, size_t buf_len, const char *format,
2214 const struct cf_sequence *sequences, size_t sequences_num)
2215{
2216 const struct cf_sequence *pseq;
2217 char cformat[32];
2218 const char *f = format;
2219 char *const max = buf + buf_len - 1;
2220 char *b = buf, *c;
2221 const char *const cmax = cformat + sizeof(cformat) - 2;
2222 int i, j;
2223
2224 if ((size_t) -1 == sequences_num) {
2225 /* Find the number of sequences. */
2226 sequences_num = 0;
2227 for (pseq = sequences; CF_LAST != pseq->type; pseq++) {
2228 sequences_num++;
2229 }
2230 }
2231
2232 while ('\0' != *f) {
2233 if ('%' == *f) {
2234 /* Sequence. */
2235
2236 f++;
2237 if ('%' == *f) {
2238 /* Double '%'. */
2239 *b++ = '%';
2240 f++;
2241 continue;
2242 }
2243
2244 /* Make format. */
2245 c = cformat;
2246 *c++ = '%';
2247 for (; !fc_isalpha(*f) && '\0' != *f && '%' != *f && cmax > c; f++) {
2248 *c++ = *f;
2249 }
2250
2251 if (!fc_isalpha(*f)) {
2252 /* Beginning of a new sequence, end of the format, or too long
2253 * sequence. */
2254 *c = '\0';
2255 j = fc_snprintf(b, max - b + 1, "%s", cformat);
2256 if (-1 == j) {
2257 return -1;
2258 }
2259 b += j;
2260 continue;
2261 }
2262
2263 for (i = 0, pseq = sequences; i < sequences_num; i++, pseq++) {
2264 if (pseq->letter == *f) {
2265 j = -2;
2266 switch (pseq->type) {
2267 case CF_BOOLEAN:
2268 *c++ = 's';
2269 *c = '\0';
2270 j = fc_snprintf(b, max - b + 1, cformat,
2271 pseq->bool_value ? "TRUE" : "FALSE");
2272 break;
2273 case CF_TRANS_BOOLEAN:
2274 *c++ = 's';
2275 *c = '\0';
2276 j = fc_snprintf(b, max - b + 1, cformat,
2277 pseq->bool_value ? _("TRUE") : _("FALSE"));
2278 break;
2279 case CF_CHARACTER:
2280 *c++ = 'c';
2281 *c = '\0';
2282 j = fc_snprintf(b, max - b + 1, cformat, pseq->char_value);
2283 break;
2284 case CF_INTEGER:
2285 *c++ = 'd';
2286 *c = '\0';
2287 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2288 break;
2289 case CF_HEXA:
2290 *c++ = 'x';
2291 *c = '\0';
2292 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2293 break;
2294 case CF_FLOAT:
2295 *c++ = 'f';
2296 *c = '\0';
2297 j = fc_snprintf(b, max - b + 1, cformat, pseq->float_value);
2298 break;
2299 case CF_POINTER:
2300 *c++ = 'p';
2301 *c = '\0';
2302 j = fc_snprintf(b, max - b + 1, cformat, pseq->ptr_value);
2303 break;
2304 case CF_STRING:
2305 *c++ = 's';
2306 *c = '\0';
2307 j = fc_snprintf(b, max - b + 1, cformat, pseq->str_value);
2308 break;
2309 case CF_LAST:
2310 break;
2311 };
2312 if (-2 == j) {
2313 log_error("Error: unsupported sequence type: %d.", pseq->type);
2314 break;
2315 }
2316 if (-1 == j) {
2317 /* Full! */
2318 return -1;
2319 }
2320 f++;
2321 b += j;
2322 break;
2323 }
2324 }
2325 if (i >= sequences_num) {
2326 /* Format not supported. */
2327 *c = '\0';
2328 j = fc_snprintf(b, max - b + 1, "%s%c", cformat, *f);
2329 if (-1 == j) {
2330 return -1;
2331 }
2332 f++;
2333 b += j;
2334 }
2335 } else {
2336 /* Not a sequence. */
2337 *b++ = *f++;
2338 }
2339 if (max <= b) {
2340 /* Too long. */
2341 *max = '\0';
2342 return -1;
2343 }
2344 }
2345 *b = '\0';
2346 return b - buf;
2347}
2348
2349/************************************************************************/
2361int fc_snprintcf(char *buf, size_t buf_len, const char *format, ...)
2362{
2363 struct cf_sequence sequences[16];
2364 size_t sequences_num = 0;
2365 va_list args;
2366
2367 /* Collect sequence array. */
2368 va_start(args, format);
2369 do {
2370 sequences[sequences_num] = va_arg(args, struct cf_sequence);
2372 break;
2373 } else {
2374 sequences_num++;
2375 }
2376 } while (ARRAY_SIZE(sequences) > sequences_num);
2377
2379 && CF_LAST != va_arg(args, struct cf_sequence).type) {
2380 log_error("Too many custom sequences. Maybe did you forget cf_end() "
2381 "at the end of the arguments?");
2382 buf[0] = '\0';
2383 va_end(args);
2384 return -1;
2385 }
2386 va_end(args);
2387
2388 return fc_vsnprintcf(buf, buf_len, format, sequences, sequences_num);
2389}
2390
2391/************************************************************************/
2395static size_t extract_escapes(const char *format, char *escapes,
2396 size_t max_escapes)
2397{
2398 static const char format_escapes[] = {
2399 '*', 'd', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f',
2400 'F', 'g', 'G', 'a', 'A', 'c', 's', 'p', 'n', '\0'
2401 };
2402 bool reordered = FALSE;
2403 size_t num = 0;
2404 int idx = 0;
2405
2407 format = strchr(format, '%');
2408 while (NULL != format) {
2409 format++;
2410 if ('%' == *format) {
2411 /* Double, not a sequence. */
2412 continue;
2413 } else if (fc_isdigit(*format)) {
2414 const char *start = format;
2415
2416 do {
2417 format++;
2418 } while (fc_isdigit(*format));
2419
2420 if ('$' == *format) {
2421 /* Strings are reordered. */
2422 sscanf(start, "%d", &idx);
2423 reordered = TRUE;
2424 }
2425 }
2426
2427 while ('\0' != *format
2428 && NULL == strchr(format_escapes, *format)) {
2429 format++;
2430 }
2431 escapes[idx] = *format;
2432
2433 /* Increase the read count. */
2434 if (reordered) {
2435 if (idx > num) {
2436 num = idx;
2437 }
2438 } else {
2439 idx++;
2440 num++;
2441 }
2442
2443 if ('*' != *format) {
2444 format = strchr(format, '%');
2445 } /* Else we didn't have found the real sequence. */
2446 }
2447 return num;
2448}
2449
2450/************************************************************************/
void astr_free(struct astring *astr)
Definition astring.c:153
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:267
void astr_clear(struct astring *astr)
Definition astring.c:205
void astr_add(struct astring *astr, const char *format,...)
Definition astring.c:287
#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:57
char * incite_cost
Definition comments.c:75
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 fc_assert_ret(condition)
Definition log.h:191
#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:359
static size_t extract_escapes(const char *format, char *escapes, size_t max_escapes)
Definition shared.c:2395
void format_time_duration(time_t t, char *buf, int maxlen)
Definition shared.c:1976
static bool wildcard_asterisk_fit(const char *pattern, const char *test)
Definition shared.c:2038
int compare_strings_ptrs(const void *first, const void *second)
Definition shared.c:369
char * user_username(char *buf, size_t bufsz)
Definition shared.c:711
int fc_snprintcf(char *buf, size_t buf_len, const char *format,...)
Definition shared.c:2361
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:124
static char * grouping
Definition shared.c:98
void dont_run_as_root(const char *argv0, const char *fallback)
Definition shared.c:1549
bool wildcard_fit_string(const char *pattern, const char *test)
Definition shared.c:2164
bool check_strlen(const char *str, size_t len, const char *errmsg)
Definition shared.c:495
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1101
static bool compare_fileinfo_name(const struct fileinfo *pa, const struct fileinfo *pb)
Definition shared.c:1197
char * user_home_dir(void)
Definition shared.c:634
void remove_trailing_spaces(char *s)
Definition shared.c:421
void free_user_home_dir(void)
Definition shared.c:665
int fc_vsnprintcf(char *buf, size_t buf_len, const char *format, const struct cf_sequence *sequences, size_t sequences_num)
Definition shared.c:2213
#define HOMEVAR
static char * storage_dir_freeciv
Definition shared.c:112
void init_nls(void)
Definition shared.c:1470
bool str_to_int(const char *str, int *pint)
Definition shared.c:517
#define default_data_path
bool str_to_float(const char *str, float *pfloat)
Definition shared.c:584
const char * m_pre_description(enum m_pre_result result)
Definition shared.c:1571
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:1027
static char * mc_group
Definition shared.c:110
char * skip_leading_spaces(char *s)
Definition shared.c:388
bool str_to_uint(const char *str, unsigned int *pint)
Definition shared.c:551
bool make_dir(const char *pathname)
Definition shared.c:1782
void free_data_dir_names(void)
Definition shared.c:866
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:1613
const struct strvec * get_scenario_dirs(void)
Definition shared.c:978
size_t loud_strlcpy(char *buffer, const char *str, size_t len, const char *errmsg)
Definition shared.c:505
char * interpret_tilde_alloc(const char *filename)
Definition shared.c:1737
#define FREECIV_SAVE_PATH
Definition shared.c:90
bool make_dir_for_file(char *filename)
Definition shared.c:1861
static void fileinfo_destroy(struct fileinfo *pfile)
Definition shared.c:1166
void interpret_tilde(char *buf, size_t buf_size, const char *filename)
Definition shared.c:1720
static struct astring realfile
Definition shared.c:114
static void remove_trailing_char(char *s, char trailing)
Definition shared.c:453
int compare_strings_strvec(const char *const *first, const char *const *second)
Definition shared.c:379
bool is_base64url(const char *s)
Definition shared.c:318
void free_multicast_group(void)
Definition shared.c:1705
static char * home_dir_user
Definition shared.c:111
void free_fileinfo_data(void)
Definition shared.c:1158
static bool is_ascii(char ch)
Definition shared.c:243
char * end_of_strn(char *str, int *nleft)
Definition shared.c:480
bool path_is_absolute(const char *filename)
Definition shared.c:1883
void free_nls(void)
Definition shared.c:1532
static bool wildcard_range_fit(const char **pattern, const char **test)
Definition shared.c:2098
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:1176
bool formats_match(const char *format1, const char *format2)
Definition shared.c:2454
const char * int_to_text(unsigned int number)
Definition shared.c:234
char scanin(const char **buf, char *delimiters, char *dest, int size)
Definition shared.c:1922
static int compare_file_name_ptrs(const struct fileinfo *const *ppa, const struct fileinfo *const *ppb)
Definition shared.c:1188
const struct strvec * get_save_dirs(void)
Definition shared.c:941
static char * expand_dir(char *tok_in, bool ok_to_free)
Definition shared.c:785
void remove_leading_spaces(char *s)
Definition shared.c:403
char * skip_to_basename(char *filepath)
Definition shared.c:1760
#define FREECIV_DATA_PATH
Definition shared.c:87
#define default_scenario_path
void array_shuffle(int *array, int n)
Definition shared.c:2019
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:1590
bool is_ascii_name(const char *name)
Definition shared.c:283
#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:140
static struct strvec * base_get_dirs(const char *dir_list)
Definition shared.c:839
const char * setup_langname(void)
Definition shared.c:1288
void randomize_base64url_string(char *s, size_t n)
Definition shared.c:339
char * get_multicast_group(bool ipv6_preferred)
Definition shared.c:1674
void free_freeciv_storage_dir(void)
Definition shared.c:696
bool is_safe_filename(const char *name)
Definition shared.c:253
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:159
const struct strvec * get_data_dirs(void)
Definition shared.c:893
char * freeciv_storage_dir(void)
Definition shared.c:678
void switch_lang(const char *lang)
Definition shared.c:1441
struct fileinfo_list * fileinfolist_infix(const struct strvec *dirs, const char *infix, bool nodups)
Definition shared.c:1211
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:223
#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:226
#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:294
@ CF_HEXA
Definition shared.h:292
@ CF_CHARACTER
Definition shared.h:290
@ CF_INTEGER
Definition shared.h:291
@ CF_TRANS_BOOLEAN
Definition shared.h:289
@ CF_LAST
Definition shared.h:297
@ CF_BOOLEAN
Definition shared.h:288
@ CF_STRING
Definition shared.h:295
@ CF_FLOAT
Definition shared.h:293
m_pre_result
Definition shared.h:207
@ M_PRE_EXACT
Definition shared.h:208
@ M_PRE_ONLY
Definition shared.h:209
@ M_PRE_LONG
Definition shared.h:212
@ M_PRE_AMBIGUOUS
Definition shared.h:210
@ M_PRE_EMPTY
Definition shared.h:211
@ M_PRE_FAIL
Definition shared.h:213
const char *(* m_pre_accessor_fn_t)(int)
Definition shared.h:220
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:170
char * fullname
Definition shared.h:169
char * name
Definition shared.h:168
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:974
bool fc_isalpha(char c)
Definition support.c:1221
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:791
bool fc_isspace(char c)
Definition support.c:1254
const char * fc_strerror(fc_errno err)
Definition support.c:611
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:1000
fc_errno fc_get_errno(void)
Definition support.c:594
bool fc_isdigit(char c)
Definition support.c:1232
int fc_strcoll(const char *str0, const char *str1)
Definition support.c:473
int fc_stat(const char *filename, struct stat *buf)
Definition support.c:576
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define fc__fallthrough
Definition support.h:119