tparm.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 1998-2003,2004,2005 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /**********************************************************************
  19. * This code is a modification of lib_tparm.c found in ncurses-5.2. The
  20. * modification are for use in grub by replacing all libc function through
  21. * special grub functions. This also meant to delete all dynamic memory
  22. * allocation and replace it by a number of fixed buffers.
  23. *
  24. * Modifications by Tilmann Bubeck <t.bubeck@reinform.de> 2002
  25. *
  26. * Resync with ncurses-5.4 by Omniflux <omniflux+devel@omniflux.com> 2005
  27. **********************************************************************/
  28. /****************************************************************************
  29. * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
  30. * and: Eric S. Raymond <esr@snark.thyrsus.com> *
  31. * and: Thomas E. Dickey, 1996 on *
  32. ****************************************************************************/
  33. /*
  34. * tparm.c
  35. *
  36. */
  37. #include <grub/misc.h>
  38. #include <grub/mm.h>
  39. #include <grub/types.h>
  40. #include <grub/tparm.h>
  41. /*
  42. * Common/troublesome character definitions
  43. */
  44. typedef char grub_bool_t;
  45. #ifndef FALSE
  46. # define FALSE (0)
  47. #endif
  48. #ifndef TRUE
  49. # define TRUE (!FALSE)
  50. #endif
  51. #define NUM_PARM 9
  52. #define NUM_VARS 26
  53. #define STACKSIZE 20
  54. #define MAX_FORMAT_LEN 256
  55. #define max(a,b) ((a) > (b) ? (a) : (b))
  56. #define isdigit(c) ((c) >= '0' && (c) <= '9')
  57. #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
  58. #define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
  59. #define UChar(c) ((unsigned char)(c))
  60. //MODULE_ID("$Id$")
  61. /*
  62. * char *
  63. * tparm(string, ...)
  64. *
  65. * Substitute the given parameters into the given string by the following
  66. * rules (taken from terminfo(5)):
  67. *
  68. * Cursor addressing and other strings requiring parame-
  69. * ters in the terminal are described by a parameterized string
  70. * capability, with like escapes %x in it. For example, to
  71. * address the cursor, the cup capability is given, using two
  72. * parameters: the row and column to address to. (Rows and
  73. * columns are numbered from zero and refer to the physical
  74. * screen visible to the user, not to any unseen memory.) If
  75. * the terminal has memory relative cursor addressing, that can
  76. * be indicated by
  77. *
  78. * The parameter mechanism uses a stack and special %
  79. * codes to manipulate it. Typically a sequence will push one
  80. * of the parameters onto the stack and then print it in some
  81. * format. Often more complex operations are necessary.
  82. *
  83. * The % encodings have the following meanings:
  84. *
  85. * %% outputs `%'
  86. * %c print pop() like %c in printf()
  87. * %s print pop() like %s in printf()
  88. * %[[:]flags][width[.precision]][doxXs]
  89. * as in printf, flags are [-+#] and space
  90. * The ':' is used to avoid making %+ or %-
  91. * patterns (see below).
  92. *
  93. * %p[1-9] push ith parm
  94. * %P[a-z] set dynamic variable [a-z] to pop()
  95. * %g[a-z] get dynamic variable [a-z] and push it
  96. * %P[A-Z] set static variable [A-Z] to pop()
  97. * %g[A-Z] get static variable [A-Z] and push it
  98. * %l push strlen(pop)
  99. * %'c' push char constant c
  100. * %{nn} push integer constant nn
  101. *
  102. * %+ %- %* %/ %m
  103. * arithmetic (%m is mod): push(pop() op pop())
  104. * %& %| %^ bit operations: push(pop() op pop())
  105. * %= %> %< logical operations: push(pop() op pop())
  106. * %A %O logical and & or operations for conditionals
  107. * %! %~ unary operations push(op pop())
  108. * %i add 1 to first two parms (for ANSI terminals)
  109. *
  110. * %? expr %t thenpart %e elsepart %;
  111. * if-then-else, %e elsepart is optional.
  112. * else-if's are possible ala Algol 68:
  113. * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
  114. *
  115. * For those of the above operators which are binary and not commutative,
  116. * the stack works in the usual way, with
  117. * %gx %gy %m
  118. * resulting in x mod y, not the reverse.
  119. */
  120. typedef struct {
  121. union {
  122. int num;
  123. char *str;
  124. } data;
  125. grub_bool_t num_type;
  126. } stack_frame;
  127. static stack_frame stack[STACKSIZE];
  128. static int stack_ptr;
  129. static const char *tparam_base = "";
  130. static char *out_buff;
  131. static grub_size_t out_size;
  132. static grub_size_t out_used;
  133. static char *fmt_buff;
  134. static grub_size_t fmt_size;
  135. static inline void
  136. get_space(grub_size_t need)
  137. {
  138. need += out_used;
  139. if (need > out_size) {
  140. out_size = need * 2;
  141. out_buff = grub_realloc(out_buff, out_size*sizeof(char));
  142. /* FIX ME! handle out_buff == 0. */
  143. }
  144. }
  145. #pragma GCC diagnostic ignored "-Wformat-nonliteral"
  146. static inline void
  147. save_text(const char *fmt, const char *s, int len)
  148. {
  149. grub_size_t s_len = grub_strlen(s);
  150. if (len > (int) s_len)
  151. s_len = len;
  152. get_space(s_len + 1);
  153. (void) grub_snprintf(out_buff + out_used, s_len + 1, fmt, s);
  154. out_used += grub_strlen(out_buff + out_used);
  155. }
  156. static inline void
  157. save_number(const char *fmt, int number, int len)
  158. {
  159. if (len < 30)
  160. len = 30; /* actually log10(MAX_INT)+1 */
  161. get_space((unsigned) len + 1);
  162. (void) grub_snprintf(out_buff + out_used, len + 1, fmt, number);
  163. out_used += grub_strlen(out_buff + out_used);
  164. }
  165. #pragma GCC diagnostic error "-Wformat-nonliteral"
  166. static inline void
  167. save_char(int c)
  168. {
  169. if (c == 0)
  170. c = 0200;
  171. get_space(1);
  172. out_buff[out_used++] = c;
  173. }
  174. static inline void
  175. npush(int x)
  176. {
  177. if (stack_ptr < STACKSIZE) {
  178. stack[stack_ptr].num_type = TRUE;
  179. stack[stack_ptr].data.num = x;
  180. stack_ptr++;
  181. }
  182. }
  183. static inline int
  184. npop(void)
  185. {
  186. int result = 0;
  187. if (stack_ptr > 0) {
  188. stack_ptr--;
  189. if (stack[stack_ptr].num_type)
  190. result = stack[stack_ptr].data.num;
  191. }
  192. return result;
  193. }
  194. static inline void
  195. spush(char *x)
  196. {
  197. if (stack_ptr < STACKSIZE) {
  198. stack[stack_ptr].num_type = FALSE;
  199. stack[stack_ptr].data.str = x;
  200. stack_ptr++;
  201. }
  202. }
  203. static inline char *
  204. spop(void)
  205. {
  206. static char dummy[] = ""; /* avoid const-cast */
  207. char *result = dummy;
  208. if (stack_ptr > 0) {
  209. stack_ptr--;
  210. if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0)
  211. result = stack[stack_ptr].data.str;
  212. }
  213. return result;
  214. }
  215. static inline const char *
  216. parse_format(const char *s, char *format, int *len)
  217. {
  218. *len = 0;
  219. if (format != 0) {
  220. grub_bool_t done = FALSE;
  221. grub_bool_t allowminus = FALSE;
  222. grub_bool_t dot = FALSE;
  223. grub_bool_t err = FALSE;
  224. char *fmt = format;
  225. int my_width = 0;
  226. int my_prec = 0;
  227. int value = 0;
  228. *len = 0;
  229. *format++ = '%';
  230. while (*s != '\0' && !done) {
  231. switch (*s) {
  232. case 'c': /* FALLTHRU */
  233. case 'd': /* FALLTHRU */
  234. case 'o': /* FALLTHRU */
  235. case 'x': /* FALLTHRU */
  236. case 'X': /* FALLTHRU */
  237. case 's':
  238. *format++ = *s;
  239. done = TRUE;
  240. break;
  241. case '.':
  242. *format++ = *s++;
  243. if (dot) {
  244. err = TRUE;
  245. } else { /* value before '.' is the width */
  246. dot = TRUE;
  247. my_width = value;
  248. }
  249. value = 0;
  250. break;
  251. case '#':
  252. *format++ = *s++;
  253. break;
  254. case ' ':
  255. *format++ = *s++;
  256. break;
  257. case ':':
  258. s++;
  259. allowminus = TRUE;
  260. break;
  261. case '-':
  262. if (allowminus) {
  263. *format++ = *s++;
  264. } else {
  265. done = TRUE;
  266. }
  267. break;
  268. default:
  269. if (isdigit(UChar(*s))) {
  270. value = (value * 10) + (*s - '0');
  271. if (value > 10000)
  272. err = TRUE;
  273. *format++ = *s++;
  274. } else {
  275. done = TRUE;
  276. }
  277. }
  278. }
  279. /*
  280. * If we found an error, ignore (and remove) the flags.
  281. */
  282. if (err) {
  283. my_width = my_prec = value = 0;
  284. format = fmt;
  285. *format++ = '%';
  286. *format++ = *s;
  287. }
  288. /*
  289. * Any value after '.' is the precision. If we did not see '.', then
  290. * the value is the width.
  291. */
  292. if (dot)
  293. my_prec = value;
  294. else
  295. my_width = value;
  296. *format = '\0';
  297. /* return maximum string length in print */
  298. *len = (my_width > my_prec) ? my_width : my_prec;
  299. }
  300. return s;
  301. }
  302. /*
  303. * Analyze the string to see how many parameters we need from the varargs list,
  304. * and what their types are. We will only accept string parameters if they
  305. * appear as a %l or %s format following an explicit parameter reference (e.g.,
  306. * %p2%s). All other parameters are numbers.
  307. *
  308. * 'number' counts coarsely the number of pop's we see in the string, and
  309. * 'popcount' shows the highest parameter number in the string. We would like
  310. * to simply use the latter count, but if we are reading termcap strings, there
  311. * may be cases that we cannot see the explicit parameter numbers.
  312. */
  313. static inline int
  314. analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
  315. {
  316. grub_size_t len2;
  317. int i;
  318. int lastpop = -1;
  319. int len;
  320. int number = 0;
  321. const char *cp = string;
  322. static char dummy[] = "";
  323. *popcount = 0;
  324. if (cp == 0)
  325. return 0;
  326. if ((len2 = grub_strlen(cp)) > fmt_size) {
  327. fmt_size = len2 + fmt_size + 2;
  328. if ((fmt_buff = grub_realloc(fmt_buff, fmt_size*sizeof(char))) == 0)
  329. return 0;
  330. }
  331. grub_memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
  332. while ((cp - string) < (int) len2) {
  333. if (*cp == '%') {
  334. cp++;
  335. cp = parse_format(cp, fmt_buff, &len);
  336. switch (*cp) {
  337. default:
  338. break;
  339. case 'd': /* FALLTHRU */
  340. case 'o': /* FALLTHRU */
  341. case 'x': /* FALLTHRU */
  342. case 'X': /* FALLTHRU */
  343. case 'c': /* FALLTHRU */
  344. if (lastpop <= 0)
  345. number++;
  346. lastpop = -1;
  347. break;
  348. case 'l':
  349. case 's':
  350. if (lastpop > 0)
  351. p_is_s[lastpop - 1] = dummy;
  352. ++number;
  353. break;
  354. case 'p':
  355. cp++;
  356. i = (UChar(*cp) - '0');
  357. if (i >= 0 && i <= NUM_PARM) {
  358. lastpop = i;
  359. if (lastpop > *popcount)
  360. *popcount = lastpop;
  361. }
  362. break;
  363. case 'P':
  364. ++number;
  365. ++cp;
  366. break;
  367. case 'g':
  368. cp++;
  369. break;
  370. case '\'':
  371. cp += 2;
  372. lastpop = -1;
  373. break;
  374. case '{':
  375. cp++;
  376. while (isdigit(UChar(*cp))) {
  377. cp++;
  378. }
  379. break;
  380. case '+':
  381. case '-':
  382. case '*':
  383. case '/':
  384. case 'm':
  385. case 'A':
  386. case 'O':
  387. case '&':
  388. case '|':
  389. case '^':
  390. case '=':
  391. case '<':
  392. case '>':
  393. lastpop = -1;
  394. number += 2;
  395. break;
  396. case '!':
  397. case '~':
  398. lastpop = -1;
  399. ++number;
  400. break;
  401. case 'i':
  402. /* will add 1 to first (usually two) parameters */
  403. break;
  404. }
  405. }
  406. if (*cp != '\0')
  407. cp++;
  408. }
  409. if (number > NUM_PARM)
  410. number = NUM_PARM;
  411. return number;
  412. }
  413. static inline char *
  414. tparam_internal(const char *string, va_list ap)
  415. {
  416. char *p_is_s[NUM_PARM];
  417. long param[NUM_PARM];
  418. int popcount;
  419. int number;
  420. int len;
  421. int level;
  422. int x, y;
  423. int i;
  424. const char *cp = string;
  425. grub_size_t len2;
  426. static int dynamic_var[NUM_VARS];
  427. static int static_vars[NUM_VARS];
  428. if (cp == 0)
  429. return 0;
  430. out_used = out_size = fmt_size = 0;
  431. len2 = (int) grub_strlen(cp);
  432. /*
  433. * Find the highest parameter-number referred to in the format string.
  434. * Use this value to limit the number of arguments copied from the
  435. * variable-length argument list.
  436. */
  437. number = analyze(cp, p_is_s, &popcount);
  438. if (fmt_buff == 0)
  439. return 0;
  440. for (i = 0; i < max(popcount, number); i++) {
  441. /*
  442. * A few caps (such as plab_norm) have string-valued parms.
  443. * We'll have to assume that the caller knows the difference, since
  444. * a char* and an int may not be the same size on the stack.
  445. */
  446. if (p_is_s[i] != 0) {
  447. p_is_s[i] = va_arg(ap, char *);
  448. } else {
  449. param[i] = va_arg(ap, long int);
  450. }
  451. }
  452. /*
  453. * This is a termcap compatibility hack. If there are no explicit pop
  454. * operations in the string, load the stack in such a way that
  455. * successive pops will grab successive parameters. That will make
  456. * the expansion of (for example) \E[%d;%dH work correctly in termcap
  457. * style, which means tparam() will expand termcap strings OK.
  458. */
  459. stack_ptr = 0;
  460. if (popcount == 0) {
  461. popcount = number;
  462. for (i = number - 1; i >= 0; i--)
  463. npush(param[i]);
  464. }
  465. while ((cp - string) < (int) len2) {
  466. if (*cp != '%') {
  467. save_char(UChar(*cp));
  468. } else {
  469. tparam_base = cp++;
  470. cp = parse_format(cp, fmt_buff, &len);
  471. switch (*cp) {
  472. default:
  473. break;
  474. case '%':
  475. save_char('%');
  476. break;
  477. case 'd': /* FALLTHRU */
  478. case 'o': /* FALLTHRU */
  479. case 'x': /* FALLTHRU */
  480. case 'X': /* FALLTHRU */
  481. save_number(fmt_buff, npop(), len);
  482. break;
  483. case 'c': /* FALLTHRU */
  484. save_char(npop());
  485. break;
  486. case 'l':
  487. save_number("%d", (int) grub_strlen(spop()), 0);
  488. break;
  489. case 's':
  490. save_text(fmt_buff, spop(), len);
  491. break;
  492. case 'p':
  493. cp++;
  494. i = (UChar(*cp) - '1');
  495. if (i >= 0 && i < NUM_PARM) {
  496. if (p_is_s[i])
  497. spush(p_is_s[i]);
  498. else
  499. npush(param[i]);
  500. }
  501. break;
  502. case 'P':
  503. cp++;
  504. if (isUPPER(*cp)) {
  505. i = (UChar(*cp) - 'A');
  506. static_vars[i] = npop();
  507. } else if (isLOWER(*cp)) {
  508. i = (UChar(*cp) - 'a');
  509. dynamic_var[i] = npop();
  510. }
  511. break;
  512. case 'g':
  513. cp++;
  514. if (isUPPER(*cp)) {
  515. i = (UChar(*cp) - 'A');
  516. npush(static_vars[i]);
  517. } else if (isLOWER(*cp)) {
  518. i = (UChar(*cp) - 'a');
  519. npush(dynamic_var[i]);
  520. }
  521. break;
  522. case '\'':
  523. cp++;
  524. npush(UChar(*cp));
  525. cp++;
  526. break;
  527. case '{':
  528. number = 0;
  529. cp++;
  530. while (isdigit(UChar(*cp))) {
  531. number = (number * 10) + (UChar(*cp) - '0');
  532. cp++;
  533. }
  534. npush(number);
  535. break;
  536. case '+':
  537. npush(npop() + npop());
  538. break;
  539. case '-':
  540. y = npop();
  541. x = npop();
  542. npush(x - y);
  543. break;
  544. case '*':
  545. npush(npop() * npop());
  546. break;
  547. case '/':
  548. y = npop();
  549. x = npop();
  550. /* GRUB has no signed divisions. */
  551. npush(y ? ((unsigned)x / (unsigned)y) : 0);
  552. break;
  553. case 'm':
  554. y = npop();
  555. x = npop();
  556. /* GRUB has no signed divisions. */
  557. npush(y ? ((unsigned)x % (unsigned)y) : 0);
  558. break;
  559. case 'A':
  560. npush(npop() && npop());
  561. break;
  562. case 'O':
  563. npush(npop() || npop());
  564. break;
  565. case '&':
  566. npush(npop() & npop());
  567. break;
  568. case '|':
  569. npush(npop() | npop());
  570. break;
  571. case '^':
  572. npush(npop() ^ npop());
  573. break;
  574. case '=':
  575. y = npop();
  576. x = npop();
  577. npush(x == y);
  578. break;
  579. case '<':
  580. y = npop();
  581. x = npop();
  582. npush(x < y);
  583. break;
  584. case '>':
  585. y = npop();
  586. x = npop();
  587. npush(x > y);
  588. break;
  589. case '!':
  590. npush(!npop());
  591. break;
  592. case '~':
  593. npush(~npop());
  594. break;
  595. case 'i':
  596. if (p_is_s[0] == 0)
  597. param[0]++;
  598. if (p_is_s[1] == 0)
  599. param[1]++;
  600. break;
  601. case '?':
  602. break;
  603. case 't':
  604. x = npop();
  605. if (!x) {
  606. /* scan forward for %e or %; at level zero */
  607. cp++;
  608. level = 0;
  609. while (*cp) {
  610. if (*cp == '%') {
  611. cp++;
  612. if (*cp == '?')
  613. level++;
  614. else if (*cp == ';') {
  615. if (level > 0)
  616. level--;
  617. else
  618. break;
  619. } else if (*cp == 'e' && level == 0)
  620. break;
  621. }
  622. if (*cp)
  623. cp++;
  624. }
  625. }
  626. break;
  627. case 'e':
  628. /* scan forward for a %; at level zero */
  629. cp++;
  630. level = 0;
  631. while (*cp) {
  632. if (*cp == '%') {
  633. cp++;
  634. if (*cp == '?')
  635. level++;
  636. else if (*cp == ';') {
  637. if (level > 0)
  638. level--;
  639. else
  640. break;
  641. }
  642. }
  643. if (*cp)
  644. cp++;
  645. }
  646. break;
  647. case ';':
  648. break;
  649. } /* endswitch (*cp) */
  650. } /* endelse (*cp == '%') */
  651. if (*cp == '\0')
  652. break;
  653. cp++;
  654. } /* endwhile (*cp) */
  655. get_space(1);
  656. out_buff[out_used] = '\0';
  657. return (out_buff);
  658. }
  659. const char *
  660. grub_terminfo_tparm (const char *string, ...)
  661. {
  662. va_list ap;
  663. char *result;
  664. if (!string)
  665. return "";
  666. va_start (ap, string);
  667. result = tparam_internal (string, ap);
  668. va_end (ap);
  669. return result;
  670. }