Freeciv21
Develop your civilization from humble roots to a global empire
shared.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2020 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 #include <fc_config.h>
15 
16 #include <climits>
17 #include <clocale>
18 #include <cstdarg>
19 #include <cstdio>
20 #include <cstdlib>
21 #include <sys/stat.h>
22 
23 #ifdef FREECIV_MSWINDOWS
24 #define ALWAYS_ROOT
25 #include <lmcons.h> // UNLEN
26 #include <shlobj.h>
27 #include <windows.h>
28 #else // FREECIV_MSWINDOWS
29 #include <unistd.h> // getuid, geteuid
30 #endif // FREECIV_MSWINDOWS
31 
32 // Qt
33 #include <QCoreApplication>
34 #include <QDateTime>
35 #include <QDir>
36 #include <QRegularExpression>
37 #include <QStandardPaths>
38 #include <QString>
39 #include <QtGlobal>
40 
41 // utility
42 #include "fciconv.h"
43 #include "fcintl.h"
44 #include "log.h"
45 #include "rand.h"
46 
47 #include "shared.h"
48 
49 static QStringList default_data_path()
50 {
51  // Make sure that all executables get the same directory.
52  auto app_name = QCoreApplication::applicationName();
53  QCoreApplication::setApplicationName("freeciv21");
54 
55  auto paths =
56  QStringList{QStringLiteral("."), QStringLiteral("data"),
57  freeciv_storage_dir() + QStringLiteral("/" DATASUBDIR),
58  QStringLiteral(FREECIV_INSTALL_DATADIR)}
59  + QStandardPaths::standardLocations(QStandardPaths::DataLocation);
60  QCoreApplication::setApplicationName(app_name);
61  return paths;
62 }
63 
64 static QStringList default_save_path()
65 {
66  return {QStringLiteral("."),
67  freeciv_storage_dir() + QStringLiteral("/saves")};
68 }
69 
70 static QStringList default_scenario_path()
71 {
72  auto paths = default_data_path();
73  for (auto &path : paths) {
74  path += QStringLiteral("/scenarios");
75  }
76  return paths;
77 }
78 
79 /* Both of these are stored in the local encoding. The grouping_sep must
80  * be converted to the internal encoding when it's used. */
81 static char *grouping = nullptr;
82 static char *grouping_sep = nullptr;
83 
84 /* As well as base64 functions, this string is used for checking for
85  * 'safe' filenames, so should not contain / \ . */
86 static const char base64url[] =
87  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
88 
89 static QStringList data_dir_names = {};
90 static QStringList save_dir_names = {};
91 static QStringList scenario_dir_names = {};
92 
93 static char *mc_group = nullptr;
94 
98 enum fc_tristate fc_tristate_and(enum fc_tristate one, enum fc_tristate two)
99 {
100  if (TRI_NO == one || TRI_NO == two) {
101  return TRI_NO;
102  }
103 
104  if (TRI_MAYBE == one || TRI_MAYBE == two) {
105  return TRI_MAYBE;
106  }
107 
108  return TRI_YES;
109 }
110 
117 const char *big_int_to_text(unsigned int mantissa, unsigned int exponent)
118 {
119  static char buf[64]; // Note that we'll be filling this in right to left.
120  char *grp = grouping;
121  char *ptr;
122  unsigned int cnt = 0;
123  char sep[64];
124  size_t seplen;
125 
126  /* We have to convert the encoding here (rather than when the locale
127  * is initialized) because it can't be done before the charsets are
128  * initialized. */
130  seplen = qstrlen(sep);
131 
132 #if 0 // Not needed while the values are unsigned.
133  fc_assert_ret_val(0 <= mantissa, nullptr);
134  fc_assert_ret_val(0 <= exponent, nullptr);
135 #endif
136 
137  if (mantissa == 0) {
138  return "0";
139  }
140 
141  /* We fill the string in backwards, starting from the right. So the first
142  * thing we do is terminate it. */
143  ptr = &buf[sizeof(buf)];
144  *(--ptr) = '\0';
145 
146  while (mantissa != 0) {
147  int dig;
148 
149  if (ptr <= buf + seplen) {
150  // Avoid a buffer overflow.
151  fc_assert_ret_val(ptr > buf + seplen, nullptr);
152  return ptr;
153  }
154 
155  // Add on another character.
156  if (exponent > 0) {
157  dig = 0;
158  exponent--;
159  } else {
160  dig = mantissa % 10;
161  mantissa /= 10;
162  }
163  *(--ptr) = '0' + dig;
164 
165  cnt++;
166  if (mantissa != 0 && cnt == *grp) {
167  /* Reached count of digits in group: insert separator and reset count.
168  */
169  cnt = 0;
170  if (*grp == CHAR_MAX) {
171  /* This test is unlikely to be necessary since we would need at
172  least 421-bit ints to break the 127 digit barrier, but why not. */
173  break;
174  }
175  ptr -= seplen;
176  fc_assert_ret_val(ptr >= buf, nullptr);
177  memcpy(ptr, sep, seplen);
178  if (*(grp + 1) != 0) {
179  // Zero means to repeat the present group-size indefinitely.
180  grp++;
181  }
182  }
183  }
184 
185  return ptr;
186 }
187 
191 const char *int_to_text(unsigned int number)
192 {
193  return big_int_to_text(number, 0);
194 }
195 
200 static bool is_ascii(char ch)
201 {
202  // this works with both signed and unsigned char's.
203  return ch >= ' ' && ch <= '~';
204 }
205 
210 bool is_safe_filename(const QString &name)
211 {
212  const QRegularExpression regex(QLatin1String("^[\\w_\\-.@]+$"));
213  return regex.match(name).hasMatch()
214  && !name.contains(QLatin1String(PARENT_DIR_OPERATOR));
215 }
216 
223 bool is_ascii_name(const char *name)
224 {
225  const char illegal_chars[] = {'|', '%', '"', ',', '*', '<', '>', '\0'};
226  int i, j;
227 
228  // must not be nullptr or empty
229  if (!name || *name == '\0') {
230  return false;
231  }
232 
233  // must begin and end with some non-space character
234  if ((*name == ' ') || (*(strchr(name, '\0') - 1) == ' ')) {
235  return false;
236  }
237 
238  /* must be composed entirely of printable ascii characters,
239  * and no illegal characters which can break ranking scripts. */
240  for (i = 0; name[i]; i++) {
241  if (!is_ascii(name[i])) {
242  return false;
243  }
244  for (j = 0; illegal_chars[j]; j++) {
245  if (name[i] == illegal_chars[j]) {
246  return false;
247  }
248  }
249  }
250 
251  // otherwise, it's okay...
252  return true;
253 }
254 
258 bool is_base64url(const char *s)
259 {
260  size_t i = 0;
261 
262  // must not be nullptr or empty
263  if (nullptr == s || '\0' == *s) {
264  return false;
265  }
266 
267  for (; '\0' != s[i]; i++) {
268  if (nullptr == strchr(base64url, s[i])) {
269  return false;
270  }
271  }
272  return true;
273 }
274 
279 void randomize_base64url_string(char *s, size_t n)
280 {
281  size_t i = 0;
282 
283  // must not be nullptr or too short
284  if (nullptr == s || 1 > n) {
285  return;
286  }
287 
288  for (; i < (n - 1); i++) {
289  s[i] = base64url[fc_rand(sizeof(base64url) - 1)];
290  }
291  s[i] = '\0';
292 }
293 
297 char *skip_leading_spaces(char *s)
298 {
299  fc_assert_ret_val(nullptr != s, nullptr);
300 
301  while (*s != '\0' && QChar::isSpace(*s)) {
302  s++;
303  }
304 
305  return s;
306 }
307 
313 {
314  char *t;
315 
316  fc_assert_ret(nullptr != s);
317  t = skip_leading_spaces(s);
318  if (t != s) {
319  while (*t != '\0') {
320  *s++ = *t++;
321  }
322  *s = '\0';
323  }
324 }
325 
331 {
332  char *t;
333  size_t len;
334 
335  fc_assert_ret(nullptr != s);
336  len = qstrlen(s);
337  if (len > 0) {
338  t = s + len - 1;
339  while (QChar::isSpace(*t)) {
340  *t = '\0';
341  if (t == s) {
342  break;
343  }
344  t--;
345  }
346  }
347 }
348 
354 {
357 }
358 
364 bool check_strlen(const char *str, size_t len, const char *errmsg)
365 {
366  fc_assert_ret_val_msg(strlen(str) < len, true, errmsg, str, len);
367  return false;
368 }
369 
373 size_t loud_strlcpy(char *buffer, const char *str, size_t len,
374  const char *errmsg)
375 {
376  (void) check_strlen(str, len, errmsg);
377  return fc_strlcpy(buffer, str, len);
378 }
379 
384 bool str_to_int(const char *str, int *pint)
385 {
386  const char *start;
387 
388  fc_assert_ret_val(nullptr != str, false);
389 
390  while (QChar::isSpace(*str)) {
391  // Skip leading spaces.
392  str++;
393  }
394 
395  start = str;
396  if ('-' == *str || '+' == *str) {
397  // Handle sign.
398  str++;
399  }
400  while (QChar::isDigit(*str)) {
401  // Digits.
402  str++;
403  }
404 
405  while (QChar::isSpace(*str)) {
406  // Ignore trailing spaces.
407  str++;
408  }
409 
410  return ('\0' == *str
411  && (nullptr == pint || 1 == sscanf(start, "%d", pint)));
412 }
413 
420 {
421  static QString storage_dir;
422  if (storage_dir.isEmpty()) {
423  // Make sure that all exe get the same directory.
424  auto app_name = QCoreApplication::applicationName();
425  QCoreApplication::setApplicationName("freeciv21");
426  storage_dir =
427  QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
428  QCoreApplication::setApplicationName(app_name);
429 
430  qDebug() << _("Storage dir:") << storage_dir;
431  }
432 
433  return storage_dir;
434 }
435 
443 char *user_username(char *buf, size_t bufsz)
444 {
445  /* This function uses a number of different methods to try to find a
446  * username. This username then has to be truncated to bufsz
447  * characters (including terminator) and checked for sanity. Note that
448  * truncating a sane name can leave you with an insane name under some
449  * charsets. */
450 
451  // If the environment variable $USER is present and sane, use it.
452  {
453  char *env = getenv("USER");
454 
455  if (env) {
456  fc_strlcpy(buf, env, bufsz);
457  if (is_ascii_name(buf)) {
458  qDebug("USER username is %s", buf);
459  return buf;
460  }
461  }
462  }
463 
464 #ifndef FREECIV_MSWINDOWS
465  {
466  fc_strlcpy(buf, qUtf8Printable(QDir::homePath().split("/").last()),
467  bufsz);
468  if (is_ascii_name(buf)) {
469  qDebug("username from homepath is %s", buf);
470  return buf;
471  }
472  }
473 #endif
474 
475 #ifdef FREECIV_MSWINDOWS
476  // On windows the GetUserName function will give us the login name.
477  {
478  char name[UNLEN + 1];
479  DWORD length = sizeof(name);
480 
481  if (GetUserName(name, &length)) {
482  fc_strlcpy(buf, name, bufsz);
483  if (is_ascii_name(buf)) {
484  qDebug("GetUserName username is %s", buf);
485  return buf;
486  }
487  }
488  }
489 #endif // FREECIV_MSWINDOWS
490 
491 #ifdef ALWAYS_ROOT
492  fc_strlcpy(buf, "name", bufsz);
493 #else
494  fc_snprintf(buf, bufsz, "name%d", static_cast<int>(getuid()));
495 #endif
496  qDebug("fake username is %s", buf);
497  fc_assert(is_ascii_name(buf));
498  return buf;
499 }
500 
506 static QStringList base_get_dirs(const char *env_var)
507 {
508  if (qEnvironmentVariableIsSet(env_var)
509  && qEnvironmentVariableIsEmpty(env_var)) {
510  qCritical(_("\"%s\" is set but empty; using default "
511  "data directories instead."),
512  env_var);
513  return {};
514  } else if (qEnvironmentVariableIsSet(env_var)) {
515  return QString(qEnvironmentVariable(env_var))
516  .split(QDir::listSeparator(), Qt::SkipEmptyParts);
517  } else {
518  return {};
519  }
520 }
521 
533 const QStringList &get_data_dirs()
534 {
535  /* The first time this function is called it will search and
536  * allocate the directory listing. Subsequently we will already
537  * know the list and can just return it. */
538  if (data_dir_names.isEmpty()) {
539  data_dir_names = base_get_dirs("FREECIV_DATA_PATH");
540  if (data_dir_names.isEmpty()) {
542  data_dir_names.removeDuplicates();
543  }
544  for (const auto &name : qAsConst(data_dir_names)) {
545  qDebug() << "Data path component:" << name;
546  }
547  }
548 
549  return data_dir_names;
550 }
551 
563 const QStringList &get_save_dirs()
564 {
565  /* The first time this function is called it will search and
566  * allocate the directory listing. Subsequently we will already
567  * know the list and can just return it. */
568  if (save_dir_names.isEmpty()) {
569  save_dir_names = base_get_dirs("FREECIV_SAVE_PATH");
570  if (save_dir_names.isEmpty()) {
572  save_dir_names.removeDuplicates();
573  }
574  for (const auto &name : qAsConst(save_dir_names)) {
575  qDebug() << "Save path component:" << name;
576  }
577  }
578 
579  return save_dir_names;
580 }
581 
594 const QStringList &get_scenario_dirs()
595 {
596  /* The first time this function is called it will search and
597  * allocate the directory listing. Subsequently we will already
598  * know the list and can just return it. */
599  if (scenario_dir_names.isEmpty()) {
600  scenario_dir_names = base_get_dirs("FREECIV_SCENARIO_PATH");
601  if (scenario_dir_names.isEmpty()) {
603  scenario_dir_names.removeDuplicates();
604  }
605  for (const auto &name : qAsConst(scenario_dir_names)) {
606  qDebug() << "Scenario path component:" << name;
607  }
608  }
609 
610  return scenario_dir_names;
611 }
612 
623 QVector<QString> *fileinfolist(const QStringList &dirs, const char *suffix)
624 {
625  fc_assert_ret_val(!strchr(suffix, '/'), nullptr);
626 
627  QVector<QString> *files = new QVector<QString>();
628  if (dirs.isEmpty()) {
629  return files;
630  }
631 
632  // First assemble a full list of names.
633  for (const auto &dirname : dirs) {
634  QDir dir(dirname);
635 
636  if (!dir.exists()) {
637  qDebug("Skipping non-existing data directory %s.",
638  qUtf8Printable(dirname));
639  continue;
640  }
641 
642  // Get all entries in the directory matching the pattern
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());
647  }
648  }
649  std::sort(files->begin(), files->end());
650  files->erase(std::unique(files->begin(), files->end()), files->end());
651  return files;
652 }
653 
661 QString fileinfoname(const QStringList &dirs, const QString &filename)
662 {
663  for (const auto &dirname : dirs) {
664  QString path = dirname + QLatin1String("/") + filename;
665  if (QFileInfo::exists(path)) {
666  return path;
667  }
668  }
669 
670  qDebug("Could not find readable file \"%s\" in data path.",
671  qUtf8Printable(filename));
672 
673  return QString();
674 }
675 
681 QFileInfoList find_files_in_path(const QStringList &path,
682  const QString &pattern, bool nodups)
683 {
684  // First assemble a full list of files.
685  auto files = QFileInfoList();
686  for (const auto &dirname : path) {
687  QDir dir(dirname);
688 
689  if (!dir.exists()) {
690  continue;
691  }
692 
693  files += dir.entryInfoList({pattern}, QDir::NoFilter);
694  }
695 
696  // Sort the list by name.
697  if (nodups) {
698  std::sort(files.begin(), files.end(),
699  [](const auto &lhs, const auto &rhs) {
700  return lhs.absoluteFilePath() < rhs.absoluteFilePath();
701  });
702  files.erase(std::unique(files.begin(), files.end()), files.end());
703  }
704 
705  // Sort the list by last modification time.
706  std::sort(files.begin(), files.end(),
707  [](const auto &lhs, const auto &rhs) {
708  return lhs.lastModified() < rhs.lastModified();
709  });
710 
711  return files;
712 }
713 
718 {
719  char *langname = nullptr;
720 
721 #ifdef ENABLE_NLS
722  langname = getenv("LANG");
723 
724 #ifdef FREECIV_MSWINDOWS
725  // set LANG by hand if it is not set
726  if (!langname) {
727  switch (PRIMARYLANGID(GetUserDefaultLangID())) {
728  case LANG_ARABIC:
729  langname = "ar";
730  break;
731  case LANG_CATALAN:
732  langname = "ca";
733  break;
734  case LANG_CZECH:
735  langname = "cs";
736  break;
737  case LANG_DANISH:
738  langname = "da";
739  break;
740  case LANG_GERMAN:
741  langname = "de";
742  break;
743  case LANG_GREEK:
744  langname = "el";
745  break;
746  case LANG_ENGLISH:
747  switch (SUBLANGID(GetUserDefaultLangID())) {
748  case SUBLANG_ENGLISH_UK:
749  langname = "en_GB";
750  break;
751  default:
752  langname = "en";
753  break;
754  }
755  break;
756  case LANG_SPANISH:
757  langname = "es";
758  break;
759  case LANG_ESTONIAN:
760  langname = "et";
761  break;
762  case LANG_FARSI:
763  langname = "fa";
764  break;
765  case LANG_FINNISH:
766  langname = "fi";
767  break;
768  case LANG_FRENCH:
769  langname = "fr";
770  break;
771  case LANG_HEBREW:
772  langname = "he";
773  break;
774  case LANG_HUNGARIAN:
775  langname = "hu";
776  break;
777  case LANG_ITALIAN:
778  langname = "it";
779  break;
780  case LANG_JAPANESE:
781  langname = "ja";
782  break;
783  case LANG_KOREAN:
784  langname = "ko";
785  break;
786  case LANG_LITHUANIAN:
787  langname = "lt";
788  break;
789  case LANG_DUTCH:
790  langname = "nl";
791  break;
792  case LANG_NORWEGIAN:
793  langname = "nb";
794  break;
795  case LANG_POLISH:
796  langname = "pl";
797  break;
798  case LANG_PORTUGUESE:
799  switch (SUBLANGID(GetUserDefaultLangID())) {
800  case SUBLANG_PORTUGUESE_BRAZILIAN:
801  langname = "pt_BR";
802  break;
803  default:
804  langname = "pt";
805  break;
806  }
807  break;
808  case LANG_ROMANIAN:
809  langname = "ro";
810  break;
811  case LANG_RUSSIAN:
812  langname = "ru";
813  break;
814  case LANG_SWEDISH:
815  langname = "sv";
816  break;
817  case LANG_TURKISH:
818  langname = "tr";
819  break;
820  case LANG_UKRAINIAN:
821  langname = "uk";
822  break;
823  case LANG_CHINESE:
824  langname = "zh_CN";
825  break;
826  }
827 
828  if (langname != nullptr) {
829  static char envstr[40];
830 
831  fc_snprintf(envstr, sizeof(envstr), "LANG=%s", langname);
832  putenv(envstr);
833  }
834  }
835 #endif // FREECIV_MSWINDOWS
836 #endif // ENABLE_NLS
837 
838  return langname;
839 }
840 
841 #ifdef FREECIV_ENABLE_NLS
845 static void autocap_update(void)
846 {
847  const char *autocap_opt_in[] = {"fi", nullptr};
848  int i;
849  bool ac_enabled = false;
850 
851  char *lang = getenv("LANG");
852 
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]) {
857  ac_enabled = true;
858  break;
859  }
860  }
861  }
862 
863  capitalization_opt_in(ac_enabled);
864 }
865 #endif // FREECIV_ENABLE_NLS
866 
871 void init_nls()
872 {
873  /*
874  * Setup the cached locale numeric formatting information. Defaults
875  * are as appropriate for the US.
876  */
877  grouping = fc_strdup("\3");
878  grouping_sep = fc_strdup(",");
879 
880 #ifdef ENABLE_NLS
881 
882 #ifdef FREECIV_MSWINDOWS
883  setup_langname(); // Makes sure LANG env variable has been set
884 #endif // FREECIV_MSWINDOWS
885 
886  (void) setlocale(LC_ALL, "");
887  (void) bindtextdomain("freeciv21-core", get_locale_dir());
888  (void) textdomain("freeciv21-core");
889 
890  /* Don't touch the defaults when LC_NUMERIC == "C".
891  This is intended to cater to the common case where:
892  1) The user is from North America. ;-)
893  2) The user has not set the proper environment variables.
894  (Most applications are (unfortunately) US-centric
895  by default, so why bother?)
896  This would result in the "C" locale being used, with grouping ""
897  and thousands_sep "", where we really want "\3" and ",". */
898 
899  if (strcmp(setlocale(LC_NUMERIC, nullptr), "C") != 0) {
900  struct lconv *lc = localeconv();
901 
902  if (lc->grouping[0] == '\0') {
903  // This actually indicates no grouping at all.
904  char *m = new char;
905  *m = CHAR_MAX;
906  grouping = m;
907  } else {
908  size_t len;
909  for (len = 0;
910  lc->grouping[len] != '\0' && lc->grouping[len] != CHAR_MAX;
911  len++) {
912  // nothing
913  }
914  len++;
915  delete[] grouping;
916  grouping = new char[len];
917  memcpy(grouping, lc->grouping, len);
918  }
919  delete[] grouping_sep;
920  grouping_sep = fc_strdup(lc->thousands_sep);
921  }
922 
923  autocap_update();
924 
925 #endif // ENABLE_NLS
926 }
927 
931 void free_nls()
932 {
933  delete[] grouping;
934  delete[] grouping_sep;
935  grouping = nullptr;
936  grouping_sep = nullptr;
937 }
938 
948 void dont_run_as_root(const char *argv0, const char *fallback)
949 {
950 #ifdef ALWAYS_ROOT
951  return;
952 #else
953  if (getuid() == 0 || geteuid() == 0) {
954  fc_fprintf(stderr,
955  _("%s: Fatal error: you're trying to run me as superuser!\n"),
956  (argv0 ? argv0
957  : fallback ? fallback
958  : "freeciv21"));
959  fc_fprintf(stderr, _("Use a non-privileged account instead.\n"));
960  exit(EXIT_FAILURE);
961  }
962 #endif // ALWAYS_ROOT
963 }
964 
973 const char *m_pre_description(enum m_pre_result result)
974 {
975  static const char *const descriptions[] = {
976  N_("exact match"), N_("only match"), N_("ambiguous"),
977  N_("empty"), N_("too long"), N_("non-match")};
978  fc_assert_ret_val(result >= 0 && result < ARRAY_SIZE(descriptions),
979  nullptr);
980  return descriptions[result];
981 }
982 
987  size_t n_names, size_t max_len_name,
988  m_pre_strncmp_fn_t cmp_fn,
989  m_strlen_fn_t len_fn, const char *prefix,
990  int *ind_result)
991 {
992  return match_prefix_full(accessor_fn, n_names, max_len_name, cmp_fn,
993  len_fn, prefix, ind_result, nullptr, 0, nullptr);
994 }
995 
1009  size_t n_names, size_t max_len_name,
1010  m_pre_strncmp_fn_t cmp_fn,
1011  m_strlen_fn_t len_fn, const char *prefix,
1012  int *ind_result, int *matches,
1013  int max_matches, int *pnum_matches)
1014 {
1015  int i, len, nmatches;
1016 
1017  if (len_fn == nullptr) {
1018  len = qstrlen(prefix);
1019  } else {
1020  len = len_fn(prefix);
1021  }
1022  if (len == 0) {
1023  return M_PRE_EMPTY;
1024  }
1025  if (len > max_len_name && max_len_name > 0) {
1026  return M_PRE_LONG;
1027  }
1028 
1029  nmatches = 0;
1030  for (i = 0; i < n_names; i++) {
1031  const char *name = accessor_fn(i);
1032 
1033  if (cmp_fn(name, prefix, len) == 0) {
1034  if (strlen(name) == len) {
1035  *ind_result = i;
1036  return M_PRE_EXACT;
1037  }
1038  if (nmatches == 0) {
1039  *ind_result = i; // first match
1040  }
1041  if (matches != nullptr && nmatches < max_matches) {
1042  matches[nmatches] = i;
1043  }
1044  nmatches++;
1045  }
1046  }
1047 
1048  if (nmatches == 1) {
1049  return M_PRE_ONLY;
1050  } else if (nmatches > 1) {
1051  if (pnum_matches != nullptr) {
1052  *pnum_matches = MIN(max_matches, nmatches);
1053  }
1054  return M_PRE_AMBIGUOUS;
1055  } else {
1056  return M_PRE_FAIL;
1057  }
1058 }
1059 
1065 char *get_multicast_group(bool ipv6_preferred)
1066 {
1067  static const char *default_multicast_group_ipv4 = "225.1.1.1";
1068  // TODO: Get useful group (this is node local)
1069  static const char *default_multicast_group_ipv6 = "FF31::8000:15B4";
1070 
1071  if (mc_group == nullptr) {
1072  char *env = getenv("FREECIV_MULTICAST_GROUP");
1073 
1074  if (env) {
1075  mc_group = fc_strdup(env);
1076  } else {
1077  if (ipv6_preferred) {
1078  mc_group = fc_strdup(default_multicast_group_ipv6);
1079  } else {
1080  mc_group = fc_strdup(default_multicast_group_ipv4);
1081  }
1082  }
1083  }
1084 
1085  return mc_group;
1086 }
1087 
1092 {
1093  delete[] mc_group;
1094  mc_group = nullptr;
1095 }
1096 
1100 QString interpret_tilde(const QString &filename)
1101 {
1102  if (filename == QLatin1String("~")) {
1103  return QDir::homePath();
1104  } else if (filename.startsWith(QLatin1String("~/"))) {
1105  return QDir::homePath() + filename.midRef(1);
1106  } else {
1107  return filename;
1108  }
1109 }
1110 
1115 bool make_dir(const QString &pathname)
1116 {
1117  // We can always create a directory with an empty name -- it's the current
1118  // folder.
1119  return pathname.isEmpty() || QDir().mkpath(interpret_tilde(pathname));
1120 }
1121 
1142 char scanin(char **buf, char *delimiters, char *dest, int size)
1143 {
1144  char *ptr, found = '?';
1145 
1146  if (*buf == nullptr || qstrlen(*buf) == 0 || size == 0) {
1147  if (dest) {
1148  dest[0] = '\0';
1149  }
1150  *buf = nullptr;
1151  return '\0';
1152  }
1153 
1154  if (dest) {
1155  qstrncpy(dest, *buf, size - 1);
1156  dest[size - 1] = '\0';
1158  ptr = strpbrk(dest, delimiters);
1159  } else {
1160  // Just skip ahead.
1161  ptr = strpbrk(*buf, delimiters);
1162  }
1163  if (ptr != nullptr) {
1164  found = *ptr;
1165  if (dest) {
1166  *ptr = '\0';
1167  }
1168  if (dest) {
1170  }
1171  *buf = strpbrk(*buf, delimiters);
1172  if (*buf != nullptr) {
1173  (*buf)++; // skip delimiter
1174  } else {
1175  }
1176  } else {
1177  *buf = nullptr;
1178  }
1179 
1180  return found;
1181 }
1182 
1187 void format_time_duration(time_t t, char *buf, int maxlen)
1188 {
1189  int seconds, minutes, hours, days;
1190  bool space = false;
1191 
1192  seconds = t % 60;
1193  minutes = (t / 60) % 60;
1194  hours = (t / (60 * 60)) % 24;
1195  days = t / (60 * 60 * 24);
1196 
1197  if (maxlen <= 0) {
1198  return;
1199  }
1200 
1201  buf[0] = '\0';
1202 
1203  if (days > 0) {
1204  cat_snprintf(buf, maxlen, "%d %s", days, PL_("day", "days", days));
1205  space = true;
1206  }
1207  if (hours > 0) {
1208  cat_snprintf(buf, maxlen, "%s%d %s", space ? " " : "", hours,
1209  PL_("hour", "hours", hours));
1210  space = true;
1211  }
1212  if (minutes > 0) {
1213  cat_snprintf(buf, maxlen, "%s%d %s", space ? " " : "", minutes,
1214  PL_("minute", "minutes", minutes));
1215  space = true;
1216  }
1217  if (seconds > 0) {
1218  cat_snprintf(buf, maxlen, "%s%d %s", space ? " " : "", seconds,
1219  PL_("second", "seconds", seconds));
1220  }
1221 }
1222 
1228 void array_shuffle(int *array, int n)
1229 {
1230  if (n > 1 && array != nullptr) {
1231  int i, j, t;
1232  for (i = 0; i < n - 1; i++) {
1233  j = i + fc_rand(n - i);
1234  t = array[j];
1235  array[j] = array[i];
1236  array[i] = t;
1237  }
1238  }
1239 }
1240 
1246 static bool wildcard_asterisk_fit(const char *pattern, const char *test)
1247 {
1248  char jump_to;
1249 
1250  // Jump over the leading asterisks.
1251  pattern++;
1252  while (true) {
1253  switch (*pattern) {
1254  case '\0':
1255  // It is a leading asterisk.
1256  return true;
1257  case '*':
1258  pattern++;
1259  continue;
1260  case '?':
1261  if ('\0' == *test) {
1262  return false;
1263  }
1264  test++;
1265  pattern++;
1266  continue;
1267  }
1268 
1269  break;
1270  }
1271 
1272  if ('[' != *pattern) {
1273  if ('\\' == *pattern) {
1274  jump_to = *(pattern + 1);
1275  } else {
1276  jump_to = *pattern;
1277  }
1278  } else {
1279  jump_to = '\0';
1280  }
1281 
1282  while ('\0' != *test) {
1283  if ('\0' != jump_to) {
1284  // Jump to next matching charather.
1285  test = strchr(test, jump_to);
1286  if (nullptr == test) {
1287  // No match.
1288  return false;
1289  }
1290  }
1291 
1292  if (wildcard_fit_string(pattern, test)) {
1293  return true;
1294  }
1295 
1296  (test)++;
1297  }
1298 
1299  return false;
1300 }
1301 
1306 static bool wildcard_range_fit(const char **pattern, const char **test)
1307 {
1308  const char *start = (*pattern + 1);
1309  char testc;
1310  bool negation;
1311 
1312  if ('\0' == **test) {
1313  // Need one character.
1314  return false;
1315  }
1316 
1317  // Find the end of the pattern.
1318  while (true) {
1319  *pattern = strchr(*pattern, ']');
1320  if (nullptr == *pattern) {
1321  // Wildcard format error.
1322  return false;
1323  } else if (*(*pattern - 1) != '\\') {
1324  // This is the end.
1325  break;
1326  } else {
1327  // Try again.
1328  (*pattern)++;
1329  }
1330  }
1331 
1332  if ('!' == *start) {
1333  negation = true;
1334  start++;
1335  } else {
1336  negation = false;
1337  }
1338  testc = **test;
1339  (*test)++;
1340  (*pattern)++;
1341 
1342  for (; start < *pattern; start++) {
1343  if ('-' == *start || '!' == *start) {
1344  // Wildcard format error.
1345  return false;
1346  } else if (start < *pattern - 2 && '-' == *(start + 1)) {
1347  // Case range.
1348  if (*start <= testc && testc <= *(start + 2)) {
1349  return !negation;
1350  }
1351  start += 2;
1352  } else if (*start == testc) {
1353  // Single character.
1354  return !negation;
1355  }
1356  }
1357 
1358  return negation;
1359 }
1360 
1372 bool wildcard_fit_string(const char *pattern, const char *test)
1373 {
1374  while (true) {
1375  switch (*pattern) {
1376  case '\0':
1377  // '/* '\0' != test. */' != test.
1378  return '\0' == *test;
1379  case '*':
1380  return wildcard_asterisk_fit(pattern, test); // Maybe recursive.
1381  case '[':
1382  if (!wildcard_range_fit(&pattern, &test)) {
1383  return false;
1384  }
1385  continue;
1386  case '?':
1387  if ('\0' == *test) {
1388  return false;
1389  }
1390  break;
1391  case '\\':
1392  pattern++;
1393  fc__fallthrough; // No break
1394  default:
1395  if (*pattern != *test) {
1396  return false;
1397  }
1398  break;
1399  }
1400  pattern++;
1401  test++;
1402  }
1403 
1404  return false;
1405 }
1406 
1421 int fc_vsnprintcf(char *buf, size_t buf_len, const char *format,
1422  const struct cf_sequence *sequences, size_t sequences_num)
1423 {
1424  const struct cf_sequence *pseq;
1425  char cformat[32];
1426  const char *f = format;
1427  char *const max = buf + buf_len - 1;
1428  char *b = buf, *c;
1429  const char *const cmax = cformat + sizeof(cformat) - 2;
1430  int i, j;
1431 
1432  if (static_cast<size_t>(-1) == sequences_num) {
1433  // Find the number of sequences.
1434  sequences_num = 0;
1435  for (pseq = sequences; CF_LAST != pseq->type; pseq++) {
1436  sequences_num++;
1437  }
1438  }
1439 
1440  while ('\0' != *f) {
1441  if ('%' == *f) {
1442  // Sequence.
1443 
1444  f++;
1445  if ('%' == *f) {
1446  // Double '%'.
1447  *b++ = '%';
1448  f++;
1449  continue;
1450  }
1451 
1452  // Make format.
1453  c = cformat;
1454  *c++ = '%';
1455  for (; !QChar::isLetter(*f) && '\0' != *f && '%' != *f && cmax > c;
1456  f++) {
1457  *c++ = *f;
1458  }
1459 
1460  if (!QChar::isLetter(*f)) {
1461  /* Beginning of a new sequence, end of the format, or too long
1462  * sequence. */
1463  *c = '\0';
1464  j = fc_snprintf(b, max - b + 1, "%s", cformat);
1465  if (-1 == j) {
1466  return -1;
1467  }
1468  b += j;
1469  continue;
1470  }
1471 
1472  for (i = 0, pseq = sequences; i < sequences_num; i++, pseq++) {
1473  if (pseq->letter == *f) {
1474  j = -2;
1475  switch (pseq->type) {
1476  case CF_BOOLEAN:
1477  *c++ = 's';
1478  *c = '\0';
1479  j = fc_snprintf(b, max - b + 1, cformat,
1480  pseq->bool_value ? "TRUE" : "FALSE");
1481  break;
1482  case CF_TRANS_BOOLEAN:
1483  *c++ = 's';
1484  *c = '\0';
1485  j = fc_snprintf(b, max - b + 1, cformat,
1486  pseq->bool_value ? _("TRUE") : _("FALSE"));
1487  break;
1488  case CF_CHARACTER:
1489  *c++ = 'c';
1490  *c = '\0';
1491  j = fc_snprintf(b, max - b + 1, cformat, pseq->char_value);
1492  break;
1493  case CF_INTEGER:
1494  *c++ = 'd';
1495  *c = '\0';
1496  j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
1497  break;
1498  case CF_HEXA:
1499  *c++ = 'x';
1500  *c = '\0';
1501  j = fc_snprintf(b, max - b + 1, cformat, pseq->int_value);
1502  break;
1503  case CF_FLOAT:
1504  *c++ = 'f';
1505  *c = '\0';
1506  j = fc_snprintf(b, max - b + 1, cformat, pseq->float_value);
1507  break;
1508  case CF_POINTER:
1509  *c++ = 'p';
1510  *c = '\0';
1511  j = fc_snprintf(b, max - b + 1, cformat, pseq->ptr_value);
1512  break;
1513  case CF_STRING:
1514  *c++ = 's';
1515  *c = '\0';
1516  j = fc_snprintf(b, max - b + 1, cformat, pseq->str_value);
1517  break;
1518  case CF_LAST:
1519  break;
1520  };
1521  if (-2 == j) {
1522  qCritical("Error: unsupported sequence type: %d.", pseq->type);
1523  break;
1524  }
1525  if (-1 == j) {
1526  // Full!
1527  return -1;
1528  }
1529  f++;
1530  b += j;
1531  break;
1532  }
1533  }
1534  if (i >= sequences_num) {
1535  // Format not supported.
1536  *c = '\0';
1537  j = fc_snprintf(b, max - b + 1, "%s%c", cformat, *f);
1538  if (-1 == j) {
1539  return -1;
1540  }
1541  f++;
1542  b += j;
1543  }
1544  } else {
1545  // Not a sequence.
1546  *b++ = *f++;
1547  }
1548  if (max <= b) {
1549  // Too long.
1550  *max = '\0';
1551  return -1;
1552  }
1553  }
1554  *b = '\0';
1555  return b - buf;
1556 }
1557 
1562 static size_t extract_escapes(const char *format, char *escapes,
1563  size_t max_escapes)
1564 {
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;
1569  size_t num = 0;
1570  int idx = 0;
1571 
1572  memset(escapes, 0, max_escapes);
1573  format = strchr(format, '%');
1574  while (nullptr != format) {
1575  format++;
1576  if ('%' == *format) {
1577  // Double, not a sequence.
1578  continue;
1579  } else if (QChar::isDigit(*format)) {
1580  const char *start = format;
1581 
1582  do {
1583  format++;
1584  } while (QChar::isDigit(*format));
1585  if ('$' == *format) {
1586  // Strings are reordered.
1587  if (1 != sscanf(start, "%d", &idx)) {
1588  reordered = true;
1589  } else {
1590  fc_assert_msg(false, "Invalid format string \"%s\"", format);
1591  }
1592  }
1593  }
1594 
1595  while ('\0' != *format && nullptr == strchr(format_escapes, *format)) {
1596  format++;
1597  }
1598  escapes[idx] = *format;
1599 
1600  // Increase the read count.
1601  if (reordered) {
1602  if (idx > num) {
1603  num = idx;
1604  }
1605  } else {
1606  idx++;
1607  num++;
1608  }
1609 
1610  if ('*' != *format) {
1611  format = strchr(format, '%');
1612  } // else we didn't have found the real sequence.
1613  }
1614  return num;
1615 }
1616 
1621 bool formats_match(const char *format1, const char *format2)
1622 {
1623  char format1_escapes[256], format2_escapes[256];
1624  size_t format1_escapes_num =
1625  extract_escapes(format1, format1_escapes, sizeof(format1_escapes));
1626  size_t format2_escapes_num =
1627  extract_escapes(format2, format2_escapes, sizeof(format2_escapes));
1628 
1629  return (
1630  format1_escapes_num == format2_escapes_num
1631  && 0 == memcmp(format1_escapes, format2_escapes, format1_escapes_num));
1632 }
void fc_fprintf(FILE *stream, const char *format,...)
Do a fprintf from the internal charset into the local charset.
Definition: fciconv.cpp:132
char * local_to_internal_string_buffer(const char *text, char *buf, size_t bufsz)
Definition: fciconv.cpp:120
const char * get_locale_dir()
Return directory containing locales.
Definition: fcintl.cpp:94
void capitalization_opt_in(bool opt_in)
Translation opts in to automatic capitalization features.
Definition: fcintl.cpp:84
#define PL_(String1, String2, n)
Definition: fcintl.h:54
#define _(String)
Definition: fcintl.h:50
#define N_(String)
Definition: fcintl.h:52
const char * name
Definition: inputfile.cpp:118
#define fc_assert_msg(condition, message,...)
Definition: log.h:96
#define fc_assert_ret(condition)
Definition: log.h:112
#define fc_assert(condition)
Definition: log.h:89
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
#define fc_assert_ret_val_msg(condition, val, message,...)
Definition: log.h:132
int len
Definition: packhand.cpp:127
#define fc_rand(_size)
Definition: rand.h:16
static size_t extract_escapes(const char *format, char *escapes, size_t max_escapes)
Extract the sequences of a format.
Definition: shared.cpp:1562
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,...
Definition: shared.cpp:1187
static bool wildcard_asterisk_fit(const char *pattern, const char *test)
Test an asterisk in the pattern against test.
Definition: shared.cpp:1246
char * skip_leading_spaces(char *s)
Returns 's' incremented to first non-space character.
Definition: shared.cpp:297
static char * grouping_sep
Definition: shared.cpp:82
static QStringList scenario_dir_names
Definition: shared.cpp:91
enum fc_tristate fc_tristate_and(enum fc_tristate one, enum fc_tristate two)
An AND function for fc_tristate.
Definition: shared.cpp:98
static char * grouping
Definition: shared.cpp:81
void dont_run_as_root(const char *argv0, const char *fallback)
If we have root privileges, die with an error.
Definition: shared.cpp:948
bool wildcard_fit_string(const char *pattern, const char *test)
Returns TRUE if test fit the pattern.
Definition: shared.cpp:1372
bool is_safe_filename(const QString &name)
Check if the name is safe security-wise.
Definition: shared.cpp:210
const QStringList & get_data_dirs()
Returns a list of data directory paths, in the order in which they should be searched.
Definition: shared.cpp:533
static QStringList save_dir_names
Definition: shared.cpp:90
bool check_strlen(const char *str, size_t len, const char *errmsg)
Check the length of the given string.
Definition: shared.cpp:364
const char * m_pre_description(enum m_pre_result result)
Return a description string of the result.
Definition: shared.cpp:973
char * setup_langname()
Language environmental variable (with emulation).
Definition: shared.cpp:717
void remove_trailing_spaces(char *s)
Terminates string pointed to by 's' to remove traling spaces; Note 's' must point to writeable memory...
Definition: shared.cpp:330
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.
Definition: shared.cpp:1421
const char * int_to_text(unsigned int number)
Return a prettily formatted string containing the given number.
Definition: shared.cpp:191
bool str_to_int(const char *str, int *pint)
Convert 'str' to it's int reprentation if possible.
Definition: shared.cpp:384
static const char base64url[]
Definition: shared.cpp:86
const QStringList & get_save_dirs()
Returns a list of save directory paths, in the order in which they should be searched.
Definition: shared.cpp:563
static QStringList base_get_dirs(const char *env_var)
Returns a list of directory paths, in the order in which they should be searched.
Definition: shared.cpp:506
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...
Definition: shared.cpp:117
static char * mc_group
Definition: shared.cpp:93
QString interpret_tilde(const QString &filename)
Interpret ~ in filename as home dir.
Definition: shared.cpp:1100
void free_nls()
Free memory allocated by Native Language Support.
Definition: shared.cpp:931
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.
Definition: shared.cpp:1142
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),...
Definition: shared.cpp:1008
static QStringList default_data_path()
Definition: shared.cpp:49
void init_nls()
Setup for Native Language Support, if configured to use it.
Definition: shared.cpp:871
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.
Definition: shared.cpp:373
static QStringList data_dir_names
Definition: shared.cpp:89
char * get_multicast_group(bool ipv6_preferred)
Returns string which gives the multicast group IP address for finding servers on the LAN,...
Definition: shared.cpp:1065
bool is_base64url(const char *s)
Check for valid base64url.
Definition: shared.cpp:258
void free_multicast_group()
Free multicast group resources.
Definition: shared.cpp:1091
static bool is_ascii(char ch)
Check whether or not the given char is a valid ascii character.
Definition: shared.cpp:200
QString fileinfoname(const QStringList &dirs, const QString &filename)
Returns a filename to access the specified file from a directory by searching all specified directori...
Definition: shared.cpp:661
static bool wildcard_range_fit(const char **pattern, const char **test)
Test a range in the pattern against test.
Definition: shared.cpp:1306
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...
Definition: shared.cpp:1621
static QStringList default_scenario_path()
Definition: shared.cpp:70
QVector< QString > * fileinfolist(const QStringList &dirs, const char *suffix)
Returns a string vector storing the filenames in the data directories matching the given suffix.
Definition: shared.cpp:623
void remove_leading_spaces(char *s)
Removes leading spaces in string pointed to by 's'.
Definition: shared.cpp:312
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...
Definition: shared.cpp:443
void array_shuffle(int *array, int n)
Randomize the elements of an array using the Fisher-Yates shuffle.
Definition: shared.cpp:1228
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().
Definition: shared.cpp:986
bool is_ascii_name(const char *name)
This is used in sundry places to make sure that names of cities, players etc.
Definition: shared.cpp:223
bool make_dir(const QString &pathname)
If the directory "pathname" does not exist, recursively create all directories until it does.
Definition: shared.cpp:1115
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.
Definition: shared.cpp:681
void randomize_base64url_string(char *s, size_t n)
generate a random string meeting criteria such as is_ascii_name(), is_base64url(),...
Definition: shared.cpp:279
static QStringList default_save_path()
Definition: shared.cpp:64
QString freeciv_storage_dir()
Returns string which gives freeciv storage dir.
Definition: shared.cpp:419
void remove_leading_trailing_spaces(char *s)
Removes leading and trailing spaces in string pointed to by 's'.
Definition: shared.cpp:353
const QStringList & get_scenario_dirs()
Returns a list of scenario directory paths, in the order in which they should be searched.
Definition: shared.cpp:594
fc_tristate
Definition: shared.h:42
@ TRI_YES
Definition: shared.h:42
@ TRI_NO
Definition: shared.h:42
@ TRI_MAYBE
Definition: shared.h:42
int(* m_pre_strncmp_fn_t)(const char *, const char *, size_t n)
Definition: shared.h:168
size_t() m_strlen_fn_t(const char *str)
Definition: shared.h:171
#define PARENT_DIR_OPERATOR
Definition: shared.h:108
#define ARRAY_SIZE(x)
Definition: shared.h:79
#define MIN(x, y)
Definition: shared.h:49
@ CF_POINTER
Definition: shared.h:219
@ CF_HEXA
Definition: shared.h:217
@ CF_CHARACTER
Definition: shared.h:215
@ CF_INTEGER
Definition: shared.h:216
@ CF_TRANS_BOOLEAN
Definition: shared.h:214
@ CF_LAST
Definition: shared.h:222
@ CF_BOOLEAN
Definition: shared.h:213
@ CF_STRING
Definition: shared.h:220
@ CF_FLOAT
Definition: shared.h:218
const char *(* m_pre_accessor_fn_t)(int)
Definition: shared.h:165
m_pre_result
Definition: shared.h:152
@ M_PRE_EXACT
Definition: shared.h:153
@ M_PRE_ONLY
Definition: shared.h:154
@ M_PRE_LONG
Definition: shared.h:157
@ M_PRE_AMBIGUOUS
Definition: shared.h:155
@ M_PRE_EMPTY
Definition: shared.h:156
@ M_PRE_FAIL
Definition: shared.h:158
size_t size
Definition: specvec.h:64
const void * ptr_value
Definition: shared.h:233
const char * str_value
Definition: shared.h:234
int int_value
Definition: shared.h:231
bool bool_value
Definition: shared.h:229
char letter
Definition: shared.h:227
float float_value
Definition: shared.h:232
char char_value
Definition: shared.h:230
enum cf_type type
Definition: shared.h:226
int fc_snprintf(char *str, size_t n, const char *format,...)
See also fc_utf8_snprintf_trunc(), fc_utf8_snprintf_rep().
Definition: support.cpp:537
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-...
Definition: support.cpp:412
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...
Definition: support.cpp:564
#define fc_strdup(str)
Definition: support.h:111
#define fc__fallthrough
Definition: support.h:49