Freeciv21
Develop your civilization from humble roots to a global empire
support.cpp
Go to the documentation of this file.
1 /*
2  /\ ___ /\ Copyright (c) 1996-2020 FREECIV 21 and Freeciv
3  ( o o ) 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
6  / \ ^ General Public License as published by the Free
7 | | // Software Foundation, either version 3 of the License,
8  \ / // or (at your option) any later version.
10  GNU General Public License along with Freeciv21.
11  If not, see https://www.gnu.org/licenses/.
12  */
13 
41 #include <fc_config.h>
42 
43 #include <cmath> // ceil()
44 #include <cstdarg>
45 #include <cstdio>
46 #include <cstdlib>
47 #include <cstring>
48 #include <sys/stat.h>
49 
50 #ifdef FREECIV_MSWINDOWS
51 #include <process.h>
52 #include <windows.h>
53 #endif // FREECIV_MSWINDOWS
54 #ifdef HAVE_STRINGS_H
55 #include <strings.h>
56 #endif
57 
58 // Qt
59 #include <QFileInfo>
60 #include <QHostInfo>
61 #include <QString>
62 #include <QThread>
63 
64 // utility
65 #include "fciconv.h"
66 #include "fcintl.h"
67 #include "log.h"
68 
69 #include "support.h"
70 
75 char *real_fc_strdup(const char *str, const char *called_as, int line,
76  const char *file)
77 {
78  Q_UNUSED(file)
79  char *dest = new char[strlen(str) + 1];
80 
81  // no need to check whether dest is non-nullptr (raises std::bad_alloc)
82  qstrcpy(dest, str);
83  return dest;
84 }
85 
89 int fc_strcasecmp(const char *str0, const char *str1)
90 {
91  auto left = QString::fromUtf8(str0);
92  auto right = QString::fromUtf8(str1);
93  return left.compare(right, Qt::CaseInsensitive);
94 }
95 
100 int fc_strncasecmp(const char *str0, const char *str1, size_t n)
101 {
102  auto left = QString::fromUtf8(str0);
103  auto right = QString::fromUtf8(str1);
104  return left.leftRef(n).compare(right.leftRef(n), Qt::CaseInsensitive);
105 }
106 
114 void make_escapes(const char *str, char *buf, size_t buf_len)
115 {
116  char *dest = buf;
117  /* Sometimes we insert 2 characters at once ('\n' -> "\\n"), so keep
118  * place for '\0' and an extra character. */
119  const char *const max = buf + buf_len - 2;
120 
121  while (*str != '\0' && dest < max) {
122  switch (*str) {
123  case '\n':
124  *dest++ = '\\';
125  *dest++ = 'n';
126  str++;
127  break;
128  case '\\':
129  case '\"':
130  *dest++ = '\\';
131  // Fallthrough.
132  default:
133  *dest++ = *str++;
134  break;
135  }
136  }
137  *dest = 0;
138 }
139 
149 QString remove_escapes(const QString &str, bool full_escapes)
150 {
151  QString copy;
152 
153  if (full_escapes) {
154  // Replace most everything
155  copy.reserve(str.length());
156 
157  bool escape = false;
158  for (const auto &c : str) {
159  if (escape && full_escapes) {
160  switch (c.unicode()) {
161  case 'n':
162  copy += '\n';
163  break;
164  case '\n':
165  // Remove the newline
166  break;
167  default:
168  copy += c;
169  }
170  escape = false;
171  } else if (c == '\\') {
172  escape = true;
173  } else {
174  copy += c;
175  }
176  }
177  } else {
178  // Replace only escaped newlines
179  copy = str;
180  copy.replace("\\\n", "\n");
181  }
182 
183  return copy;
184 }
185 
189 size_t effectivestrlenquote(const char *str)
190 {
191  int len;
192  if (!str) {
193  return 0;
194  }
195 
196  len = qstrlen(str);
197 
198  if (str[0] == '"' && str[len - 1] == '"') {
199  return len - 2;
200  }
201 
202  return len;
203 }
204 
209 int fc_strncasequotecmp(const char *str0, const char *str1, size_t n)
210 {
211  auto left = QString::fromUtf8(str0);
212  auto right = QString::fromUtf8(str1);
213  if (left.startsWith(QLatin1String("\""))
214  && left.endsWith(QLatin1String("\""))) {
215  left = left.mid(1, left.length() - 2);
216  }
217  if (right.startsWith(QLatin1String("\""))
218  && right.endsWith(QLatin1String("\""))) {
219  right = right.mid(1, right.length() - 2);
220  }
221  return left.leftRef(n).compare(right.leftRef(n), Qt::CaseInsensitive);
222 }
223 
227 int fc_strcoll(const char *str0, const char *str1)
228 {
229  return strcoll(str0, str1);
230 }
231 
235 int fc_stricoll(const char *str0, const char *str1)
236 {
237  /* We prefer _stricoll() over stricoll() since
238  * latter is not declared in MinGW headers causing compiler
239  * warning, preventing -Werror builds. */
240 #if defined(ENABLE_NLS) && defined(HAVE__STRICOLL)
241  return _stricoll(str0, str1);
242 #elif defined(ENABLE_NLS) && defined(HAVE_STRICOLL)
243  return stricoll(str0, str1);
244 #elif defined(ENABLE_NLS) && defined(HAVE_STRCASECOLL)
245  return strcasecoll(str0, str1);
246 #else
247  return fc_strcasecmp(str0, str1);
248 #endif
249 }
250 
255 FILE *fc_fopen(const char *filename, const char *opentype)
256 {
257 #ifdef FREECIV_MSWINDOWS
258  FILE *result;
259  char *filename_in_local_encoding =
261 
262  result = fopen(filename_in_local_encoding, opentype);
263  free(filename_in_local_encoding);
264  return result;
265 #else // FREECIV_MSWINDOWS
266  return fopen(filename, opentype);
267 #endif // FREECIV_MSWINDOWS
268 }
269 
274 int fc_remove(const char *filename)
275 {
276 #ifdef FREECIV_MSWINDOWS
277  int result;
278  char *filename_in_local_encoding =
280 
281  result = remove(filename_in_local_encoding);
282  free(filename_in_local_encoding);
283  return result;
284 #else // FREECIV_MSWINDOWS
285  return remove(filename);
286 #endif // FREECIV_MSWINDOWS
287 }
288 
293 int fc_stat(const char *filename, struct stat *buf)
294 {
295 #ifdef FREECIV_MSWINDOWS
296  int result;
297  char *filename_in_local_encoding =
299 
300  result = stat(filename_in_local_encoding, buf);
301  free(filename_in_local_encoding);
302  return result;
303 #else // FREECIV_MSWINDOWS
304  return stat(filename, buf);
305 #endif // FREECIV_MSWINDOWS
306 }
307 
312 {
313 #ifdef FREECIV_MSWINDOWS
314  return GetLastError();
315 #else // FREECIV_MSWINDOWS
316  return errno;
317 #endif // FREECIV_MSWINDOWS
318 }
319 
328 const char *fc_strerror(fc_errno err)
329 {
330 #ifdef FREECIV_MSWINDOWS
331  static char buf[256];
332 
333  if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
334  | FORMAT_MESSAGE_IGNORE_INSERTS,
335  nullptr, err, 0, buf, sizeof(buf), nullptr)) {
336  fc_snprintf(buf, sizeof(buf), _("error %ld (failed FormatMessage)"),
337  err);
338  }
339  return buf;
340 #else // FREECIV_MSWINDOWS
341  static char buf[256];
342  return local_to_internal_string_buffer(strerror(err), buf, sizeof(buf));
343 #endif // FREECIV_MSWINDOWS
344 }
345 
349 void fc_usleep(unsigned long usec) { QThread::usleep(usec); }
350 
356 bool fc_strrep(char *str, size_t len, const char *search,
357  const char *replace)
358 {
359  size_t len_search, len_replace;
360  char *s, *p;
361 
362  fc_assert_ret_val(str != nullptr, false);
363  if (search == nullptr || replace == nullptr) {
364  return true;
365  }
366 
367  len_search = qstrlen(search);
368  len_replace = qstrlen(replace);
369 
370  s = str;
371  while (s != nullptr) {
372  p = strstr(s, search);
373  if (p == nullptr) {
374  // nothing found
375  break;
376  }
377 
378  if (len < (strlen(str) + len_replace - len_search + 1)) {
379  // sizeof(str) not large enough to do the replacement
380  return false;
381  }
382 
383  memmove(p + len_replace, p + len_search, qstrlen(p + len_search) + 1);
384  memcpy(p, replace, len_replace);
385  s = p + len_replace;
386  }
387 
388  return true;
389 }
390 
412 size_t fc_strlcpy(char *dest, const char *src, size_t n)
413 {
414  fc_assert_ret_val(nullptr != dest, -1);
415  fc_assert_ret_val(nullptr != src, -1);
416  fc_assert_ret_val(0 < n, -1);
417 
418  auto source = QString::fromUtf8(src);
419 
420  // Strategy: cut the string after n-1 code points, and encode. If the
421  // encoded version is too long to copy to the output buffer, remove the
422  // last character and repeat.
423  size_t cut_at = n - 1;
424  QByteArray encoded;
425  do {
426  encoded = source.leftRef(cut_at--).toUtf8();
427  } while (cut_at > 0 && encoded.size() + 1 > n);
428 
429  if (cut_at == 0) {
430  // Can't put anything in the buffer except the \0
431  // Perhaps there's a character with diacritics taking many bytes at the
432  // beginning, or the size of the buffer is just 1.
433  *dest = '\0';
434  return 1;
435  } else {
436  // Can put something
437  memcpy(dest, encoded.data(), encoded.size() + 1);
438  return encoded.size() + 1;
439  }
440 }
441 
448 size_t fc_strlcat(char *dest, const char *src, size_t n)
449 {
450  size_t start;
451 
452  start = qstrlen(dest);
453 
454  fc_assert(start < n);
455 
456  return fc_strlcpy(dest + start, src, n - start) + start;
457 }
458 
510 // "64k should be big enough for anyone" ;-)
511 #define VSNP_BUF_SIZE (64 * 1024)
512 int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
513 {
514  int r;
515 
516  /* This may be overzealous, but I suspect any triggering of these to
517  * be bugs. */
518 
519  fc_assert_ret_val(nullptr != str, -1);
520  fc_assert_ret_val(0 < n, -1);
521  fc_assert_ret_val(nullptr != format, -1);
522 
523  r = vsnprintf(str, n, format, ap);
524  str[n - 1] = 0;
525 
526  // Convert C99 return value to C89.
527  if (r >= n) {
528  return -1;
529  }
530 
531  return r;
532 }
533 
537 int fc_snprintf(char *str, size_t n, const char *format, ...)
538 {
539  int ret;
540  va_list ap;
541 
542  fc_assert_ret_val(nullptr != format, -1);
543 
544  va_start(ap, format);
545  ret = fc_vsnprintf(str, n, format, ap);
546  va_end(ap);
547  return ret;
548 }
549 
564 int cat_snprintf(char *str, size_t n, const char *format, ...)
565 {
566  size_t len;
567  int ret;
568  va_list ap;
569 
570  fc_assert_ret_val(nullptr != format, -1);
571  fc_assert_ret_val(nullptr != str, -1);
572  fc_assert_ret_val(0 < n, -1);
573 
574  len = qstrlen(str);
575  fc_assert_ret_val(len < n, -1);
576 
577  va_start(ap, format);
578  ret = fc_vsnprintf(str + len, n - len, format, ap);
579  va_end(ap);
580  return (-1 == ret ? -1 : ret + len);
581 }
582 
586 int fc_gethostname(char *buf, size_t len)
587 {
588  auto name = QHostInfo::localHostName();
589  fc_strlcpy(buf, name.toUtf8().data(), len);
590  return 0;
591 }
592 
597 int fc_break_lines(char *str, size_t desired_len)
598 {
599  size_t slen = static_cast<size_t>(qstrlen(str));
600  int num_lines = 0;
601  bool not_end = true;
602  /* At top of this loop, s points to the rest of string,
603  * either at start or after inserted newline: */
604  do {
605  bool double_break = false;
606  if (str && *str != '\0' && slen > desired_len) {
607  char *c;
608 
609  num_lines++;
610 
611  // check if there is already a newline:
612  for (c = str; c < str + desired_len; c++) {
613  if (*c == '\n') {
614  slen -= c + 1 - str;
615  str = c + 1;
616  double_break = true;
617  break;
618  }
619  }
620  if (double_break) {
621  continue;
622  }
623 
624  // find space and break:
625  for (c = str + desired_len; c > str; c--) {
626  if (QChar::isSpace(*c)) {
627  *c = '\n';
628  slen -= c + 1 - str;
629  str = c + 1;
630  double_break = true;
631  break;
632  }
633  }
634  if (double_break) {
635  continue;
636  }
637 
638  // couldn't find a good break; settle for a bad one...
639  for (c = str + desired_len + 1; *c != '\0'; c++) {
640  if (QChar::isSpace(*c)) {
641  *c = '\n';
642  slen -= c + 1 - str;
643  str = c + 1;
644  break;
645  }
646  }
647  }
648  not_end = false;
649  } while (not_end);
650 
651  return num_lines;
652 }
653 
657 int fc_at_quick_exit(void (*func)())
658 {
659 #ifdef HAVE_AT_QUICK_EXIT
660  return at_quick_exit(func);
661 #else // HAVE_AT_QUICK_EXIT
662  return -1;
663 #endif // HAVE_AT_QUICK_EXIT
664 }
char * local_to_internal_string_buffer(const char *text, char *buf, size_t bufsz)
Definition: fciconv.cpp:120
char * internal_to_local_string_malloc(const char *text)
Definition: fciconv.cpp:106
#define _(String)
Definition: fcintl.h:50
const char * name
Definition: inputfile.cpp:118
get_token_fn_t func
Definition: inputfile.cpp:119
#define fc_assert(condition)
Definition: log.h:89
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
int len
Definition: packhand.cpp:127
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
int fc_gethostname(char *buf, size_t len)
Call gethostname() if supported, else just returns -1.
Definition: support.cpp:586
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
void make_escapes(const char *str, char *buf, size_t buf_len)
Copies a string and convert the following characters:
Definition: support.cpp:114
int fc_strcasecmp(const char *str0, const char *str1)
Compare strings like strcmp(), but ignoring case.
Definition: support.cpp:89
void fc_usleep(unsigned long usec)
Suspend execution for the specified number of microseconds.
Definition: support.cpp:349
char * real_fc_strdup(const char *str, const char *called_as, int line, const char *file)
Function used by fc_strdup macro, strdup() replacement No need to check return value.
Definition: support.cpp:75
size_t fc_strlcat(char *dest, const char *src, size_t n)
fc_strlcat() provides utf-8 version of (non-standard) function strlcat() It is intended as more user-...
Definition: support.cpp:448
bool fc_strrep(char *str, size_t len, const char *search, const char *replace)
Replace 'search' by 'replace' within 'str'.
Definition: support.cpp:356
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
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition: support.cpp:512
const char * fc_strerror(fc_errno err)
Return a string which describes a given error (errno-style.) The string is converted as necessary fro...
Definition: support.cpp:328
QString remove_escapes(const QString &str, bool full_escapes)
Copies a string.
Definition: support.cpp:149
int fc_break_lines(char *str, size_t desired_len)
Replace the spaces by line breaks when the line lenght is over the desired one.
Definition: support.cpp:597
int fc_strcoll(const char *str0, const char *str1)
Wrapper function for strcoll().
Definition: support.cpp:227
int fc_stat(const char *filename, struct stat *buf)
Wrapper function for stat() with filename conversion to local encoding on Windows.
Definition: support.cpp:293
int fc_at_quick_exit(void(*func)())
Set quick_exit() callback if possible.
Definition: support.cpp:657
int fc_strncasequotecmp(const char *str0, const char *str1, size_t n)
Compare strings like strncasecmp() but ignoring surrounding quotes in either string.
Definition: support.cpp:209
FILE * fc_fopen(const char *filename, const char *opentype)
Wrapper function for fopen() with filename conversion to local encoding on Windows.
Definition: support.cpp:255
size_t effectivestrlenquote(const char *str)
Count length of string without possible surrounding quotes.
Definition: support.cpp:189
int fc_stricoll(const char *str0, const char *str1)
Wrapper function for stricoll().
Definition: support.cpp:235
int fc_remove(const char *filename)
Wrapper function for remove() with filename conversion to local encoding on Windows.
Definition: support.cpp:274
int fc_strncasecmp(const char *str0, const char *str1, size_t n)
Compare strings like strncmp(), but ignoring case.
Definition: support.cpp:100
fc_errno fc_get_errno()
Returns last error code.
Definition: support.cpp:311
int fc_errno
Definition: support.h:55