Freeciv-3.3: /fast/freeciv/nightly/main/utility/shared.c Source File

Freeciv-3.3

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