14 #include <fc_config.h>
23 #ifdef FREECIV_MSWINDOWS
33 #include <QCoreApplication>
36 #include <QRegularExpression>
37 #include <QStandardPaths>
52 auto app_name = QCoreApplication::applicationName();
53 QCoreApplication::setApplicationName(
"freeciv21");
56 QStringList{QStringLiteral(
"."), QStringLiteral(
"data"),
58 QStringLiteral(FREECIV_INSTALL_DATADIR)}
59 + QStandardPaths::standardLocations(QStandardPaths::DataLocation);
60 QCoreApplication::setApplicationName(app_name);
66 return {QStringLiteral(
"."),
73 for (
auto &path : paths) {
74 path += QStringLiteral(
"/scenarios");
87 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
122 unsigned int cnt = 0;
130 seplen = qstrlen(sep);
143 ptr = &buf[
sizeof(buf)];
146 while (mantissa != 0) {
149 if (ptr <= buf + seplen) {
163 *(--ptr) =
'0' + dig;
166 if (mantissa != 0 && cnt == *grp) {
170 if (*grp == CHAR_MAX) {
177 memcpy(ptr, sep, seplen);
178 if (*(grp + 1) != 0) {
203 return ch >=
' ' && ch <=
'~';
212 const QRegularExpression regex(QLatin1String(
"^[\\w_\\-.@]+$"));
213 return regex.match(
name).hasMatch()
225 const char illegal_chars[] = {
'|',
'%',
'"',
',',
'*',
'<',
'>',
'\0'};
234 if ((*
name ==
' ') || (*(strchr(
name,
'\0') - 1) ==
' ')) {
240 for (i = 0;
name[i]; i++) {
244 for (j = 0; illegal_chars[j]; j++) {
245 if (
name[i] == illegal_chars[j]) {
263 if (
nullptr == s ||
'\0' == *s) {
267 for (;
'\0' != s[i]; i++) {
268 if (
nullptr == strchr(
base64url, s[i])) {
284 if (
nullptr == s || 1 > n) {
288 for (; i < (n - 1); i++) {
301 while (*s !=
'\0' && QChar::isSpace(*s)) {
339 while (QChar::isSpace(*t)) {
390 while (QChar::isSpace(*str)) {
396 if (
'-' == *str ||
'+' == *str) {
400 while (QChar::isDigit(*str)) {
405 while (QChar::isSpace(*str)) {
411 && (
nullptr == pint || 1 == sscanf(start,
"%d", pint)));
421 static QString storage_dir;
422 if (storage_dir.isEmpty()) {
424 auto app_name = QCoreApplication::applicationName();
425 QCoreApplication::setApplicationName(
"freeciv21");
427 QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
428 QCoreApplication::setApplicationName(app_name);
430 qDebug() <<
_(
"Storage dir:") << storage_dir;
453 char *env = getenv(
"USER");
458 qDebug(
"USER username is %s", buf);
464 #ifndef FREECIV_MSWINDOWS
466 fc_strlcpy(buf, qUtf8Printable(QDir::homePath().split(
"/").last()),
469 qDebug(
"username from homepath is %s", buf);
475 #ifdef FREECIV_MSWINDOWS
478 char name[UNLEN + 1];
479 DWORD length =
sizeof(
name);
481 if (GetUserName(
name, &length)) {
484 qDebug(
"GetUserName username is %s", buf);
494 fc_snprintf(buf, bufsz,
"name%d",
static_cast<int>(getuid()));
496 qDebug(
"fake username is %s", buf);
508 if (qEnvironmentVariableIsSet(env_var)
509 && qEnvironmentVariableIsEmpty(env_var)) {
510 qCritical(
_(
"\"%s\" is set but empty; using default "
511 "data directories instead."),
514 }
else if (qEnvironmentVariableIsSet(env_var)) {
515 return QString(qEnvironmentVariable(env_var))
516 .split(QDir::listSeparator(), Qt::SkipEmptyParts);
545 qDebug() <<
"Data path component:" <<
name;
575 qDebug() <<
"Save path component:" <<
name;
606 qDebug() <<
"Scenario path component:" <<
name;
628 if (dirs.isEmpty()) {
633 for (
const auto &dirname : dirs) {
637 qDebug(
"Skipping non-existing data directory %s.",
638 qUtf8Printable(dirname));
643 dir.setNameFilters({QStringLiteral(
"*") + QString::fromUtf8(suffix)});
644 for (
auto name : dir.entryList()) {
645 name.truncate(
name.length() - qstrlen(suffix));
646 files->append(
name.toUtf8().data());
649 std::sort(files->begin(), files->end());
650 files->erase(std::unique(files->begin(), files->end()), files->end());
663 for (
const auto &dirname : dirs) {
664 QString path = dirname + QLatin1String(
"/") + filename;
665 if (QFileInfo::exists(path)) {
670 qDebug(
"Could not find readable file \"%s\" in data path.",
671 qUtf8Printable(filename));
682 const QString &pattern,
bool nodups)
685 auto files = QFileInfoList();
686 for (
const auto &dirname : path) {
693 files += dir.entryInfoList({pattern}, QDir::NoFilter);
698 std::sort(files.begin(), files.end(),
699 [](
const auto &lhs,
const auto &rhs) {
700 return lhs.absoluteFilePath() < rhs.absoluteFilePath();
702 files.erase(std::unique(files.begin(), files.end()), files.end());
706 std::sort(files.begin(), files.end(),
707 [](
const auto &lhs,
const auto &rhs) {
708 return lhs.lastModified() < rhs.lastModified();
719 char *langname =
nullptr;
722 langname = getenv(
"LANG");
724 #ifdef FREECIV_MSWINDOWS
727 switch (PRIMARYLANGID(GetUserDefaultLangID())) {
747 switch (SUBLANGID(GetUserDefaultLangID())) {
748 case SUBLANG_ENGLISH_UK:
786 case LANG_LITHUANIAN:
798 case LANG_PORTUGUESE:
799 switch (SUBLANGID(GetUserDefaultLangID())) {
800 case SUBLANG_PORTUGUESE_BRAZILIAN:
828 if (langname !=
nullptr) {
829 static char envstr[40];
831 fc_snprintf(envstr,
sizeof(envstr),
"LANG=%s", langname);
841 #ifdef FREECIV_ENABLE_NLS
845 static void autocap_update(
void)
847 const char *autocap_opt_in[] = {
"fi",
nullptr};
849 bool ac_enabled =
false;
851 char *lang = getenv(
"LANG");
853 if (lang !=
nullptr && lang[0] !=
'\0' && lang[1] !=
'\0') {
854 for (i = 0; autocap_opt_in[i] !=
nullptr && !ac_enabled; i++) {
855 if (lang[0] == autocap_opt_in[i][0]
856 && lang[1] == autocap_opt_in[i][1]) {
882 #ifdef FREECIV_MSWINDOWS
886 (void) setlocale(LC_ALL,
"");
888 (void) textdomain(
"freeciv21-core");
899 if (strcmp(setlocale(LC_NUMERIC,
nullptr),
"C") != 0) {
900 struct lconv *lc = localeconv();
902 if (lc->grouping[0] ==
'\0') {
910 lc->grouping[
len] !=
'\0' && lc->grouping[
len] != CHAR_MAX;
953 if (getuid() == 0 || geteuid() == 0) {
955 _(
"%s: Fatal error: you're trying to run me as superuser!\n"),
957 : fallback ? fallback
959 fc_fprintf(stderr,
_(
"Use a non-privileged account instead.\n"));
975 static const char *
const descriptions[] = {
976 N_(
"exact match"),
N_(
"only match"),
N_(
"ambiguous"),
977 N_(
"empty"),
N_(
"too long"),
N_(
"non-match")};
980 return descriptions[result];
987 size_t n_names,
size_t max_len_name,
993 len_fn, prefix, ind_result,
nullptr, 0,
nullptr);
1009 size_t n_names,
size_t max_len_name,
1012 int *ind_result,
int *matches,
1013 int max_matches,
int *pnum_matches)
1015 int i,
len, nmatches;
1017 if (len_fn ==
nullptr) {
1018 len = qstrlen(prefix);
1020 len = len_fn(prefix);
1025 if (
len > max_len_name && max_len_name > 0) {
1030 for (i = 0; i < n_names; i++) {
1031 const char *
name = accessor_fn(i);
1033 if (cmp_fn(
name, prefix,
len) == 0) {
1038 if (nmatches == 0) {
1041 if (matches !=
nullptr && nmatches < max_matches) {
1042 matches[nmatches] = i;
1048 if (nmatches == 1) {
1050 }
else if (nmatches > 1) {
1051 if (pnum_matches !=
nullptr) {
1052 *pnum_matches =
MIN(max_matches, nmatches);
1067 static const char *default_multicast_group_ipv4 =
"225.1.1.1";
1069 static const char *default_multicast_group_ipv6 =
"FF31::8000:15B4";
1072 char *env = getenv(
"FREECIV_MULTICAST_GROUP");
1077 if (ipv6_preferred) {
1102 if (filename == QLatin1String(
"~")) {
1103 return QDir::homePath();
1104 }
else if (filename.startsWith(QLatin1String(
"~/"))) {
1105 return QDir::homePath() + filename.midRef(1);
1119 return pathname.isEmpty() || QDir().mkpath(
interpret_tilde(pathname));
1144 char *ptr, found =
'?';
1146 if (*buf ==
nullptr || qstrlen(*buf) == 0 ||
size == 0) {
1155 qstrncpy(dest, *buf,
size - 1);
1156 dest[
size - 1] =
'\0';
1158 ptr = strpbrk(dest, delimiters);
1161 ptr = strpbrk(*buf, delimiters);
1163 if (ptr !=
nullptr) {
1171 *buf = strpbrk(*buf, delimiters);
1172 if (*buf !=
nullptr) {
1189 int seconds, minutes, hours, days;
1193 minutes = (t / 60) % 60;
1194 hours = (t / (60 * 60)) % 24;
1195 days = t / (60 * 60 * 24);
1208 cat_snprintf(buf, maxlen,
"%s%d %s", space ?
" " :
"", hours,
1209 PL_(
"hour",
"hours", hours));
1213 cat_snprintf(buf, maxlen,
"%s%d %s", space ?
" " :
"", minutes,
1214 PL_(
"minute",
"minutes", minutes));
1218 cat_snprintf(buf, maxlen,
"%s%d %s", space ?
" " :
"", seconds,
1219 PL_(
"second",
"seconds", seconds));
1230 if (n > 1 && array !=
nullptr) {
1232 for (i = 0; i < n - 1; i++) {
1235 array[j] = array[i];
1261 if (
'\0' == *test) {
1272 if (
'[' != *pattern) {
1273 if (
'\\' == *pattern) {
1274 jump_to = *(pattern + 1);
1282 while (
'\0' != *test) {
1283 if (
'\0' != jump_to) {
1285 test = strchr(test, jump_to);
1286 if (
nullptr == test) {
1308 const char *start = (*pattern + 1);
1312 if (
'\0' == **test) {
1319 *pattern = strchr(*pattern,
']');
1320 if (
nullptr == *pattern) {
1323 }
else if (*(*pattern - 1) !=
'\\') {
1332 if (
'!' == *start) {
1342 for (; start < *pattern; start++) {
1343 if (
'-' == *start ||
'!' == *start) {
1346 }
else if (start < *pattern - 2 &&
'-' == *(start + 1)) {
1348 if (*start <= testc && testc <= *(start + 2)) {
1352 }
else if (*start == testc) {
1378 return '\0' == *test;
1387 if (
'\0' == *test) {
1395 if (*pattern != *test) {
1422 const struct cf_sequence *sequences,
size_t sequences_num)
1426 const char *f = format;
1427 char *
const max = buf + buf_len - 1;
1429 const char *
const cmax = cformat +
sizeof(cformat) - 2;
1432 if (
static_cast<size_t>(-1) == sequences_num) {
1435 for (pseq = sequences;
CF_LAST != pseq->
type; pseq++) {
1440 while (
'\0' != *f) {
1455 for (; !QChar::isLetter(*f) &&
'\0' != *f &&
'%' != *f && cmax > c;
1460 if (!QChar::isLetter(*f)) {
1472 for (i = 0, pseq = sequences; i < sequences_num; i++, pseq++) {
1473 if (pseq->
letter == *f) {
1475 switch (pseq->
type) {
1522 qCritical(
"Error: unsupported sequence type: %d.", pseq->
type);
1534 if (i >= sequences_num) {
1537 j =
fc_snprintf(b, max - b + 1,
"%s%c", cformat, *f);
1565 static const char format_escapes[] = {
'*',
'd',
'i',
'o',
'u',
'x',
'X',
1566 'e',
'E',
'f',
'F',
'g',
'G',
'a',
1567 'A',
'c',
's',
'p',
'n',
'\0'};
1568 bool reordered =
false;
1572 memset(escapes, 0, max_escapes);
1573 format = strchr(format,
'%');
1574 while (
nullptr != format) {
1576 if (
'%' == *format) {
1579 }
else if (QChar::isDigit(*format)) {
1580 const char *start = format;
1584 }
while (QChar::isDigit(*format));
1585 if (
'$' == *format) {
1587 if (1 != sscanf(start,
"%d", &idx)) {
1590 fc_assert_msg(
false,
"Invalid format string \"%s\"", format);
1595 while (
'\0' != *format &&
nullptr == strchr(format_escapes, *format)) {
1598 escapes[idx] = *format;
1610 if (
'*' != *format) {
1611 format = strchr(format,
'%');
1623 char format1_escapes[256], format2_escapes[256];
1624 size_t format1_escapes_num =
1626 size_t format2_escapes_num =
1630 format1_escapes_num == format2_escapes_num
1631 && 0 == memcmp(format1_escapes, format2_escapes, format1_escapes_num));
void fc_fprintf(FILE *stream, const char *format,...)
Do a fprintf from the internal charset into the local charset.
char * local_to_internal_string_buffer(const char *text, char *buf, size_t bufsz)
const char * get_locale_dir()
Return directory containing locales.
void capitalization_opt_in(bool opt_in)
Translation opts in to automatic capitalization features.
#define PL_(String1, String2, n)
#define fc_assert_msg(condition, message,...)
#define fc_assert_ret(condition)
#define fc_assert(condition)
#define fc_assert_ret_val(condition, val)
#define fc_assert_ret_val_msg(condition, val, message,...)
static size_t extract_escapes(const char *format, char *escapes, size_t max_escapes)
Extract the sequences of a format.
void format_time_duration(time_t t, char *buf, int maxlen)
Convenience function to nicely format a time_t seconds value in to a string with hours,...
static bool wildcard_asterisk_fit(const char *pattern, const char *test)
Test an asterisk in the pattern against test.
char * skip_leading_spaces(char *s)
Returns 's' incremented to first non-space character.
static char * grouping_sep
static QStringList scenario_dir_names
enum fc_tristate fc_tristate_and(enum fc_tristate one, enum fc_tristate two)
An AND function for fc_tristate.
void dont_run_as_root(const char *argv0, const char *fallback)
If we have root privileges, die with an error.
bool wildcard_fit_string(const char *pattern, const char *test)
Returns TRUE if test fit the pattern.
bool is_safe_filename(const QString &name)
Check if the name is safe security-wise.
const QStringList & get_data_dirs()
Returns a list of data directory paths, in the order in which they should be searched.
static QStringList save_dir_names
bool check_strlen(const char *str, size_t len, const char *errmsg)
Check the length of the given string.
const char * m_pre_description(enum m_pre_result result)
Return a description string of the result.
char * setup_langname()
Language environmental variable (with emulation).
void remove_trailing_spaces(char *s)
Terminates string pointed to by 's' to remove traling spaces; Note 's' must point to writeable memory...
int fc_vsnprintcf(char *buf, size_t buf_len, const char *format, const struct cf_sequence *sequences, size_t sequences_num)
Print a string with a custom format.
const char * int_to_text(unsigned int number)
Return a prettily formatted string containing the given number.
bool str_to_int(const char *str, int *pint)
Convert 'str' to it's int reprentation if possible.
static const char base64url[]
const QStringList & get_save_dirs()
Returns a list of save directory paths, in the order in which they should be searched.
static QStringList base_get_dirs(const char *env_var)
Returns a list of directory paths, in the order in which they should be searched.
const char * big_int_to_text(unsigned int mantissa, unsigned int exponent)
Returns a statically allocated string containing a nicely-formatted version of the given number accor...
QString interpret_tilde(const QString &filename)
Interpret ~ in filename as home dir.
void free_nls()
Free memory allocated by Native Language Support.
char scanin(char **buf, char *delimiters, char *dest, int size)
Scan in a word or set of words from start to but not including any of the given delimiters.
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)
Given n names, with maximum length max_len_name, accessed by accessor_fn(0) to accessor_fn(n-1),...
static QStringList default_data_path()
void init_nls()
Setup for Native Language Support, if configured to use it.
size_t loud_strlcpy(char *buffer, const char *str, size_t len, const char *errmsg)
Call check_strlen() on str and then strlcpy() it into buffer.
static QStringList data_dir_names
char * get_multicast_group(bool ipv6_preferred)
Returns string which gives the multicast group IP address for finding servers on the LAN,...
bool is_base64url(const char *s)
Check for valid base64url.
void free_multicast_group()
Free multicast group resources.
static bool is_ascii(char ch)
Check whether or not the given char is a valid ascii character.
QString fileinfoname(const QStringList &dirs, const QString &filename)
Returns a filename to access the specified file from a directory by searching all specified directori...
static bool wildcard_range_fit(const char **pattern, const char **test)
Test a range in the pattern against test.
bool formats_match(const char *format1, const char *format2)
Returns TRUE iff both formats are compatible (if 'format1' can be used instead 'format2' and reciproc...
static QStringList default_scenario_path()
QVector< QString > * fileinfolist(const QStringList &dirs, const char *suffix)
Returns a string vector storing the filenames in the data directories matching the given suffix.
void remove_leading_spaces(char *s)
Removes leading spaces in string pointed to by 's'.
char * user_username(char *buf, size_t bufsz)
Returns string which gives user's username, as specified by $USER or as given in password file for th...
void array_shuffle(int *array, int n)
Randomize the elements of an array using the Fisher-Yates shuffle.
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)
See match_prefix_full().
bool is_ascii_name(const char *name)
This is used in sundry places to make sure that names of cities, players etc.
bool make_dir(const QString &pathname)
If the directory "pathname" does not exist, recursively create all directories until it does.
QFileInfoList find_files_in_path(const QStringList &path, const QString &pattern, bool nodups)
Search for file names matching the pattern in the provided list of directories.
void randomize_base64url_string(char *s, size_t n)
generate a random string meeting criteria such as is_ascii_name(), is_base64url(),...
static QStringList default_save_path()
QString freeciv_storage_dir()
Returns string which gives freeciv storage dir.
void remove_leading_trailing_spaces(char *s)
Removes leading and trailing spaces in string pointed to by 's'.
const QStringList & get_scenario_dirs()
Returns a list of scenario directory paths, in the order in which they should be searched.
int(* m_pre_strncmp_fn_t)(const char *, const char *, size_t n)
size_t() m_strlen_fn_t(const char *str)
#define PARENT_DIR_OPERATOR
const char *(* m_pre_accessor_fn_t)(int)
int fc_snprintf(char *str, size_t n, const char *format,...)
See also fc_utf8_snprintf_trunc(), fc_utf8_snprintf_rep().
size_t fc_strlcpy(char *dest, const char *src, size_t n)
fc_strlcpy() provides utf-8 version of (non-standard) function strlcpy() It is intended as more user-...
int cat_snprintf(char *str, size_t n, const char *format,...)
cat_snprintf is like a combination of fc_snprintf and fc_strlcat; it does snprintf to the end of an e...