ncurses 4.1
[ncurses.git] / ncurses / lib_tparm.c
1
2 /***************************************************************************
3 *                            COPYRIGHT NOTICE                              *
4 ****************************************************************************
5 *                ncurses is copyright (C) 1992-1995                        *
6 *                          Zeyd M. Ben-Halim                               *
7 *                          zmbenhal@netcom.com                             *
8 *                          Eric S. Raymond                                 *
9 *                          esr@snark.thyrsus.com                           *
10 *                                                                          *
11 *        Permission is hereby granted to reproduce and distribute ncurses  *
12 *        by any means and for any fee, whether alone or as part of a       *
13 *        larger distribution, in source or in binary form, PROVIDED        *
14 *        this notice is included with any such distribution, and is not    *
15 *        removed from any of its header files. Mention of ncurses in any   *
16 *        applications linked with it is highly appreciated.                *
17 *                                                                          *
18 *        ncurses comes AS IS with no warranty, implied or expressed.       *
19 *                                                                          *
20 ***************************************************************************/
21
22
23 /*
24  *      tparm.c
25  *
26  */
27
28 #include <curses.priv.h>
29
30 #include <term.h>
31
32 MODULE_ID("$Id: lib_tparm.c,v 1.18 1997/04/26 18:37:50 tom Exp $")
33
34 /*
35  *      char *
36  *      tparm(string, ...)
37  *
38  *      Substitute the given parameters into the given string by the following
39  *      rules (taken from terminfo(5)):
40  *
41  *           Cursor addressing and other strings  requiring  parame-
42  *      ters in the terminal are described by a parameterized string
43  *      capability, with like escapes %x in  it.   For  example,  to
44  *      address  the  cursor, the cup capability is given, using two
45  *      parameters: the row and column to  address  to.   (Rows  and
46  *      columns  are  numbered  from  zero and refer to the physical
47  *      screen visible to the user, not to any  unseen  memory.)  If
48  *      the terminal has memory relative cursor addressing, that can
49  *      be indicated by
50  *      
51  *           The parameter mechanism uses  a  stack  and  special  %
52  *      codes  to manipulate it.  Typically a sequence will push one
53  *      of the parameters onto the stack and then print it  in  some
54  *      format.  Often more complex operations are necessary.
55  *      
56  *           The % encodings have the following meanings:
57  *      
58  *           %%        outputs `%'
59  *           %d        print pop() like %d in printf()
60  *           %2d       print pop() like %2d in printf()
61  *           %02d      print pop() like %02d in printf()
62  *           %3d       print pop() like %3d in printf()
63  *           %03d      print pop() like %03d in printf()
64  *           %2x       print pop() like %2x in printf()
65  *           %02x      print pop() like %02x in printf()
66  *           %3x       print pop() like %3x in printf()
67  *           %03x      print pop() like %03x in printf()
68  *           %c        print pop() like %c in printf()
69  *           %s        print pop() like %s in printf()
70  *      
71  *           %p[1-9]   push ith parm
72  *           %P[a-z]   set variable [a-z] to pop()
73  *           %g[a-z]   get variable [a-z] and push it
74  *           %'c'      push char constant c
75  *           %{nn}     push integer constant nn
76  *      
77  *           %+ %- %* %/ %m
78  *                     arithmetic (%m is mod): push(pop() op pop())
79  *           %& %| %^  bit operations: push(pop() op pop())
80  *           %= %> %<  logical operations: push(pop() op pop())
81  *           %A %O     logical and & or operations for conditionals
82  *           %! %~     unary operations push(op pop())
83  *           %i        add 1 to first two parms (for ANSI terminals)
84  *      
85  *           %? expr %t thenpart %e elsepart %;
86  *                     if-then-else, %e elsepart is optional.
87  *                     else-if's are possible ala Algol 68:
88  *                     %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
89  *      
90  *      For those of the above operators which are binary and not commutative,
91  *      the stack works in the usual way, with
92  *                      %gx %gy %m
93  *      resulting in x mod y, not the reverse.
94  */
95
96 #define L_BRACE '{'
97 #define R_BRACE '}'
98
99 #define STACKSIZE       20
100
101 typedef union {
102         unsigned int    num;
103         char           *str;
104 } stack_frame;
105
106 static  stack_frame     stack[STACKSIZE];
107 static  int     stack_ptr;
108 #ifdef TRACE
109 static const char *tname;
110 #endif /* TRACE */
111
112 static char  *out_buff;
113 static size_t out_size;
114 static size_t out_used;
115
116 #if NO_LEAKS
117 void _nc_free_tparm(void)
118 {
119         if (out_buff != 0) {
120                 FreeAndNull(out_buff);
121                 out_size = 0;
122                 out_used = 0;
123         }
124 }
125 #endif
126
127 static void save_text(char *s)
128 {
129         size_t  want = strlen(s);
130         size_t  need = want + out_used + 1;
131
132         if (need > out_size) {
133                 out_size = need * 2;
134                 if (out_buff == 0)
135                         out_buff = malloc(out_size);
136                 else
137                         out_buff = realloc(out_buff, out_size);
138         }
139         (void)strcpy(out_buff + out_used, s);
140         out_used += want;
141 }
142
143 static void save_number(const char *fmt, int number)
144 {
145         char temp[80];
146         (void)sprintf(temp, fmt, number);
147         save_text(temp);
148 }
149
150 static inline void save_char(int c)
151 {
152         static char text[2];
153         text[0] = c;
154         save_text(text);
155 }
156
157 static inline void npush(int x)
158 {
159         if (stack_ptr < STACKSIZE) {
160                 stack[stack_ptr].num = x;
161         stack_ptr++;
162     }
163 }
164
165 static inline int npop(void)
166 {
167         return   (stack_ptr > 0  ?  stack[--stack_ptr].num  :  0);
168 }
169
170 static inline char *spop(void)
171 {
172         return   (stack_ptr > 0  ?  stack[--stack_ptr].str  :  0);
173 }
174
175 static inline char *tparam_internal(const char *string, va_list ap)
176 {
177 int     param[9];
178 int     popcount;
179 int     variable[26];
180 char    len;
181 int     number;
182 int     level;
183 int     x, y;
184 int     i;
185 register const char *cp;
186
187         out_used = 0;
188         if (string == NULL)
189                 return NULL;
190
191         /*
192          * Find the highest parameter-number referred to in the format string.
193          * Use this value to limit the number of arguments copied from the
194          * variable-length argument list.
195          */
196         for (cp = string, popcount = number = 0; *cp != '\0'; cp++) {
197                 if (cp[0] == '%' && cp[1] != '\0') {
198                         switch (cp[1]) {
199                         case '%':
200                                 cp++;
201                                 break;
202                         case 'i':
203                                 if (popcount < 2)
204                                         popcount = 2;
205                                 break;
206                         case 'p':
207                                 cp++;
208                                 if (cp[1] >= '1' && cp[1] <= '9') {
209                                         int c = cp[1] - '0';
210                                         if (c > popcount)
211                                                 popcount = c;
212                                 }
213                                 break;
214                         case '0': case '1': case '2': case '3': case '4':
215                         case '5': case '6': case '7': case '8': case '9':
216                         case 'd': case 'c': case 's':
217                                 ++number;
218                                 break;
219                         }
220                 }
221         }
222
223         if (number > 9) number = 9;
224         for (i = 0; i < max(popcount, number); i++) {
225                 /*
226                  * FIXME: potential loss here if sizeof(int) != sizeof(char *).
227                  * A few caps (such as plab_norm) have string-valued parms.
228                  */
229                 param[i] = va_arg(ap, int);
230         }
231
232         /*
233          * This is a termcap compatibility hack.  If there are no explicit pop
234          * operations in the string, load the stack in such a way that 
235          * successive pops will grab successive parameters.  That will make
236          * the expansion of (for example) \E[%d;%dH work correctly in termcap
237          * style, which means tparam() will expand termcap strings OK.
238          */
239         stack_ptr = 0;
240         if (popcount == 0) {
241                 popcount = number;
242                 for (i = number - 1; i >= 0; i--)
243                         npush(param[i]);
244         }
245
246 #ifdef TRACE
247         if (_nc_tracing & TRACE_CALLS) {
248                 for (i = 0; i < popcount; i++)
249                         save_number(", %d", param[i]);
250                 _tracef(T_CALLED("%s(%s%s)"), tname, _nc_visbuf(string), out_buff);
251                 out_used = 0;
252         }
253 #endif /* TRACE */
254
255         while (*string) {
256                 if (*string != '%')
257                         save_char(*string);
258                 else {
259                         string++;
260                         switch (*string) {
261                         default:
262                                 break;
263                         case '%':
264                                 save_char('%');
265                                 break;
266
267                         case 'd':
268                                 save_number("%d", npop());
269                                 break;
270
271                         case 'x':
272                                 save_number("%x", npop());
273                                 break;
274
275                         case '0':
276                                 string++;
277                                 len = *string;
278                                 if (len == '2'  ||  len == '3')
279                                 {
280                                         ++string;
281                                         if (*string == 'd') {
282                                                 if (len == '2')
283                                                         save_number("%02d", npop());
284                                                 else
285                                                         save_number("%03d", npop());
286                                         }
287                                         else if (*string == 'x') {
288                                                 if (len == '2')
289                                                         save_number("%02x", npop());
290                                                 else
291                                                         save_number("%03x", npop());
292                                         }
293                                 }
294                                 break;
295
296                         case '2':
297                                 string++;
298                                 if (*string == 'd') {
299                                         save_number("%2d", npop());
300                                 }
301                                 else if (*string == 'x') {
302                                         save_number("%2x", npop());
303                                 }
304                                 break;
305
306                         case '3':
307                                 string++;
308                                 if (*string == 'd') {
309                                         save_number("%3d", npop());
310                                 }
311                                 else if (*string == 'x') {
312                                         save_number("%3x", npop());
313                                 }
314                                 break;
315
316                         case 'c':
317                                 save_char(npop());
318                                 break;
319
320                         case 's':
321                                 save_text(spop());
322                                 break;
323
324                         case 'p':
325                                 string++;
326                                 if (*string >= '1'  &&  *string <= '9')
327                                         npush(param[*string - '1']);
328                                 break;
329
330                         case 'P':
331                                 string++;
332                                 if (*string >= 'a'  &&  *string <= 'z')
333                                         variable[*string - 'a'] = npop();
334                                 break;
335
336                         case 'g':
337                                 string++;
338                                 if (*string >= 'a'  &&  *string <= 'z')
339                                         npush(variable[*string - 'a']);
340                                 break;
341
342                         case '\'':
343                                 string++;
344                                 npush(*string);
345                                 string++;
346                                 break;
347
348                         case L_BRACE:
349                                 number = 0;
350                                 string++;
351                                 while (*string >= '0'  &&  *string <= '9') {
352                                         number = number * 10 + *string - '0';
353                                         string++;
354                                 }
355                                 npush(number);
356                                 break;
357
358                         case '+':
359                                 npush(npop() + npop());
360                                 break;
361
362                         case '-':
363                                 y = npop();
364                                 x = npop();
365                                 npush(x - y);
366                                 break;
367
368                         case '*':
369                                 npush(npop() * npop());
370                                 break;
371
372                         case '/':
373                                 y = npop();
374                                 x = npop();
375                                 npush(x / y);
376                                 break;
377
378                         case 'm':
379                                 y = npop();
380                                 x = npop();
381                                 npush(x % y);
382                                 break;
383
384                         case 'A':
385                                 npush(npop() && npop());
386                                 break;
387
388                         case 'O':
389                                 npush(npop() || npop());
390                                 break;
391
392                         case '&':
393                                 npush(npop() & npop());
394                                 break;
395
396                         case '|':
397                                 npush(npop() | npop());
398                                 break;
399
400                         case '^':
401                                 npush(npop() ^ npop());
402                                 break;
403
404                         case '=':
405                                 y = npop();
406                                 x = npop();
407                                 npush(x == y);
408                                 break;
409
410                         case '<':
411                                 y = npop();
412                                 x = npop();
413                                 npush(x < y);
414                                 break;
415
416                         case '>':
417                                 y = npop();
418                                 x = npop();
419                                 npush(x > y);
420                                 break;
421
422                         case '!':
423                                 npush(! npop());
424                                 break;
425
426                         case '~':
427                                 npush(~ npop());
428                                 break;
429
430                         case 'i':
431                                 param[0]++;
432                                 param[1]++;
433                                 break;
434
435                         case '?':
436                                 break;
437
438                         case 't':
439                                 x = npop();
440                                 if (!x) {
441                                         /* scan forward for %e or %; at level zero */
442                                         string++;
443                                         level = 0;
444                                         while (*string) {
445                                                 if (*string == '%') {
446                                                         string++;
447                                                         if (*string == '?')
448                                                                 level++;
449                                                         else if (*string == ';') {
450                                                                 if (level > 0)
451                                                                         level--;
452                                                                 else
453                                                                         break;
454                                                         }
455                                                         else if (*string == 'e'  && level == 0)
456                                                                 break;
457                                                 }
458
459                                                 if (*string)
460                                                         string++;
461                                         }
462                                 }
463                                 break;
464
465                         case 'e':
466                                 /* scan forward for a %; at level zero */
467                                 string++;
468                                 level = 0;
469                                 while (*string) {
470                                         if (*string == '%') {
471                                                 string++;
472                                                 if (*string == '?')
473                                                         level++;
474                                                 else if (*string == ';') {
475                                                         if (level > 0)
476                                                                 level--;
477                                                         else
478                                                                 break;
479                                                 }
480                                         }
481
482                                         if (*string)
483                                                 string++;
484                                 }
485                                 break;
486
487                         case ';':
488                                 break;
489
490                         } /* endswitch (*string) */
491                 } /* endelse (*string == '%') */
492
493                 if (*string == '\0')
494                         break;
495
496                 string++;
497         } /* endwhile (*string) */
498
499         T((T_RETURN("%s"), _nc_visbuf(out_buff)));
500         return(out_buff);
501 }
502
503 char *tparm(const char *string, ...)
504 {
505 va_list ap;
506 char *result;
507
508         va_start(ap, string);
509 #ifdef TRACE
510         tname = "tparm";
511 #endif /* TRACE */
512         result = tparam_internal(string, ap);
513         va_end(ap);
514         return result;
515 }
516
517 #ifdef __UNUSED__       /* we never documented this, and it confuses Emacs */
518 char *tparam(const char *string, char *buffer, int bufsiz, ...)
519 {
520 va_list ap;
521 char *result = 0;
522
523         va_start(ap, bufsiz);
524 #ifdef TRACE
525         tname = "tparam";
526 #endif /* TRACE */
527         if (tparam_internal(string, ap) != 0
528          && (int)out_used < bufsiz)
529                 result = strcpy(buffer, out_buff);
530         va_end(ap);
531         return result;
532 }
533 #endif /* __UNUSED */