20#ifdef FREECIV_HAVE_SYS_TYPES_H
25#ifdef FREECIV_HAVE_DIRENT_H
47#ifdef FREECIV_MSWINDOWS
68#ifndef DEFAULT_DATA_PATH
69#define DEFAULT_DATA_PATH "." PATH_SEPARATOR \
70 "data" PATH_SEPARATOR \
71 FREECIV_STORAGE_DIR DIR_SEPARATOR DATASUBDIR
73#ifndef DEFAULT_SAVE_PATH
74#define DEFAULT_SAVE_PATH "." PATH_SEPARATOR \
75 FREECIV_STORAGE_DIR DIR_SEPARATOR "saves"
77#ifndef DEFAULT_SCENARIO_PATH
78#define DEFAULT_SCENARIO_PATH \
80 "data" DIR_SEPARATOR "scenarios" PATH_SEPARATOR \
81 FREECIV_STORAGE_DIR DATASUBDIR DIR_SEPARATOR "scenarios" PATH_SEPARATOR \
82 FREECIV_STORAGE_DIR DIR_SEPARATOR "scenarios"
86#ifndef FREECIV_DATA_PATH
87#define FREECIV_DATA_PATH "FREECIV_DATA_PATH"
89#ifndef FREECIV_SAVE_PATH
90#define FREECIV_SAVE_PATH "FREECIV_SAVE_PATH"
92#ifndef FREECIV_SCENARIO_PATH
93#define FREECIV_SCENARIO_PATH "FREECIV_SCENARIO_PATH"
104 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
119static char *
expand_dir(
char *tok_in,
bool ok_to_free);
164 unsigned int cnt = 0;
172 seplen = strlen(sep);
185 ptr = &buf[
sizeof(buf)];
188 while (mantissa != 0) {
191 if (ptr <= buf + seplen) {
205 *(--ptr) =
'0' + dig;
208 if (mantissa != 0 && cnt == *grp) {
211 if (*grp == CHAR_MAX) {
218 memcpy(ptr, sep, seplen);
219 if (*(grp + 1) != 0) {
245 return ch >=
' ' && ch <=
'~';
261 for (;
'\0' !=
name[i]; i++) {
284 const char illegal_chars[] = {
'|',
'%',
'"',
',',
'*',
'<',
'>',
'\0'};
293 if ((*
name ==
' ') || (*(strchr(
name,
'\0') - 1) ==
' ')) {
299 for (i = 0;
name[i]; i++) {
303 for (j = 0; illegal_chars[j]; j++) {
304 if (
name[i] == illegal_chars[j]) {
322 if (NULL == s ||
'\0' == *s) {
326 for (;
'\0' != s[i]; i++) {
343 if (NULL == s || 1 >
n) {
347 for (; i < (
n - 1); i++) {
360 return fc_strcoll((
const char *) first, (
const char *) second);
370 return fc_strcoll(*((
const char **) first), *((
const char **) second));
379 const char *
const *second)
458 t = s + strlen(s) -1;
459 while (t>=s && (*t) == trailing) {
524 if (
'-' == *
str ||
'+' == *
str) {
538 return (
'\0' == *
str && (NULL == pint || 1 == sscanf(start,
"%d", pint)));
571 return (
'\0' == *
str && (NULL == pint || 1 == sscanf(start,
"%u", pint)));
592 if (
'-' == *
str ||
'+' == *
str) {
618 return (
'\0' == *
str && dot
619 && (NULL == pfloat || 1 == sscanf(start,
"%f", pfloat)));
634#ifdef FREECIV_MSWINDOWS
635#define HOMEVAR "APPDATA"
637#define HOMEVAR "HOME"
715 char *env = getenv(
"USER");
730 struct passwd *pwent = getpwuid(getuid());
742#ifdef FREECIV_MSWINDOWS
745 char name[UNLEN + 1];
746 DWORD length =
sizeof(
name);
748 if (GetUserName(
name, &length)) {
794 log_error(
"For \"%s\" in path cannot expand '~'"
801 log_verbose(
"No HOME, skipping path component %s", tok);
804 int len = strlen(home) + i;
818 if (i == -1 && ok_to_free) {
894#ifdef FREECIV_APPIMAGE
900 "%s/usr/share/freeciv",
903#define default_data_path DEFAULT_DATA_PATH
908 log_error(
_(
"\"%s\" is set but empty; using default \"%s\" "
909 "data directories instead."),
944 log_error(
_(
"\"%s\" is set but empty; using default \"%s\" "
945 "save directories instead."),
979#ifdef FREECIV_APPIMAGE
987 "%s/usr/share/freeciv/scenarios",
990#define default_scenario_path DEFAULT_SCENARIO_PATH
995 log_error(
_(
"\"%s\" is set but empty; using default \"%s\" "
996 "scenario directories instead."),
1003 log_verbose(
"Scenario path component: %s", dirname);
1023 size_t suffix_len = strlen(suffix);
1034 struct dirent *
entry;
1039 if (errno == ENOENT) {
1040 log_verbose(
"Skipping non-existing data directory %s.",
1044 log_error(
_(
"Could not read data directory %s: %s."),
1051 while ((
entry = readdir(dir))) {
1052 size_t len = strlen(
entry->d_name);
1055 if (
len > suffix_len
1056 && strcmp(suffix,
entry->d_name +
len - suffix_len) == 0) {
1061 match[
len - suffix_len] =
'\0';
1096#ifndef DIR_SEPARATOR_IS_DEFAULT
1097 char fnbuf[filename != NULL ? strlen(filename) + 1 : 1];
1100 const char *fnbuf = filename;
1123#ifndef DIR_SEPARATOR_IS_DEFAULT
1124 for (i = 0; filename[i] !=
'\0'; i++) {
1125 if (filename[i] ==
'/') {
1128 fnbuf[i] = filename[i];
1143 log_verbose(
"Could not find readable file \"%s\" in data path.", filename);
1172 time_t a = (*ppa)->mtime;
1173 time_t b = (*ppb)->mtime;
1175 return ((a < b) ? 1 : (a > b) ? -1 : 0);
1184 return fc_strcoll((*ppa)->name, (*ppb)->name);
1205 const char *infix,
bool nodups)
1207 struct fileinfo_list *res;
1218 struct dirent *
entry;
1227 while ((
entry = readdir(dir))) {
1234 if ((ptr = strstr(filename, infix))) {
1237 size_t len = strlen(dirname) + strlen(filename) + 2;
1242 if (
fc_stat(fullname, &buf) == 0) {
1248 file->
name = filename;
1250 file->
mtime = buf.st_mtime;
1252 fileinfo_list_append(res, file);
1283 const char *langname = NULL;
1286 langname = getenv(
"LANG");
1288#ifdef FREECIV_MSWINDOWS
1291 switch (PRIMARYLANGID(GetUserDefaultLangID())) {
1311 switch (SUBLANGID(GetUserDefaultLangID())) {
1312 case SUBLANG_ENGLISH_UK:
1338 case LANG_HUNGARIAN:
1350 case LANG_LITHUANIAN:
1356 case LANG_NORWEGIAN:
1362 case LANG_PORTUGUESE:
1363 switch (SUBLANGID(GetUserDefaultLangID())) {
1364 case SUBLANG_PORTUGUESE_BRAZILIAN:
1384 case LANG_UKRAINIAN:
1392 if (langname != NULL) {
1393 static char envstr[40];
1395 fc_snprintf(envstr,
sizeof(envstr),
"LANG=%s", langname);
1405#ifdef FREECIV_ENABLE_NLS
1409static void autocap_update(
void)
1411 char *autocap_opt_in[] = {
"fi", NULL };
1413 bool ac_enabled =
FALSE;
1415 char *lang = getenv(
"LANG");
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]) {
1436#ifdef FREECIV_ENABLE_NLS
1438 setenv(
"LANG", lang,
TRUE);
1441 static char envstr[40];
1443 fc_snprintf(envstr,
sizeof(envstr),
"LANG=%s", lang);
1448 (void) setlocale(LC_ALL,
"");
1474#ifdef FREECIV_MSWINDOWS
1478 (void) setlocale(LC_ALL,
"");
1491 if (strcmp(setlocale(LC_NUMERIC, NULL),
"C") != 0) {
1492 struct lconv *lc = localeconv();
1494 if (lc->grouping[0] ==
'\0') {
1505 lc->grouping[
len] !=
'\0' && lc->grouping[
len] != CHAR_MAX;
len++) {
1544#if (defined(ALWAYS_ROOT) || defined(__EMX__) || defined(__BEOS__))
1547 if (getuid() == 0 || geteuid() == 0) {
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"));
1566 static const char *
const descriptions[] = {
1575 return descriptions[result];
1583 size_t max_len_name,
1590 len_fn, prefix, ind_result, NULL, 0, NULL);
1606 size_t max_len_name,
1615 int i,
len, nmatches;
1617 if (len_fn == NULL) {
1618 len = strlen(prefix);
1620 len = len_fn(prefix);
1625 if (
len > max_len_name && max_len_name > 0) {
1630 for (i = 0; i < n_names; i++) {
1631 const char *
name = accessor_fn(i);
1633 if (cmp_fn(
name, prefix,
len) == 0) {
1638 if (nmatches == 0) {
1641 if (matches != NULL && nmatches < max_matches) {
1642 matches[nmatches] = i;
1648 if (nmatches == 1) {
1650 }
else if (nmatches > 1) {
1651 if (pnum_matches != NULL) {
1652 *pnum_matches =
MIN(max_matches, nmatches);
1667 static char *default_multicast_group_ipv4 =
"225.1.1.1";
1668#ifdef FREECIV_IPV6_SUPPORT
1670 static char *default_multicast_group_ipv6 =
"FF31::8000:15B4";
1674 char *env = getenv(
"FREECIV_MULTICAST_GROUP");
1679#ifdef FREECIV_IPV6_SUPPORT
1680 if (ipv6_preferred) {
1715 }
else if (filename[0] ==
'~' && filename[1] ==
'\0') {
1736 sz = strlen(home) + strlen(filename) + 2;
1740 }
else if (filename[0] ==
'~' && filename[1] ==
'\0') {
1756 for (j = strlen(filepath); j >= 0; j--) {
1758 return &filepath[j+1];
1777 if (pathname[0] ==
'\0') {
1787 }
else if (dir[0] !=
'\0' && dir[1] ==
':' && dir[2] ==
'\\') {
1799#ifdef FREECIV_MSWINDOWS
1807 bool failure =
FALSE;
1809 if (_mkdir(path_in_local_encoding) == -1
1814 free(path_in_local_encoding);
1822 if (mkdir(path, 0755) == -1
1829 if (mkdir(path, 0755) == -1
1860 log_debug(
"Create directory \"%s\"", filename);
1879#ifdef FREECIV_MSWINDOWS
1880 if (strchr(filename,
':')) {
1884 if (filename[0] ==
'/') {
1912char scanin(
const char **buf,
char *delimiters,
char *dest,
int size)
1914 char *ptr, found =
'?';
1916 if (*buf == NULL || strlen(*buf) == 0 ||
size == 0) {
1925 strncpy(dest, *buf,
size-1);
1926 dest[
size-1] =
'\0';
1928 ptr = strpbrk(dest, delimiters);
1931 ptr = strpbrk(*buf, delimiters);
1941 *buf = strpbrk(*buf, delimiters);
1959 int seconds, minutes, hours, days;
1963 minutes = (t / 60) % 60;
1964 hours = (t / (60 * 60)) % 24;
1965 days = t / (60 * 60 * 24);
1979 space ?
" " :
"", hours,
PL_(
"hour",
"hours", hours));
1985 minutes,
PL_(
"minute",
"minutes", minutes));
1991 seconds,
PL_(
"second",
"seconds", seconds));
2002 if (
n > 1 && array != NULL) {
2004 for (i = 0; i <
n - 1; i++) {
2007 array[j] = array[i];
2033 if (
'\0' == *test) {
2044 if (
'[' != *pattern) {
2045 if (
'\\' == *pattern) {
2046 jump_to = *(pattern + 1);
2054 while (
'\0' != *test) {
2055 if (
'\0' != jump_to) {
2057 test = strchr(test, jump_to);
2080 const char *start = (*pattern + 1);
2084 if (
'\0' == **test) {
2091 *pattern = strchr(*pattern,
']');
2092 if (NULL == *pattern) {
2095 }
else if (*(*pattern - 1) !=
'\\') {
2104 if (
'!' == *start) {
2114 for (; start < *pattern; start++) {
2115 if (
'-' == *start ||
'!' == *start) {
2118 }
else if (start < *pattern - 2 &&
'-' == *(start + 1)) {
2120 if (*start <= testc && testc <= *(start + 2)) {
2124 }
else if (*start == testc) {
2150 return '\0' == *test;
2159 if (
'\0' == *test) {
2167 if (*pattern != *test) {
2194 const struct cf_sequence *sequences,
size_t sequences_num)
2198 const char *f = format;
2199 char *
const max = buf + buf_len - 1;
2201 const char *
const cmax = cformat +
sizeof(cformat) - 2;
2204 if ((
size_t) -1 == sequences_num) {
2207 for (pseq = sequences;
CF_LAST != pseq->
type; pseq++) {
2212 while (
'\0' != *f) {
2227 for (; !
fc_isalpha(*f) &&
'\0' != *f &&
'%' != *f && cmax > c; f++) {
2243 for (i = 0, pseq = sequences; i < sequences_num; i++, pseq++) {
2244 if (pseq->
letter == *f) {
2246 switch (pseq->
type) {
2293 log_error(
"Error: unsupported sequence type: %d.", pseq->
type);
2305 if (i >= sequences_num) {
2308 j =
fc_snprintf(b, max - b + 1,
"%s%c", cformat, *f);
2344 size_t sequences_num = 0;
2348 va_start(args, format);
2350 sequences[sequences_num] = va_arg(args,
struct cf_sequence);
2356 }
while (
ARRAY_SIZE(sequences) > sequences_num);
2360 log_error(
"Too many custom sequences. Maybe did you forget cf_end() "
2361 "at the end of the arguments?");
2368 return fc_vsnprintcf(buf, buf_len, format, sequences, sequences_num);
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'
2382 bool reordered =
FALSE;
2386 memset(escapes, 0, max_escapes);
2387 format = strchr(format,
'%');
2388 while (NULL != format) {
2390 if (
'%' == *format) {
2394 const char *start = format;
2399 if (
'$' == *format) {
2401 sscanf(start,
"%d", &idx);
2406 while (
'\0' != *format
2407 && NULL == strchr(format_escapes, *format)) {
2410 escapes[idx] = *format;
2422 if (
'*' != *format) {
2423 format = strchr(format,
'%');
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));
2441 return (format1_escapes_num == format2_escapes_num
2442 && 0 == memcmp(format1_escapes, format2_escapes,
2443 format1_escapes_num));
void astr_free(struct astring *astr)
void astr_set(struct astring *astr, const char *format,...)
void astr_clear(struct astring *astr)
void astr_add(struct astring *astr, const char *format,...)
static const char * astr_str(const struct astring *astr) fc__attribute((nonnull(1)))
DIR * fc_opendir(const char *dir_to_open)
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)
void capitalization_opt_in(bool opt_in)
#define bindtextdomain(Package, Directory)
#define PL_(String1, String2, n)
#define textdomain(Domain)
#define fc_assert_ret(condition)
#define log_verbose(message,...)
#define fc_assert(condition)
#define fc_assert_ret_val(condition, val)
#define log_debug(message,...)
#define log_normal(message,...)
#define log_error(message,...)
#define fc_assert_ret_val_msg(condition, val, message,...)
int compare_strings(const void *first, const void *second)
static size_t extract_escapes(const char *format, char *escapes, size_t max_escapes)
void format_time_duration(time_t t, char *buf, int maxlen)
static bool wildcard_asterisk_fit(const char *pattern, const char *test)
int compare_strings_ptrs(const void *first, const void *second)
char * user_username(char *buf, size_t bufsz)
int fc_snprintcf(char *buf, size_t buf_len, const char *format,...)
static char * grouping_sep
enum fc_tristate fc_tristate_and(enum fc_tristate one, enum fc_tristate two)
void dont_run_as_root(const char *argv0, const char *fallback)
bool wildcard_fit_string(const char *pattern, const char *test)
bool check_strlen(const char *str, size_t len, const char *errmsg)
const char * fileinfoname(const struct strvec *dirs, const char *filename)
static bool compare_fileinfo_name(const struct fileinfo *pa, const struct fileinfo *pb)
char * user_home_dir(void)
void remove_trailing_spaces(char *s)
void free_user_home_dir(void)
int fc_vsnprintcf(char *buf, size_t buf_len, const char *format, const struct cf_sequence *sequences, size_t sequences_num)
static char * storage_dir_freeciv
bool str_to_int(const char *str, int *pint)
#define default_data_path
bool str_to_float(const char *str, float *pfloat)
const char * m_pre_description(enum m_pre_result result)
static struct strvec * data_dir_names
#define FREECIV_SCENARIO_PATH
static const char base64url[]
struct strvec * fileinfolist(const struct strvec *dirs, const char *suffix)
char * skip_leading_spaces(char *s)
bool str_to_uint(const char *str, unsigned int *pint)
bool make_dir(const char *pathname)
void free_data_dir_names(void)
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)
const struct strvec * get_scenario_dirs(void)
size_t loud_strlcpy(char *buffer, const char *str, size_t len, const char *errmsg)
char * interpret_tilde_alloc(const char *filename)
#define FREECIV_SAVE_PATH
bool make_dir_for_file(char *filename)
static void fileinfo_destroy(struct fileinfo *pfile)
void interpret_tilde(char *buf, size_t buf_size, const char *filename)
static struct astring realfile
static void remove_trailing_char(char *s, char trailing)
int compare_strings_strvec(const char *const *first, const char *const *second)
bool is_base64url(const char *s)
void free_multicast_group(void)
static char * home_dir_user
void free_fileinfo_data(void)
static bool is_ascii(char ch)
char * end_of_strn(char *str, int *nleft)
bool path_is_absolute(const char *filename)
static bool wildcard_range_fit(const char **pattern, const char **test)
static struct strvec * scenario_dir_names
static int compare_file_mtime_ptrs(const struct fileinfo *const *ppa, const struct fileinfo *const *ppb)
bool formats_match(const char *format1, const char *format2)
const char * int_to_text(unsigned int number)
char scanin(const char **buf, char *delimiters, char *dest, int size)
static int compare_file_name_ptrs(const struct fileinfo *const *ppa, const struct fileinfo *const *ppb)
const struct strvec * get_save_dirs(void)
static char * expand_dir(char *tok_in, bool ok_to_free)
void remove_leading_spaces(char *s)
char * skip_to_basename(char *filepath)
#define FREECIV_DATA_PATH
#define default_scenario_path
void array_shuffle(int *array, int n)
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)
bool is_ascii_name(const char *name)
#define DEFAULT_SAVE_PATH
enum fc_tristate fc_tristate_or(enum fc_tristate one, enum fc_tristate two)
static struct strvec * base_get_dirs(const char *dir_list)
const char * setup_langname(void)
void randomize_base64url_string(char *s, size_t n)
char * get_multicast_group(bool ipv6_preferred)
void free_freeciv_storage_dir(void)
bool is_safe_filename(const char *name)
void remove_leading_trailing_spaces(char *s)
const char * big_int_to_text(unsigned int mantissa, unsigned int exponent)
const struct strvec * get_data_dirs(void)
char * freeciv_storage_dir(void)
void switch_lang(const char *lang)
struct fileinfo_list * fileinfolist_infix(const struct strvec *dirs, const char *infix, bool nodups)
static struct strvec * save_dir_names
int(* m_pre_strncmp_fn_t)(const char *, const char *, size_t n)
#define DIR_SEPARATOR_CHAR
size_t() m_strlen_fn_t(const char *str)
#define PARENT_DIR_OPERATOR
const char *(* m_pre_accessor_fn_t)(int)
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
int fc_snprintf(char *str, size_t n, const char *format,...)
size_t fc_strlcpy(char *dest, const char *src, size_t n)
const char * fc_strerror(fc_errno err)
int cat_snprintf(char *str, size_t n, const char *format,...)
fc_errno fc_get_errno(void)
int fc_strcoll(const char *str0, const char *str1)
int fc_stat(const char *filename, struct stat *buf)