Freeciv-3.1
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;
108static struct strvec *scenario_dir_names = NULL;
109
110static char *mc_group = NULL;
111static char *home_dir_user = NULL;
112static char *storage_dir_freeciv = 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. */
175 fc_assert_ret_val(0 <= mantissa, NULL);
176 fc_assert_ret_val(0 <= exponent, NULL);
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. */
193 fc_assert_ret_val(ptr > buf + seplen, NULL);
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 fc_assert_ret_val(ptr >= buf, NULL);
218 memcpy(ptr, sep, seplen);
219 if (*(grp + 1) != 0) {
220 /* Zero means to repeat the present group-size indefinitely. */
221 grp++;
222 }
223 }
224 }
225
226 return ptr;
227}
228
229
230/************************************************************************/
233const char *int_to_text(unsigned int number)
234{
235 return big_int_to_text(number, 0);
236}
237
238/************************************************************************/
242static bool is_ascii(char ch)
243{
244 /* this works with both signed and unsigned char's. */
245 return ch >= ' ' && ch <= '~';
246}
247
248/************************************************************************/
252bool is_safe_filename(const char *name)
253{
254 int i = 0;
255
256 /* must not be NULL or empty */
257 if (!name || *name == '\0') {
258 return FALSE;
259 }
260
261 for (; '\0' != name[i]; i++) {
262 if ('.' != name[i] && NULL == strchr(base64url, name[i])) {
263 return FALSE;
264 }
265 }
266
267 /* we don't allow the filename to ascend directories */
268 if (strstr(name, PARENT_DIR_OPERATOR)) {
269 return FALSE;
270 }
271
272 /* Otherwise, it is okay... */
273 return TRUE;
274}
275
276/************************************************************************/
282bool is_ascii_name(const char *name)
283{
284 const char illegal_chars[] = {'|', '%', '"', ',', '*', '<', '>', '\0'};
285 int i, j;
286
287 /* must not be NULL or empty */
288 if (!name || *name == '\0') {
289 return FALSE;
290 }
291
292 /* must begin and end with some non-space character */
293 if ((*name == ' ') || (*(strchr(name, '\0') - 1) == ' ')) {
294 return FALSE;
295 }
296
297 /* must be composed entirely of printable ascii characters,
298 * and no illegal characters which can break ranking scripts. */
299 for (i = 0; name[i]; i++) {
300 if (!is_ascii(name[i])) {
301 return FALSE;
302 }
303 for (j = 0; illegal_chars[j]; j++) {
304 if (name[i] == illegal_chars[j]) {
305 return FALSE;
306 }
307 }
308 }
309
310 /* otherwise, it's okay... */
311 return TRUE;
312}
313
314/************************************************************************/
317bool is_base64url(const char *s)
318{
319 size_t i = 0;
320
321 /* must not be NULL or empty */
322 if (NULL == s || '\0' == *s) {
323 return FALSE;
324 }
325
326 for (; '\0' != s[i]; i++) {
327 if (NULL == strchr(base64url, s[i])) {
328 return FALSE;
329 }
330 }
331 return TRUE;
332}
333
334/************************************************************************/
338void randomize_base64url_string(char *s, size_t n)
339{
340 size_t i = 0;
341
342 /* must not be NULL or too short */
343 if (NULL == s || 1 > n) {
344 return;
345 }
346
347 for (; i < (n - 1); i++) {
348 s[i] = base64url[fc_rand(sizeof(base64url) - 1)];
349 }
350 s[i] = '\0';
351}
352
353/************************************************************************/
358int compare_strings(const void *first, const void *second)
359{
360 return fc_strcoll((const char *) first, (const char *) second);
361}
362
363/************************************************************************/
368int compare_strings_ptrs(const void *first, const void *second)
369{
370 return fc_strcoll(*((const char **) first), *((const char **) second));
371}
372
373/************************************************************************/
378int compare_strings_strvec(const char *const *first,
379 const char *const *second)
380{
381 return fc_strcoll(*first, *second);
382}
383
384/************************************************************************/
388{
389 fc_assert_ret_val(NULL != s, NULL);
390
391 while (*s != '\0' && fc_isspace(*s)) {
392 s++;
393 }
394
395 return s;
396}
397
398/************************************************************************/
403{
404 char *t;
405
406 fc_assert_ret(NULL != s);
407 t = skip_leading_spaces(s);
408 if (t != s) {
409 while (*t != '\0') {
410 *s++ = *t++;
411 }
412 *s = '\0';
413 }
414}
415
416/************************************************************************/
421{
422 char *t;
423 size_t len;
424
425 fc_assert_ret(NULL != s);
426 len = strlen(s);
427 if (len > 0) {
428 t = s + len -1;
429 while (fc_isspace(*t)) {
430 *t = '\0';
431 if (t == s) {
432 break;
433 }
434 t--;
435 }
436 }
437}
438
439/************************************************************************/
448
449/************************************************************************/
452static void remove_trailing_char(char *s, char trailing)
453{
454 char *t;
455
456 fc_assert_ret(NULL != s);
457
458 t = s + strlen(s) -1;
459 while (t>=s && (*t) == trailing) {
460 *t = '\0';
461 t--;
462 }
463}
464
465/************************************************************************/
479char *end_of_strn(char *str, int *nleft)
480{
481 int len = strlen(str);
482 *nleft -= len;
483 fc_assert_ret_val(0 < (*nleft), NULL); /* space for the terminating nul */
484 return str + len;
485}
486
487/************************************************************************/
492bool check_strlen(const char *str, size_t len, const char *errmsg)
493{
494 fc_assert_ret_val_msg(strlen(str) < len, TRUE, errmsg, str, len);
495 return FALSE;
496}
497
498/************************************************************************/
501size_t loud_strlcpy(char *buffer, const char *str, size_t len,
502 const char *errmsg)
503{
504 (void) check_strlen(str, len, errmsg);
505 return fc_strlcpy(buffer, str, len);
506}
507
508/************************************************************************/
512bool str_to_int(const char *str, int *pint)
513{
514 const char *start;
515
516 fc_assert_ret_val(NULL != str, FALSE);
517
518 while (fc_isspace(*str)) {
519 /* Skip leading spaces. */
520 str++;
521 }
522
523 start = str;
524 if ('-' == *str || '+' == *str) {
525 /* Handle sign. */
526 str++;
527 }
528 while (fc_isdigit(*str)) {
529 /* Digits. */
530 str++;
531 }
532
533 while (fc_isspace(*str)) {
534 /* Ignore trailing spaces. */
535 str++;
536 }
537
538 return ('\0' == *str && (NULL == pint || 1 == sscanf(start, "%d", pint)));
539}
540
541/************************************************************************/
545bool str_to_uint(const char *str, unsigned int *pint)
546{
547 const char *start;
548
549 fc_assert_ret_val(NULL != str, FALSE);
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 && (NULL == pint || 1 == sscanf(start, "%u", pint)));
572}
573
574/************************************************************************/
578bool str_to_float(const char *str, float *pfloat)
579{
580 bool dot;
581 const char *start;
582
583 fc_assert_ret_val(NULL != str, FALSE);
584
585 while (fc_isspace(*str)) {
586 /* Skip leading spaces. */
587 str++;
588 }
589
590 start = str;
591
592 if ('-' == *str || '+' == *str) {
593 /* Handle sign. */
594 str++;
595 }
596 while (fc_isdigit(*str)) {
597 /* Digits. */
598 str++;
599 }
600
601 if (*str == '.') {
602 dot = TRUE;
603 str++;
604
605 while (fc_isdigit(*str)) {
606 /* Digits. */
607 str++;
608 }
609 } else {
610 dot = FALSE;
611 }
612
613 while (fc_isspace(*str)) {
614 /* Ignore trailing spaces. */
615 str++;
616 }
617
618 return ('\0' == *str && dot
619 && (NULL == pfloat || 1 == sscanf(start, "%f", pfloat)));
620}
621
622/************************************************************************/
628char *user_home_dir(void)
629{
630#ifdef AMIGA
631 return "PROGDIR:";
632#else /* AMIGA */
633
634#ifdef FREECIV_MSWINDOWS
635#define HOMEVAR "APPDATA"
636#else
637#define HOMEVAR "HOME"
638#endif
639
640 if (home_dir_user == NULL) {
641 char *env = getenv(HOMEVAR);
642
643 if (env) {
646 } else {
647 log_error("Could not find home directory (" HOMEVAR " is not set).");
648 home_dir_user = NULL;
649 }
650 }
651
652 return home_dir_user;
653#endif /* AMIGA */
654}
655
656/************************************************************************/
660{
661 if (home_dir_user != NULL) {
662 free(home_dir_user);
663 home_dir_user = NULL;
664 }
665}
666
667/************************************************************************/
673{
674 if (storage_dir_freeciv == NULL) {
675 storage_dir_freeciv = fc_malloc(strlen(FREECIV_STORAGE_DIR) + 1);
676
677 strcpy(storage_dir_freeciv, FREECIV_STORAGE_DIR);
678
680
681 log_verbose(_("Storage dir is \"%s\"."), storage_dir_freeciv);
682 }
683
684 return storage_dir_freeciv;
685}
686
687/************************************************************************/
691{
692 if (storage_dir_freeciv != NULL) {
694 storage_dir_freeciv = NULL;
695 }
696}
697
698/************************************************************************/
705char *user_username(char *buf, size_t bufsz)
706{
707 /* This function uses a number of different methods to try to find a
708 * username. This username then has to be truncated to bufsz
709 * characters (including terminator) and checked for sanity. Note that
710 * truncating a sane name can leave you with an insane name under some
711 * charsets. */
712
713 /* If the environment variable $USER is present and sane, use it. */
714 {
715 char *env = getenv("USER");
716
717 if (env) {
718 fc_strlcpy(buf, env, bufsz);
719 if (is_ascii_name(buf)) {
720 log_verbose("USER username is %s", buf);
721 return buf;
722 }
723 }
724 }
725
726#ifdef HAVE_GETPWUID
727 /* Otherwise if getpwuid() is available we can use it to find the true
728 * username. */
729 {
730 struct passwd *pwent = getpwuid(getuid());
731
732 if (pwent) {
733 fc_strlcpy(buf, pwent->pw_name, bufsz);
734 if (is_ascii_name(buf)) {
735 log_verbose("getpwuid username is %s", buf);
736 return buf;
737 }
738 }
739 }
740#endif /* HAVE_GETPWUID */
741
742#ifdef FREECIV_MSWINDOWS
743 /* On windows the GetUserName function will give us the login name. */
744 {
745 char name[UNLEN + 1];
746 DWORD length = sizeof(name);
747
748 if (GetUserName(name, &length)) {
749 fc_strlcpy(buf, name, bufsz);
750 if (is_ascii_name(buf)) {
751 log_verbose("GetUserName username is %s", buf);
752 return buf;
753 }
754 }
755 }
756#endif /* FREECIV_MSWINDOWS */
757
758#ifdef ALWAYS_ROOT
759 fc_strlcpy(buf, "name", bufsz);
760#else
761 fc_snprintf(buf, bufsz, "name%d", (int) getuid());
762#endif
763 log_verbose("fake username is %s", buf);
765 return buf;
766}
767
768/************************************************************************/
778static char *expand_dir(char *tok_in, bool ok_to_free)
779{
780 int i; /* strlen(tok), or -1 as flag */
781 char *tok;
782 char **ret = &tok; /* Return tok by default */
783 char *allocated;
784
785 tok = skip_leading_spaces(tok_in);
787 if (strcmp(tok, DIR_SEPARATOR) != 0) {
789 }
790
791 i = strlen(tok);
792 if (tok[0] == '~') {
793 if (i > 1 && tok[1] != DIR_SEPARATOR_CHAR) {
794 log_error("For \"%s\" in path cannot expand '~'"
795 " except as '~" DIR_SEPARATOR "'; ignoring", tok);
796 i = 0; /* skip this one */
797 } else {
798 char *home = user_home_dir();
799
800 if (!home) {
801 log_verbose("No HOME, skipping path component %s", tok);
802 i = 0;
803 } else {
804 int len = strlen(home) + i; /* +1 -1 */
805
806 allocated = fc_malloc(len);
807 ret = &allocated;
808
809 fc_snprintf(allocated, len, "%s%s", home, tok + 1);
810 i = -1; /* flag to free tok below */
811 }
812 }
813 }
814
815 if (i != 0) {
816 /* We could check whether the directory exists and
817 * is readable etc? Don't currently. */
818 if (i == -1 && ok_to_free) {
819 free(tok);
820 tok = NULL;
821 }
822 }
823
824 return *ret;
825}
826
827/************************************************************************/
832static struct strvec *base_get_dirs(const char *dir_list)
833{
834 struct strvec *dirs = strvec_new();
835 char *path, *tok;
836
837 path = fc_strdup(dir_list); /* something we can strtok */
838 tok = strtok(path, PATH_SEPARATOR);
839 do {
840 char *dir = expand_dir(tok, FALSE);
841
842 if (dir != NULL) {
843 strvec_append(dirs, dir);
844 if (dir != tok) {
845 free(dir);
846 }
847 }
848
849 tok = strtok(NULL, PATH_SEPARATOR);
850 } while (tok);
851
852 free(path);
853 return dirs;
854}
855
856/************************************************************************/
860{
861 if (data_dir_names != NULL) {
863 data_dir_names = NULL;
864 }
865 if (save_dir_names != NULL) {
867 save_dir_names = NULL;
868 }
869 if (scenario_dir_names != NULL) {
871 scenario_dir_names = NULL;
872 }
873}
874
875/************************************************************************/
886const struct strvec *get_data_dirs(void)
887{
888 /* The first time this function is called it will search and
889 * allocate the directory listing. Subsequently we will already
890 * know the list and can just return it. */
891 if (NULL == data_dir_names) {
892 const char *path;
893
894#ifdef FREECIV_APPIMAGE
895 char default_data_path[5000];
896
898 FREECIV_STORAGE_DIR DIR_SEPARATOR DATASUBDIR
900 "%s/usr/share/freeciv",
901 getenv("APPDIR"));
902#else /* FREECIV_APPIMAGE */
903#define default_data_path DEFAULT_DATA_PATH
904#endif /* FREECIV_APPIMAGE */
905
906 if ((path = getenv(FREECIV_DATA_PATH)) && '\0' == path[0]) {
907 /* TRANS: <FREECIV_DATA_PATH> configuration error */
908 log_error(_("\"%s\" is set but empty; using default \"%s\" "
909 "data directories instead."),
911 path = NULL;
912 }
913 data_dir_names = base_get_dirs(NULL != path ? path : default_data_path);
914 strvec_remove_duplicate(data_dir_names, strcmp); /* Don't set a path both. */
916 log_verbose("Data path component: %s", dirname);
918 }
919
920 return data_dir_names;
921}
922
923/************************************************************************/
934const struct strvec *get_save_dirs(void)
935{
936 /* The first time this function is called it will search and
937 * allocate the directory listing. Subsequently we will already
938 * know the list and can just return it. */
939 if (NULL == save_dir_names) {
940 const char *path;
941
942 if ((path = getenv(FREECIV_SAVE_PATH)) && '\0' == path[0]) {
943 /* TRANS: <FREECIV_SAVE_PATH> configuration error */
944 log_error(_("\"%s\" is set but empty; using default \"%s\" "
945 "save directories instead."),
947 path = NULL;
948 }
949 save_dir_names = base_get_dirs(NULL != path ? path : DEFAULT_SAVE_PATH);
950 strvec_remove_duplicate(save_dir_names, strcmp); /* Don't set a path both. */
952 log_verbose("Save path component: %s", dirname);
954 }
955
956 return save_dir_names;
957}
958
959/************************************************************************/
971const struct strvec *get_scenario_dirs(void)
972{
973 /* The first time this function is called it will search and
974 * allocate the directory listing. Subsequently we will already
975 * know the list and can just return it. */
976 if (NULL == scenario_dir_names) {
977 const char *path;
978
979#ifdef FREECIV_APPIMAGE
980 char default_scenario_path[5000];
981
983 FREECIV_STORAGE_DIR DIR_SEPARATOR DATASUBDIR DIR_SEPARATOR "scenarios"
985 FREECIV_STORAGE_DIR DIR_SEPARATOR "scenarios"
987 "%s/usr/share/freeciv/scenarios",
988 getenv("APPDIR"));
989#else /* FREECIV_APPIMAGE */
990#define default_scenario_path DEFAULT_SCENARIO_PATH
991#endif /* FREECIV_APPIMAGE */
992
993 if ((path = getenv(FREECIV_SCENARIO_PATH)) && '\0' == path[0]) {
994 /* TRANS: <FREECIV_SCENARIO_PATH> configuration error */
995 log_error( _("\"%s\" is set but empty; using default \"%s\" "
996 "scenario directories instead."),
998 path = NULL;
999 }
1001 strvec_remove_duplicate(scenario_dir_names, strcmp); /* Don't set a path both. */
1003 log_verbose("Scenario path component: %s", dirname);
1005 }
1006
1007 return scenario_dir_names;
1008}
1009
1010/************************************************************************/
1020struct strvec *fileinfolist(const struct strvec *dirs, const char *suffix)
1021{
1022 struct strvec *files = strvec_new();
1023 size_t suffix_len = strlen(suffix);
1024
1025 fc_assert_ret_val(!strchr(suffix, DIR_SEPARATOR_CHAR), NULL);
1026
1027 if (NULL == dirs) {
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);
1070
1071 /* Sort the list and remove duplications. */
1072 strvec_remove_duplicate(files, strcmp);
1074
1075 return files;
1076}
1077
1078/************************************************************************/
1094const char *fileinfoname(const struct strvec *dirs, const char *filename)
1095{
1096#ifndef DIR_SEPARATOR_IS_DEFAULT
1097 char fnbuf[filename != NULL ? 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 (NULL == dirs) {
1104 return NULL;
1105 }
1106
1107 if (!filename) {
1108 bool first = TRUE;
1109
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 }
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 }
1142
1143 log_verbose("Could not find readable file \"%s\" in data path.", filename);
1144
1145 return NULL;
1146}
1147
1148/************************************************************************/
1152{
1154}
1155
1156/************************************************************************/
1159static void fileinfo_destroy(struct fileinfo *pfile)
1160{
1161 free(pfile->name);
1162 free(pfile->fullname);
1163 free(pfile);
1164}
1165
1166/************************************************************************/
1169static int compare_file_mtime_ptrs(const struct fileinfo *const *ppa,
1170 const struct fileinfo *const *ppb)
1171{
1172 time_t a = (*ppa)->mtime;
1173 time_t b = (*ppb)->mtime;
1174
1175 return ((a < b) ? 1 : (a > b) ? -1 : 0);
1176}
1177
1178/************************************************************************/
1181static int compare_file_name_ptrs(const struct fileinfo *const *ppa,
1182 const struct fileinfo *const *ppb)
1183{
1184 return fc_strcoll((*ppa)->name, (*ppb)->name);
1185}
1186
1187/************************************************************************/
1190static bool compare_fileinfo_name(const struct fileinfo *pa,
1191 const struct fileinfo *pb)
1192{
1193 return 0 == fc_strcoll(pa->name, pb->name);
1194}
1195
1196/************************************************************************/
1204struct fileinfo_list *fileinfolist_infix(const struct strvec *dirs,
1205 const char *infix, bool nodups)
1206{
1207 struct fileinfo_list *res;
1208
1209 if (NULL == dirs) {
1210 return NULL;
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);
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/************************************************************************/
1281const char *setup_langname(void)
1282{
1283 const char *langname = NULL;
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 != NULL) {
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", NULL };
1412 int i;
1413 bool ac_enabled = FALSE;
1414
1415 char *lang = getenv("LANG");
1416
1417 if (lang != NULL && lang[0] != '\0' && lang[1] != '\0') {
1418 for (i = 0; autocap_opt_in[i] != NULL && !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/************************************************************************/
1434void switch_lang(const char *lang)
1435{
1436#ifdef FREECIV_ENABLE_NLS
1437#ifdef HAVE_SETENV
1438 setenv("LANG", lang, TRUE);
1439#else /* HAVE_SETENV */
1440 if (lang != NULL) {
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 */
1456#endif /* FREECIV_ENABLE_NLS */
1457}
1458
1459/************************************************************************/
1463void init_nls(void)
1464{
1465 /*
1466 * Setup the cached locale numeric formatting information. Defaults
1467 * are as appropriate for the US.
1468 */
1469 grouping = fc_strdup("\3");
1470 grouping_sep = fc_strdup(",");
1471
1472#ifdef ENABLE_NLS
1473
1474#ifdef FREECIV_MSWINDOWS
1475 setup_langname(); /* Makes sure LANG env variable has been set */
1476#endif /* FREECIV_MSWINDOWS */
1477
1478 (void) setlocale(LC_ALL, "");
1479 (void) bindtextdomain("freeciv-core", get_locale_dir());
1480 (void) textdomain("freeciv-core");
1481
1482 /* Don't touch the defaults when LC_NUMERIC == "C".
1483 This is intended to cater to the common case where:
1484 1) The user is from North America. ;-)
1485 2) The user has not set the proper environment variables.
1486 (Most applications are (unfortunately) US-centric
1487 by default, so why bother?)
1488 This would result in the "C" locale being used, with grouping ""
1489 and thousands_sep "", where we really want "\3" and ",". */
1490
1491 if (strcmp(setlocale(LC_NUMERIC, NULL), "C") != 0) {
1492 struct lconv *lc = localeconv();
1493
1494 if (lc->grouping[0] == '\0') {
1495 /* This actually indicates no grouping at all. */
1496 char *m = malloc(sizeof(char));
1497 *m = CHAR_MAX;
1498 grouping = m;
1499 } else {
1500 size_t len;
1501 for (len = 0;
1502 lc->grouping[len] != '\0' && lc->grouping[len] != CHAR_MAX; len++) {
1503 /* nothing */
1504 }
1505 len++;
1506 free(grouping);
1508 memcpy(grouping, lc->grouping, len);
1509 }
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/************************************************************************/
1522void free_nls(void)
1523{
1524 free(grouping);
1525 grouping = NULL;
1526 free(grouping_sep);
1527 grouping_sep = NULL;
1528}
1529
1530/************************************************************************/
1539void dont_run_as_root(const char *argv0, const char *fallback)
1540{
1541#if (defined(ALWAYS_ROOT) || defined(__EMX__) || defined(__BEOS__))
1542 return;
1543#else
1544 if (getuid() == 0 || geteuid() == 0) {
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/************************************************************************/
1561const char *m_pre_description(enum m_pre_result result)
1562{
1563 static const char * const descriptions[] = {
1564 N_("exact match"),
1565 N_("only match"),
1566 N_("ambiguous"),
1567 N_("empty"),
1568 N_("too long"),
1569 N_("non-match")
1570 };
1571 fc_assert_ret_val(result >= 0 && result < ARRAY_SIZE(descriptions), NULL);
1572 return descriptions[result];
1573}
1574
1575/************************************************************************/
1579 size_t n_names,
1580 size_t max_len_name,
1581 m_pre_strncmp_fn_t cmp_fn,
1582 m_strlen_fn_t len_fn,
1583 const char *prefix,
1584 int *ind_result)
1585{
1586 return match_prefix_full(accessor_fn, n_names, max_len_name, cmp_fn,
1587 len_fn, prefix, ind_result, NULL, 0, NULL);
1588}
1589
1590/************************************************************************/
1602 size_t n_names,
1603 size_t max_len_name,
1604 m_pre_strncmp_fn_t cmp_fn,
1605 m_strlen_fn_t len_fn,
1606 const char *prefix,
1607 int *ind_result,
1608 int *matches,
1609 int max_matches,
1610 int *pnum_matches)
1611{
1612 int i, len, nmatches;
1613
1614 if (len_fn == NULL) {
1615 len = strlen(prefix);
1616 } else {
1617 len = len_fn(prefix);
1618 }
1619 if (len == 0) {
1620 return M_PRE_EMPTY;
1621 }
1622 if (len > max_len_name && max_len_name > 0) {
1623 return M_PRE_LONG;
1624 }
1625
1626 nmatches = 0;
1627 for (i = 0; i < n_names; i++) {
1628 const char *name = accessor_fn(i);
1629
1630 if (cmp_fn(name, prefix, len) == 0) {
1631 if (strlen(name) == len) {
1632 *ind_result = i;
1633 return M_PRE_EXACT;
1634 }
1635 if (nmatches == 0) {
1636 *ind_result = i; /* first match */
1637 }
1638 if (matches != NULL && nmatches < max_matches) {
1639 matches[nmatches] = i;
1640 }
1641 nmatches++;
1642 }
1643 }
1644
1645 if (nmatches == 1) {
1646 return M_PRE_ONLY;
1647 } else if (nmatches > 1) {
1648 if (pnum_matches != NULL) {
1649 *pnum_matches = MIN(max_matches, nmatches);
1650 }
1651 return M_PRE_AMBIGUOUS;
1652 } else {
1653 return M_PRE_FAIL;
1654 }
1655}
1656
1657/************************************************************************/
1662char *get_multicast_group(bool ipv6_preferred)
1663{
1664 static char *default_multicast_group_ipv4 = "225.1.1.1";
1665#ifdef FREECIV_IPV6_SUPPORT
1666 /* TODO: Get useful group (this is node local) */
1667 static char *default_multicast_group_ipv6 = "FF31::8000:15B4";
1668#endif /* IPv6 support */
1669
1670 if (mc_group == NULL) {
1671 char *env = getenv("FREECIV_MULTICAST_GROUP");
1672
1673 if (env) {
1674 mc_group = fc_strdup(env);
1675 } else {
1676#ifdef FREECIV_IPV6_SUPPORT
1677 if (ipv6_preferred) {
1678 mc_group = fc_strdup(default_multicast_group_ipv6);
1679 } else
1680#endif /* IPv6 support */
1681 {
1682 mc_group = fc_strdup(default_multicast_group_ipv4);
1683 }
1684 }
1685 }
1686
1687 return mc_group;
1688}
1689
1690/************************************************************************/
1694{
1695 if (mc_group != NULL) {
1696 free(mc_group);
1697 mc_group = NULL;
1698 }
1699}
1700
1701/************************************************************************/
1708void interpret_tilde(char *buf, size_t buf_size, const char *filename)
1709{
1710 if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1711 fc_snprintf(buf, buf_size, "%s" DIR_SEPARATOR "%s", user_home_dir(), filename + 2);
1712 } else if (filename[0] == '~' && filename[1] == '\0') {
1713 strncpy(buf, user_home_dir(), buf_size);
1714 } else {
1715 strncpy(buf, filename, buf_size);
1716 }
1717}
1718
1719/************************************************************************/
1725char *interpret_tilde_alloc(const char *filename)
1726{
1727 if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1728 const char *home = user_home_dir();
1729 size_t sz;
1730 char *buf;
1731
1732 filename += 2; /* Skip past "~/" */
1733 sz = strlen(home) + strlen(filename) + 2;
1734 buf = fc_malloc(sz);
1735 fc_snprintf(buf, sz, "%s/%s", home, filename);
1736 return buf;
1737 } else if (filename[0] == '~' && filename[1] == '\0') {
1738 return fc_strdup(user_home_dir());
1739 } else {
1740 return fc_strdup(filename);
1741 }
1742}
1743
1744/************************************************************************/
1748char *skip_to_basename(char *filepath)
1749{
1750 int j;
1751 fc_assert_ret_val(NULL != filepath, NULL);
1752
1753 for (j = strlen(filepath); j >= 0; j--) {
1754 if (filepath[j] == DIR_SEPARATOR_CHAR) {
1755 return &filepath[j+1];
1756 }
1757 }
1758 return filepath;
1759}
1760
1761/************************************************************************/
1769bool make_dir(const char *pathname)
1770{
1771 char *dir;
1772 char *path = NULL;
1773
1774 if (pathname[0] == '\0') {
1775 return FALSE;
1776 }
1777
1778 path = interpret_tilde_alloc(pathname);
1779 dir = path;
1780
1781 if (*dir == '/') {
1782 /* Don't consider root as directory separator, but skip it. */
1783 dir++;
1784 } else if (dir[0] != '\0' && dir[1] == ':' && dir[2] == '\\') {
1785 /* Don't consider Windows Drive a directory to create, but skip it. */
1786 dir += 3;
1787 }
1788
1789 do {
1790 dir = strchr(dir, DIR_SEPARATOR_CHAR);
1791 /* We set the current / with 0, and restore it afterwards */
1792 if (dir) {
1793 *dir = '\0';
1794 }
1795
1796#ifdef FREECIV_MSWINDOWS
1797#ifdef HAVE__MKDIR
1798 /* Prefer _mkdir() in Windows even if mkdir() would seem to be available -
1799 * chances are that it's wrong kind of mkdir().
1800 * TODO: Make a configure check for mkdir() that also makes sure that it
1801 * takes two parameters, and prefer such proper mkdir() here. */
1802 {
1803 char *path_in_local_encoding = internal_to_local_string_malloc(path);
1804 bool failure = FALSE;
1805
1806 if (_mkdir(path_in_local_encoding) == -1
1807 && fc_get_errno() != ERROR_ALREADY_EXISTS) {
1808 failure = TRUE;
1809 }
1810
1811 free(path_in_local_encoding);
1812
1813 if (failure) {
1814 free(path);
1815 return FALSE;
1816 }
1817 }
1818#else /* HAVE__MKDIR */
1819 if (mkdir(path, 0755) == -1
1820 && fc_get_errno() != EEXIST) {
1821 free(path);
1822 return FALSE;
1823 }
1824#endif /* HAVE__MKDIR */
1825#else /* FREECIV_MSWINDOWS */
1826 if (mkdir(path, 0755) == -1
1827 && fc_get_errno() != EEXIST) {
1828 free(path);
1829 return FALSE;
1830 }
1831#endif /* FREECIV_MSWINDOWS */
1832
1833 if (dir) {
1834 *dir = DIR_SEPARATOR_CHAR;
1835 dir++;
1836 }
1837 } while (dir);
1838
1839 free(path);
1840
1841 return TRUE;
1842}
1843
1844/************************************************************************/
1848bool make_dir_for_file(char *filename)
1849{
1850 int i;
1851
1852 for (i = strlen(filename) - 1 ; filename[i] != DIR_SEPARATOR_CHAR ; i--) {
1853 /* Nothing */
1854 }
1855
1856 filename[i] = '\0';
1857 log_debug("Create directory \"%s\"", filename);
1858
1859 if (!make_dir(filename)) {
1860 return FALSE;
1861 }
1862 filename[i] = DIR_SEPARATOR_CHAR;
1863
1864 return TRUE;
1865}
1866
1867/************************************************************************/
1870bool path_is_absolute(const char *filename)
1871{
1872 if (!filename) {
1873 return FALSE;
1874 }
1875
1876#ifdef FREECIV_MSWINDOWS
1877 if (strchr(filename, ':')) {
1878 return TRUE;
1879 }
1880#else /* FREECIV_MSWINDOWS */
1881 if (filename[0] == '/') {
1882 return TRUE;
1883 }
1884#endif /* FREECIV_MSWINDOWS */
1885
1886 return FALSE;
1887}
1888
1889/************************************************************************/
1909char scanin(const char **buf, char *delimiters, char *dest, int size)
1910{
1911 char *ptr, found = '?';
1912
1913 if (*buf == NULL || strlen(*buf) == 0 || size == 0) {
1914 if (dest) {
1915 dest[0] = '\0';
1916 }
1917 *buf = NULL;
1918 return '\0';
1919 }
1920
1921 if (dest) {
1922 strncpy(dest, *buf, size-1);
1923 dest[size-1] = '\0';
1925 ptr = strpbrk(dest, delimiters);
1926 } else {
1927 /* Just skip ahead. */
1928 ptr = strpbrk(*buf, delimiters);
1929 }
1930 if (ptr != NULL) {
1931 found = *ptr;
1932 if (dest) {
1933 *ptr = '\0';
1934 }
1935 if (dest) {
1937 }
1938 *buf = strpbrk(*buf, delimiters);
1939 if (*buf != NULL) {
1940 (*buf)++; /* skip delimiter */
1941 } else {
1942 }
1943 } else {
1944 *buf = NULL;
1945 }
1946
1947 return found;
1948}
1949
1950/************************************************************************/
1954void format_time_duration(time_t t, char *buf, int maxlen)
1955{
1956 int seconds, minutes, hours, days;
1957 bool space = FALSE;
1958
1959 seconds = t % 60;
1960 minutes = (t / 60) % 60;
1961 hours = (t / (60 * 60)) % 24;
1962 days = t / (60 * 60 * 24);
1963
1964 if (maxlen <= 0) {
1965 return;
1966 }
1967
1968 buf[0] = '\0';
1969
1970 if (days > 0) {
1971 cat_snprintf(buf, maxlen, "%d %s", days, PL_("day", "days", days));
1972 space = TRUE;
1973 }
1974 if (hours > 0) {
1975 cat_snprintf(buf, maxlen, "%s%d %s",
1976 space ? " " : "", hours, PL_("hour", "hours", hours));
1977 space = TRUE;
1978 }
1979 if (minutes > 0) {
1980 cat_snprintf(buf, maxlen, "%s%d %s",
1981 space ? " " : "",
1982 minutes, PL_("minute", "minutes", minutes));
1983 space = TRUE;
1984 }
1985 if (seconds > 0) {
1986 cat_snprintf(buf, maxlen, "%s%d %s",
1987 space ? " " : "",
1988 seconds, PL_("second", "seconds", seconds));
1989 }
1990}
1991
1992/************************************************************************/
1997void array_shuffle(int *array, int n)
1998{
1999 if (n > 1 && array != NULL) {
2000 int i, j, t;
2001 for (i = 0; i < n - 1; i++) {
2002 j = i + fc_rand(n - i);
2003 t = array[j];
2004 array[j] = array[i];
2005 array[i] = t;
2006 }
2007 }
2008}
2009
2010/************************************************************************/
2015static bool wildcard_asterisk_fit(const char *pattern, const char *test)
2016{
2017 char jump_to;
2018
2019 /* Jump over the leading asterisks. */
2020 pattern++;
2021 while (TRUE) {
2022 switch (*pattern) {
2023 case '\0':
2024 /* It is a leading asterisk. */
2025 return TRUE;
2026 case '*':
2027 pattern++;
2028 continue;
2029 case '?':
2030 if ('\0' == *test) {
2031 return FALSE;
2032 }
2033 test++;
2034 pattern++;
2035 continue;
2036 }
2037
2038 break;
2039 }
2040
2041 if ('[' != *pattern) {
2042 if ('\\' == *pattern) {
2043 jump_to = *(pattern + 1);
2044 } else {
2045 jump_to = *pattern;
2046 }
2047 } else {
2048 jump_to = '\0';
2049 }
2050
2051 while ('\0' != *test) {
2052 if ('\0' != jump_to) {
2053 /* Jump to next matching charather. */
2054 test = strchr(test, jump_to);
2055 if (NULL == test) {
2056 /* No match. */
2057 return FALSE;
2058 }
2059 }
2060
2061 if (wildcard_fit_string(pattern, test)) {
2062 return TRUE;
2063 }
2064
2065 (test)++;
2066 }
2067
2068 return FALSE;
2069}
2070
2071/************************************************************************/
2075static bool wildcard_range_fit(const char **pattern, const char **test)
2076{
2077 const char *start = (*pattern + 1);
2078 char testc;
2079 bool negation;
2080
2081 if ('\0' == **test) {
2082 /* Need one character. */
2083 return FALSE;
2084 }
2085
2086 /* Find the end of the pattern. */
2087 while (TRUE) {
2088 *pattern = strchr(*pattern, ']');
2089 if (NULL == *pattern) {
2090 /* Wildcard format error. */
2091 return FALSE;
2092 } else if (*(*pattern - 1) != '\\') {
2093 /* This is the end. */
2094 break;
2095 } else {
2096 /* Try again. */
2097 (*pattern)++;
2098 }
2099 }
2100
2101 if ('!' == *start) {
2102 negation = TRUE;
2103 start++;
2104 } else {
2105 negation = FALSE;
2106 }
2107 testc = **test;
2108 (*test)++;
2109 (*pattern)++;
2110
2111 for (; start < *pattern; start++) {
2112 if ('-' == *start || '!' == *start) {
2113 /* Wildcard format error. */
2114 return FALSE;
2115 } else if (start < *pattern - 2 && '-' == *(start + 1)) {
2116 /* Case range. */
2117 if (*start <= testc && testc <= *(start + 2)) {
2118 return !negation;
2119 }
2120 start += 2;
2121 } else if (*start == testc) {
2122 /* Single character. */
2123 return !negation;
2124 }
2125 }
2126
2127 return negation;
2128}
2129
2130/************************************************************************/
2141bool wildcard_fit_string(const char *pattern, const char *test)
2142{
2143 while (TRUE) {
2144 switch (*pattern) {
2145 case '\0':
2146 /* '\0' != test. */
2147 return '\0' == *test;
2148 case '*':
2149 return wildcard_asterisk_fit(pattern, test); /* Maybe recursive. */
2150 case '[':
2151 if (!wildcard_range_fit(&pattern, &test)) {
2152 return FALSE;
2153 }
2154 continue;
2155 case '?':
2156 if ('\0' == *test) {
2157 return FALSE;
2158 }
2159 break;
2160 case '\\':
2161 pattern++;
2162 fc__fallthrough; /* No break */
2163 default:
2164 if (*pattern != *test) {
2165 return FALSE;
2166 }
2167 break;
2168 }
2169 pattern++;
2170 test++;
2171 }
2172
2173 return FALSE;
2174}
2175
2176/************************************************************************/
2190int fc_vsnprintcf(char *buf, size_t buf_len, const char *format,
2191 const struct cf_sequence *sequences, size_t sequences_num)
2192{
2193 const struct cf_sequence *pseq;
2194 char cformat[32];
2195 const char *f = format;
2196 char *const max = buf + buf_len - 1;
2197 char *b = buf, *c;
2198 const char *const cmax = cformat + sizeof(cformat) - 2;
2199 int i, j;
2200
2201 if ((size_t) -1 == sequences_num) {
2202 /* Find the number of sequences. */
2203 sequences_num = 0;
2204 for (pseq = sequences; CF_LAST != pseq->type; pseq++) {
2205 sequences_num++;
2206 }
2207 }
2208
2209 while ('\0' != *f) {
2210 if ('%' == *f) {
2211 /* Sequence. */
2212
2213 f++;
2214 if ('%' == *f) {
2215 /* Double '%'. */
2216 *b++ = '%';
2217 f++;
2218 continue;
2219 }
2220
2221 /* Make format. */
2222 c = cformat;
2223 *c++ = '%';
2224 for (; !fc_isalpha(*f) && '\0' != *f && '%' != *f && cmax > c; f++) {
2225 *c++ = *f;
2226 }
2227
2228 if (!fc_isalpha(*f)) {
2229 /* Beginning of a new sequence, end of the format, or too long
2230 * sequence. */
2231 *c = '\0';
2232 j = fc_snprintf(b, max - b + 1, "%s", cformat);
2233 if (-1 == j) {
2234 return -1;
2235 }
2236 b += j;
2237 continue;
2238 }
2239
2240 for (i = 0, pseq = sequences; i < sequences_num; i++, pseq++) {
2241 if (pseq->letter == *f) {
2242 j = -2;
2243 switch (pseq->type) {
2244 case CF_BOOLEAN:
2245 *c++ = 's';
2246 *c = '\0';
2247 j = fc_snprintf(b, max - b + 1, cformat,
2248 pseq->bool_value ? "TRUE" : "FALSE");
2249 break;
2250 case CF_TRANS_BOOLEAN:
2251 *c++ = 's';
2252 *c = '\0';
2253 j = fc_snprintf(b, max - b + 1, cformat,
2254 pseq->bool_value ? _("TRUE") : _("FALSE"));
2255 break;
2256 case CF_CHARACTER:
2257 *c++ = 'c';
2258 *c = '\0';
2259 j = fc_snprintf(b, max - b + 1, cformat, pseq->char_value);
2260 break;
2261 case CF_INTEGER:
2262 *c++ = 'd';
2263 *c = '\0';
2264 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2265 break;
2266 case CF_HEXA:
2267 *c++ = 'x';
2268 *c = '\0';
2269 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2270 break;
2271 case CF_FLOAT:
2272 *c++ = 'f';
2273 *c = '\0';
2274 j = fc_snprintf(b, max - b + 1, cformat, pseq->float_value);
2275 break;
2276 case CF_POINTER:
2277 *c++ = 'p';
2278 *c = '\0';
2279 j = fc_snprintf(b, max - b + 1, cformat, pseq->ptr_value);
2280 break;
2281 case CF_STRING:
2282 *c++ = 's';
2283 *c = '\0';
2284 j = fc_snprintf(b, max - b + 1, cformat, pseq->str_value);
2285 break;
2286 case CF_LAST:
2287 break;
2288 };
2289 if (-2 == j) {
2290 log_error("Error: unsupported sequence type: %d.", pseq->type);
2291 break;
2292 }
2293 if (-1 == j) {
2294 /* Full! */
2295 return -1;
2296 }
2297 f++;
2298 b += j;
2299 break;
2300 }
2301 }
2302 if (i >= sequences_num) {
2303 /* Format not supported. */
2304 *c = '\0';
2305 j = fc_snprintf(b, max - b + 1, "%s%c", cformat, *f);
2306 if (-1 == j) {
2307 return -1;
2308 }
2309 f++;
2310 b += j;
2311 }
2312 } else {
2313 /* Not a sequence. */
2314 *b++ = *f++;
2315 }
2316 if (max <= b) {
2317 /* Too long. */
2318 *max = '\0';
2319 return -1;
2320 }
2321 }
2322 *b = '\0';
2323 return b - buf;
2324}
2325
2326/************************************************************************/
2338int fc_snprintcf(char *buf, size_t buf_len, const char *format, ...)
2339{
2340 struct cf_sequence sequences[16];
2341 size_t sequences_num = 0;
2342 va_list args;
2343
2344 /* Collect sequence array. */
2345 va_start(args, format);
2346 do {
2347 sequences[sequences_num] = va_arg(args, struct cf_sequence);
2348 if (CF_LAST == sequences[sequences_num].type) {
2349 break;
2350 } else {
2351 sequences_num++;
2352 }
2353 } while (ARRAY_SIZE(sequences) > sequences_num);
2354
2355 if (ARRAY_SIZE(sequences) <= sequences_num
2356 && CF_LAST != va_arg(args, struct cf_sequence).type) {
2357 log_error("Too many custom sequences. Maybe did you forget cf_end() "
2358 "at the end of the arguments?");
2359 buf[0] = '\0';
2360 va_end(args);
2361 return -1;
2362 }
2363 va_end(args);
2364
2365 return fc_vsnprintcf(buf, buf_len, format, sequences, sequences_num);
2366}
2367
2368/************************************************************************/
2372static size_t extract_escapes(const char *format, char *escapes,
2373 size_t max_escapes)
2374{
2375 static const char format_escapes[] = {
2376 '*', 'd', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f',
2377 'F', 'g', 'G', 'a', 'A', 'c', 's', 'p', 'n', '\0'
2378 };
2379 bool reordered = FALSE;
2380 size_t num = 0;
2381 int idx = 0;
2382
2383 memset(escapes, 0, max_escapes);
2384 format = strchr(format, '%');
2385 while (NULL != format) {
2386 format++;
2387 if ('%' == *format) {
2388 /* Double, not a sequence. */
2389 continue;
2390 } else if (fc_isdigit(*format)) {
2391 const char *start = format;
2392
2393 do {
2394 format++;
2395 } while (fc_isdigit(*format));
2396 if ('$' == *format) {
2397 /* Strings are reordered. */
2398 sscanf(start, "%d", &idx);
2399 reordered = TRUE;
2400 }
2401 }
2402
2403 while ('\0' != *format
2404 && NULL == strchr(format_escapes, *format)) {
2405 format++;
2406 }
2407 escapes[idx] = *format;
2408
2409 /* Increase the read count. */
2410 if (reordered) {
2411 if (idx > num) {
2412 num = idx;
2413 }
2414 } else {
2415 idx++;
2416 num++;
2417 }
2418
2419 if ('*' != *format) {
2420 format = strchr(format, '%');
2421 } /* else we didn't have found the real sequence. */
2422 }
2423 return num;
2424}
2425
2426/************************************************************************/
2430bool formats_match(const char *format1, const char *format2)
2431{
2432 char format1_escapes[256], format2_escapes[256];
2433 size_t format1_escapes_num = extract_escapes(format1, format1_escapes,
2434 sizeof(format1_escapes));
2435 size_t format2_escapes_num = extract_escapes(format2, format2_escapes,
2436 sizeof(format2_escapes));
2437
2438 return (format1_escapes_num == format2_escapes_num
2439 && 0 == memcmp(format1_escapes, format2_escapes,
2440 format1_escapes_num));
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:48
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:1312
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:125
#define fc_rand(_size)
Definition rand.h:34
int compare_strings(const void *first, const void *second)
Definition shared.c:358
static size_t extract_escapes(const char *format, char *escapes, size_t max_escapes)
Definition shared.c:2372
void format_time_duration(time_t t, char *buf, int maxlen)
Definition shared.c:1954
static bool wildcard_asterisk_fit(const char *pattern, const char *test)
Definition shared.c:2015
int compare_strings_ptrs(const void *first, const void *second)
Definition shared.c:368
char * user_username(char *buf, size_t bufsz)
Definition shared.c:705
int fc_snprintcf(char *buf, size_t buf_len, const char *format,...)
Definition shared.c:2338
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:1539
bool wildcard_fit_string(const char *pattern, const char *test)
Definition shared.c:2141
bool check_strlen(const char *str, size_t len, const char *errmsg)
Definition shared.c:492
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1094
static bool compare_fileinfo_name(const struct fileinfo *pa, const struct fileinfo *pb)
Definition shared.c:1190
char * user_home_dir(void)
Definition shared.c:628
void remove_trailing_spaces(char *s)
Definition shared.c:420
void free_user_home_dir(void)
Definition shared.c:659
int fc_vsnprintcf(char *buf, size_t buf_len, const char *format, const struct cf_sequence *sequences, size_t sequences_num)
Definition shared.c:2190
#define HOMEVAR
static char * storage_dir_freeciv
Definition shared.c:112
void init_nls(void)
Definition shared.c:1463
bool str_to_int(const char *str, int *pint)
Definition shared.c:512
#define default_data_path
bool str_to_float(const char *str, float *pfloat)
Definition shared.c:578
const char * m_pre_description(enum m_pre_result result)
Definition shared.c:1561
static struct strvec * data_dir_names
Definition shared.c:106
#define FREECIV_SCENARIO_PATH
Definition shared.c:93
static const char base64url[]
Definition shared.c:103
struct strvec * fileinfolist(const struct strvec *dirs, const char *suffix)
Definition shared.c:1020
static char * mc_group
Definition shared.c:110
char * skip_leading_spaces(char *s)
Definition shared.c:387
bool str_to_uint(const char *str, unsigned int *pint)
Definition shared.c:545
bool make_dir(const char *pathname)
Definition shared.c:1769
void free_data_dir_names(void)
Definition shared.c:859
enum m_pre_result match_prefix_full(m_pre_accessor_fn_t accessor_fn, size_t n_names, size_t max_len_name, m_pre_strncmp_fn_t cmp_fn, m_strlen_fn_t len_fn, const char *prefix, int *ind_result, int *matches, int max_matches, int *pnum_matches)
Definition shared.c:1601
const struct strvec * get_scenario_dirs(void)
Definition shared.c:971
size_t loud_strlcpy(char *buffer, const char *str, size_t len, const char *errmsg)
Definition shared.c:501
char * interpret_tilde_alloc(const char *filename)
Definition shared.c:1725
#define FREECIV_SAVE_PATH
Definition shared.c:90
bool make_dir_for_file(char *filename)
Definition shared.c:1848
static void fileinfo_destroy(struct fileinfo *pfile)
Definition shared.c:1159
void interpret_tilde(char *buf, size_t buf_size, const char *filename)
Definition shared.c:1708
static struct astring realfile
Definition shared.c:114
static void remove_trailing_char(char *s, char trailing)
Definition shared.c:452
int compare_strings_strvec(const char *const *first, const char *const *second)
Definition shared.c:378
bool is_base64url(const char *s)
Definition shared.c:317
void free_multicast_group(void)
Definition shared.c:1693
static char * home_dir_user
Definition shared.c:111
void free_fileinfo_data(void)
Definition shared.c:1151
static bool is_ascii(char ch)
Definition shared.c:242
char * end_of_strn(char *str, int *nleft)
Definition shared.c:479
bool path_is_absolute(const char *filename)
Definition shared.c:1870
void free_nls(void)
Definition shared.c:1522
static bool wildcard_range_fit(const char **pattern, const char **test)
Definition shared.c:2075
static struct strvec * scenario_dir_names
Definition shared.c:108
static int compare_file_mtime_ptrs(const struct fileinfo *const *ppa, const struct fileinfo *const *ppb)
Definition shared.c:1169
bool formats_match(const char *format1, const char *format2)
Definition shared.c:2430
const char * int_to_text(unsigned int number)
Definition shared.c:233
char scanin(const char **buf, char *delimiters, char *dest, int size)
Definition shared.c:1909
static int compare_file_name_ptrs(const struct fileinfo *const *ppa, const struct fileinfo *const *ppb)
Definition shared.c:1181
const struct strvec * get_save_dirs(void)
Definition shared.c:934
static char * expand_dir(char *tok_in, bool ok_to_free)
Definition shared.c:778
void remove_leading_spaces(char *s)
Definition shared.c:402
char * skip_to_basename(char *filepath)
Definition shared.c:1748
#define FREECIV_DATA_PATH
Definition shared.c:87
#define default_scenario_path
void array_shuffle(int *array, int n)
Definition shared.c:1997
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:1578
bool is_ascii_name(const char *name)
Definition shared.c:282
#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:832
const char * setup_langname(void)
Definition shared.c:1281
void randomize_base64url_string(char *s, size_t n)
Definition shared.c:338
char * get_multicast_group(bool ipv6_preferred)
Definition shared.c:1662
void free_freeciv_storage_dir(void)
Definition shared.c:690
bool is_safe_filename(const char *name)
Definition shared.c:252
void remove_leading_trailing_spaces(char *s)
Definition shared.c:443
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:886
char * freeciv_storage_dir(void)
Definition shared.c:672
void switch_lang(const char *lang)
Definition shared.c:1434
struct fileinfo_list * fileinfolist_infix(const struct strvec *dirs, const char *infix, bool nodups)
Definition shared.c:1204
static struct strvec * save_dir_names
Definition shared.c:107
fc_tristate
Definition shared.h:46
@ TRI_YES
Definition shared.h:46
@ TRI_NO
Definition shared.h:46
@ TRI_MAYBE
Definition shared.h:46
int(* m_pre_strncmp_fn_t)(const char *, const char *, size_t n)
Definition shared.h: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
const void * ptr_value
Definition shared.h:308
const char * str_value
Definition shared.h:309
int int_value
Definition shared.h:306
bool bool_value
Definition shared.h:304
char letter
Definition shared.h:302
float float_value
Definition shared.h:307
char char_value
Definition shared.h:305
enum cf_type type
Definition shared.h:301
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:969
bool fc_isalpha(char c)
Definition support.c:1216
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:787
bool fc_isspace(char c)
Definition support.c:1249
const char * fc_strerror(fc_errno err)
Definition support.c:610
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:995
fc_errno fc_get_errno(void)
Definition support.c:593
bool fc_isdigit(char c)
Definition support.c:1227
int fc_strcoll(const char *str0, const char *str1)
Definition support.c:472
int fc_stat(const char *filename, struct stat *buf)
Definition support.c:575
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define fc__fallthrough
Definition support.h:109