Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / gnulib-lib / tparm.c
1 /* Substitution of parameters in strings from terminal descriptions.
2    Copyright (C) 2006, 2012, 2015 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 General Public License as published by
6    the Free Software Foundation; either version 3 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 General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://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
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
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
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
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
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
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 *
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 }