tparm.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  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. static inline void
  146. save_text(const char *fmt, const char *s, int len)
  147. {
  148. grub_size_t s_len = grub_strlen(s);
  149. if (len > (int) s_len)
  150. s_len = len;
  151. get_space(s_len + 1);
  152. (void) grub_snprintf(out_buff + out_used, s_len + 1, fmt, s);
  153. out_used += grub_strlen(out_buff + out_used);
  154. }
  155. static inline void
  156. save_number(const char *fmt, int number, int len)
  157. {
  158. if (len < 30)
  159. len = 30; /* actually log10(MAX_INT)+1 */
  160. get_space((unsigned) len + 1);
  161. (void) grub_snprintf(out_buff + out_used, len + 1, fmt, number);
  162. out_used += grub_strlen(out_buff + out_used);
  163. }
  164. static inline void
  165. save_char(int c)
  166. {
  167. if (c == 0)
  168. c = 0200;
  169. get_space(1);
  170. out_buff[out_used++] = c;
  171. }
  172. static inline void
  173. npush(int x)
  174. {
  175. if (stack_ptr < STACKSIZE) {
  176. stack[stack_ptr].num_type = TRUE;
  177. stack[stack_ptr].data.num = x;
  178. stack_ptr++;
  179. }
  180. }
  181. static inline int
  182. npop(void)
  183. {
  184. int result = 0;
  185. if (stack_ptr > 0) {
  186. stack_ptr--;
  187. if (stack[stack_ptr].num_type)
  188. result = stack[stack_ptr].data.num;
  189. }
  190. return result;
  191. }
  192. static inline void
  193. spush(char *x)
  194. {
  195. if (stack_ptr < STACKSIZE) {
  196. stack[stack_ptr].num_type = FALSE;
  197. stack[stack_ptr].data.str = x;
  198. stack_ptr++;
  199. }
  200. }
  201. static inline char *
  202. spop(void)
  203. {
  204. static char dummy[] = ""; /* avoid const-cast */
  205. char *result = dummy;
  206. if (stack_ptr > 0) {
  207. stack_ptr--;
  208. if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0)
  209. result = stack[stack_ptr].data.str;
  210. }
  211. return result;
  212. }
  213. static inline const char *
  214. parse_format(const char *s, char *format, int *len)
  215. {
  216. *len = 0;
  217. if (format != 0) {
  218. grub_bool_t done = FALSE;
  219. grub_bool_t allowminus = FALSE;
  220. grub_bool_t dot = FALSE;
  221. grub_bool_t err = FALSE;
  222. char *fmt = format;
  223. int my_width = 0;
  224. int my_prec = 0;
  225. int value = 0;
  226. *len = 0;
  227. *format++ = '%';
  228. while (*s != '\0' && !done) {
  229. switch (*s) {
  230. case 'c': /* FALLTHRU */
  231. case 'd': /* FALLTHRU */
  232. case 'o': /* FALLTHRU */
  233. case 'x': /* FALLTHRU */
  234. case 'X': /* FALLTHRU */
  235. case 's':
  236. *format++ = *s;
  237. done = TRUE;
  238. break;
  239. case '.':
  240. *format++ = *s++;
  241. if (dot) {
  242. err = TRUE;
  243. } else { /* value before '.' is the width */
  244. dot = TRUE;
  245. my_width = value;
  246. }
  247. value = 0;
  248. break;
  249. case '#':
  250. *format++ = *s++;
  251. break;
  252. case ' ':
  253. *format++ = *s++;
  254. break;
  255. case ':':
  256. s++;
  257. allowminus = TRUE;
  258. break;
  259. case '-':
  260. if (allowminus) {
  261. *format++ = *s++;
  262. } else {
  263. done = TRUE;
  264. }
  265. break;
  266. default:
  267. if (isdigit(UChar(*s))) {
  268. value = (value * 10) + (*s - '0');
  269. if (value > 10000)
  270. err = TRUE;
  271. *format++ = *s++;
  272. } else {
  273. done = TRUE;
  274. }
  275. }
  276. }
  277. /*
  278. * If we found an error, ignore (and remove) the flags.
  279. */
  280. if (err) {
  281. my_width = my_prec = value = 0;
  282. format = fmt;
  283. *format++ = '%';
  284. *format++ = *s;
  285. }
  286. /*
  287. * Any value after '.' is the precision. If we did not see '.', then
  288. * the value is the width.
  289. */
  290. if (dot)
  291. my_prec = value;
  292. else
  293. my_width = value;
  294. *format = '\0';
  295. /* return maximum string length in print */
  296. *len = (my_width > my_prec) ? my_width : my_prec;
  297. }
  298. return s;
  299. }
  300. /*
  301. * Analyze the string to see how many parameters we need from the varargs list,
  302. * and what their types are. We will only accept string parameters if they
  303. * appear as a %l or %s format following an explicit parameter reference (e.g.,
  304. * %p2%s). All other parameters are numbers.
  305. *
  306. * 'number' counts coarsely the number of pop's we see in the string, and
  307. * 'popcount' shows the highest parameter number in the string. We would like
  308. * to simply use the latter count, but if we are reading termcap strings, there
  309. * may be cases that we cannot see the explicit parameter numbers.
  310. */
  311. static inline int
  312. analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
  313. {
  314. grub_size_t len2;
  315. int i;
  316. int lastpop = -1;
  317. int len;
  318. int number = 0;
  319. const char *cp = string;
  320. static char dummy[] = "";
  321. *popcount = 0;
  322. if (cp == 0)
  323. return 0;
  324. if ((len2 = grub_strlen(cp)) > fmt_size) {
  325. fmt_size = len2 + fmt_size + 2;
  326. if ((fmt_buff = grub_realloc(fmt_buff, fmt_size*sizeof(char))) == 0)
  327. return 0;
  328. }
  329. grub_memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
  330. while ((cp - string) < (int) len2) {
  331. if (*cp == '%') {
  332. cp++;
  333. cp = parse_format(cp, fmt_buff, &len);
  334. switch (*cp) {
  335. default:
  336. break;
  337. case 'd': /* FALLTHRU */
  338. case 'o': /* FALLTHRU */
  339. case 'x': /* FALLTHRU */
  340. case 'X': /* FALLTHRU */
  341. case 'c': /* FALLTHRU */
  342. if (lastpop <= 0)
  343. number++;
  344. lastpop = -1;
  345. break;
  346. case 'l':
  347. case 's':
  348. if (lastpop > 0)
  349. p_is_s[lastpop - 1] = dummy;
  350. ++number;
  351. break;
  352. case 'p':
  353. cp++;
  354. i = (UChar(*cp) - '0');
  355. if (i >= 0 && i <= NUM_PARM) {
  356. lastpop = i;
  357. if (lastpop > *popcount)
  358. *popcount = lastpop;
  359. }
  360. break;
  361. case 'P':
  362. ++number;
  363. ++cp;
  364. break;
  365. case 'g':
  366. cp++;
  367. break;
  368. case '\'':
  369. cp += 2;
  370. lastpop = -1;
  371. break;
  372. case '{':
  373. cp++;
  374. while (isdigit(UChar(*cp))) {
  375. cp++;
  376. }
  377. break;
  378. case '+':
  379. case '-':
  380. case '*':
  381. case '/':
  382. case 'm':
  383. case 'A':
  384. case 'O':
  385. case '&':
  386. case '|':
  387. case '^':
  388. case '=':
  389. case '<':
  390. case '>':
  391. lastpop = -1;
  392. number += 2;
  393. break;
  394. case '!':
  395. case '~':
  396. lastpop = -1;
  397. ++number;
  398. break;
  399. case 'i':
  400. /* will add 1 to first (usually two) parameters */
  401. break;
  402. }
  403. }
  404. if (*cp != '\0')
  405. cp++;
  406. }
  407. if (number > NUM_PARM)
  408. number = NUM_PARM;
  409. return number;
  410. }
  411. static inline char *
  412. tparam_internal(const char *string, va_list ap)
  413. {
  414. char *p_is_s[NUM_PARM];
  415. long param[NUM_PARM];
  416. int popcount;
  417. int number;
  418. int len;
  419. int level;
  420. int x, y;
  421. int i;
  422. const char *cp = string;
  423. grub_size_t len2;
  424. static int dynamic_var[NUM_VARS];
  425. static int static_vars[NUM_VARS];
  426. if (cp == 0)
  427. return 0;
  428. out_used = out_size = fmt_size = 0;
  429. len2 = (int) grub_strlen(cp);
  430. /*
  431. * Find the highest parameter-number referred to in the format string.
  432. * Use this value to limit the number of arguments copied from the
  433. * variable-length argument list.
  434. */
  435. number = analyze(cp, p_is_s, &popcount);
  436. if (fmt_buff == 0)
  437. return 0;
  438. for (i = 0; i < max(popcount, number); i++) {
  439. /*
  440. * A few caps (such as plab_norm) have string-valued parms.
  441. * We'll have to assume that the caller knows the difference, since
  442. * a char* and an int may not be the same size on the stack.
  443. */
  444. if (p_is_s[i] != 0) {
  445. p_is_s[i] = va_arg(ap, char *);
  446. } else {
  447. param[i] = va_arg(ap, long int);
  448. }
  449. }
  450. /*
  451. * This is a termcap compatibility hack. If there are no explicit pop
  452. * operations in the string, load the stack in such a way that
  453. * successive pops will grab successive parameters. That will make
  454. * the expansion of (for example) \E[%d;%dH work correctly in termcap
  455. * style, which means tparam() will expand termcap strings OK.
  456. */
  457. stack_ptr = 0;
  458. if (popcount == 0) {
  459. popcount = number;
  460. for (i = number - 1; i >= 0; i--)
  461. npush(param[i]);
  462. }
  463. while ((cp - string) < (int) len2) {
  464. if (*cp != '%') {
  465. save_char(UChar(*cp));
  466. } else {
  467. tparam_base = cp++;
  468. cp = parse_format(cp, fmt_buff, &len);
  469. switch (*cp) {
  470. default:
  471. break;
  472. case '%':
  473. save_char('%');
  474. break;
  475. case 'd': /* FALLTHRU */
  476. case 'o': /* FALLTHRU */
  477. case 'x': /* FALLTHRU */
  478. case 'X': /* FALLTHRU */
  479. save_number(fmt_buff, npop(), len);
  480. break;
  481. case 'c': /* FALLTHRU */
  482. save_char(npop());
  483. break;
  484. case 'l':
  485. save_number("%d", (int) grub_strlen(spop()), 0);
  486. break;
  487. case 's':
  488. save_text(fmt_buff, spop(), len);
  489. break;
  490. case 'p':
  491. cp++;
  492. i = (UChar(*cp) - '1');
  493. if (i >= 0 && i < NUM_PARM) {
  494. if (p_is_s[i])
  495. spush(p_is_s[i]);
  496. else
  497. npush(param[i]);
  498. }
  499. break;
  500. case 'P':
  501. cp++;
  502. if (isUPPER(*cp)) {
  503. i = (UChar(*cp) - 'A');
  504. static_vars[i] = npop();
  505. } else if (isLOWER(*cp)) {
  506. i = (UChar(*cp) - 'a');
  507. dynamic_var[i] = npop();
  508. }
  509. break;
  510. case 'g':
  511. cp++;
  512. if (isUPPER(*cp)) {
  513. i = (UChar(*cp) - 'A');
  514. npush(static_vars[i]);
  515. } else if (isLOWER(*cp)) {
  516. i = (UChar(*cp) - 'a');
  517. npush(dynamic_var[i]);
  518. }
  519. break;
  520. case '\'':
  521. cp++;
  522. npush(UChar(*cp));
  523. cp++;
  524. break;
  525. case '{':
  526. number = 0;
  527. cp++;
  528. while (isdigit(UChar(*cp))) {
  529. number = (number * 10) + (UChar(*cp) - '0');
  530. cp++;
  531. }
  532. npush(number);
  533. break;
  534. case '+':
  535. npush(npop() + npop());
  536. break;
  537. case '-':
  538. y = npop();
  539. x = npop();
  540. npush(x - y);
  541. break;
  542. case '*':
  543. npush(npop() * npop());
  544. break;
  545. case '/':
  546. y = npop();
  547. x = npop();
  548. npush(y ? (x / y) : 0);
  549. break;
  550. case 'm':
  551. y = npop();
  552. x = npop();
  553. npush(y ? (x % y) : 0);
  554. break;
  555. case 'A':
  556. npush(npop() && npop());
  557. break;
  558. case 'O':
  559. npush(npop() || npop());
  560. break;
  561. case '&':
  562. npush(npop() & npop());
  563. break;
  564. case '|':
  565. npush(npop() | npop());
  566. break;
  567. case '^':
  568. npush(npop() ^ npop());
  569. break;
  570. case '=':
  571. y = npop();
  572. x = npop();
  573. npush(x == y);
  574. break;
  575. case '<':
  576. y = npop();
  577. x = npop();
  578. npush(x < y);
  579. break;
  580. case '>':
  581. y = npop();
  582. x = npop();
  583. npush(x > y);
  584. break;
  585. case '!':
  586. npush(!npop());
  587. break;
  588. case '~':
  589. npush(~npop());
  590. break;
  591. case 'i':
  592. if (p_is_s[0] == 0)
  593. param[0]++;
  594. if (p_is_s[1] == 0)
  595. param[1]++;
  596. break;
  597. case '?':
  598. break;
  599. case 't':
  600. x = npop();
  601. if (!x) {
  602. /* scan forward for %e or %; at level zero */
  603. cp++;
  604. level = 0;
  605. while (*cp) {
  606. if (*cp == '%') {
  607. cp++;
  608. if (*cp == '?')
  609. level++;
  610. else if (*cp == ';') {
  611. if (level > 0)
  612. level--;
  613. else
  614. break;
  615. } else if (*cp == 'e' && level == 0)
  616. break;
  617. }
  618. if (*cp)
  619. cp++;
  620. }
  621. }
  622. break;
  623. case 'e':
  624. /* scan forward for a %; at level zero */
  625. cp++;
  626. level = 0;
  627. while (*cp) {
  628. if (*cp == '%') {
  629. cp++;
  630. if (*cp == '?')
  631. level++;
  632. else if (*cp == ';') {
  633. if (level > 0)
  634. level--;
  635. else
  636. break;
  637. }
  638. }
  639. if (*cp)
  640. cp++;
  641. }
  642. break;
  643. case ';':
  644. break;
  645. } /* endswitch (*cp) */
  646. } /* endelse (*cp == '%') */
  647. if (*cp == '\0')
  648. break;
  649. cp++;
  650. } /* endwhile (*cp) */
  651. get_space(1);
  652. out_buff[out_used] = '\0';
  653. return (out_buff);
  654. }
  655. char *
  656. grub_terminfo_tparm (const char *string, ...)
  657. {
  658. va_list ap;
  659. char *result;
  660. va_start (ap, string);
  661. result = tparam_internal (string, ap);
  662. va_end (ap);
  663. return result;
  664. }