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 *ptr, found = '?';
1925
1926 if (*buf == NULL || strlen(*buf) == 0 || size == 0) {
1927 if (dest) {
1928 dest[0] = '\0';
1929 }
1930 *buf = NULL;
1931 return '\0';
1932 }
1933
1934 if (dest) {
1935 strncpy(dest, *buf, size-1);
1936 dest[size-1] = '\0';
1938 ptr = strpbrk(dest, delimiters);
1939 } else {
1940 /* Just skip ahead. */
1941 ptr = strpbrk(*buf, delimiters);
1942 }
1943 if (ptr != NULL) {
1944 found = *ptr;
1945 if (dest) {
1946 *ptr = '\0';
1947 }
1948 if (dest) {
1950 }
1951 *buf = strpbrk(*buf, delimiters);
1952 if (*buf != NULL) {
1953 (*buf)++; /* Skip delimiter */
1954 } else {
1955 }
1956 } else {
1957 *buf = NULL;
1958 }
1959
1960 return found;
1961}
1962
1963/************************************************************************/
1968{
1969 int seconds, minutes, hours, days;
1970 bool space = FALSE;
1971
1972 seconds = t % 60;
1973 minutes = (t / 60) % 60;
1974 hours = (t / (60 * 60)) % 24;
1975 days = t / (60 * 60 * 24);
1976
1977 if (maxlen <= 0) {
1978 return;
1979 }
1980
1981 buf[0] = '\0';
1982
1983 if (days > 0) {
1984 cat_snprintf(buf, maxlen, "%d %s", days, PL_("day", "days", days));
1985 space = TRUE;
1986 }
1987 if (hours > 0) {
1988 cat_snprintf(buf, maxlen, "%s%d %s",
1989 space ? " " : "", hours, PL_("hour", "hours", hours));
1990 space = TRUE;
1991 }
1992 if (minutes > 0) {
1993 cat_snprintf(buf, maxlen, "%s%d %s",
1994 space ? " " : "",
1995 minutes, PL_("minute", "minutes", minutes));
1996 space = TRUE;
1997 }
1998 if (seconds > 0) {
1999 cat_snprintf(buf, maxlen, "%s%d %s",
2000 space ? " " : "",
2001 seconds, PL_("second", "seconds", seconds));
2002 }
2003}
2004
2005/************************************************************************/
2010void array_shuffle(int *array, int n)
2011{
2012 if (n > 1 && array != NULL) {
2013 int i, j, t;
2014
2015 for (i = 0; i < n - 1; i++) {
2016 j = i + fc_rand(n - i);
2017 t = array[j];
2018 array[j] = array[i];
2019 array[i] = t;
2020 }
2021 }
2022}
2023
2024/************************************************************************/
2029static bool wildcard_asterisk_fit(const char *pattern, const char *test)
2030{
2031 char jump_to;
2032
2033 /* Jump over the leading asterisks. */
2034 pattern++;
2035 while (TRUE) {
2036 switch (*pattern) {
2037 case '\0':
2038 /* It is a leading asterisk. */
2039 return TRUE;
2040 case '*':
2041 pattern++;
2042 continue;
2043 case '?':
2044 if ('\0' == *test) {
2045 return FALSE;
2046 }
2047 test++;
2048 pattern++;
2049 continue;
2050 }
2051
2052 break;
2053 }
2054
2055 if ('[' != *pattern) {
2056 if ('\\' == *pattern) {
2057 jump_to = *(pattern + 1);
2058 } else {
2059 jump_to = *pattern;
2060 }
2061 } else {
2062 jump_to = '\0';
2063 }
2064
2065 while ('\0' != *test) {
2066 if ('\0' != jump_to) {
2067 /* Jump to next matching charather. */
2068 test = strchr(test, jump_to);
2069 if (NULL == test) {
2070 /* No match. */
2071 return FALSE;
2072 }
2073 }
2074
2076 return TRUE;
2077 }
2078
2079 (test)++;
2080 }
2081
2082 return FALSE;
2083}
2084
2085/************************************************************************/
2089static bool wildcard_range_fit(const char **pattern, const char **test)
2090{
2091 const char *start = (*pattern + 1);
2092 char testc;
2093 bool negation;
2094
2095 if ('\0' == **test) {
2096 /* Need one character. */
2097 return FALSE;
2098 }
2099
2100 /* Find the end of the pattern. */
2101 while (TRUE) {
2102 *pattern = strchr(*pattern, ']');
2103 if (NULL == *pattern) {
2104 /* Wildcard format error. */
2105 return FALSE;
2106 } else if (*(*pattern - 1) != '\\') {
2107 /* This is the end. */
2108 break;
2109 } else {
2110 /* Try again. */
2111 (*pattern)++;
2112 }
2113 }
2114
2115 if ('!' == *start) {
2116 negation = TRUE;
2117 start++;
2118 } else {
2119 negation = FALSE;
2120 }
2121 testc = **test;
2122 (*test)++;
2123 (*pattern)++;
2124
2125 for (; start < *pattern; start++) {
2126 if ('-' == *start || '!' == *start) {
2127 /* Wildcard format error. */
2128 return FALSE;
2129 } else if (start < *pattern - 2 && '-' == *(start + 1)) {
2130 /* Case range. */
2131 if (*start <= testc && testc <= *(start + 2)) {
2132 return !negation;
2133 }
2134 start += 2;
2135 } else if (*start == testc) {
2136 /* Single character. */
2137 return !negation;
2138 }
2139 }
2140
2141 return negation;
2142}
2143
2144/************************************************************************/
2155bool wildcard_fit_string(const char *pattern, const char *test)
2156{
2157 while (TRUE) {
2158 switch (*pattern) {
2159 case '\0':
2160 /* '\0' != test. */
2161 return '\0' == *test;
2162 case '*':
2163 return wildcard_asterisk_fit(pattern, test); /* Maybe recursive. */
2164 case '[':
2165 if (!wildcard_range_fit(&pattern, &test)) {
2166 return FALSE;
2167 }
2168 continue;
2169 case '?':
2170 if ('\0' == *test) {
2171 return FALSE;
2172 }
2173 break;
2174 case '\\':
2175 pattern++;
2177 default:
2178 if (*pattern != *test) {
2179 return FALSE;
2180 }
2181 break;
2182 }
2183 pattern++;
2184 test++;
2185 }
2186
2187 return FALSE;
2188}
2189
2190/************************************************************************/
2204int fc_vsnprintcf(char *buf, size_t buf_len, const char *format,
2205 const struct cf_sequence *sequences, size_t sequences_num)
2206{
2207 const struct cf_sequence *pseq;
2208 char cformat[32];
2209 const char *f = format;
2210 char *const max = buf + buf_len - 1;
2211 char *b = buf, *c;
2212 const char *const cmax = cformat + sizeof(cformat) - 2;
2213 int i, j;
2214
2215 if ((size_t) -1 == sequences_num) {
2216 /* Find the number of sequences. */
2217 sequences_num = 0;
2218 for (pseq = sequences; CF_LAST != pseq->type; pseq++) {
2219 sequences_num++;
2220 }
2221 }
2222
2223 while ('\0' != *f) {
2224 if ('%' == *f) {
2225 /* Sequence. */
2226
2227 f++;
2228 if ('%' == *f) {
2229 /* Double '%'. */
2230 *b++ = '%';
2231 f++;
2232 continue;
2233 }
2234
2235 /* Make format. */
2236 c = cformat;
2237 *c++ = '%';
2238 for (; !fc_isalpha(*f) && '\0' != *f && '%' != *f && cmax > c; f++) {
2239 *c++ = *f;
2240 }
2241
2242 if (!fc_isalpha(*f)) {
2243 /* Beginning of a new sequence, end of the format, or too long
2244 * sequence. */
2245 *c = '\0';
2246 j = fc_snprintf(b, max - b + 1, "%s", cformat);
2247 if (-1 == j) {
2248 return -1;
2249 }
2250 b += j;
2251 continue;
2252 }
2253
2254 for (i = 0, pseq = sequences; i < sequences_num; i++, pseq++) {
2255 if (pseq->letter == *f) {
2256 j = -2;
2257 switch (pseq->type) {
2258 case CF_BOOLEAN:
2259 *c++ = 's';
2260 *c = '\0';
2261 j = fc_snprintf(b, max - b + 1, cformat,
2262 pseq->bool_value ? "TRUE" : "FALSE");
2263 break;
2264 case CF_TRANS_BOOLEAN:
2265 *c++ = 's';
2266 *c = '\0';
2267 j = fc_snprintf(b, max - b + 1, cformat,
2268 pseq->bool_value ? _("TRUE") : _("FALSE"));
2269 break;
2270 case CF_CHARACTER:
2271 *c++ = 'c';
2272 *c = '\0';
2273 j = fc_snprintf(b, max - b + 1, cformat, pseq->char_value);
2274 break;
2275 case CF_INTEGER:
2276 *c++ = 'd';
2277 *c = '\0';
2278 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2279 break;
2280 case CF_HEXA:
2281 *c++ = 'x';
2282 *c = '\0';
2283 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2284 break;
2285 case CF_FLOAT:
2286 *c++ = 'f';
2287 *c = '\0';
2288 j = fc_snprintf(b, max - b + 1, cformat, pseq->float_value);
2289 break;
2290 case CF_POINTER:
2291 *c++ = 'p';
2292 *c = '\0';
2293 j = fc_snprintf(b, max - b + 1, cformat, pseq->ptr_value);
2294 break;
2295 case CF_STRING:
2296 *c++ = 's';
2297 *c = '\0';
2298 j = fc_snprintf(b, max - b + 1, cformat, pseq->str_value);
2299 break;
2300 case CF_LAST:
2301 break;
2302 };
2303 if (-2 == j) {
2304 log_error("Error: unsupported sequence type: %d.", pseq->type);
2305 break;
2306 }
2307 if (-1 == j) {
2308 /* Full! */
2309 return -1;
2310 }
2311 f++;
2312 b += j;
2313 break;
2314 }
2315 }
2316 if (i >= sequences_num) {
2317 /* Format not supported. */
2318 *c = '\0';
2319 j = fc_snprintf(b, max - b + 1, "%s%c", cformat, *f);
2320 if (-1 == j) {
2321 return -1;
2322 }
2323 f++;
2324 b += j;
2325 }
2326 } else {
2327 /* Not a sequence. */
2328 *b++ = *f++;
2329 }
2330 if (max <= b) {
2331 /* Too long. */
2332 *max = '\0';
2333 return -1;
2334 }
2335 }
2336 *b = '\0';
2337 return b - buf;
2338}
2339
2340/************************************************************************/
2352int fc_snprintcf(char *buf, size_t buf_len, const char *format, ...)
2353{
2354 struct cf_sequence sequences[16];
2355 size_t sequences_num = 0;
2356 va_list args;
2357
2358 /* Collect sequence array. */
2359 va_start(args, format);
2360 do {
2361 sequences[sequences_num] = va_arg(args, struct cf_sequence);
2363 break;
2364 } else {
2365 sequences_num++;
2366 }
2367 } while (ARRAY_SIZE(sequences) > sequences_num);
2368
2370 && CF_LAST != va_arg(args, struct cf_sequence).type) {
2371 log_error("Too many custom sequences. Maybe did you forget cf_end() "
2372 "at the end of the arguments?");
2373 buf[0] = '\0';
2374 va_end(args);
2375 return -1;
2376 }
2377 va_end(args);
2378
2379 return fc_vsnprintcf(buf, buf_len, format, sequences, sequences_num);
2380}
2381
2382/************************************************************************/
2386static size_t extract_escapes(const char *format, char *escapes,
2387 size_t max_escapes)
2388{
2389 static const char format_escapes[] = {
2390 '*', 'd', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f',
2391 'F', 'g', 'G', 'a', 'A', 'c', 's', 'p', 'n', '\0'
2392 };
2393 bool reordered = FALSE;
2394 size_t num = 0;
2395 int idx = 0;
2396
2398 format = strchr(format, '%');
2399 while (NULL != format) {
2400 format++;
2401 if ('%' == *format) {
2402 /* Double, not a sequence. */
2403 continue;
2404 } else if (fc_isdigit(*format)) {
2405 const char *start = format;
2406
2407 do {
2408 format++;
2409 } while (fc_isdigit(*format));
2410
2411 if ('$' == *format) {
2412 /* Strings are reordered. */
2413 sscanf(start, "%d", &idx);
2414 reordered = TRUE;
2415 }
2416 }
2417
2418 while ('\0' != *format
2419 && NULL == strchr(format_escapes, *format)) {
2420 format++;
2421 }
2422 escapes[idx] = *format;
2423
2424 /* Increase the read count. */
2425 if (reordered) {
2426 if (idx > num) {
2427 num = idx;
2428 }
2429 } else {
2430 idx++;
2431 num++;
2432 }
2433
2434 if ('*' != *format) {
2435 format = strchr(format, '%');
2436 } /* Else we didn't have found the real sequence. */
2437 }
2438 return num;
2439}
2440
2441/************************************************************************/
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: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:2386
void format_time_duration(time_t t, char *buf, int maxlen)
Definition shared.c:1967
static bool wildcard_asterisk_fit(const char *pattern, const char *test)
Definition shared.c:2029
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:2352
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:2155
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:2204
#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:2089
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:2445
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:2010
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