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 = fc_malloc(sizeof(char));
1497
1498 free(grouping);
1499 *m = CHAR_MAX;
1500 grouping = m;
1501 } else {
1502 size_t len;
1503
1504 for (len = 0;
1505 lc->grouping[len] != '\0' && lc->grouping[len] != CHAR_MAX; len++) {
1506 /* nothing */
1507 }
1508 len++;
1509 free(grouping);
1511 memcpy(grouping, lc->grouping, len);
1512 }
1513 free(grouping_sep);
1514 grouping_sep = fc_strdup(lc->thousands_sep);
1515 }
1516
1517 autocap_update();
1518
1519#endif /* ENABLE_NLS */
1520}
1521
1522/************************************************************************/
1525void free_nls(void)
1526{
1527 free(grouping);
1528 grouping = NULL;
1529 free(grouping_sep);
1530 grouping_sep = NULL;
1531}
1532
1533/************************************************************************/
1542void dont_run_as_root(const char *argv0, const char *fallback)
1543{
1544#if (defined(ALWAYS_ROOT) || defined(__EMX__) || defined(__BEOS__))
1545 return;
1546#else
1547 if (getuid() == 0 || geteuid() == 0) {
1548 fc_fprintf(stderr,
1549 _("%s: Fatal error: you're trying to run me as superuser!\n"),
1550 (argv0 ? argv0 : fallback ? fallback : "freeciv"));
1551 fc_fprintf(stderr, _("Use a non-privileged account instead.\n"));
1552 exit(EXIT_FAILURE);
1553 }
1554#endif /* ALWAYS_ROOT */
1555}
1556
1557/************************************************************************/
1564const char *m_pre_description(enum m_pre_result result)
1565{
1566 static const char * const descriptions[] = {
1567 N_("exact match"),
1568 N_("only match"),
1569 N_("ambiguous"),
1570 N_("empty"),
1571 N_("too long"),
1572 N_("non-match")
1573 };
1574 fc_assert_ret_val(result >= 0 && result < ARRAY_SIZE(descriptions), NULL);
1575 return descriptions[result];
1576}
1577
1578/************************************************************************/
1582 size_t n_names,
1583 size_t max_len_name,
1584 m_pre_strncmp_fn_t cmp_fn,
1585 m_strlen_fn_t len_fn,
1586 const char *prefix,
1587 int *ind_result)
1588{
1589 return match_prefix_full(accessor_fn, n_names, max_len_name, cmp_fn,
1590 len_fn, prefix, ind_result, NULL, 0, NULL);
1591}
1592
1593/************************************************************************/
1605 size_t n_names,
1606 size_t max_len_name,
1607 m_pre_strncmp_fn_t cmp_fn,
1608 m_strlen_fn_t len_fn,
1609 const char *prefix,
1610 int *ind_result,
1611 int *matches,
1612 int max_matches,
1613 int *pnum_matches)
1614{
1615 int i, len, nmatches;
1616
1617 if (len_fn == NULL) {
1618 len = strlen(prefix);
1619 } else {
1620 len = len_fn(prefix);
1621 }
1622 if (len == 0) {
1623 return M_PRE_EMPTY;
1624 }
1625 if (len > max_len_name && max_len_name > 0) {
1626 return M_PRE_LONG;
1627 }
1628
1629 nmatches = 0;
1630 for (i = 0; i < n_names; i++) {
1631 const char *name = accessor_fn(i);
1632
1633 if (cmp_fn(name, prefix, len) == 0) {
1634 if (strlen(name) == len) {
1635 *ind_result = i;
1636 return M_PRE_EXACT;
1637 }
1638 if (nmatches == 0) {
1639 *ind_result = i; /* first match */
1640 }
1641 if (matches != NULL && nmatches < max_matches) {
1642 matches[nmatches] = i;
1643 }
1644 nmatches++;
1645 }
1646 }
1647
1648 if (nmatches == 1) {
1649 return M_PRE_ONLY;
1650 } else if (nmatches > 1) {
1651 if (pnum_matches != NULL) {
1652 *pnum_matches = MIN(max_matches, nmatches);
1653 }
1654 return M_PRE_AMBIGUOUS;
1655 } else {
1656 return M_PRE_FAIL;
1657 }
1658}
1659
1660/************************************************************************/
1665char *get_multicast_group(bool ipv6_preferred)
1666{
1667 static char *default_multicast_group_ipv4 = "225.1.1.1";
1668#ifdef FREECIV_IPV6_SUPPORT
1669 /* TODO: Get useful group (this is node local) */
1670 static char *default_multicast_group_ipv6 = "FF31::8000:15B4";
1671#endif /* IPv6 support */
1672
1673 if (mc_group == NULL) {
1674 char *env = getenv("FREECIV_MULTICAST_GROUP");
1675
1676 if (env) {
1677 mc_group = fc_strdup(env);
1678 } else {
1679#ifdef FREECIV_IPV6_SUPPORT
1680 if (ipv6_preferred) {
1681 mc_group = fc_strdup(default_multicast_group_ipv6);
1682 } else
1683#endif /* IPv6 support */
1684 {
1685 mc_group = fc_strdup(default_multicast_group_ipv4);
1686 }
1687 }
1688 }
1689
1690 return mc_group;
1691}
1692
1693/************************************************************************/
1697{
1698 if (mc_group != NULL) {
1699 free(mc_group);
1700 mc_group = NULL;
1701 }
1702}
1703
1704/************************************************************************/
1711void interpret_tilde(char *buf, size_t buf_size, const char *filename)
1712{
1713 if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1714 fc_snprintf(buf, buf_size, "%s" DIR_SEPARATOR "%s", user_home_dir(), filename + 2);
1715 } else if (filename[0] == '~' && filename[1] == '\0') {
1716 strncpy(buf, user_home_dir(), buf_size);
1717 } else {
1718 strncpy(buf, filename, buf_size);
1719 }
1720}
1721
1722/************************************************************************/
1728char *interpret_tilde_alloc(const char *filename)
1729{
1730 if (filename[0] == '~' && filename[1] == DIR_SEPARATOR_CHAR) {
1731 const char *home = user_home_dir();
1732 size_t sz;
1733 char *buf;
1734
1735 filename += 2; /* Skip past "~/" */
1736 sz = strlen(home) + strlen(filename) + 2;
1737 buf = fc_malloc(sz);
1738 fc_snprintf(buf, sz, "%s/%s", home, filename);
1739 return buf;
1740 } else if (filename[0] == '~' && filename[1] == '\0') {
1741 return fc_strdup(user_home_dir());
1742 } else {
1743 return fc_strdup(filename);
1744 }
1745}
1746
1747/************************************************************************/
1751char *skip_to_basename(char *filepath)
1752{
1753 int j;
1754 fc_assert_ret_val(NULL != filepath, NULL);
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/************************************************************************/
1772bool make_dir(const char *pathname)
1773{
1774 char *dir;
1775 char *path = NULL;
1776
1777 if (pathname[0] == '\0') {
1778 return FALSE;
1779 }
1780
1781 path = interpret_tilde_alloc(pathname);
1782 dir = path;
1783
1784 if (*dir == '/') {
1785 /* Don't consider root as directory separator, but skip it. */
1786 dir++;
1787 } else if (dir[0] != '\0' && dir[1] == ':' && dir[2] == '\\') {
1788 /* Don't consider Windows Drive a directory to create, but skip it. */
1789 dir += 3;
1790 }
1791
1792 do {
1793 dir = strchr(dir, DIR_SEPARATOR_CHAR);
1794 /* We set the current / with 0, and restore it afterwards */
1795 if (dir) {
1796 *dir = '\0';
1797 }
1798
1799#ifdef FREECIV_MSWINDOWS
1800#ifdef HAVE__MKDIR
1801 /* Prefer _mkdir() in Windows even if mkdir() would seem to be available -
1802 * chances are that it's wrong kind of mkdir().
1803 * TODO: Make a configure check for mkdir() that also makes sure that it
1804 * takes two parameters, and prefer such proper mkdir() here. */
1805 {
1806 char *path_in_local_encoding = internal_to_local_string_malloc(path);
1807 bool failure = FALSE;
1808
1809 if (_mkdir(path_in_local_encoding) == -1
1810 && fc_get_errno() != ERROR_ALREADY_EXISTS) {
1811 failure = TRUE;
1812 }
1813
1814 free(path_in_local_encoding);
1815
1816 if (failure) {
1817 free(path);
1818 return FALSE;
1819 }
1820 }
1821#else /* HAVE__MKDIR */
1822 if (mkdir(path, 0755) == -1
1823 && fc_get_errno() != EEXIST) {
1824 free(path);
1825 return FALSE;
1826 }
1827#endif /* HAVE__MKDIR */
1828#else /* FREECIV_MSWINDOWS */
1829 if (mkdir(path, 0755) == -1
1830 && fc_get_errno() != EEXIST) {
1831 free(path);
1832 return FALSE;
1833 }
1834#endif /* FREECIV_MSWINDOWS */
1835
1836 if (dir) {
1837 *dir = DIR_SEPARATOR_CHAR;
1838 dir++;
1839 }
1840 } while (dir);
1841
1842 free(path);
1843
1844 return TRUE;
1845}
1846
1847/************************************************************************/
1851bool make_dir_for_file(char *filename)
1852{
1853 int i;
1854
1855 for (i = strlen(filename) - 1 ; filename[i] != DIR_SEPARATOR_CHAR ; i--) {
1856 /* Nothing */
1857 }
1858
1859 filename[i] = '\0';
1860 log_debug("Create directory \"%s\"", filename);
1861
1862 if (!make_dir(filename)) {
1863 return FALSE;
1864 }
1865 filename[i] = DIR_SEPARATOR_CHAR;
1866
1867 return TRUE;
1868}
1869
1870/************************************************************************/
1873bool path_is_absolute(const char *filename)
1874{
1875 if (!filename) {
1876 return FALSE;
1877 }
1878
1879#ifdef FREECIV_MSWINDOWS
1880 if (strchr(filename, ':')) {
1881 return TRUE;
1882 }
1883#else /* FREECIV_MSWINDOWS */
1884 if (filename[0] == '/') {
1885 return TRUE;
1886 }
1887#endif /* FREECIV_MSWINDOWS */
1888
1889 return FALSE;
1890}
1891
1892/************************************************************************/
1912char scanin(const char **buf, char *delimiters, char *dest, int size)
1913{
1914 char *ptr, found = '?';
1915
1916 if (*buf == NULL || strlen(*buf) == 0 || size == 0) {
1917 if (dest) {
1918 dest[0] = '\0';
1919 }
1920 *buf = NULL;
1921 return '\0';
1922 }
1923
1924 if (dest) {
1925 strncpy(dest, *buf, size-1);
1926 dest[size-1] = '\0';
1928 ptr = strpbrk(dest, delimiters);
1929 } else {
1930 /* Just skip ahead. */
1931 ptr = strpbrk(*buf, delimiters);
1932 }
1933 if (ptr != NULL) {
1934 found = *ptr;
1935 if (dest) {
1936 *ptr = '\0';
1937 }
1938 if (dest) {
1940 }
1941 *buf = strpbrk(*buf, delimiters);
1942 if (*buf != NULL) {
1943 (*buf)++; /* skip delimiter */
1944 } else {
1945 }
1946 } else {
1947 *buf = NULL;
1948 }
1949
1950 return found;
1951}
1952
1953/************************************************************************/
1957void format_time_duration(time_t t, char *buf, int maxlen)
1958{
1959 int seconds, minutes, hours, days;
1960 bool space = FALSE;
1961
1962 seconds = t % 60;
1963 minutes = (t / 60) % 60;
1964 hours = (t / (60 * 60)) % 24;
1965 days = t / (60 * 60 * 24);
1966
1967 if (maxlen <= 0) {
1968 return;
1969 }
1970
1971 buf[0] = '\0';
1972
1973 if (days > 0) {
1974 cat_snprintf(buf, maxlen, "%d %s", days, PL_("day", "days", days));
1975 space = TRUE;
1976 }
1977 if (hours > 0) {
1978 cat_snprintf(buf, maxlen, "%s%d %s",
1979 space ? " " : "", hours, PL_("hour", "hours", hours));
1980 space = TRUE;
1981 }
1982 if (minutes > 0) {
1983 cat_snprintf(buf, maxlen, "%s%d %s",
1984 space ? " " : "",
1985 minutes, PL_("minute", "minutes", minutes));
1986 space = TRUE;
1987 }
1988 if (seconds > 0) {
1989 cat_snprintf(buf, maxlen, "%s%d %s",
1990 space ? " " : "",
1991 seconds, PL_("second", "seconds", seconds));
1992 }
1993}
1994
1995/************************************************************************/
2000void array_shuffle(int *array, int n)
2001{
2002 if (n > 1 && array != NULL) {
2003 int i, j, t;
2004 for (i = 0; i < n - 1; i++) {
2005 j = i + fc_rand(n - i);
2006 t = array[j];
2007 array[j] = array[i];
2008 array[i] = t;
2009 }
2010 }
2011}
2012
2013/************************************************************************/
2018static bool wildcard_asterisk_fit(const char *pattern, const char *test)
2019{
2020 char jump_to;
2021
2022 /* Jump over the leading asterisks. */
2023 pattern++;
2024 while (TRUE) {
2025 switch (*pattern) {
2026 case '\0':
2027 /* It is a leading asterisk. */
2028 return TRUE;
2029 case '*':
2030 pattern++;
2031 continue;
2032 case '?':
2033 if ('\0' == *test) {
2034 return FALSE;
2035 }
2036 test++;
2037 pattern++;
2038 continue;
2039 }
2040
2041 break;
2042 }
2043
2044 if ('[' != *pattern) {
2045 if ('\\' == *pattern) {
2046 jump_to = *(pattern + 1);
2047 } else {
2048 jump_to = *pattern;
2049 }
2050 } else {
2051 jump_to = '\0';
2052 }
2053
2054 while ('\0' != *test) {
2055 if ('\0' != jump_to) {
2056 /* Jump to next matching charather. */
2057 test = strchr(test, jump_to);
2058 if (NULL == test) {
2059 /* No match. */
2060 return FALSE;
2061 }
2062 }
2063
2064 if (wildcard_fit_string(pattern, test)) {
2065 return TRUE;
2066 }
2067
2068 (test)++;
2069 }
2070
2071 return FALSE;
2072}
2073
2074/************************************************************************/
2078static bool wildcard_range_fit(const char **pattern, const char **test)
2079{
2080 const char *start = (*pattern + 1);
2081 char testc;
2082 bool negation;
2083
2084 if ('\0' == **test) {
2085 /* Need one character. */
2086 return FALSE;
2087 }
2088
2089 /* Find the end of the pattern. */
2090 while (TRUE) {
2091 *pattern = strchr(*pattern, ']');
2092 if (NULL == *pattern) {
2093 /* Wildcard format error. */
2094 return FALSE;
2095 } else if (*(*pattern - 1) != '\\') {
2096 /* This is the end. */
2097 break;
2098 } else {
2099 /* Try again. */
2100 (*pattern)++;
2101 }
2102 }
2103
2104 if ('!' == *start) {
2105 negation = TRUE;
2106 start++;
2107 } else {
2108 negation = FALSE;
2109 }
2110 testc = **test;
2111 (*test)++;
2112 (*pattern)++;
2113
2114 for (; start < *pattern; start++) {
2115 if ('-' == *start || '!' == *start) {
2116 /* Wildcard format error. */
2117 return FALSE;
2118 } else if (start < *pattern - 2 && '-' == *(start + 1)) {
2119 /* Case range. */
2120 if (*start <= testc && testc <= *(start + 2)) {
2121 return !negation;
2122 }
2123 start += 2;
2124 } else if (*start == testc) {
2125 /* Single character. */
2126 return !negation;
2127 }
2128 }
2129
2130 return negation;
2131}
2132
2133/************************************************************************/
2144bool wildcard_fit_string(const char *pattern, const char *test)
2145{
2146 while (TRUE) {
2147 switch (*pattern) {
2148 case '\0':
2149 /* '\0' != test. */
2150 return '\0' == *test;
2151 case '*':
2152 return wildcard_asterisk_fit(pattern, test); /* Maybe recursive. */
2153 case '[':
2154 if (!wildcard_range_fit(&pattern, &test)) {
2155 return FALSE;
2156 }
2157 continue;
2158 case '?':
2159 if ('\0' == *test) {
2160 return FALSE;
2161 }
2162 break;
2163 case '\\':
2164 pattern++;
2165 fc__fallthrough; /* No break */
2166 default:
2167 if (*pattern != *test) {
2168 return FALSE;
2169 }
2170 break;
2171 }
2172 pattern++;
2173 test++;
2174 }
2175
2176 return FALSE;
2177}
2178
2179/************************************************************************/
2193int fc_vsnprintcf(char *buf, size_t buf_len, const char *format,
2194 const struct cf_sequence *sequences, size_t sequences_num)
2195{
2196 const struct cf_sequence *pseq;
2197 char cformat[32];
2198 const char *f = format;
2199 char *const max = buf + buf_len - 1;
2200 char *b = buf, *c;
2201 const char *const cmax = cformat + sizeof(cformat) - 2;
2202 int i, j;
2203
2204 if ((size_t) -1 == sequences_num) {
2205 /* Find the number of sequences. */
2206 sequences_num = 0;
2207 for (pseq = sequences; CF_LAST != pseq->type; pseq++) {
2208 sequences_num++;
2209 }
2210 }
2211
2212 while ('\0' != *f) {
2213 if ('%' == *f) {
2214 /* Sequence. */
2215
2216 f++;
2217 if ('%' == *f) {
2218 /* Double '%'. */
2219 *b++ = '%';
2220 f++;
2221 continue;
2222 }
2223
2224 /* Make format. */
2225 c = cformat;
2226 *c++ = '%';
2227 for (; !fc_isalpha(*f) && '\0' != *f && '%' != *f && cmax > c; f++) {
2228 *c++ = *f;
2229 }
2230
2231 if (!fc_isalpha(*f)) {
2232 /* Beginning of a new sequence, end of the format, or too long
2233 * sequence. */
2234 *c = '\0';
2235 j = fc_snprintf(b, max - b + 1, "%s", cformat);
2236 if (-1 == j) {
2237 return -1;
2238 }
2239 b += j;
2240 continue;
2241 }
2242
2243 for (i = 0, pseq = sequences; i < sequences_num; i++, pseq++) {
2244 if (pseq->letter == *f) {
2245 j = -2;
2246 switch (pseq->type) {
2247 case CF_BOOLEAN:
2248 *c++ = 's';
2249 *c = '\0';
2250 j = fc_snprintf(b, max - b + 1, cformat,
2251 pseq->bool_value ? "TRUE" : "FALSE");
2252 break;
2253 case CF_TRANS_BOOLEAN:
2254 *c++ = 's';
2255 *c = '\0';
2256 j = fc_snprintf(b, max - b + 1, cformat,
2257 pseq->bool_value ? _("TRUE") : _("FALSE"));
2258 break;
2259 case CF_CHARACTER:
2260 *c++ = 'c';
2261 *c = '\0';
2262 j = fc_snprintf(b, max - b + 1, cformat, pseq->char_value);
2263 break;
2264 case CF_INTEGER:
2265 *c++ = 'd';
2266 *c = '\0';
2267 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2268 break;
2269 case CF_HEXA:
2270 *c++ = 'x';
2271 *c = '\0';
2272 j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
2273 break;
2274 case CF_FLOAT:
2275 *c++ = 'f';
2276 *c = '\0';
2277 j = fc_snprintf(b, max - b + 1, cformat, pseq->float_value);
2278 break;
2279 case CF_POINTER:
2280 *c++ = 'p';
2281 *c = '\0';
2282 j = fc_snprintf(b, max - b + 1, cformat, pseq->ptr_value);
2283 break;
2284 case CF_STRING:
2285 *c++ = 's';
2286 *c = '\0';
2287 j = fc_snprintf(b, max - b + 1, cformat, pseq->str_value);
2288 break;
2289 case CF_LAST:
2290 break;
2291 };
2292 if (-2 == j) {
2293 log_error("Error: unsupported sequence type: %d.", pseq->type);
2294 break;
2295 }
2296 if (-1 == j) {
2297 /* Full! */
2298 return -1;
2299 }
2300 f++;
2301 b += j;
2302 break;
2303 }
2304 }
2305 if (i >= sequences_num) {
2306 /* Format not supported. */
2307 *c = '\0';
2308 j = fc_snprintf(b, max - b + 1, "%s%c", cformat, *f);
2309 if (-1 == j) {
2310 return -1;
2311 }
2312 f++;
2313 b += j;
2314 }
2315 } else {
2316 /* Not a sequence. */
2317 *b++ = *f++;
2318 }
2319 if (max <= b) {
2320 /* Too long. */
2321 *max = '\0';
2322 return -1;
2323 }
2324 }
2325 *b = '\0';
2326 return b - buf;
2327}
2328
2329/************************************************************************/
2341int fc_snprintcf(char *buf, size_t buf_len, const char *format, ...)
2342{
2343 struct cf_sequence sequences[16];
2344 size_t sequences_num = 0;
2345 va_list args;
2346
2347 /* Collect sequence array. */
2348 va_start(args, format);
2349 do {
2350 sequences[sequences_num] = va_arg(args, struct cf_sequence);
2351 if (CF_LAST == sequences[sequences_num].type) {
2352 break;
2353 } else {
2354 sequences_num++;
2355 }
2356 } while (ARRAY_SIZE(sequences) > sequences_num);
2357
2358 if (ARRAY_SIZE(sequences) <= sequences_num
2359 && CF_LAST != va_arg(args, struct cf_sequence).type) {
2360 log_error("Too many custom sequences. Maybe did you forget cf_end() "
2361 "at the end of the arguments?");
2362 buf[0] = '\0';
2363 va_end(args);
2364 return -1;
2365 }
2366 va_end(args);
2367
2368 return fc_vsnprintcf(buf, buf_len, format, sequences, sequences_num);
2369}
2370
2371/************************************************************************/
2375static size_t extract_escapes(const char *format, char *escapes,
2376 size_t max_escapes)
2377{
2378 static const char format_escapes[] = {
2379 '*', 'd', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f',
2380 'F', 'g', 'G', 'a', 'A', 'c', 's', 'p', 'n', '\0'
2381 };
2382 bool reordered = FALSE;
2383 size_t num = 0;
2384 int idx = 0;
2385
2386 memset(escapes, 0, max_escapes);
2387 format = strchr(format, '%');
2388 while (NULL != format) {
2389 format++;
2390 if ('%' == *format) {
2391 /* Double, not a sequence. */
2392 continue;
2393 } else if (fc_isdigit(*format)) {
2394 const char *start = format;
2395
2396 do {
2397 format++;
2398 } while (fc_isdigit(*format));
2399 if ('$' == *format) {
2400 /* Strings are reordered. */
2401 sscanf(start, "%d", &idx);
2402 reordered = TRUE;
2403 }
2404 }
2405
2406 while ('\0' != *format
2407 && NULL == strchr(format_escapes, *format)) {
2408 format++;
2409 }
2410 escapes[idx] = *format;
2411
2412 /* Increase the read count. */
2413 if (reordered) {
2414 if (idx > num) {
2415 num = idx;
2416 }
2417 } else {
2418 idx++;
2419 num++;
2420 }
2421
2422 if ('*' != *format) {
2423 format = strchr(format, '%');
2424 } /* else we didn't have found the real sequence. */
2425 }
2426 return num;
2427}
2428
2429/************************************************************************/
2433bool formats_match(const char *format1, const char *format2)
2434{
2435 char format1_escapes[256], format2_escapes[256];
2436 size_t format1_escapes_num = extract_escapes(format1, format1_escapes,
2437 sizeof(format1_escapes));
2438 size_t format2_escapes_num = extract_escapes(format2, format2_escapes,
2439 sizeof(format2_escapes));
2440
2441 return (format1_escapes_num == format2_escapes_num
2442 && 0 == memcmp(format1_escapes, format2_escapes,
2443 format1_escapes_num));
2444}
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:2375
void format_time_duration(time_t t, char *buf, int maxlen)
Definition shared.c:1957
static bool wildcard_asterisk_fit(const char *pattern, const char *test)
Definition shared.c:2018
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:2341
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:1542
bool wildcard_fit_string(const char *pattern, const char *test)
Definition shared.c:2144
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:2193
#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:1564
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:1772
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:1604
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:1728
#define FREECIV_SAVE_PATH
Definition shared.c:90
bool make_dir_for_file(char *filename)
Definition shared.c:1851
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:1711
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:1696
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:1873
void free_nls(void)
Definition shared.c:1525
static bool wildcard_range_fit(const char **pattern, const char **test)
Definition shared.c:2078
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:2433
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:1912
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:1751
#define FREECIV_DATA_PATH
Definition shared.c:87
#define default_scenario_path
void array_shuffle(int *array, int n)
Definition shared.c:2000
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:1581
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:1665
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