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