Xonotic
string.qh
Go to the documentation of this file.
1 #pragma once
2 
3 #include "nil.qh"
4 #include "sort.qh"
5 #include "oo.qh"
6 
7 // string logic
8 //
9 // true: is truthy
10 // == "": is equal to ""
11 // is "": has the same string index as the string constant ""
12 // strunzone: can be strunzoned
13 //
14 // | | true | == "" | is "" | strunzone |
15 // | :----------: | :--: | :---: | :---: | :-------: |
16 // | nil | | yes | | |
17 // | strcat(nil) | yes | yes | | |
18 // | strzone(nil) | yes | yes | | yes |
19 // | "" | yes | yes | yes | |
20 // | strcat("") | yes | yes | | |
21 // | strzone("") | yes | yes | | yes |
22 // | "s" | yes | | | |
23 // | strcat("s") | yes | | | |
24 // | strzone("s") | yes | | | yes |
25 
26 #ifdef CSQC
27  float stringwidth_colors(string s, vector theSize)
28  {
29  return stringwidth_builtin(s, true, theSize);
30  }
31 
32  float stringwidth_nocolors(string s, vector theSize)
33  {
34  return stringwidth_builtin(s, false, theSize);
35  }
36 #endif
37 #ifdef MENUQC
38  float stringwidth_colors(string s, vector theSize)
39  {
40  return stringwidth(s, true, theSize);
41  }
42 
43  float stringwidth_nocolors(string s, vector theSize)
44  {
45  return stringwidth(s, false, theSize);
46  }
47 #endif
48 
49 #define strcpy(this, s) MACRO_BEGIN \
50  if (this) { \
51  strunzone(this); \
52  } \
53  this = strzone(s); \
54 MACRO_END
55 
56 #define strfree(this) MACRO_BEGIN \
57  if (this) { \
58  strunzone(this); \
59  } \
60  this = string_null; \
61 MACRO_END
62 
63 // Returns the number of days since 0000-03-01 (March 1, year 0)
64 // Starting counting from March, as the 1st month of the year, February becomes the 12th and last month,
65 // so its variable duration does not affect, given that the 29th is the last day of the period
67 int days_up_to_date(int Y, int M, int D)
68 {
69  int years = (M <= 2) ? Y - 1 : Y;
70 
71  int leap_days = floor(years / 4) - floor(years / 100) + floor(years / 400);
72 
73  // using these 2 formulas to save 2 arrays or switches (performance isn't important here)
74  int months = (M <= 2) ? (M + 9) : (M - 3); // 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
75  int leftover_days = (M <= 2) ? (M + 5) : floor(0.58 * M - 1.1); // 6, 7, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5
76 
77  int month_days = 30 * months + leftover_days;
78 
79  return 365 * years + month_days + D + leap_days;
80 }
81 
82 #define DAYS_UP_TO_EPOCH 719469 // days_up_to_date(1970, 1, 1);
83 
84 // Returns the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
85 // This function exists only as a replacement for strftime(false, "%s") which doesn't work
86 // on Windows (%s is not supported) and at least in some linux systems doesn't return the
87 // correct result
88 // NOTE: at the current date, the number (string) returned by both strftime(false, "%s") and
89 // strftime_s() is so high that can't be converted to int (with ftos) without precision loss
91 string strftime_s()
92 {
93  string date = strftime(false, "%Y-%m-%d %H:%M:%S");
94  int i, seconds = 0;
95  i =0; int Y = stof(substring(date, i, 4)); // years
96  i+=5; int M = stof(substring(date, i, 2)); // months
97  i+=3; int D = stof(substring(date, i, 2)); // days
98 
99  i+=3; seconds += stof(substring(date, i, 2)) * 60 * 60; // hours
100  i+=3; seconds += stof(substring(date, i, 2)) * 60; // minutes
101  i+=3; seconds += stof(substring(date, i, 2)); // seconds
102 
103  // doing so we loose precision
104  //seconds += (days_up_to_date(Y, M, D) - DAYS_UP_TO_EPOCH) * 24 * 60 * 60;
105  //return ftos(seconds);
106 
107  int days_since_epoch = days_up_to_date(Y, M, D) - DAYS_UP_TO_EPOCH;
108  // use hundreds of seconds as unit to avoid precision loss
109  int hundreds_of_seconds = days_since_epoch * 24 * 6 * 6;
110  hundreds_of_seconds += floor(seconds / 100);
111 
112  // tens of seconds and seconds
113  string seconds_str = ftos(seconds % 100);
114  if ((seconds % 100) < 10)
115  seconds_str = strcat("0", seconds_str);
116 
117  return strcat(ftos(hundreds_of_seconds), seconds_str);
118 }
119 
122 ERASEABLE
123 string seconds_tostring(float seconds)
124 {
125  bool negative = false;
126  if (seconds < 0)
127  {
128  negative = true;
129  seconds = -seconds;
130  if (floor(seconds) != seconds)
131  seconds += 1; // make floor work in the other direction
132  }
133  int minutes = floor(seconds / 60);
134  seconds -= minutes * 60;
135  if (negative)
136  return sprintf("-%d:%02d", minutes, seconds);
137  return sprintf("%d:%02d", minutes, seconds);
138 }
139 
143 ERASEABLE
144 string clockedtime_tostring(int tm, bool hundredths)
145 {
146  if (tm < 0)
147  return strcat("0:00:0", hundredths ? "0" : "");
148  int acc = hundredths ? 6000 : 600;
149  int seconds = floor(tm + 0.5);
150  int minutes = floor(seconds / acc);
151  seconds -= minutes * acc;
152  // NOTE: the start digit of s is a placeholder and won't be displayed
153  string s = ftos(acc * 10 + seconds);
154  return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, hundredths ? 2 : 1));
155 }
156 
157 #define mmsst(tm) clockedtime_tostring(tm, false)
158 #define mmssth(tm) clockedtime_tostring(tm, true)
159 
160 ERASEABLE
161 string format_time(float seconds)
162 {
163  seconds = floor(seconds + 0.5);
164  float days = floor(seconds / 864000);
165  seconds -= days * 864000;
166  float hours = floor(seconds / 36000);
167  seconds -= hours * 36000;
168  float minutes = floor(seconds / 600);
169  seconds -= minutes * 600;
170  if (days > 0) return sprintf(_("%d days, %02d:%02d:%02d"), days, hours, minutes, seconds);
171  else return sprintf(_("%02d:%02d:%02d"), hours, minutes, seconds);
172 }
173 
175 
176 ERASEABLE
177 string ColorTranslateRGB(string s)
178 {
179  return (ColorTranslateMode & 1) ? strdecolorize(s) : s;
180 }
181 
182 #ifdef GAMEQC
183 // color code replace, place inside of sprintf and parse the string... defaults described as constants
184 // foreground/normal colors
185 string autocvar_hud_colorset_foreground_1 = "2"; // F1 - Green // primary priority (important names, etc)
186 string autocvar_hud_colorset_foreground_2 = "3"; // F2 - Yellow // secondary priority (items, locations, numbers, etc)
187 string autocvar_hud_colorset_foreground_3 = "4"; // F3 - Blue // tertiary priority or relatively inconsequential text
188 string autocvar_hud_colorset_foreground_4 = "1"; // F4 - Red // notice/attention grabbing texting
189 // "kill" colors
190 string autocvar_hud_colorset_kill_1 = "1"; // K1 - Red // "bad" or "dangerous" text (death messages against you, kill notifications, etc)
191 string autocvar_hud_colorset_kill_2 = "3"; // K2 - Yellow // similar to above, but less important... OR, a highlight out of above message type
192 string autocvar_hud_colorset_kill_3 = "4"; // K3 - Blue // "good" or "beneficial" text (you fragging someone, etc)
193 // background color
194 string autocvar_hud_colorset_background = "7"; // BG - White // neutral/unimportant text
195 
197 string CCR(string input)
198 {
199  // foreground/normal colors
200  input = strreplace("^F1", strcat("^", autocvar_hud_colorset_foreground_1), input);
201  input = strreplace("^F2", strcat("^", autocvar_hud_colorset_foreground_2), input);
202  input = strreplace("^F3", strcat("^", autocvar_hud_colorset_foreground_3), input);
203  input = strreplace("^F4", strcat("^", autocvar_hud_colorset_foreground_4), input);
204 
205  // "kill" colors
206  input = strreplace("^K1", strcat("^", autocvar_hud_colorset_kill_1), input);
207  input = strreplace("^K2", strcat("^", autocvar_hud_colorset_kill_2), input);
208  input = strreplace("^K3", strcat("^", autocvar_hud_colorset_kill_3), input);
209 
210  // background colors
211  input = strreplace("^BG", strcat("^", autocvar_hud_colorset_background), input);
212  input = strreplace("^N", "^7", input); // "none"-- reset to white...
213  return input;
214 }
215 #endif
216 
217 #define startsWith(haystack, needle) (strstrofs(haystack, needle, 0) == 0)
218 
219 ERASEABLE
220 bool startsWithNocase(string haystack, string needle)
221 {
222  return strcasecmp(substring(haystack, 0, strlen(needle)), needle) == 0;
223 }
224 
225 noref string _endsWith_suffix;
226 #define endsWith(this, suffix) (_endsWith_suffix = suffix, substring(this, -strlen(_endsWith_suffix), -1) == _endsWith_suffix)
227 
229 ERASEABLE
230 string fstrunzone(string s)
231 {
232  if (!s) return s;
233  string sc = strcat(s, "");
234  strunzone(s);
235  return sc;
236 }
237 
239 ERASEABLE
240 string car(string s)
241 {
242  int o = strstrofs(s, " ", 0);
243  if (o < 0) return s;
244  return substring(s, 0, o);
245 }
246 
248 ERASEABLE
249 string cdr(string s)
250 {
251  int o = strstrofs(s, " ", 0);
252  if (o < 0) return string_null;
253  return substring(s, o + 1, strlen(s) - (o + 1));
254 }
255 
256 ERASEABLE
257 string cons(string a, string b)
258 {
259  if (a == "") return b;
260  if (b == "") return a;
261  return strcat(a, " ", b);
262 }
263 
264 ERASEABLE
265 string cons_mid(string a, string mid, string b)
266 {
267  if (a == "") return b;
268  if (b == "") return a;
269  return strcat(a, mid, b);
270 }
271 
272 ERASEABLE
273 string substring_range(string s, float b, float e)
274 {
275  return substring(s, b, e - b);
276 }
277 
278 ERASEABLE
279 string swapwords(string str, float i, float j)
280 {
281  float n;
282  string s1, s2, s3, s4, s5;
283  float si, ei, sj, ej, s0, en;
284  n = tokenizebyseparator(str, " "); // must match g_maplist processing in ShuffleMaplist and "shuffle"
285  si = argv_start_index(i);
286  sj = argv_start_index(j);
287  ei = argv_end_index(i);
288  ej = argv_end_index(j);
289  s0 = argv_start_index(0);
290  en = argv_end_index(n - 1);
291  s1 = substring_range(str, s0, si);
292  s2 = substring_range(str, si, ei);
293  s3 = substring_range(str, ei, sj);
294  s4 = substring_range(str, sj, ej);
295  s5 = substring_range(str, ej, en);
296  return strcat(s1, s4, s3, s2, s5);
297 }
298 
300 ERASEABLE
301 void _shufflewords_swapfunc(float i, float j, entity pass)
302 {
304 }
305 
306 ERASEABLE
307 string shufflewords(string str)
308 {
309  _shufflewords_str = str;
310  int n = tokenizebyseparator(str, " ");
312  str = _shufflewords_str;
314  return str;
315 }
316 
317 ERASEABLE
318 string unescape(string in)
319 {
320  in = strzone(in); // but it doesn't seem to be necessary in my tests at least
321 
322  int len = strlen(in);
323  string str = "";
324  for (int i = 0; i < len; ++i)
325  {
326  string s = substring(in, i, 1);
327  if (s == "\\")
328  {
329  s = substring(in, i + 1, 1);
330  if (s == "n") str = strcat(str, "\n");
331  else if (s == "\\") str = strcat(str, "\\");
332  else str = strcat(str, substring(in, i, 2));
333  ++i;
334  continue;
335  }
336  str = strcat(str, s);
337  }
338  strunzone(in);
339  return str;
340 }
341 
342 ERASEABLE
343 string strwords(string s, int w)
344 {
345  int endpos = 0;
346  for ( ; w && endpos >= 0; --w)
347  endpos = strstrofs(s, " ", endpos + 1);
348  if (endpos < 0) return s;
349  return substring(s, 0, endpos);
350 }
351 
352 #define strhasword(s, w) (strstrofs(strcat(" ", s, " "), strcat(" ", w, " "), 0) >= 0)
353 
354 ERASEABLE
355 int u8_strsize(string s)
356 {
357  int l = 0;
358  for (int i = 0, c; (c = str2chr(s, i)) > 0; ++i, ++l)
359  {
360  l += (c >= 0x80);
361  l += (c >= 0x800);
362  l += (c >= 0x10000);
363  }
364  return l;
365 }
366 
367 // List of Unicode spaces: http://jkorpela.fi/chars/spaces.html
368 ERASEABLE
369 bool isInvisibleString(string s)
370 {
371  s = strdecolorize(s);
372  bool utf8 = cvar("utf8_enable");
373  for (int i = 0, n = strlen(s); i < n; ++i)
374  {
375  int c = str2chr(s, i);
376  switch (c)
377  {
378  case 0:
379  case 32: // space
380  break;
381  case 192: // charmap space
382  if (!utf8) break;
383  return false;
384  case 0xE000: // invisible char of the utf8 quake charmap
385  case 0xE00A: // invisible char of the utf8 quake charmap
386  case 0xE0A0: // invisible char of the utf8 quake charmap
387  case 0xE020: // invisible char of the utf8 quake charmap
388  case 0x00A0: // NO-BREAK SPACE
389  //case 0x1680: // OGHAM SPACE MARK
390  case 0x180E: // MONGOLIAN VOWEL SEPARATOR
391  case 0x2000: // EN QUAD
392  case 0x2001: // EM QUAD
393  case 0x2002: // EN SPACE
394  case 0x2003: // EM SPACE
395  case 0x2004: // THREE-PER-EM SPACE
396  case 0x2005: // FOUR-PER-EM SPACE
397  case 0x2006: // SIX-PER-EM SPACE
398  case 0x2007: // FIGURE SPACE
399  case 0x2008: // PUNCTUATION SPACE
400  case 0x2009: // THIN SPACE
401  case 0x200A: // HAIR SPACE
402  case 0x200B: // ZERO WIDTH SPACE
403  case 0x202F: // NARROW NO-BREAK SPACE
404  case 0x205F: // MEDIUM MATHEMATICAL SPACE
405  case 0x3000: // IDEOGRAPHIC SPACE
406  case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
407  if (utf8) break;
408  default:
409  return false;
410  }
411  }
412  return true;
413 }
414 
415 // Multiline text file buffers
416 
417 ERASEABLE
418 int buf_load(string pFilename)
419 {
420  int buf = buf_create();
421  if (buf < 0) return -1;
422  int fh = fopen(pFilename, FILE_READ);
423  if (fh < 0)
424  {
425  buf_del(buf);
426  return -1;
427  }
428  string l;
429  for (int i = 0; (l = fgets(fh)); ++i)
430  bufstr_set(buf, i, l);
431  fclose(fh);
432  return buf;
433 }
434 
435 ERASEABLE
436 void buf_save(float buf, string pFilename)
437 {
438  int fh = fopen(pFilename, FILE_WRITE);
439  if (fh < 0) error(strcat("Can't write buf to ", pFilename));
440  int n = buf_getsize(buf);
441  for (int i = 0; i < n; ++i)
442  fputs(fh, strcat(bufstr_get(buf, i), "\n"));
443  fclose(fh);
444 }
445 
449 ERASEABLE
450 string ftos_decimals(float number, int decimals)
451 {
452  // inhibit stupid negative zero
453  if (number == 0) number = 0;
454  return sprintf("%.*f", decimals, number);
455 }
456 
460 ERASEABLE
462 {
463  // inhibit stupid negative zero
464  if (number == 0) number = 0;
465  return sprintf("%.7g", number);
466 }
467 
468 ERASEABLE
469 int vercmp_recursive(string v1, string v2)
470 {
471  int dot1 = strstrofs(v1, ".", 0);
472  int dot2 = strstrofs(v2, ".", 0);
473  string s1 = (dot1 == -1) ? v1 : substring(v1, 0, dot1);
474  string s2 = (dot2 == -1) ? v2 : substring(v2, 0, dot2);
475 
476  float r;
477  r = stof(s1) - stof(s2);
478  if (r != 0) return r;
479 
480  r = strcasecmp(s1, s2);
481  if (r != 0) return r;
482 
483  if (dot1 == -1) return (dot2 == -1) ? 0 : -1;
484  else return (dot2 == -1) ? 1 : vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999));
485 }
486 
487 ERASEABLE
488 int vercmp(string v1, string v2)
489 {
490  if (strcasecmp(v1, v2) == 0) return 0; // early out check
491 
492  // "git" beats all
493  if (v1 == "git") return 1;
494  if (v2 == "git") return -1;
495 
496  return vercmp_recursive(v1, v2);
497 }
498 
499 const string HEXDIGITS_MINSET = "0123456789ABCDEFabcdef";
500 const string HEXDIGITS = "0123456789ABCDEF0123456789abcdef";
501 #define HEXDIGIT_TO_DEC_RAW(d) (strstrofs(HEXDIGITS, (d), 0))
502 #define HEXDIGIT_TO_DEC(d) ((HEXDIGIT_TO_DEC_RAW(d) | 0x10) - 0x10)
503 #define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS_MINSET, (d), 1))
504 #define IS_HEXDIGIT(d) (strstrofs(HEXDIGITS_MINSET, (d), 0) >= 0)
505 
506 const string DIGITS = "0123456789";
507 #define IS_DIGIT(d) (strstrofs(DIGITS, (d), 0) >= 0)
508 
509 // returns true if the caret at position pos is escaped
510 ERASEABLE
511 bool isCaretEscaped(string theText, float pos)
512 {
513  // count all the previous carets
514  int carets = 0;
515  while(pos - carets >= 1 && substring(theText, pos - carets - 1, 1) == "^")
516  ++carets;
517  // if number of previous carets is odd then this carets is escaped
518  return (carets & 1);
519 }
520 
521 ERASEABLE
522 bool isValidColorCodeValue(string theText, int cc_len, int tag_start)
523 {
524  if (cc_len == 2)
525  return IS_DIGIT(substring(theText, tag_start + 1, 1));
526  if (cc_len == 5)
527  return (IS_HEXDIGIT(substring(theText, tag_start + 2, 1))
528  && IS_HEXDIGIT(substring(theText, tag_start + 3, 1))
529  && IS_HEXDIGIT(substring(theText, tag_start + 4, 1)));
530  return false;
531 }
532 
533 // it returns 0 if pos is NOT in the middle or at the end of a color code
534 // otherwise it returns a vector with color code length as the first component
535 // and the offset from '^' position to pos as the second component
536 // e.g.:
537 // "j^2kl" | returns 0 if pos == 0 or 1 or 4
538 // ^^ | returns '2 1' or '2 2' if pos == 2 or 3
539 ERASEABLE
540 vector checkColorCode(string theText, int text_len, int pos, bool check_at_the_end)
541 {
542  if (text_len == 0)
543  text_len = strlen(theText);
544  string tag_type = "^";
545  int cc_len = 2;
546  int tag_len = 1;
547 
548  LABEL(check_color_tag)
549 
550  int ofs = cc_len;
551  if (!check_at_the_end)
552  ofs--;
553  for (; ofs >= 1; ofs--)
554  {
555  if (!(pos >= ofs && text_len >= pos + (cc_len - ofs)))
556  continue;
557  if(substring(theText, pos - ofs, tag_len) == tag_type)
558  {
559  if (!isCaretEscaped(theText, pos - ofs) && isValidColorCodeValue(theText, cc_len, pos - ofs))
560  return eX * cc_len + eY * ofs;
561  }
562  }
563  if (cc_len == 2)
564  {
565  tag_type = "^x";
566  cc_len = 5;
567  tag_len = 2;
568  goto check_color_tag;
569  }
570  return '0 0 0';
571 }
ERASEABLE string ftos_decimals(float number, int decimals)
converts a number to a string with the indicated number of decimals
Definition: string.qh:450
ERASEABLE int buf_load(string pFilename)
Definition: string.qh:418
string string_null
Definition: nil.qh:9
ERASEABLE int u8_strsize(string s)
Definition: string.qh:355
spree_inf s1 s2 s3loc s2 s1
Definition: all.inc:265
const vector eY
Definition: vector.qh:45
spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2 f1 f1points s1 s2
Definition: all.inc:438
int int number
Definition: impulse.qc:89
const string HEXDIGITS_MINSET
Definition: string.qh:499
#define str2chr
Definition: dpextensions.qh:45
ERASEABLE string swapwords(string str, float i, float j)
Definition: string.qh:279
#define stringwidth
Definition: csprogsdefs.qh:29
ERASEABLE bool isCaretEscaped(string theText, float pos)
Definition: string.qh:511
ERASEABLE string ftos_mindecimals(float number)
converts a number to a string with the minimum number of decimals
Definition: string.qh:461
entity() spawn
ERASEABLE string cdr(string s)
returns all but first word
Definition: string.qh:249
const float FILE_READ
Definition: csprogsdefs.qc:231
noref string _endsWith_suffix
Definition: string.qh:225
ERASEABLE void buf_save(float buf, string pFilename)
Definition: string.qh:436
ERASEABLE string clockedtime_tostring(int tm, bool hundredths)
Definition: string.qh:144
ERASEABLE string shufflewords(string str)
Definition: string.qh:307
const string HEXDIGITS
Definition: string.qh:500
#define ERASEABLE
Definition: _all.inc:35
ERASEABLE string car(string s)
returns first word
Definition: string.qh:240
ERASEABLE string unescape(string in)
Definition: string.qh:318
ERASEABLE void shuffle(float n, swapfunc_t swap, entity pass)
Definition: sort.qh:42
ERASEABLE int days_up_to_date(int Y, int M, int D)
Definition: string.qh:67
ERASEABLE string substring_range(string s, float b, float e)
Definition: string.qh:273
#define argv_end_index
Definition: dpextensions.qh:30
#define buf_create
Definition: dpextensions.qh:63
ERASEABLE string cons_mid(string a, string mid, string b)
Definition: string.qh:265
ERASEABLE void _shufflewords_swapfunc(float i, float j, entity pass)
Definition: string.qh:301
#define argv_start_index
Definition: dpextensions.qh:27
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
ERASEABLE int vercmp_recursive(string v1, string v2)
Definition: string.qh:469
ERASEABLE string ColorTranslateRGB(string s)
Definition: string.qh:177
int ColorTranslateMode
Definition: string.qh:174
ERASEABLE int vercmp(string v1, string v2)
Definition: string.qh:488
ERASEABLE bool isValidColorCodeValue(string theText, int cc_len, int tag_start)
Definition: string.qh:522
const string DIGITS
Definition: string.qh:506
ERASEABLE string fstrunzone(string s)
unzone the string, and return it as tempstring.
Definition: string.qh:230
#define NULL
Definition: post.qh:17
ERASEABLE bool startsWithNocase(string haystack, string needle)
Definition: string.qh:220
#define strstrofs
Definition: dpextensions.qh:42
#define IS_DIGIT(d)
Definition: string.qh:507
vector(float skel, float bonenum) _skel_get_boneabs_hidden
#define IS_HEXDIGIT(d)
Definition: string.qh:504
const float FILE_WRITE
Definition: csprogsdefs.qc:233
const vector eX
Definition: vector.qh:44
ERASEABLE vector checkColorCode(string theText, int text_len, int pos, bool check_at_the_end)
Definition: string.qh:540
#define tokenizebyseparator
Definition: dpextensions.qh:21
#define LABEL(id)
Definition: compiler.qh:36
string _shufflewords_str
Definition: string.qh:299
ERASEABLE string strwords(string s, int w)
Definition: string.qh:343
ERASEABLE string cons(string a, string b)
Definition: string.qh:257
#define strcasecmp
Definition: dpextensions.qh:57
#define pass(name, colormin, colormax)
ERASEABLE bool isInvisibleString(string s)
Definition: string.qh:369
ERASEABLE string strftime_s()
Definition: string.qh:91
#define DAYS_UP_TO_EPOCH
Definition: string.qh:82
ERASEABLE string format_time(float seconds)
Definition: string.qh:161
ERASEABLE string seconds_tostring(float seconds)
Definition: string.qh:123