Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pretty-format.c 34KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108
  1. #include <assert.h>
  2. #include <inttypes.h>
  3. #include <limits.h>
  4. #include <ctype.h>
  5. #include <string.h>
  6. #include <time.h>
  7. #include <stdio.h>
  8. #include <rtosc/rtosc.h>
  9. #include <rtosc/pretty-format.h>
  10. /** Call snprintf and assert() that it did fit into the buffer */
  11. static int asnprintf(char* str, size_t size, const char* format, ...)
  12. {
  13. va_list args;
  14. va_start(args, format);
  15. int written = vsnprintf(str, size, format, args);
  16. assert(written >= 0); // no error
  17. // snprintf(3) says that a return value of size or more bytes means
  18. // that the output has been truncated
  19. assert((size_t)written < size);
  20. va_end(args);
  21. return written;
  22. }
  23. static const rtosc_print_options* default_print_options
  24. = &((rtosc_print_options) { true, 2, " ", 80});
  25. /**
  26. * Return the char that represents the escape sequence
  27. *
  28. * @param c The escape sequence, e.g. '\n'
  29. * @param chr True if the character appears in a single character
  30. * (vs in a string)
  31. * @return The character describing the escape sequence, e.g. 'n';
  32. * -1 if none exists
  33. */
  34. static int as_escaped_char(int c, int chr)
  35. {
  36. switch(c)
  37. {
  38. case '\a': return 'a';
  39. case '\b': return 'b';
  40. case '\t': return 't';
  41. case '\n': return 'n';
  42. case '\v': return 'v';
  43. case '\f': return 'f';
  44. case '\r': return 'r';
  45. case '\\': return '\\';
  46. default:
  47. if(chr && c == '\'')
  48. return '\'';
  49. else if(!chr && c == '"')
  50. return '"';
  51. else return -1;
  52. }
  53. }
  54. // internal function for rtosc_print_arg_val
  55. static void break_string(char** buffer, size_t bs, int wrt, int* cols_used)
  56. {
  57. // unquoted, this means: "\<newline> "
  58. int tmp = asnprintf(*buffer, bs, "\"\\\n \"");
  59. wrt += tmp;
  60. *buffer += tmp;
  61. *cols_used = 5;
  62. }
  63. size_t rtosc_print_arg_val(const rtosc_arg_val_t *arg,
  64. char *buffer, size_t bs,
  65. const rtosc_print_options* opt,
  66. int *cols_used)
  67. {
  68. size_t wrt = 0;
  69. if(!opt)
  70. opt = default_print_options;
  71. assert(arg);
  72. const rtosc_arg_t* val = &arg->val;
  73. switch(arg->type) {
  74. case 'T':
  75. assert(bs>4);
  76. strncpy(buffer, "true", bs);
  77. wrt = 4;
  78. break;
  79. case 'F':
  80. assert(bs>5);
  81. strncpy(buffer, "false", bs);
  82. wrt = 5;
  83. break;
  84. case 'N':
  85. assert(bs>3);
  86. strncpy(buffer, "nil", bs);
  87. wrt = 3;
  88. break;
  89. case 'I':
  90. assert(bs>3);
  91. strncpy(buffer, "inf", bs);
  92. wrt = 3;
  93. break;
  94. case 'h':
  95. wrt = asnprintf(buffer, bs, "%"PRId64"h", val->h);
  96. break;
  97. case 't': // write to ISO 8601 date
  98. {
  99. if(val->t == 1)
  100. wrt = asnprintf(buffer, bs, "immediately");
  101. else
  102. {
  103. time_t t = (time_t)(val->t >> 32);
  104. int32_t secfracs = val->t & (0xffffffff);
  105. struct tm* m_tm = localtime(&t);
  106. const char* strtimefmt = (secfracs || m_tm->tm_sec)
  107. ? "%Y-%m-%d %H:%M:%S"
  108. : (m_tm->tm_hour || m_tm->tm_min)
  109. ? "%Y-%m-%d %H:%M"
  110. : "%Y-%m-%d";
  111. wrt = strftime(buffer, bs, strtimefmt, m_tm);
  112. assert(wrt);
  113. if(secfracs)
  114. {
  115. int rd = 0;
  116. int prec = opt->floating_point_precision;
  117. assert(prec>=0);
  118. assert(prec<100);
  119. // convert fractions -> float
  120. char lossless[16];
  121. asnprintf(lossless, 16, "0x%xp-32", secfracs);
  122. float flt;
  123. sscanf(lossless, "%f%n", &flt, &rd);
  124. assert(rd);
  125. // append float
  126. char fmtstr[8];
  127. asnprintf(fmtstr, 5, "%%.%df", prec);
  128. int lastwrt = wrt;
  129. wrt += asnprintf(buffer + wrt, bs - wrt,
  130. fmtstr, flt);
  131. // snip part before separator
  132. const char* sep = strchr(buffer + lastwrt, '.');
  133. assert(sep);
  134. memmove(buffer + lastwrt, sep, strlen(sep)+1);
  135. wrt -= (sep - (buffer + lastwrt));
  136. if(opt->lossless)
  137. wrt += asnprintf(buffer + wrt, bs - wrt,
  138. " (...+%as)", flt);
  139. } // if secfracs
  140. } // else
  141. break;
  142. }
  143. case 'r':
  144. wrt = asnprintf(buffer, bs, "#%02x%02x%02x%02x",
  145. (val->i >> 24) & 0xff,
  146. (val->i >> 16) & 0xff,
  147. (val->i >> 8) & 0xff,
  148. val->i & 0xff
  149. );
  150. break;
  151. case 'd':
  152. case 'f':
  153. {
  154. int prec = opt->floating_point_precision;
  155. assert(prec>=0);
  156. assert(prec<100);
  157. char fmtstr[9];
  158. if(arg->type == 'f')
  159. {
  160. // e.g. "%.42f" or "%.0f.":
  161. asnprintf(fmtstr, 6, "%%#.%df", prec);
  162. wrt = asnprintf(buffer, bs, fmtstr, val->f);
  163. if(opt->lossless)
  164. wrt += asnprintf(buffer + wrt, bs - wrt,
  165. " (%a)", val->f);
  166. }
  167. else
  168. {
  169. // e.g. "%.42lfd" or "%.0lf.d"
  170. asnprintf(fmtstr, 8, "%%#.%dlfd", prec);
  171. wrt = asnprintf(buffer, bs, fmtstr, val->d);
  172. if(opt->lossless)
  173. wrt += asnprintf(buffer + wrt, bs - wrt,
  174. " (%la)", val->d);
  175. }
  176. break;
  177. }
  178. case 'c':
  179. {
  180. int is_esc = (as_escaped_char(val->i, true) != -1);
  181. wrt = asnprintf(buffer, bs, "'%s%c'",
  182. is_esc ? "\\" : "",
  183. is_esc ? as_escaped_char(val->i, true)
  184. : val->i);
  185. break;
  186. }
  187. case 'i':
  188. wrt = asnprintf(buffer, bs, "%"PRId32, val->i);
  189. break;
  190. case 'm':
  191. wrt = asnprintf(buffer, bs, "MIDI [0x%02x 0x%02x 0x%02x 0x%02x]",
  192. val->m[0], val->m[1], val->m[2], val->m[3]);
  193. break;
  194. case 's':
  195. case 'S':
  196. {
  197. bool plain; // e.g. without quotes
  198. if(arg->type == 'S')
  199. {
  200. plain = true; // "Symbol": are quotes required?
  201. if(*val->s != '_' && !isalpha(*val->s))
  202. plain = false;
  203. else for(const char* s = val->s + 1; *s && plain; ++s)
  204. plain = (*s == '_' || (isalnum(*s)));
  205. }
  206. else plain = false;
  207. if(plain)
  208. {
  209. wrt = asnprintf(buffer, bs, "%s", val->s);
  210. break;
  211. }
  212. else
  213. {
  214. char* b = buffer;
  215. *b++ = '"';
  216. for(const char* s = val->s; *s; ++s)
  217. {
  218. if(*cols_used >= opt->linelength - 2)
  219. break_string(&b, bs, wrt, cols_used);
  220. assert(bs);
  221. int as_esc = as_escaped_char(*s, false);
  222. if(as_esc != -1) {
  223. assert(bs-1);
  224. *b++ = '\\';
  225. *b++ = as_esc;
  226. *cols_used += 2;
  227. if(as_esc == 'n')
  228. break_string(&b, bs, wrt, cols_used);
  229. }
  230. else {
  231. *b++ = *s;
  232. ++*cols_used;
  233. }
  234. }
  235. assert(bs >= 2);
  236. *b++ = '"';
  237. if(arg->type == 'S') {
  238. assert(bs >= 2);
  239. *b++ = 'S';
  240. }
  241. *b = 0;
  242. wrt += (b-buffer);
  243. break;
  244. }
  245. }
  246. case 'b':
  247. wrt = asnprintf(buffer, bs, "[%d ", val->b.len);
  248. *cols_used += wrt;
  249. buffer += wrt;
  250. for(int32_t i = 0; i < val->b.len; ++i)
  251. {
  252. if(*cols_used >= opt->linelength - 6)
  253. {
  254. int tmp = asnprintf(buffer-1, bs+1, "\n ");
  255. wrt += (tmp-1);
  256. buffer += (tmp-1);
  257. *cols_used = 4;
  258. }
  259. wrt += asnprintf(buffer, bs, "0x%02x ", val->b.data[i]);
  260. bs -= 5;
  261. buffer += 5;
  262. *cols_used += 5;
  263. }
  264. buffer[-1] = ']';
  265. break;
  266. default:
  267. ;
  268. }
  269. switch(arg->type)
  270. {
  271. case 's':
  272. case 'b':
  273. // these can break the line, so they compute *cols_used themselves
  274. break;
  275. default:
  276. *cols_used += wrt;
  277. }
  278. return wrt;
  279. }
  280. size_t rtosc_print_arg_vals(const rtosc_arg_val_t *args, size_t n,
  281. char *buffer, size_t bs,
  282. const rtosc_print_options *opt, int cols_used)
  283. {
  284. size_t wrt=0;
  285. int args_written_this_line = (cols_used) ? 1 : 0;
  286. if(!opt)
  287. opt = default_print_options;
  288. size_t sep_len = strlen(opt->sep);
  289. char* last_sep = buffer - 1;
  290. for(size_t i = 0; i < n; ++i)
  291. {
  292. size_t tmp = rtosc_print_arg_val(args++, buffer, bs, opt, &cols_used);
  293. wrt += tmp;
  294. buffer += tmp;
  295. bs -= tmp;
  296. ++args_written_this_line;
  297. // did we break the line length,
  298. // and this is not the first arg written in this line?
  299. if(cols_used > opt->linelength && (args_written_this_line > 1))
  300. {
  301. // insert "\n "
  302. *last_sep = '\n';
  303. assert(bs >= 4);
  304. memmove(last_sep+5, last_sep+1, tmp);
  305. last_sep[1] = last_sep[2] = last_sep[3] = last_sep[4] = ' ';
  306. cols_used = 4 + wrt;
  307. wrt += 4;
  308. buffer += 4;
  309. bs -= 4;
  310. args_written_this_line = 0;
  311. }
  312. if(i<n-1)
  313. {
  314. assert(sep_len < bs);
  315. last_sep = buffer;
  316. strncpy(buffer, opt->sep, bs);
  317. cols_used += sep_len;
  318. wrt += sep_len;
  319. buffer += sep_len;
  320. bs -= sep_len;
  321. }
  322. }
  323. return wrt;
  324. }
  325. size_t rtosc_print_message(const char* address,
  326. const rtosc_arg_val_t *args, size_t n,
  327. char *buffer, size_t bs,
  328. const rtosc_print_options* opt,
  329. int cols_used)
  330. {
  331. size_t wrt = asnprintf(buffer, bs, "%s ", address);
  332. cols_used += wrt;
  333. buffer += wrt;
  334. bs -= wrt;
  335. wrt += rtosc_print_arg_vals(args, n, buffer, bs, opt, cols_used);
  336. return wrt;
  337. }
  338. /**
  339. * Increase @p s while property @property is true and the end has not yet
  340. * been reached
  341. */
  342. static void skip_while(const char** s, int (*property)(int))
  343. {
  344. for(;**s && (*property)(**s);++*s);
  345. }
  346. /**
  347. * Parse the string pointed to by @p src conforming to the format string @p
  348. * @param src Pointer to the input string
  349. * @param fmt Format string for sscanf(). Must suppress all assignments,
  350. * except the last one, which must be "%n" and be at the string's end.
  351. * @return The number of bytes skipped from the string pointed to by @p src
  352. */
  353. static int skip_fmt(const char** src, const char* fmt)
  354. {
  355. assert(!strncmp(fmt + strlen(fmt) - 2, "%n", 2));
  356. int rd = 0;
  357. sscanf(*src, fmt, &rd);
  358. *src += rd;
  359. return rd;
  360. }
  361. /**
  362. * Behave like skip_fmt() , but set *src to NULL if the string didn't match
  363. * @see skip_fmt
  364. */
  365. static int skip_fmt_null(const char** src, const char* fmt)
  366. {
  367. int result = skip_fmt(src, fmt);
  368. if(!result)
  369. *src = NULL;
  370. return result;
  371. }
  372. /** Helper function for scanf_fmtstr() */
  373. static const char* try_fmt(const char* src, int exp, const char* fmt,
  374. char* typesrc, char type)
  375. {
  376. int rd = 0;
  377. sscanf(src, fmt, &rd);
  378. if(rd == exp)
  379. {
  380. *typesrc = type;
  381. return fmt;
  382. }
  383. else
  384. return NULL;
  385. }
  386. /**
  387. * Return the right format string for skipping the next numeric value
  388. *
  389. * This can be used to find out how to sscanf() ints, floats and doubles.
  390. * If the string needs to be read, too, see scanf_fmtstr_scan() below.
  391. * @param src The beginning of the numeric value to scan
  392. * @param type If non-NULL, the corresponding rtosc argument character
  393. * (h,i,f,d) will be written here
  394. * @return The format string to use or NULL
  395. */
  396. static const char* scanf_fmtstr(const char* src, char* type)
  397. {
  398. const char* end = src;
  399. // skip to string end, word end or a closing paranthesis
  400. for(;*end && !isspace(*end) && (*end != ')');++end);
  401. int exp = end - src;
  402. // store type byte in the parameter, or in a temporary variable?
  403. char tmp;
  404. char* _type = type ? type : &tmp;
  405. const char i32[] = "%*"PRIi32"%n";
  406. const char* r; // result
  407. int ok = (r = try_fmt(src, exp, "%*"PRIi64"h%n", _type, 'h'))
  408. || (r = try_fmt(src, exp, "%*d%n", _type, 'i'))
  409. || (r = try_fmt(src, exp, "%*"PRIi32"i%n", _type, 'i'))
  410. || (r = try_fmt(src, exp, i32, _type, 'i'))
  411. || (r = try_fmt(src, exp, "%*lfd%n", _type, 'd'))
  412. || (r = try_fmt(src, exp, "%*ff%n", _type, 'f'))
  413. || (r = try_fmt(src, exp, "%*f%n", _type, 'f'));
  414. (void)ok;
  415. if(r == i32)
  416. r = "%*x%n";
  417. return r;
  418. }
  419. /** Return the right format string for reading the next numeric value */
  420. static const char* scanf_fmtstr_scan(const char* src, char* bytes8,
  421. char* type)
  422. {
  423. const char *buf = scanf_fmtstr(src, type);
  424. assert(buf);
  425. assert(bytes8);
  426. strncpy(bytes8, buf, 8);
  427. *++bytes8 = '%'; // transform "%*" to "%"
  428. return bytes8;
  429. }
  430. /** Skip the next numeric at @p src */
  431. static size_t skip_numeric(const char** src, char* type)
  432. {
  433. const char* scan_str = scanf_fmtstr(*src, type);
  434. if(!scan_str) return 0;
  435. else return skip_fmt(src, scan_str);
  436. }
  437. /**
  438. * Return the escape sequence that's generated with the given character
  439. * @param c The character, e.g. 'n'
  440. * @param chr True if the character appears in a single character
  441. * (vs in a string)
  442. * @return The escape sequence generated by the character, e.g. '\n';
  443. * 0 if none exists
  444. */
  445. static char get_escaped_char(char c, int chr)
  446. {
  447. switch(c)
  448. {
  449. case 'a': return '\a';
  450. case 'b': return '\b';
  451. case 't': return '\t';
  452. case 'n': return '\n';
  453. case 'v': return '\v';
  454. case 'f': return '\f';
  455. case 'r': return '\r';
  456. case '\\': return '\\';
  457. default:
  458. if(chr && c == '\'')
  459. return '\'';
  460. else if(!chr && c == '"')
  461. return '"';
  462. else
  463. return 0;
  464. }
  465. }
  466. /** Called inside a string at @p src, skips until after the closing quote */
  467. static const char* end_of_printed_string(const char* src)
  468. {
  469. bool escaped = false;
  470. ++src;
  471. bool cont;
  472. do
  473. {
  474. for(; *src && (escaped || *src != '"') ; ++src)
  475. {
  476. if(escaped) // last char introduced an escape sequence?
  477. {
  478. if(!get_escaped_char(*src, false))
  479. return NULL; // bad escape sequence
  480. }
  481. escaped = (*src == '\\') ? (!escaped) : false;
  482. }
  483. if(*src == '"' && src[1] == '\\') {
  484. skip_fmt_null(&src, "\"\\ \"%n");
  485. cont = true;
  486. }
  487. else
  488. cont = false;
  489. } while(cont);
  490. if(!src || !*src)
  491. return NULL;
  492. return ++src;
  493. }
  494. /**
  495. * Skips string @p exp at the current string pointed to by @p str,
  496. * but only if it's a separated word, i.e. if there's a char after the string,
  497. * it mus be a slash or whitespace.
  498. *
  499. * @return The position after the word, or NULL if the word was not present
  500. * at @p str .
  501. */
  502. static const char* skip_word(const char* exp, const char** str)
  503. {
  504. size_t explen = strlen(exp);
  505. const char* cur = *str;
  506. int match = (!strncmp(exp, cur, explen) &&
  507. (!cur[explen] || cur[explen] == '/' || isspace(cur[explen])));
  508. if(match) {
  509. *str += explen;
  510. return *str;
  511. }
  512. else return NULL;
  513. }
  514. /**
  515. * Tries to skip the next identifier beginning at @p str
  516. *
  517. * @return The position after the identifier, or NULL if there's no identifier
  518. * at @p str .
  519. */
  520. static const char* skip_identifier(const char* str)
  521. {
  522. if(!isalpha(*str) && *str != '_')
  523. return NULL;
  524. else
  525. {
  526. ++str;
  527. for(; isalnum(*str) || *str == '_'; ++str) ;
  528. return str;
  529. }
  530. }
  531. const char* rtosc_skip_next_printed_arg(const char* src)
  532. {
  533. switch(*src)
  534. {
  535. case 't':
  536. if(!skip_word("true", &src))
  537. src = skip_identifier(src);
  538. break;
  539. case 'f':
  540. if(!skip_word("false", &src))
  541. src = skip_identifier(src);
  542. break;
  543. case 'n':
  544. if(!skip_word("nil", &src))
  545. if(!skip_word("now", &src))
  546. src = skip_identifier(src);
  547. break;
  548. case 'i':
  549. if(!skip_word("inf", &src))
  550. if(!skip_word("immediately", &src))
  551. src = skip_identifier(src);
  552. break;
  553. case '#':
  554. for(size_t i = 0; i<8; ++i)
  555. {
  556. ++src;
  557. if(!isxdigit(*src)) {
  558. src = NULL;
  559. i = 8;
  560. }
  561. }
  562. if(src) ++src;
  563. break;
  564. case '\'':
  565. {
  566. int esc = -1;
  567. if(strlen(src) < 3)
  568. return NULL;
  569. // type 1: '<noslash>' => normal char
  570. // type 2: '\<noquote>' => escaped char
  571. // type 3: '\'' => escaped quote
  572. // type 4: '\' => mistyped backslash
  573. if(src[1] == '\\') {
  574. if(src[2] == '\'' && (!src[3] || isspace(src[3])))
  575. {
  576. // type 4
  577. // the user inputs '\', which is wrong, but
  578. // we accept it as a backslash anyways
  579. }
  580. else
  581. {
  582. ++src; // type 2 or 3
  583. esc = get_escaped_char(src[1], 1);
  584. }
  585. }
  586. // if the last char was no single quote,
  587. // or we had an invalid escape sequence, return NULL
  588. src = (!esc || src[2] != '\'') ? NULL : (src + 3);
  589. break;
  590. }
  591. case '"':
  592. src = end_of_printed_string(src);
  593. if(src && *src == 'S')
  594. ++src;
  595. break;
  596. case 'M':
  597. if(!strncmp("MIDI", src, 4) && (isspace(src[4]) || src[4] == '['))
  598. skip_fmt_null(&src, "MIDI [ 0x%*x 0x%*x 0x%*x 0x%*x ]%n");
  599. else
  600. src = skip_identifier(src);
  601. break;
  602. case '[':
  603. {
  604. int rd = 0, blobsize = 0;
  605. sscanf(src, "[ %i %n", &blobsize, &rd);
  606. src = rd ? (src + rd) : NULL;
  607. for(;src && *src == '0';) // i.e. 0x...
  608. {
  609. skip_fmt_null(&src, "0x%*x %n");
  610. blobsize--;
  611. }
  612. if(blobsize)
  613. src = NULL;
  614. if(src)
  615. src = (*src == ']') ? (src + 1) : NULL;
  616. break;
  617. }
  618. default:
  619. {
  620. // is it an identifier?
  621. if(*src == '_' || isalpha(*src))
  622. {
  623. for(; *src == '_' || isalnum(*src); ++src) ;
  624. }
  625. // is it a date? (vs a numeric)
  626. else if(skip_fmt(&src, "%*4d-%*1d%*1d-%*1d%*1d%n"))
  627. {
  628. if(skip_fmt(&src, " %*2d:%*1d%*1d%n"))
  629. if(skip_fmt(&src, ":%*1d%*1d%n"))
  630. if(skip_fmt(&src, ".%*d%n"))
  631. {
  632. if(skip_fmt(&src, " ( ... + 0x%n"))
  633. {
  634. skip_fmt(&src, "%*x.%n");
  635. if(skip_fmt(&src, "%*xp%n"))
  636. {
  637. int rd = 0, expm;
  638. sscanf(src, "-%d s )%n", &expm, &rd);
  639. if(rd && expm > 0 && expm <= 32)
  640. {
  641. // ok
  642. src += rd;
  643. }
  644. else
  645. src = NULL;
  646. }
  647. else
  648. src = NULL;
  649. }
  650. }
  651. }
  652. else
  653. {
  654. char type;
  655. int rd = skip_numeric(&src, &type);
  656. if(!rd) { src = NULL; break; }
  657. const char* after_num = src;
  658. skip_while(&after_num, isspace);
  659. if(*after_num == '(')
  660. {
  661. if (type == 'f' || type =='d')
  662. {
  663. // skip lossless representation
  664. src = ++after_num;
  665. skip_while(&src, isspace);
  666. rd = skip_numeric(&src, NULL);
  667. if(!rd) { src = NULL; break; }
  668. skip_fmt_null(&src, " )%n");
  669. }
  670. else
  671. src = NULL;
  672. }
  673. }
  674. }
  675. }
  676. return src;
  677. }
  678. int rtosc_count_printed_arg_vals(const char* src)
  679. {
  680. int num = 0;
  681. skip_while(&src, isspace);
  682. while (*src == '%')
  683. skip_fmt(&src, "%*[^\n] %n");
  684. for(; src && *src && *src != '/'; ++num)
  685. {
  686. src = rtosc_skip_next_printed_arg(src);
  687. if(src) // parse error
  688. {
  689. skip_while(&src, isspace);
  690. if(*src && !isspace(*src))
  691. {
  692. while (*src == '%')
  693. skip_fmt(&src, "%*[^\n] %n");
  694. }
  695. }
  696. }
  697. return src ? num : -num;
  698. }
  699. int rtosc_count_printed_arg_vals_of_msg(const char* msg)
  700. {
  701. skip_while(&msg, isspace);
  702. while (*msg == '%')
  703. skip_fmt(&msg, "%*[^\n] %n");
  704. if (*msg == '/') {
  705. for(; *msg && !isspace(*msg); ++msg);
  706. return rtosc_count_printed_arg_vals(msg);
  707. }
  708. else if(!*msg)
  709. return INT_MIN;
  710. else
  711. return -1;
  712. }
  713. //! Tries to parse an identifier at @p src and stores it in @p arg
  714. const char* parse_identifier(const char* src, rtosc_arg_val_t *arg,
  715. char* buffer_for_strings,
  716. size_t* bufsize)
  717. {
  718. if(*src == '_' || isalpha(*src))
  719. {
  720. arg->type = 'S';
  721. arg->val.s = buffer_for_strings;
  722. for(; *src == '_' || isalnum(*src); ++src)
  723. {
  724. --*bufsize;
  725. assert(*bufsize);
  726. *buffer_for_strings = *src;
  727. ++buffer_for_strings;
  728. }
  729. --*bufsize;
  730. assert(*bufsize);
  731. *buffer_for_strings = 0;
  732. ++buffer_for_strings;
  733. }
  734. return src;
  735. }
  736. size_t rtosc_scan_arg_val(const char* src,
  737. rtosc_arg_val_t *arg,
  738. char* buffer_for_strings, size_t* bufsize)
  739. {
  740. int rd = 0;
  741. const char* start = src;
  742. switch(*src)
  743. {
  744. case 't':
  745. case 'f':
  746. case 'n':
  747. case 'i':
  748. {
  749. const char* src_backup = src;
  750. // timestamps "immediately" or "now"?
  751. if(skip_word("immediately", &src) || skip_word("now", &src))
  752. {
  753. arg->type = 't';
  754. arg->val.t = 1;
  755. }
  756. else if(skip_word("nil", &src) ||
  757. skip_word("inf", &src) ||
  758. skip_word("true", &src) ||
  759. skip_word("false", &src) )
  760. {
  761. arg->type = arg->val.T = toupper(*src_backup);
  762. }
  763. else
  764. {
  765. // no reserved keyword => identifier
  766. src = parse_identifier(src, arg, buffer_for_strings, bufsize);
  767. }
  768. break;
  769. }
  770. case '#':
  771. {
  772. arg->type = 'r';
  773. sscanf(++src, "%x", &arg->val.i);
  774. src+=8;
  775. break;
  776. }
  777. case '\'':
  778. // type 1: '<noslash>' => normal char
  779. // type 2: '\<noquote>' => escaped char
  780. // type 3: '\'' => escaped quote
  781. // type 4: '\'<isspace> => mistyped backslash
  782. arg->type = 'c';
  783. if(*++src == '\\')
  784. {
  785. if(src[2] && !isspace(src[2])) // escaped and 4 chars
  786. arg->val.i = get_escaped_char(*++src, true);
  787. else // escaped, but only 3 chars: type 4
  788. arg->val.i = '\\';
  789. }
  790. else // non-escaped
  791. arg->val.i = *src;
  792. src+=2;
  793. break;
  794. case '"':
  795. {
  796. ++src; // skip obligatory '"'
  797. char* dest = buffer_for_strings;
  798. bool cont;
  799. do
  800. {
  801. while(*src != '"')
  802. {
  803. (*bufsize)--;
  804. assert(*bufsize);
  805. if(*src == '\\') {
  806. *dest++ = get_escaped_char(*++src, false);
  807. ++src;
  808. }
  809. else
  810. *dest++ = *src++;
  811. }
  812. if(src[1] == '\\')
  813. {
  814. skip_fmt(&src, "\"\\ \"%n");
  815. cont = true;
  816. }
  817. else
  818. cont = false;
  819. } while (cont);
  820. *dest = 0;
  821. ++src; // skip final '"'
  822. (*bufsize)--;
  823. arg->val.s = buffer_for_strings;
  824. if(*src == 'S')
  825. {
  826. ++src;
  827. (*bufsize)--;
  828. arg->type = 'S';
  829. }
  830. else
  831. arg->type = 's';
  832. break;
  833. }
  834. case 'M':
  835. {
  836. if(!strncmp("MIDI", src, 4) && (isspace(src[4]) || src[4] == '['))
  837. {
  838. arg->type = 'm';
  839. int32_t tmp[4];
  840. sscanf(src, "MIDI [ 0x%"PRIx32" 0x%"PRIx32
  841. " 0x%"PRIx32" 0x%"PRIx32" ]%n",
  842. tmp, tmp + 1, tmp + 2, tmp + 3, &rd); src+=rd;
  843. for(size_t i = 0; i < 4; ++i)
  844. arg->val.m[i] = tmp[i]; // copy to 8 bit array
  845. }
  846. else
  847. src = parse_identifier(src, arg, buffer_for_strings, bufsize);
  848. break;
  849. }
  850. case '[': // blob
  851. {
  852. arg->type = 'b';
  853. while( isspace(*++src) ) ;
  854. sscanf(src, "%"PRIi32" %n", &arg->val.b.len, &rd);
  855. src +=rd;
  856. assert(*bufsize >= (size_t)arg->val.b.len);
  857. *bufsize -= (size_t)arg->val.b.len;
  858. arg->val.b.data = (uint8_t*)buffer_for_strings;
  859. for(int32_t i = 0; i < arg->val.b.len; ++i)
  860. {
  861. int32_t tmp;
  862. int rd;
  863. sscanf(src, "0x%x %n", &tmp, &rd);
  864. arg->val.b.data[i] = tmp;
  865. src+=rd;
  866. }
  867. ++src; // skip ']'
  868. break;
  869. }
  870. default:
  871. // is it an identifier?
  872. if(*src == '_' || isalpha(*src))
  873. {
  874. arg->type = 'S';
  875. arg->val.s = buffer_for_strings;
  876. for(; *src == '_' || isalnum(*src); ++src)
  877. {
  878. --*bufsize;
  879. assert(*bufsize);
  880. *buffer_for_strings = *src;
  881. ++buffer_for_strings;
  882. }
  883. --*bufsize;
  884. assert(*bufsize);
  885. *buffer_for_strings = 0;
  886. ++buffer_for_strings;
  887. }
  888. // "YYYY-" => it's a date
  889. else if(src[0] && src[1] && src[2] && src[3] && src[4] == '-')
  890. {
  891. arg->val.t = 0;
  892. struct tm m_tm;
  893. m_tm.tm_hour = 0;
  894. m_tm.tm_min = 0;
  895. m_tm.tm_sec = 0;
  896. sscanf(src, "%4d-%2d-%2d%n",
  897. &m_tm.tm_year, &m_tm.tm_mon, &m_tm.tm_mday, &rd);
  898. src+=rd;
  899. float secfracsf;
  900. rd = 0;
  901. sscanf(src, " %2d:%2d%n", &m_tm.tm_hour, &m_tm.tm_min, &rd);
  902. if(rd)
  903. src+=rd;
  904. rd = 0;
  905. sscanf(src, ":%2d%n", &m_tm.tm_sec, &rd);
  906. if(rd)
  907. src+=rd;
  908. // lossless format is appended in parantheses?
  909. // => take it directly from there
  910. if(skip_fmt(&src, "%*f (%n"))
  911. {
  912. sscanf(src, " ... + 0x%8"PRIx64"p-32 s )%n",
  913. &arg->val.t, &rd);
  914. src += rd;
  915. }
  916. // float number, but not lossless?
  917. // => convert it to fractions of seconds
  918. else if(*src == '.')
  919. {
  920. sscanf(src, "%f%n", &secfracsf, &rd);
  921. src += rd;
  922. // convert float -> secfracs
  923. char secfracs_as_hex[16];
  924. asnprintf(secfracs_as_hex, 16, "%a", secfracsf);
  925. assert(secfracs_as_hex[3]=='.'); // 0x?.
  926. secfracs_as_hex[3] = secfracs_as_hex[2]; // remove '.'
  927. uint64_t secfracs;
  928. int exp;
  929. sscanf(secfracs_as_hex + 3,
  930. "%"PRIx64"p-%i", &secfracs, &exp);
  931. const char* p = strchr(secfracs_as_hex, 'p');
  932. assert(p);
  933. int lshift = 32-exp-((int)(p-(secfracs_as_hex+4))<<2);
  934. assert(lshift > 0);
  935. secfracs <<= lshift;
  936. assert((secfracs & 0xFFFFFFFF) == secfracs);
  937. arg->val.t = secfracs;
  938. }
  939. else
  940. {
  941. // no fractional / floating seconds part
  942. }
  943. // adjust ranges to be POSIX conform
  944. m_tm.tm_year -= 1900;
  945. --m_tm.tm_mon;
  946. // don't mess around with Daylight Saving Time
  947. m_tm.tm_isdst = -1;
  948. arg->val.t |= (((uint64_t)mktime(&m_tm)) << ((uint64_t)32));
  949. arg->type = 't';
  950. }
  951. else
  952. {
  953. char bytes8[8];
  954. char type = arg->type = 0;
  955. bool repeat_once = false;
  956. do
  957. {
  958. rd = 0;
  959. const char *fmtstr = scanf_fmtstr_scan(src, bytes8,
  960. &type);
  961. if(!arg->type) // the first occurence determins the type
  962. arg->type = type;
  963. switch(type)
  964. {
  965. case 'h':
  966. sscanf(src, fmtstr, &arg->val.h, &rd); break;
  967. case 'i':
  968. sscanf(src, fmtstr, &arg->val.i, &rd); break;
  969. case 'f':
  970. sscanf(src, fmtstr, &arg->val.f, &rd); break;
  971. case 'd':
  972. sscanf(src, fmtstr, &arg->val.d, &rd); break;
  973. }
  974. src += rd;
  975. if(repeat_once)
  976. {
  977. // we have read the lossless part. skip spaces and ')'
  978. skip_fmt(&src, " )%n");
  979. repeat_once = false;
  980. }
  981. else
  982. {
  983. // is a lossless part appended in parantheses?
  984. const char* after_num = src;
  985. skip_while(&after_num, isspace);
  986. if(*after_num == '(') {
  987. ++after_num;
  988. skip_while(&after_num, isspace);
  989. src = after_num;
  990. repeat_once = true;
  991. }
  992. }
  993. } while(repeat_once);
  994. } // date vs integer
  995. // case ident
  996. } // switch
  997. return (size_t)(src-start);
  998. }
  999. size_t rtosc_scan_arg_vals(const char* src,
  1000. rtosc_arg_val_t *args, size_t n,
  1001. char* buffer_for_strings, size_t bufsize)
  1002. {
  1003. size_t last_bufsize;
  1004. size_t rd=0;
  1005. for(size_t i = 0; i < n; ++i)
  1006. {
  1007. last_bufsize = bufsize;
  1008. size_t tmp = rtosc_scan_arg_val(src, args + i,
  1009. buffer_for_strings, &bufsize);
  1010. src += tmp;
  1011. rd += tmp;
  1012. size_t written = last_bufsize - bufsize;
  1013. buffer_for_strings += written;
  1014. do
  1015. {
  1016. rd += skip_fmt(&src, " %n");
  1017. while(*src == '%')
  1018. rd += skip_fmt(&src, "%*[^\n]%n");
  1019. } while(isspace(*src));
  1020. }
  1021. return rd;
  1022. }
  1023. size_t rtosc_scan_message(const char* src,
  1024. char* address, size_t adrsize,
  1025. rtosc_arg_val_t *args, size_t n,
  1026. char* buffer_for_strings, size_t bufsize)
  1027. {
  1028. size_t rd = 0;
  1029. for(;*src && isspace(*src); ++src) ++rd;
  1030. while (*src == '%')
  1031. rd += skip_fmt(&src, "%*[^\n] %n");
  1032. assert(*src == '/');
  1033. for(; *src && !isspace(*src) && rd < adrsize; ++rd)
  1034. *address++ = *src++;
  1035. assert(rd < adrsize); // otherwise, the address was too long
  1036. *address = 0;
  1037. for(;*src && isspace(*src); ++src) ++rd;
  1038. rd += rtosc_scan_arg_vals(src, args, n, buffer_for_strings, bufsize);
  1039. return rd;
  1040. }