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