e1b50b7b4f2e621b72aa32cacd7cd4cfdbcf8e3b
[ncurses.git] / ncurses / tinfo / captoinfo.c
1 /****************************************************************************
2  * Copyright (c) 1998-2001,2002 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  *      captoinfo.c --- conversion between termcap and terminfo formats
36  *
37  *      The captoinfo() code was swiped from Ross Ridge's mytinfo package,
38  *      adapted to fit ncurses by Eric S. Raymond <esr@snark.thyrsus.com>.
39  *
40  *      There is just one entry point:
41  *
42  *      char *_nc_captoinfo(n, s, parametrized)
43  *
44  *      Convert value s for termcap string capability named n into terminfo
45  *      format.
46  *
47  *      This code recognizes all the standard 4.4BSD %-escapes:
48  *
49  *      %%       output `%'
50  *      %d       output value as in printf %d
51  *      %2       output value as in printf %2d
52  *      %3       output value as in printf %3d
53  *      %.       output value as in printf %c
54  *      %+x      add x to value, then do %.
55  *      %>xy     if value > x then add y, no output
56  *      %r       reverse order of two parameters, no output
57  *      %i       increment by one, no output
58  *      %n       exclusive-or all parameters with 0140 (Datamedia 2500)
59  *      %B       BCD (16*(value/10)) + (value%10), no output
60  *      %D       Reverse coding (value - 2*(value%16)), no output (Delta Data).
61  *
62  *      Also, %02 and %03 are accepted as synonyms for %2 and %3.
63  *
64  *      Besides all the standard termcap escapes, this translator understands
65  *      the following extended escapes:
66  *
67  *      used by GNU Emacs termcap libraries
68  *              %a[+*-/=][cp]x  GNU arithmetic.
69  *              %m              xor the first two parameters by 0177
70  *              %b              backup to previous parameter
71  *              %f              skip this parameter
72  *
73  *      used by the University of Waterloo (MFCF) termcap libraries
74  *              %-x      subtract parameter FROM char x and output it as a char
75  *              %ax      add the character x to parameter
76  *
77  *      If #define WATERLOO is on, also enable these translations:
78  *
79  *              %sx      subtract parameter FROM the character x
80  *
81  *      By default, this Waterloo translations are not compiled in, because
82  *      the Waterloo %s conflicts with the way terminfo uses %s in strings for
83  *      function programming.
84  *
85  *      Note the two definitions of %a: the GNU definition is translated if the
86  *      characters after the 'a' are valid for it, otherwise the UW definition
87  *      is translated.
88  */
89
90 #include <curses.priv.h>
91
92 #include <ctype.h>
93 #include <tic.h>
94
95 MODULE_ID("$Id: captoinfo.c,v 1.43 2002/09/28 16:38:59 tom Exp $")
96
97 #define MAX_PUSHED      16      /* max # args we can push onto the stack */
98
99 static int stack[MAX_PUSHED];   /* the stack */
100 static int stackptr;            /* the next empty place on the stack */
101 static int onstack;             /* the top of stack */
102 static int seenm;               /* seen a %m */
103 static int seenn;               /* seen a %n */
104 static int seenr;               /* seen a %r */
105 static int param;               /* current parameter */
106 static char *dp;                /* pointer to end of the converted string */
107
108 static char *my_string;
109 static size_t my_length;
110
111 static char *
112 init_string(void)
113 /* initialize 'my_string', 'my_length' */
114 {
115     if (my_string == 0)
116         my_string = typeMalloc(char, my_length = 256);
117     if (my_string == 0)
118         _nc_err_abort(MSG_NO_MEMORY);
119
120     *my_string = '\0';
121     return my_string;
122 }
123
124 static char *
125 save_string(char *d, const char *const s)
126 {
127     size_t have = (d - my_string);
128     size_t need = have + strlen(s) + 2;
129     if (need > my_length) {
130         my_string = (char *) realloc(my_string, my_length = (need + need));
131         if (my_string == 0)
132             _nc_err_abort(MSG_NO_MEMORY);
133         d = my_string + have;
134     }
135     (void) strcpy(d, s);
136     return d + strlen(d);
137 }
138
139 static inline char *
140 save_char(char *s, int c)
141 {
142     static char temp[2];
143     temp[0] = (char) c;
144     return save_string(s, temp);
145 }
146
147 static void
148 push(void)
149 /* push onstack on to the stack */
150 {
151     if (stackptr > MAX_PUSHED)
152         _nc_warning("string too complex to convert");
153     else
154         stack[stackptr++] = onstack;
155 }
156
157 static void
158 pop(void)
159 /* pop the top of the stack into onstack */
160 {
161     if (stackptr == 0) {
162         if (onstack == 0)
163             _nc_warning("I'm confused");
164         else
165             onstack = 0;
166     } else
167         onstack = stack[--stackptr];
168     param++;
169 }
170
171 static int
172 cvtchar(register const char *sp)
173 /* convert a character to a terminfo push */
174 {
175     unsigned char c = 0;
176     int len;
177
178     switch (*sp) {
179     case '\\':
180         switch (*++sp) {
181         case '\'':
182         case '$':
183         case '\\':
184         case '%':
185             c = *sp;
186             len = 2;
187             break;
188         case '\0':
189             c = '\\';
190             len = 1;
191             break;
192         case '0':
193         case '1':
194         case '2':
195         case '3':
196             len = 1;
197             while (isdigit(UChar(*sp))) {
198                 c = 8 * c + (*sp++ - '0');
199                 len++;
200             }
201             break;
202         default:
203             c = *sp;
204             len = 2;
205             break;
206         }
207         break;
208     case '^':
209         c = (*++sp & 0x1f);
210         len = 2;
211         break;
212     default:
213         c = *sp;
214         len = 1;
215     }
216     if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') {
217         dp = save_string(dp, "%\'");
218         dp = save_char(dp, c);
219         dp = save_char(dp, '\'');
220     } else {
221         dp = save_string(dp, "%{");
222         if (c > 99)
223             dp = save_char(dp, c / 100 + '0');
224         if (c > 9)
225             dp = save_char(dp, ((int) (c / 10)) % 10 + '0');
226         dp = save_char(dp, c % 10 + '0');
227         dp = save_char(dp, '}');
228     }
229     return len;
230 }
231
232 static void
233 getparm(int parm, int n)
234 /* push n copies of param on the terminfo stack if not already there */
235 {
236     if (seenr) {
237         if (parm == 1)
238             parm = 2;
239         else if (parm == 2)
240             parm = 1;
241     }
242     if (onstack == parm) {
243         if (n > 1) {
244             _nc_warning("string may not be optimal");
245             dp = save_string(dp, "%Pa");
246             while (n--) {
247                 dp = save_string(dp, "%ga");
248             }
249         }
250         return;
251     }
252     if (onstack != 0)
253         push();
254
255     onstack = parm;
256
257     while (n--) {
258         dp = save_string(dp, "%p");
259         dp = save_char(dp, '0' + parm);
260     }
261
262     if (seenn && parm < 3) {
263         dp = save_string(dp, "%{96}%^");
264     }
265
266     if (seenm && parm < 3) {
267         dp = save_string(dp, "%{127}%^");
268     }
269 }
270
271 /*
272  * Convert a termcap string to terminfo format.
273  * 'cap' is the relevant terminfo capability index.
274  * 's' is the string value of the capability.
275  * 'parametrized' tells what type of translations to do:
276  *      % translations if 1
277  *      pad translations if >=0
278  */
279 char *
280 _nc_captoinfo(const char *cap, const char *s, int const parametrized)
281 {
282     const char *capstart;
283
284     stackptr = 0;
285     onstack = 0;
286     seenm = 0;
287     seenn = 0;
288     seenr = 0;
289     param = 1;
290
291     dp = init_string();
292
293     /* skip the initial padding (if we haven't been told not to) */
294     capstart = 0;
295     if (s == 0)
296         s = "";
297     if (parametrized >= 0 && isdigit(UChar(*s)))
298         for (capstart = s;; s++)
299             if (!(isdigit(UChar(*s)) || *s == '*' || *s == '.'))
300                 break;
301
302     while (*s != '\0') {
303         switch (*s) {
304         case '%':
305             s++;
306             if (parametrized < 1) {
307                 dp = save_char(dp, '%');
308                 break;
309             }
310             switch (*s++) {
311             case '%':
312                 dp = save_char(dp, '%');
313                 break;
314             case 'r':
315                 if (seenr++ == 1) {
316                     _nc_warning("saw %%r twice in %s", cap);
317                 }
318                 break;
319             case 'm':
320                 if (seenm++ == 1) {
321                     _nc_warning("saw %%m twice in %s", cap);
322                 }
323                 break;
324             case 'n':
325                 if (seenn++ == 1) {
326                     _nc_warning("saw %%n twice in %s", cap);
327                 }
328                 break;
329             case 'i':
330                 dp = save_string(dp, "%i");
331                 break;
332             case '6':
333             case 'B':
334                 getparm(param, 1);
335                 dp = save_string(dp, "%{10}%/%{16}%*");
336                 getparm(param, 1);
337                 dp = save_string(dp, "%{10}%m%+");
338                 break;
339             case '8':
340             case 'D':
341                 getparm(param, 2);
342                 dp = save_string(dp, "%{2}%*%-");
343                 break;
344             case '>':
345                 getparm(param, 2);
346                 /* %?%{x}%>%t%{y}%+%; */
347                 dp = save_string(dp, "%?");
348                 s += cvtchar(s);
349                 dp = save_string(dp, "%>%t");
350                 s += cvtchar(s);
351                 dp = save_string(dp, "%+%;");
352                 break;
353             case 'a':
354                 if ((*s == '=' || *s == '+' || *s == '-'
355                      || *s == '*' || *s == '/')
356                     && (s[1] == 'p' || s[1] == 'c')
357                     && s[2] != '\0') {
358                     int l;
359                     l = 2;
360                     if (*s != '=')
361                         getparm(param, 1);
362                     if (s[1] == 'p') {
363                         getparm(param + s[2] - '@', 1);
364                         if (param != onstack) {
365                             pop();
366                             param--;
367                         }
368                         l++;
369                     } else
370                         l += cvtchar(s + 2);
371                     switch (*s) {
372                     case '+':
373                         dp = save_string(dp, "%+");
374                         break;
375                     case '-':
376                         dp = save_string(dp, "%-");
377                         break;
378                     case '*':
379                         dp = save_string(dp, "%*");
380                         break;
381                     case '/':
382                         dp = save_string(dp, "%/");
383                         break;
384                     case '=':
385                         if (seenr) {
386                             if (param == 1)
387                                 onstack = 2;
388                             else if (param == 2)
389                                 onstack = 1;
390                             else
391                                 onstack = param;
392                         } else
393                             onstack = param;
394                         break;
395                     }
396                     s += l;
397                     break;
398                 }
399                 getparm(param, 1);
400                 s += cvtchar(s);
401                 dp = save_string(dp, "%+");
402                 break;
403             case '+':
404                 getparm(param, 1);
405                 s += cvtchar(s);
406                 dp = save_string(dp, "%+%c");
407                 pop();
408                 break;
409             case 's':
410 #ifdef WATERLOO
411                 s += cvtchar(s);
412                 getparm(param, 1);
413                 dp = save_string(dp, "%-");
414 #else
415                 getparm(param, 1);
416                 dp = save_string(dp, "%s");
417                 pop();
418 #endif /* WATERLOO */
419                 break;
420             case '-':
421                 s += cvtchar(s);
422                 getparm(param, 1);
423                 dp = save_string(dp, "%-%c");
424                 pop();
425                 break;
426             case '.':
427                 getparm(param, 1);
428                 dp = save_string(dp, "%c");
429                 pop();
430                 break;
431             case '0':           /* not clear any of the historical termcaps did this */
432                 if (*s == '3')
433                     goto see03;
434                 else if (*s != '2')
435                     goto invalid;
436                 /* FALLTHRU */
437             case '2':
438                 getparm(param, 1);
439                 dp = save_string(dp, "%2d");
440                 pop();
441                 break;
442             case '3':
443               see03:
444                 getparm(param, 1);
445                 dp = save_string(dp, "%3d");
446                 pop();
447                 break;
448             case 'd':
449                 getparm(param, 1);
450                 dp = save_string(dp, "%d");
451                 pop();
452                 break;
453             case 'f':
454                 param++;
455                 break;
456             case 'b':
457                 param--;
458                 break;
459             case '\\':
460                 dp = save_string(dp, "%\\");
461                 break;
462             default:
463               invalid:
464                 dp = save_char(dp, '%');
465                 s--;
466                 _nc_warning("unknown %% code %s (%#x) in %s",
467                             unctrl((chtype) *s), UChar(*s), cap);
468                 break;
469             }
470             break;
471 #ifdef REVISIBILIZE
472         case '\\':
473             dp = save_char(dp, *s++);
474             dp = save_char(dp, *s++);
475             break;
476         case '\n':
477             dp = save_string(dp, "\\n");
478             s++;
479             break;
480         case '\t':
481             dp = save_string(dp, "\\t");
482             s++;
483             break;
484         case '\r':
485             dp = save_string(dp, "\\r");
486             s++;
487             break;
488         case '\200':
489             dp = save_string(dp, "\\0");
490             s++;
491             break;
492         case '\f':
493             dp = save_string(dp, "\\f");
494             s++;
495             break;
496         case '\b':
497             dp = save_string(dp, "\\b");
498             s++;
499             break;
500         case ' ':
501             dp = save_string(dp, "\\s");
502             s++;
503             break;
504         case '^':
505             dp = save_string(dp, "\\^");
506             s++;
507             break;
508         case ':':
509             dp = save_string(dp, "\\:");
510             s++;
511             break;
512         case ',':
513             dp = save_string(dp, "\\,");
514             s++;
515             break;
516         default:
517             if (*s == '\033') {
518                 dp = save_string(dp, "\\E");
519                 s++;
520             } else if (*s > 0 && *s < 32) {
521                 dp = save_char(dp, '^');
522                 dp = save_char(dp, *s + '@');
523                 s++;
524             } else if (*s <= 0 || *s >= 127) {
525                 dp = save_char(dp, '\\');
526                 dp = save_char(dp, ((*s & 0300) >> 6) + '0');
527                 dp = save_char(dp, ((*s & 0070) >> 3) + '0');
528                 dp = save_char(dp, (*s & 0007) + '0');
529                 s++;
530             } else
531                 dp = save_char(dp, *s++);
532             break;
533 #else
534         default:
535             dp = save_char(dp, *s++);
536             break;
537 #endif
538         }
539     }
540
541     /*
542      * Now, if we stripped off some leading padding, add it at the end
543      * of the string as mandatory padding.
544      */
545     if (capstart) {
546         dp = save_string(dp, "$<");
547         for (s = capstart;; s++)
548             if (isdigit(UChar(*s)) || *s == '*' || *s == '.')
549                 dp = save_char(dp, *s);
550             else
551                 break;
552         dp = save_string(dp, "/>");
553     }
554
555     (void) save_char(dp, '\0');
556     return (my_string);
557 }
558
559 /*
560  * Check for an expression that corresponds to "%B" (BCD):
561  *      (parameter / 10) * 16 + (parameter % 10)
562  */
563 static int
564 bcd_expression(const char *str)
565 {
566     /* leave this non-const for HPUX */
567     static char fmt[] = "%%p%c%%{10}%%/%%{16}%%*%%p%c%%{10}%%m%%+";
568     int len = 0;
569     char ch1, ch2;
570
571     if (sscanf(str, fmt, &ch1, &ch2) == 2
572         && isdigit(UChar(ch1))
573         && isdigit(UChar(ch2))
574         && (ch1 == ch2)) {
575         len = 28;
576 #ifndef NDEBUG
577         {
578             char buffer[80];
579             int tst;
580             sprintf(buffer, fmt, ch1, ch2);
581             tst = strlen(buffer) - 1;
582             assert(len == tst);
583         }
584 #endif
585     }
586     return len;
587 }
588
589 static char *
590 save_tc_char(char *bufptr, int c1)
591 {
592     char temp[80];
593
594     if (is7bits(c1) && isprint(c1)) {
595         if (c1 == ':' || c1 == '\\')
596             bufptr = save_char(bufptr, '\\');
597         bufptr = save_char(bufptr, c1);
598     } else {
599         if (c1 == (c1 & 0x1f))  /* iscntrl() returns T on 255 */
600             (void) strcpy(temp, unctrl((chtype) c1));
601         else
602             (void) sprintf(temp, "\\%03o", c1);
603         bufptr = save_string(bufptr, temp);
604     }
605     return bufptr;
606 }
607
608 static char *
609 save_tc_inequality(char *bufptr, int c1, int c2)
610 {
611     bufptr = save_string(bufptr, "%>");
612     bufptr = save_tc_char(bufptr, c1);
613     bufptr = save_tc_char(bufptr, c2);
614     return bufptr;
615 }
616
617 /*
618  * Here are the capabilities infotocap assumes it can translate to:
619  *
620  *     %%       output `%'
621  *     %d       output value as in printf %d
622  *     %2       output value as in printf %2d
623  *     %3       output value as in printf %3d
624  *     %.       output value as in printf %c
625  *     %+c      add character c to value, then do %.
626  *     %>xy     if value > x then add y, no output
627  *     %r       reverse order of two parameters, no output
628  *     %i       increment by one, no output
629  *     %n       exclusive-or all parameters with 0140 (Datamedia 2500)
630  *     %B       BCD (16*(value/10)) + (value%10), no output
631  *     %D       Reverse coding (value - 2*(value%16)), no output (Delta Data).
632  *     %m       exclusive-or all parameters with 0177 (not in 4.4BSD)
633  */
634
635 /*
636  * Convert a terminfo string to termcap format.  Parameters are as in
637  * _nc_captoinfo().
638  */
639 char *
640 _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parametrized)
641 {
642     int seenone = 0, seentwo = 0, saw_m = 0, saw_n = 0;
643     const char *padding;
644     const char *trimmed = 0;
645     char ch1 = 0, ch2 = 0;
646     char *bufptr = init_string();
647     int len;
648     bool syntax_error = FALSE;
649
650     /* we may have to move some trailing mandatory padding up front */
651     padding = str + strlen(str) - 1;
652     if (*padding == '>' && *--padding == '/') {
653         --padding;
654         while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*')
655             padding--;
656         if (*padding == '<' && *--padding == '$')
657             trimmed = padding;
658         padding += 2;
659
660         while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*')
661             bufptr = save_char(bufptr, *padding++);
662     }
663
664     for (; *str && str != trimmed; str++) {
665         int c1, c2;
666         char *cp = 0;
667
668         if (str[0] == '\\' && (str[1] == '^' || str[1] == ',')) {
669             bufptr = save_char(bufptr, *++str);
670         } else if (str[0] == '$' && str[1] == '<') {    /* discard padding */
671             str += 2;
672             while (isdigit(UChar(*str))
673                    || *str == '.'
674                    || *str == '*'
675                    || *str == '/'
676                    || *str == '>')
677                 str++;
678             --str;
679         } else if (str[0] == '%' && str[1] == '%') {    /* escaped '%' */
680             bufptr = save_string(bufptr, "%%");
681         } else if (*str != '%' || (parametrized < 1)) {
682             bufptr = save_char(bufptr, *str);
683         } else if (sscanf(str, "%%?%%{%d}%%>%%t%%{%d}%%+%%;", &c1, &c2) == 2) {
684             str = strchr(str, ';');
685             bufptr = save_tc_inequality(bufptr, c1, c2);
686         } else if (sscanf(str, "%%?%%{%d}%%>%%t%%'%c'%%+%%;", &c1, &ch2) == 2) {
687             str = strchr(str, ';');
688             bufptr = save_tc_inequality(bufptr, c1, c2);
689         } else if (sscanf(str, "%%?%%'%c'%%>%%t%%{%d}%%+%%;", &ch1, &c2) == 2) {
690             str = strchr(str, ';');
691             bufptr = save_tc_inequality(bufptr, c1, c2);
692         } else if (sscanf(str, "%%?%%'%c'%%>%%t%%'%c'%%+%%;", &ch1, &ch2) == 2) {
693             str = strchr(str, ';');
694             bufptr = save_tc_inequality(bufptr, c1, c2);
695         } else if ((len = bcd_expression(str)) != 0) {
696             str += len;
697             bufptr = save_string(bufptr, "%B");
698         } else if ((sscanf(str, "%%{%d}%%+%%c", &c1) == 1
699                     || sscanf(str, "%%'%c'%%+%%c", &ch1) == 1)
700                    && (cp = strchr(str, '+'))) {
701             str = cp + 2;
702             bufptr = save_string(bufptr, "%+");
703
704             if (ch1)
705                 c1 = ch1;
706             bufptr = save_tc_char(bufptr, c1);
707         }
708         /* FIXME: this "works" for 'delta' */
709         else if (strncmp(str, "%{2}%*%-", 8) == 0) {
710             str += 7;
711             bufptr = save_string(bufptr, "%D");
712         } else if (strncmp(str, "%{96}%^", 7) == 0) {
713             str += 6;
714             if (saw_m++ == 0) {
715                 bufptr = save_string(bufptr, "%n");
716             }
717         } else if (strncmp(str, "%{127}%^", 8) == 0) {
718             str += 7;
719             if (saw_n++ == 0) {
720                 bufptr = save_string(bufptr, "%m");
721             }
722         } else {                /* cm-style format element */
723             str++;
724             switch (*str) {
725             case '%':
726                 bufptr = save_char(bufptr, '%');
727                 break;
728
729             case '0':
730             case '1':
731             case '2':
732             case '3':
733             case '4':
734             case '5':
735             case '6':
736             case '7':
737             case '8':
738             case '9':
739                 bufptr = save_char(bufptr, '%');
740                 while (isdigit(UChar(*str)))
741                     bufptr = save_char(bufptr, *str++);
742                 if (strchr("doxX.", *str)) {
743                     if (*str != 'd')    /* termcap doesn't have octal, hex */
744                         return 0;
745                 }
746                 break;
747
748             case 'd':
749                 bufptr = save_string(bufptr, "%d");
750                 break;
751
752             case 'c':
753                 bufptr = save_string(bufptr, "%.");
754                 break;
755
756                 /*
757                  * %s isn't in termcap, but it's convenient to pass it through
758                  * so we can represent things like terminfo pfkey strings in
759                  * termcap notation.
760                  */
761             case 's':
762                 bufptr = save_string(bufptr, "%s");
763                 break;
764
765             case 'p':
766                 str++;
767                 if (*str == '1')
768                     seenone = 1;
769                 else if (*str == '2') {
770                     if (!seenone && !seentwo) {
771                         bufptr = save_string(bufptr, "%r");
772                         seentwo++;
773                     }
774                 } else if (*str >= '3')
775                     return (0);
776                 break;
777
778             case 'i':
779                 bufptr = save_string(bufptr, "%i");
780                 break;
781
782             default:
783                 bufptr = save_char(bufptr, *str);
784                 syntax_error = TRUE;
785                 break;
786             }                   /* endswitch (*str) */
787         }                       /* endelse (*str == '%') */
788
789         if (*str == '\0')
790             break;
791
792     }                           /* endwhile (*str) */
793
794     return (syntax_error ? NULL : my_string);
795 }
796
797 #ifdef MAIN
798
799 int curr_line;
800
801 int
802 main(int argc, char *argv[])
803 {
804     int c, tc = FALSE;
805
806     while ((c = getopt(argc, argv, "c")) != EOF)
807         switch (c) {
808         case 'c':
809             tc = TRUE;
810             break;
811         }
812
813     curr_line = 0;
814     for (;;) {
815         char buf[BUFSIZ];
816
817         ++curr_line;
818         if (fgets(buf, sizeof(buf), stdin) == 0)
819             break;
820         buf[strlen(buf) - 1] = '\0';
821         _nc_set_source(buf);
822
823         if (tc) {
824             char *cp = _nc_infotocap("to termcap", buf, 1);
825
826             if (cp)
827                 (void) fputs(cp, stdout);
828         } else
829             (void) fputs(_nc_captoinfo("to terminfo", buf, 1), stdout);
830         (void) putchar('\n');
831     }
832     return (0);
833 }
834 #endif /* MAIN */
835
836 /* captoinfo.c ends here */