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