1 /* Substitution of parameters in strings from terminal descriptions.
2 Copyright (C) 2006, 2012 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 /* Originally by Ross Ridge, Public Domain, 92/02/01 07:30:36 */
18
19 #include <config.h>
20
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "c-ctype.h"
26
27 #ifdef USE_SCCS_IDS
28 static const char SCCSid[] = "@(#) mytinfo tparm.c 3.2 92/02/01 public domain, By Ross Ridge";
29 #endif
30
31 #ifndef MAX_PUSHED
32 #define MAX_PUSHED 32
33 #endif
34
35 #define ARG 1
36 #define NUM 2
37
38 #define INTEGER 1
39 #define STRING 2
40
41 #define MAX_LINE 640
42
43 typedef struct stack_str
44 {
45 int type;
46 int argnum;
47 int value;
48 } stack;
49
50 static stack S[MAX_PUSHED];
51 static stack vars['z'-'a'+1];
52 static int pos = 0;
53
54 static
55 struct arg_str
56 {
57 int type;
58 int integer;
59 char *string;
60 } arg_list[10];
61
62 static int argcnt;
63
64 static va_list tparm_args;
65
66 static int
pusharg(int arg)67 pusharg (int arg)
68 {
69 if (pos == MAX_PUSHED)
70 return 1;
71 S[pos].type = ARG;
72 S[pos++].argnum = arg;
73 return 0;
74 }
75
76 static int
pushnum(int num)77 pushnum (int num)
78 {
79 if (pos == MAX_PUSHED)
80 return 1;
81 S[pos].type = NUM;
82 S[pos++].value = num;
83 return 0;
84 }
85
86 static int
getarg(int argnum,int type,void * p)87 getarg (int argnum, int type, void *p)
88 {
89 while (argcnt < argnum)
90 {
91 arg_list[argcnt].type = INTEGER;
92 arg_list[argcnt++].integer = (int) va_arg (tparm_args, int);
93 }
94 if (argcnt > argnum)
95 {
96 if (arg_list[argnum].type != type)
97 return 1;
98 else if (type == STRING)
99 *(char **)p = arg_list[argnum].string;
100 else
101 *(int *)p = arg_list[argnum].integer;
102 }
103 else
104 {
105 arg_list[argcnt].type = type;
106 if (type == STRING)
107 *(char **)p = arg_list[argcnt++].string = (char *) va_arg (tparm_args, char *);
108 else
109 *(int *)p = arg_list[argcnt++].integer = (int) va_arg (tparm_args, int);
110 }
111 return 0;
112 }
113
114 static int
popstring(char ** str)115 popstring (char **str)
116 {
117 if (pos-- == 0)
118 return 1;
119 if (S[pos].type != ARG)
120 return 1;
121 return getarg (S[pos].argnum, STRING, str);
122 }
123
124 static int
popnum(int * num)125 popnum (int *num)
126 {
127 if (pos-- == 0)
128 return 1;
129 switch (S[pos].type)
130 {
131 case ARG:
132 return getarg (S[pos].argnum, INTEGER, num);
133 case NUM:
134 *num = S[pos].value;
135 return 0;
136 }
137 return 1;
138 }
139
140 static int
cvtchar(const char * sp,char * c)141 cvtchar (const char *sp, char *c)
142 {
143 switch (*sp)
144 {
145 case '\\':
146 switch (*++sp)
147 {
148 case '\'':
149 case '$':
150 case '\\':
151 case '%':
152 *c = *sp;
153 return 2;
154 case '\0':
155 *c = '\\';
156 return 1;
157 case '0':
158 if (sp[1] == '0' && sp[2] == '0')
159 {
160 *c = '\0';
161 return 4;
162 }
163 *c = '\200'; /* '\0' ???? */
164 return 2;
165 default:
166 *c = *sp;
167 return 2;
168 }
169 default:
170 *c = *sp;
171 return 1;
172 }
173 }
174
175 /* sigh... this has got to be the ugliest code I've ever written.
176 Trying to handle everything has its cost, I guess.
177
178 It actually isn't to hard to figure out if a given % code is supposed
179 to be interpeted with its termcap or terminfo meaning since almost
180 all terminfo codes are invalid unless something has been pushed on
181 the stack and termcap strings will never push things on the stack
182 (%p isn't used by termcap). So where we have a choice we make the
183 decision by wether or not somthing has been pushed on the stack.
184 The static variable termcap keeps track of this; it starts out set
185 to 1 and is incremented as each argument processed by a termcap % code,
186 however if something is pushed on the stack it's set to 0 and the
187 rest of the % codes are interpeted as terminfo % codes. Another way
188 of putting it is that if termcap equals one we haven't decided either
189 way yet, if it equals zero we're looking for terminfo codes, and if
190 its greater than 1 we're looking for termcap codes.
191
192 Terminfo % codes:
193
194 %% output a '%'
195 %[[:][-+# ][width][.precision]][doxXs]
196 output pop according to the printf format
197 %c output pop as a char
198 %'c' push character constant c.
199 %{n} push decimal constant n.
200 %p[1-9] push paramter [1-9]
201 %g[a-z] push variable [a-z]
202 %P[a-z] put pop in variable [a-z]
203 %l push the length of pop (a string)
204 %+ add pop to pop and push the result
205 %- subtract pop from pop and push the result
206 %* multiply pop and pop and push the result
207 %& bitwise and pop and pop and push the result
208 %| bitwise or pop and pop and push the result
209 %^ bitwise xor pop and pop and push the result
210 %~ push the bitwise not of pop
211 %= compare if pop and pop are equal and push the result
212 %> compare if pop is less than pop and push the result
213 %< compare if pop is greater than pop and push the result
214 %A logical and pop and pop and push the result
215 %O logical or pop and pop and push the result
216 %! push the logical not of pop
217 %? condition %t if_true [%e if_false] %;
218 if condtion evaulates as true then evaluate if_true,
219 else evaluate if_false. elseif's can be done:
220 %? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %;
221 %i add one to parameters 1 and 2. (ANSI)
222
223 Termcap Codes:
224
225 %% output a %
226 %. output parameter as a character
227 %d output parameter as a decimal number
228 %2 output parameter in printf format %02d
229 %3 output parameter in printf format %03d
230 %+x add the character x to parameter and output it as a character
231 (UW) %-x subtract parameter FROM the character x and output it as a char
232 (UW) %ax add the character x to parameter
233 (GNU) %a[+*-/=][cp]x
234 GNU arithmetic.
235 (UW) %sx subtract parameter FROM the character x
236 %>xy if parameter > character x then add character y to parameter
237 %B convert to BCD (parameter = (parameter/10)*16 + parameter%16)
238 %D Delta Data encode (parameter = parameter - 2*(paramter%16))
239 %i increment the first two parameters by one
240 %n xor the first two parameters by 0140
241 (GNU) %m xor the first two parameters by 0177
242 %r swap the first two parameters
243 (GNU) %b backup to previous parameter
244 (GNU) %f skip this parameter
245
246 Note the two definitions of %a, the GNU definition is used if the characters
247 after the 'a' are valid, otherwise the UW definition is used.
248
249 (GNU) used by GNU Emacs termcap libraries
250 (UW) used by the University of Waterloo (MFCF) termcap libraries
251
252 */
253
254 char *
tparm(const char * str,...)255 tparm (const char *str, ...)
256 {
257 static int termcap;
258 static char OOPS[] = "OOPS";
259 static char buf[MAX_LINE];
260 const char *sp;
261 char *dp;
262 char *fmt;
263 char scan_for;
264 int scan_depth;
265 int if_depth;
266 char fmt_buf[MAX_LINE];
267 char sbuf[MAX_LINE];
268
269 va_start (tparm_args, str);
270
271 sp = str;
272 dp = buf;
273 scan_for = 0;
274 scan_depth = 0;
275 if_depth = 0;
276 argcnt = 0;
277 pos = 0;
278 termcap = 1;
279 while (*sp != '\0')
280 {
281 switch (*sp)
282 {
283 case '\\':
284 if (scan_for)
285 {
286 if (*++sp != '\0')
287 sp++;
288 break;
289 }
290 *dp++ = *sp++;
291 if (*sp != '\0')
292 *dp++ = *sp++;
293 break;
294 case '%':
295 sp++;
296 if (scan_for)
297 {
298 if (*sp == scan_for && if_depth == scan_depth)
299 {
300 if (scan_for == ';')
301 if_depth--;
302 scan_for = 0;
303 }
304 else if (*sp == '?')
305 if_depth++;
306 else if (*sp == ';')
307 {
308 if (if_depth == 0)
309 return OOPS;
310 else
311 if_depth--;
312 }
313 sp++;
314 break;
315 }
316 fmt = NULL;
317 switch (*sp)
318 {
319 case '%':
320 *dp++ = *sp++;
321 break;
322 case '+':
323 if (!termcap)
324 {
325 int i, j;
326 if (popnum (&j) || popnum (&i))
327 return OOPS;
328 i += j;
329 if (pushnum (i))
330 return OOPS;
331 sp++;
332 break;
333 }
334 /* FALLTHROUGH */
335 case 'C':
336 if (*sp == 'C')
337 {
338 int i;
339 if (getarg (termcap - 1, INTEGER, &i))
340 return OOPS;
341 if (i >= 96)
342 {
343 i /= 96;
344 if (i == '$')
345 *dp++ = '\\';
346 *dp++ = i;
347 }
348 }
349 fmt = "%c";
350 /* FALLTHROUGH */
351 case 'a':
352 if (!termcap)
353 return OOPS;
354 {
355 int i;
356 if (getarg (termcap - 1, INTEGER, &i))
357 return OOPS;
358 if (*++sp == '\0')
359 return OOPS;
360 if ((sp[1] == 'p' || sp[1] == 'c')
361 && sp[2] != '\0' && fmt == NULL)
362 {
363 /* GNU arithmetic parameter, what they really need is
364 terminfo. */
365 int val;
366 int lc;
367 if (sp[1] == 'p'
368 && getarg (termcap - 1 + sp[2] - '@', INTEGER, &val))
369 return OOPS;
370 if (sp[1] == 'c')
371 {
372 char c;
373 lc = cvtchar (sp + 2, &c) + 2;
374 /* Mask out 8th bit so \200 can be used for \0 as per
375 GNU docs. */
376 val = c & 0177;
377 }
378 else
379 lc = 2;
380 switch (sp[0])
381 {
382 case '=':
383 break;
384 case '+':
385 val = i + val;
386 break;
387 case '-':
388 val = i - val;
389 break;
390 case '*':
391 val = i * val;
392 break;
393 case '/':
394 val = i / val;
395 break;
396 default:
397 /* Not really GNU's %a after all... */
398 {
399 char c;
400 lc = cvtchar (sp, &c);
401 val = c + i;
402 }
403 break;
404 }
405 arg_list[termcap - 1].integer = val;
406 sp += lc;
407 break;
408 }
409 {
410 char c;
411 sp += cvtchar (sp, &c);
412 arg_list[termcap - 1].integer = c + i;
413 }
414 }
415 if (fmt == NULL)
416 break;
417 sp--;
418 /* FALLTHROUGH */
419 case '-':
420 if (!termcap)
421 {
422 int i, j;
423 if (popnum (&j) || popnum (&i))
424 return OOPS;
425 i -= j;
426 if (pushnum (i))
427 return OOPS;
428 sp++;
429 break;
430 }
431 fmt = "%c";
432 /* FALLTHROUGH */
433 case 's':
434 if (termcap && (fmt == NULL || *sp == '-'))
435 {
436 int i;
437 if (getarg (termcap - 1, INTEGER, &i))
438 return OOPS;
439 if (*++sp == '\0')
440 return OOPS;
441 {
442 char c;
443 sp += cvtchar (sp, &c);
444 arg_list[termcap - 1].integer = c - i;
445 }
446 if (fmt == NULL)
447 break;
448 sp--;
449 }
450 if (!termcap)
451 return OOPS;
452 /* FALLTHROUGH */
453 case '.':
454 if (termcap && fmt == NULL)
455 fmt = "%c";
456 /* FALLTHROUGH */
457 case 'd':
458 if (termcap && fmt == NULL)
459 fmt = "%d";
460 /* FALLTHROUGH */
461 case '2':
462 if (termcap && fmt == NULL)
463 fmt = "%02d";
464 /* FALLTHROUGH */
465 case '3':
466 if (termcap && fmt == NULL)
467 fmt = "%03d";
468 /* FALLTHROUGH */
469 case ':': case ' ': case '#': case 'u':
470 case 'x': case 'X': case 'o': case 'c':
471 case '0': case '1': case '4': case '5':
472 case '6': case '7': case '8': case '9':
473 if (fmt == NULL)
474 {
475 if (termcap)
476 return OOPS;
477 if (*sp == ':')
478 sp++;
479 fmt = fmt_buf;
480 *fmt++ = '%';
481 while (*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd'
482 && *sp != 'o' && *sp != 'c' && *sp != 'u')
483 {
484 if (*sp == '\0')
485 return OOPS;
486 *fmt++ = *sp++;
487 }
488 *fmt++ = *sp;
489 *fmt = '\0';
490 fmt = fmt_buf;
491 }
492 {
493 char conv_char = fmt[strlen (fmt) - 1];
494 if (conv_char == 's')
495 {
496 char *s;
497 if (popstring (&s))
498 return OOPS;
499 sprintf (sbuf, fmt, s);
500 }
501 else
502 {
503 int i;
504 if (termcap)
505 {
506 if (getarg (termcap++ - 1, INTEGER, &i))
507 return OOPS;
508 }
509 else
510 if (popnum (&i))
511 return OOPS;
512 if (i == 0 && conv_char == 'c')
513 strcpy (sbuf, "\000");
514 else
515 sprintf (sbuf, fmt, i);
516 }
517 }
518 sp++;
519 fmt = sbuf;
520 while (*fmt != '\0')
521 {
522 if (*fmt == '$')
523 *dp++ = '\\';
524 *dp++ = *fmt++;
525 }
526 break;
527 case 'r':
528 {
529 int i;
530 if (!termcap || getarg (1, INTEGER, &i))
531 return OOPS;
532 arg_list[1].integer = arg_list[0].integer;
533 arg_list[0].integer = i;
534 }
535 sp++;
536 break;
537 case 'i':
538 {
539 int i;
540 if (getarg (1, INTEGER, &i) || arg_list[0].type != INTEGER)
541 return OOPS;
542 }
543 arg_list[1].integer++;
544 arg_list[0].integer++;
545 sp++;
546 break;
547 case 'n':
548 {
549 int i;
550 if (!termcap || getarg (1, INTEGER, &i))
551 return OOPS;
552 }
553 arg_list[0].integer ^= 0140;
554 arg_list[1].integer ^= 0140;
555 sp++;
556 break;
557 case '>':
558 if (!termcap)
559 {
560 int i, j;
561 if (popnum (&j) || popnum (&i))
562 return OOPS;
563 i = (i > j);
564 if (pushnum (i))
565 return OOPS;
566 sp++;
567 break;
568 }
569 {
570 int i;
571 if (getarg (termcap-1, INTEGER, &i))
572 return OOPS;
573 {
574 char c;
575 sp += cvtchar (sp, &c);
576 if (i > c)
577 {
578 sp += cvtchar (sp, &c);
579 arg_list[termcap-1].integer += c;
580 }
581 else
582 sp += cvtchar (sp, &c);
583 }
584 }
585 sp++;
586 break;
587 case 'B':
588 {
589 int i;
590 if (!termcap || getarg (termcap-1, INTEGER, &i))
591 return OOPS;
592 arg_list[termcap-1].integer = 16 * (i / 10) + i % 10;
593 }
594 sp++;
595 break;
596 case 'D':
597 {
598 int i;
599 if (!termcap || getarg (termcap-1, INTEGER, &i))
600 return OOPS;
601 arg_list[termcap-1].integer = i - 2 * (i % 16);
602 }
603 sp++;
604 break;
605 case 'p':
606 if (termcap > 1)
607 return OOPS;
608 if (*++sp == '\0')
609 return OOPS;
610 {
611 int i = (*sp == '0' ? 9 : *sp - '1');
612 if (i < 0 || i > 9)
613 return OOPS;
614 if (pusharg (i))
615 return OOPS;
616 }
617 termcap = 0;
618 sp++;
619 break;
620 case 'P':
621 if (termcap || *++sp == '\0')
622 return OOPS;
623 {
624 int i = *sp++ - 'a';
625 if (i < 0 || i > 25)
626 return OOPS;
627 if (pos-- == 0)
628 return OOPS;
629 switch (vars[i].type = S[pos].type)
630 {
631 case ARG:
632 vars[i].argnum = S[pos].argnum;
633 break;
634 case NUM:
635 vars[i].value = S[pos].value;
636 break;
637 }
638 }
639 break;
640 case 'g':
641 if (termcap || *++sp == '\0')
642 return OOPS;
643 {
644 int i = *sp++ - 'a';
645 if (i < 0 || i > 25)
646 return OOPS;
647 switch (vars[i].type)
648 {
649 case ARG:
650 if (pusharg (vars[i].argnum))
651 return OOPS;
652 break;
653 case NUM:
654 if (pushnum (vars[i].value))
655 return OOPS;
656 break;
657 }
658 }
659 break;
660 case '\'':
661 if (termcap > 1)
662 return OOPS;
663 if (*++sp == '\0')
664 return OOPS;
665 {
666 char c;
667 sp += cvtchar (sp, &c);
668 if (pushnum (c) || *sp++ != '\'')
669 return OOPS;
670 }
671 termcap = 0;
672 break;
673 case '{':
674 if (termcap > 1)
675 return OOPS;
676 {
677 int i;
678 i = 0;
679 sp++;
680 while (c_isdigit (*sp))
681 i = 10 * i + *sp++ - '0';
682 if (*sp++ != '}' || pushnum (i))
683 return OOPS;
684 }
685 termcap = 0;
686 break;
687 case 'l':
688 {
689 int i;
690 char *s;
691 if (termcap || popstring (&s))
692 return OOPS;
693 i = strlen (s);
694 if (pushnum (i))
695 return OOPS;
696 }
697 sp++;
698 break;
699 case '*':
700 {
701 int i, j;
702 if (termcap || popnum (&j) || popnum (&i))
703 return OOPS;
704 i *= j;
705 if (pushnum (i))
706 return OOPS;
707 }
708 sp++;
709 break;
710 case '/':
711 {
712 int i, j;
713 if (termcap || popnum (&j) || popnum (&i))
714 return OOPS;
715 i /= j;
716 if (pushnum (i))
717 return OOPS;
718 }
719 sp++;
720 break;
721 case 'm':
722 if (termcap)
723 {
724 int i;
725 if (getarg (1, INTEGER, &i))
726 return OOPS;
727 arg_list[0].integer ^= 0177;
728 arg_list[1].integer ^= 0177;
729 sp++;
730 break;
731 }
732 {
733 int i, j;
734 if (popnum (&j) || popnum (&i))
735 return OOPS;
736 i %= j;
737 if (pushnum (i))
738 return OOPS;
739 }
740 sp++;
741 break;
742 case '&':
743 {
744 int i, j;
745 if (popnum (&j) || popnum (&i))
746 return OOPS;
747 i &= j;
748 if (pushnum (i))
749 return OOPS;
750 }
751 sp++;
752 break;
753 case '|':
754 {
755 int i, j;
756 if (popnum (&j) || popnum (&i))
757 return OOPS;
758 i |= j;
759 if (pushnum (i))
760 return OOPS;
761 }
762 sp++;
763 break;
764 case '^':
765 {
766 int i, j;
767 if (popnum (&j) || popnum (&i))
768 return OOPS;
769 i ^= j;
770 if (pushnum (i))
771 return OOPS;
772 }
773 sp++;
774 break;
775 case '=':
776 {
777 int i, j;
778 if (popnum (&j) || popnum (&i))
779 return OOPS;
780 i = (i == j);
781 if (pushnum (i))
782 return OOPS;
783 }
784 sp++;
785 break;
786 case '<':
787 {
788 int i, j;
789 if (popnum (&j) || popnum (&i))
790 return OOPS;
791 i = (i < j);
792 if (pushnum (i))
793 return OOPS;
794 }
795 sp++;
796 break;
797 case 'A':
798 {
799 int i, j;
800 if (popnum (&j) || popnum (&i))
801 return OOPS;
802 i = (i && j);
803 if (pushnum (i))
804 return OOPS;
805 }
806 sp++;
807 break;
808 case 'O':
809 {
810 int i, j;
811 if (popnum (&j) || popnum (&i))
812 return OOPS;
813 i = (i || j);
814 if (pushnum (i))
815 return OOPS;
816 }
817 sp++;
818 break;
819 case '!':
820 {
821 int i;
822 if (popnum (&i))
823 return OOPS;
824 i = !i;
825 if (pushnum (i))
826 return OOPS;
827 }
828 sp++;
829 break;
830 case '~':
831 {
832 int i;
833 if (popnum (&i))
834 return OOPS;
835 i = ~i;
836 if (pushnum (i))
837 return OOPS;
838 }
839 sp++;
840 break;
841 case '?':
842 if (termcap > 1)
843 return OOPS;
844 termcap = 0;
845 if_depth++;
846 sp++;
847 break;
848 case 't':
849 {
850 int i;
851 if (popnum (&i) || if_depth == 0)
852 return OOPS;
853 if (!i)
854 {
855 scan_for = 'e';
856 scan_depth = if_depth;
857 }
858 }
859 sp++;
860 break;
861 case 'e':
862 if (if_depth == 0)
863 return OOPS;
864 scan_for = ';';
865 scan_depth = if_depth;
866 sp++;
867 break;
868 case ';':
869 if (if_depth-- == 0)
870 return OOPS;
871 sp++;
872 break;
873 case 'b':
874 if (--termcap < 1)
875 return OOPS;
876 sp++;
877 break;
878 case 'f':
879 if (!termcap++)
880 return OOPS;
881 sp++;
882 break;
883 }
884 break;
885 default:
886 if (scan_for)
887 sp++;
888 else
889 *dp++ = *sp++;
890 break;
891 }
892 }
893 va_end (tparm_args);
894 *dp = '\0';
895 return buf;
896 }
897