1 /****************************************************************************
2  * Copyright (c) 1998,1999,2000 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 *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.37 2000/04/01 20:07:34 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("Out of 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("Out of 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, char c)
141 {
142     static char temp[2];
143     temp[0] = 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(*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 char *
272 _nc_captoinfo(
273 /* convert a termcap string to terminfo format */
274     register const char *cap,	/* relevant terminfo capability index */
275     register const char *s,	/* string value of the capability */
276     int const parametrized	/* do % translations if 1, pad translations if >=0 */
277 )
278 {
279     const char *capstart;
280 
281     stackptr = 0;
282     onstack = 0;
283     seenm = 0;
284     seenn = 0;
285     seenr = 0;
286     param = 1;
287 
288     dp = init_string();
289 
290     /* skip the initial padding (if we haven't been told not to) */
291     capstart = 0;
292     if (s == 0)
293 	s = "";
294     if (parametrized >= 0 && isdigit(*s))
295 	for (capstart = s;; s++)
296 	    if (!(isdigit(*s) || *s == '*' || *s == '.'))
297 		break;
298 
299     while (*s != '\0') {
300 	switch (*s) {
301 	case '%':
302 	    s++;
303 	    if (parametrized < 1) {
304 		dp = save_char(dp, '%');
305 		break;
306 	    }
307 	    switch (*s++) {
308 	    case '%':
309 		dp = save_char(dp, '%');
310 		break;
311 	    case 'r':
312 		if (seenr++ == 1) {
313 		    _nc_warning("saw %%r twice in %s", cap);
314 		}
315 		break;
316 	    case 'm':
317 		if (seenm++ == 1) {
318 		    _nc_warning("saw %%m twice in %s", cap);
319 		}
320 		break;
321 	    case 'n':
322 		if (seenn++ == 1) {
323 		    _nc_warning("saw %%n twice in %s", cap);
324 		}
325 		break;
326 	    case 'i':
327 		dp = save_string(dp, "%i");
328 		break;
329 	    case '6':
330 	    case 'B':
331 		getparm(param, 1);
332 		dp = save_string(dp, "%{10}%/%{16}%*");
333 		getparm(param, 1);
334 		dp = save_string(dp, "%{10}%m%+");
335 		break;
336 	    case '8':
337 	    case 'D':
338 		getparm(param, 2);
339 		dp = save_string(dp, "%{2}%*%-");
340 		break;
341 	    case '>':
342 		getparm(param, 2);
343 		/* %?%{x}%>%t%{y}%+%; */
344 		dp = save_string(dp, "%?");
345 		s += cvtchar(s);
346 		dp = save_string(dp, "%>%t");
347 		s += cvtchar(s);
348 		dp = save_string(dp, "%+%;");
349 		break;
350 	    case 'a':
351 		if ((*s == '=' || *s == '+' || *s == '-'
352 			|| *s == '*' || *s == '/')
353 		    && (s[1] == 'p' || s[1] == 'c')
354 		    && s[2] != '\0') {
355 		    int l;
356 		    l = 2;
357 		    if (*s != '=')
358 			getparm(param, 1);
359 		    if (s[1] == 'p') {
360 			getparm(param + s[2] - '@', 1);
361 			if (param != onstack) {
362 			    pop();
363 			    param--;
364 			}
365 			l++;
366 		    } else
367 			l += cvtchar(s + 2);
368 		    switch (*s) {
369 		    case '+':
370 			dp = save_string(dp, "%+");
371 			break;
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 			if (seenr) {
383 			    if (param == 1)
384 				onstack = 2;
385 			    else if (param == 2)
386 				onstack = 1;
387 			    else
388 				onstack = param;
389 			} else
390 			    onstack = param;
391 			break;
392 		    }
393 		    s += l;
394 		    break;
395 		}
396 		getparm(param, 1);
397 		s += cvtchar(s);
398 		dp = save_string(dp, "%+");
399 		break;
400 	    case '+':
401 		getparm(param, 1);
402 		s += cvtchar(s);
403 		dp = save_string(dp, "%+%c");
404 		pop();
405 		break;
406 	    case 's':
407 #ifdef WATERLOO
408 		s += cvtchar(s);
409 		getparm(param, 1);
410 		dp = save_string(dp, "%-");
411 #else
412 		getparm(param, 1);
413 		dp = save_string(dp, "%s");
414 		pop();
415 #endif /* WATERLOO */
416 		break;
417 	    case '-':
418 		s += cvtchar(s);
419 		getparm(param, 1);
420 		dp = save_string(dp, "%-%c");
421 		pop();
422 		break;
423 	    case '.':
424 		getparm(param, 1);
425 		dp = save_string(dp, "%c");
426 		pop();
427 		break;
428 	    case '0':		/* not clear any of the historical termcaps did this */
429 		if (*s == '3')
430 		    goto see03;
431 		else if (*s != '2')
432 		    goto invalid;
433 		/* FALLTHRU */
434 	    case '2':
435 		getparm(param, 1);
436 		dp = save_string(dp, "%2d");
437 		pop();
438 		break;
439 	    case '3':
440 	      see03:
441 		getparm(param, 1);
442 		dp = save_string(dp, "%3d");
443 		pop();
444 		break;
445 	    case 'd':
446 		getparm(param, 1);
447 		dp = save_string(dp, "%d");
448 		pop();
449 		break;
450 	    case 'f':
451 		param++;
452 		break;
453 	    case 'b':
454 		param--;
455 		break;
456 	    case '\\':
457 		dp = save_string(dp, "%\\");
458 		break;
459 	    default:
460 	      invalid:
461 		dp = save_char(dp, '%');
462 		s--;
463 		_nc_warning("unknown %% code %s (%#x) in %s",
464 		    unctrl(*s), (*s) & 0xff, cap);
465 		break;
466 	    }
467 	    break;
468 #ifdef REVISIBILIZE
469 	case '\\':
470 	    dp = save_char(dp, *s++);
471 	    dp = save_char(dp, *s++);
472 	    break;
473 	case '\n':
474 	    dp = save_string(dp, "\\n");
475 	    s++;
476 	    break;
477 	case '\t':
478 	    dp = save_string(dp, "\\t");
479 	    s++;
480 	    break;
481 	case '\r':
482 	    dp = save_string(dp, "\\r");
483 	    s++;
484 	    break;
485 	case '\200':
486 	    dp = save_string(dp, "\\0");
487 	    s++;
488 	    break;
489 	case '\f':
490 	    dp = save_string(dp, "\\f");
491 	    s++;
492 	    break;
493 	case '\b':
494 	    dp = save_string(dp, "\\b");
495 	    s++;
496 	    break;
497 	case ' ':
498 	    dp = save_string(dp, "\\s");
499 	    s++;
500 	    break;
501 	case '^':
502 	    dp = save_string(dp, "\\^");
503 	    s++;
504 	    break;
505 	case ':':
506 	    dp = save_string(dp, "\\:");
507 	    s++;
508 	    break;
509 	case ',':
510 	    dp = save_string(dp, "\\,");
511 	    s++;
512 	    break;
513 	default:
514 	    if (*s == '\033') {
515 		dp = save_string(dp, "\\E");
516 		s++;
517 	    } else if (*s > 0 && *s < 32) {
518 		dp = save_char(dp, '^');
519 		dp = save_char(dp, *s + '@');
520 		s++;
521 	    } else if (*s <= 0 || *s >= 127) {
522 		dp = save_char(dp, '\\');
523 		dp = save_char(dp, ((*s & 0300) >> 6) + '0');
524 		dp = save_char(dp, ((*s & 0070) >> 3) + '0');
525 		dp = save_char(dp, (*s & 0007) + '0');
526 		s++;
527 	    } else
528 		dp = save_char(dp, *s++);
529 	    break;
530 #else
531 	default:
532 	    dp = save_char(dp, *s++);
533 	    break;
534 #endif
535 	}
536     }
537 
538     /*
539      * Now, if we stripped off some leading padding, add it at the end
540      * of the string as mandatory padding.
541      */
542     if (capstart) {
543 	dp = save_string(dp, "$<");
544 	for (s = capstart;; s++)
545 	    if (isdigit(*s) || *s == '*' || *s == '.')
546 		dp = save_char(dp, *s);
547 	    else
548 		break;
549 	dp = save_string(dp, "/>");
550     }
551 
552     (void) save_char(dp, '\0');
553     return (my_string);
554 }
555 
556 /*
557  * Check for an expression that corresponds to "%B" (BCD):
558  *	(parameter / 10) * 16 + (parameter % 10)
559  */
560 static int
561 bcd_expression(const char *str)
562 {
563     /* leave this non-const for HPUX */
564     static char fmt[] = "%%p%c%%{10}%%/%%{16}%%*%%p%c%%{10}%%m%%+";
565     int len = 0;
566     char ch1, ch2;
567 
568     if (sscanf(str, fmt, &ch1, &ch2) == 2
569 	&& isdigit(ch1)
570 	&& isdigit(ch2)
571 	&& (ch1 == ch2)) {
572 	len = 28;
573 #ifndef NDEBUG
574 	{
575 	    char buffer[80];
576 	    int tst;
577 	    sprintf(buffer, fmt, ch1, ch2);
578 	    tst = strlen(buffer) - 1;
579 	    assert(len == tst);
580 	}
581 #endif
582     }
583     return len;
584 }
585 
586 static char *
587 save_tc_char(char *bufptr, int c1)
588 {
589     char temp[80];
590 
591     if (is7bits(c1) && isprint(c1)) {
592 	if (c1 == ':' || c1 == '\\')
593 	    bufptr = save_char(bufptr, '\\');
594 	bufptr = save_char(bufptr, c1);
595     } else {
596 	if (c1 == (c1 & 0x1f))	/* iscntrl() returns T on 255 */
597 	    (void) strcpy(temp, unctrl(c1));
598 	else
599 	    (void) sprintf(temp, "\\%03o", c1);
600 	bufptr = save_string(bufptr, temp);
601     }
602     return bufptr;
603 }
604 
605 static char *
606 save_tc_inequality(char *bufptr, int c1, int c2)
607 {
608     bufptr = save_string(bufptr, "%>");
609     bufptr = save_tc_char(bufptr, c1);
610     bufptr = save_tc_char(bufptr, c2);
611     return bufptr;
612 }
613 
614 /*
615  * Here are the capabilities infotocap assumes it can translate to:
616  *
617  *     %%       output `%'
618  *     %d       output value as in printf %d
619  *     %2       output value as in printf %2d
620  *     %3       output value as in printf %3d
621  *     %.       output value as in printf %c
622  *     %+c      add character c to value, then do %.
623  *     %>xy     if value > x then add y, no output
624  *     %r       reverse order of two parameters, no output
625  *     %i       increment by one, no output
626  *     %n       exclusive-or all parameters with 0140 (Datamedia 2500)
627  *     %B       BCD (16*(value/10)) + (value%10), no output
628  *     %D       Reverse coding (value - 2*(value%16)), no output (Delta Data).
629  *     %m       exclusive-or all parameters with 0177 (not in 4.4BSD)
630  */
631 
632 char *
633 _nc_infotocap(
634 /* convert a terminfo string to termcap format */
635     register const char *cap GCC_UNUSED,	/* relevant termcap capability index */
636     register const char *str,	/* string value of the capability */
637     int const parametrized	/* do % translations if 1, pad translations if >=0 */
638 )
639 {
640     int seenone = 0, seentwo = 0, saw_m = 0, saw_n = 0;
641     const char *padding;
642     const char *trimmed = 0;
643     char ch1 = 0, ch2 = 0;
644     char *bufptr = init_string();
645     int len;
646     bool syntax_error = FALSE;
647 
648     /* we may have to move some trailing mandatory padding up front */
649     padding = str + strlen(str) - 1;
650     if (*padding == '>' && *--padding == '/') {
651 	--padding;
652 	while (isdigit(*padding) || *padding == '.' || *padding == '*')
653 	    padding--;
654 	if (*padding == '<' && *--padding == '$')
655 	    trimmed = padding;
656 	padding += 2;
657 
658 	while (isdigit(*padding) || *padding == '.' || *padding == '*')
659 	    bufptr = save_char(bufptr, *padding++);
660     }
661 
662     for (; *str && str != trimmed; str++) {
663 	int c1, c2;
664 	char *cp = 0;
665 
666 	if (str[0] == '\\' && (str[1] == '^' || str[1] == ',')) {
667 	    bufptr = save_char(bufptr, *++str);
668 	} else if (str[0] == '$' && str[1] == '<') {	/* discard padding */
669 	    str += 2;
670 	    while (isdigit(*str) || *str == '.' || *str == '*' || *str ==
671 		'/' || *str == '>')
672 		str++;
673 	    --str;
674 	} else if (str[0] == '%' && str[1] == '%') {	/* escaped '%' */
675 	    bufptr = save_string(bufptr, "%%");
676 	} else if (*str != '%' || (parametrized < 1)) {
677 	    bufptr = save_char(bufptr, *str);
678 	} else if (sscanf(str, "%%?%%{%d}%%>%%t%%{%d}%%+%%;", &c1, &c2) == 2) {
679 	    str = strchr(str, ';');
680 	    bufptr = save_tc_inequality(bufptr, c1, c2);
681 	} else if (sscanf(str, "%%?%%{%d}%%>%%t%%'%c'%%+%%;", &c1, &ch2) == 2) {
682 	    str = strchr(str, ';');
683 	    bufptr = save_tc_inequality(bufptr, c1, c2);
684 	} else if (sscanf(str, "%%?%%'%c'%%>%%t%%{%d}%%+%%;", &ch1, &c2) == 2) {
685 	    str = strchr(str, ';');
686 	    bufptr = save_tc_inequality(bufptr, c1, c2);
687 	} else if (sscanf(str, "%%?%%'%c'%%>%%t%%'%c'%%+%%;", &ch1, &ch2) == 2) {
688 	    str = strchr(str, ';');
689 	    bufptr = save_tc_inequality(bufptr, c1, c2);
690 	} else if ((len = bcd_expression(str)) != 0) {
691 	    str += len;
692 	    bufptr = save_string(bufptr, "%B");
693 	} else if ((sscanf(str, "%%{%d}%%+%%c", &c1) == 1
694 		    || sscanf(str, "%%'%c'%%+%%c", &ch1) == 1)
695 	    && (cp = strchr(str, '+'))) {
696 	    str = cp + 2;
697 	    bufptr = save_string(bufptr, "%+");
698 
699 	    if (ch1)
700 		c1 = ch1;
701 	    bufptr = save_tc_char(bufptr, c1);
702 	}
703 	/* FIXME: this "works" for 'delta' */
704 	else if (strncmp(str, "%{2}%*%-", 8) == 0) {
705 	    str += 7;
706 	    bufptr = save_string(bufptr, "%D");
707 	} else if (strncmp(str, "%{96}%^", 7) == 0) {
708 	    str += 6;
709 	    if (saw_m++ == 0) {
710 		bufptr = save_string(bufptr, "%n");
711 	    }
712 	} else if (strncmp(str, "%{127}%^", 8) == 0) {
713 	    str += 7;
714 	    if (saw_n++ == 0) {
715 		bufptr = save_string(bufptr, "%m");
716 	    }
717 	} else {		/* cm-style format element */
718 	    str++;
719 	    switch (*str) {
720 	    case '%':
721 		bufptr = save_char(bufptr, '%');
722 		break;
723 
724 	    case '0':
725 	    case '1':
726 	    case '2':
727 	    case '3':
728 	    case '4':
729 	    case '5':
730 	    case '6':
731 	    case '7':
732 	    case '8':
733 	    case '9':
734 		bufptr = save_char(bufptr, '%');
735 		while (isdigit(*str))
736 		    bufptr = save_char(bufptr, *str++);
737 		if (strchr("doxX.", *str)) {
738 		    if (*str != 'd')	/* termcap doesn't have octal, hex */
739 			return 0;
740 		}
741 		break;
742 
743 	    case 'd':
744 		bufptr = save_string(bufptr, "%d");
745 		break;
746 
747 	    case 'c':
748 		bufptr = save_string(bufptr, "%.");
749 		break;
750 
751 		/*
752 		 * %s isn't in termcap, but it's convenient to pass it through
753 		 * so we can represent things like terminfo pfkey strings in
754 		 * termcap notation.
755 		 */
756 	    case 's':
757 		bufptr = save_string(bufptr, "%s");
758 		break;
759 
760 	    case 'p':
761 		str++;
762 		if (*str == '1')
763 		    seenone = 1;
764 		else if (*str == '2') {
765 		    if (!seenone && !seentwo) {
766 			bufptr = save_string(bufptr, "%r");
767 			seentwo++;
768 		    }
769 		} else if (*str >= '3')
770 		    return (0);
771 		break;
772 
773 	    case 'i':
774 		bufptr = save_string(bufptr, "%i");
775 		break;
776 
777 	    default:
778 		bufptr = save_char(bufptr, *str);
779 		syntax_error = TRUE;
780 		break;
781 	    }			/* endswitch (*str) */
782 	}			/* endelse (*str == '%') */
783 
784 	if (*str == '\0')
785 	    break;
786 
787     }				/* endwhile (*str) */
788 
789     return (syntax_error ? NULL : my_string);
790 }
791 
792 #ifdef MAIN
793 
794 int curr_line;
795 
796 int
797 main(int argc, char *argv[])
798 {
799     int c, tc = FALSE;
800 
801     while ((c = getopt(argc, argv, "c")) != EOF)
802 	switch (c) {
803 	case 'c':
804 	    tc = TRUE;
805 	    break;
806 	}
807 
808     curr_line = 0;
809     for (;;) {
810 	char buf[BUFSIZ];
811 
812 	++curr_line;
813 	if (fgets(buf, sizeof(buf), stdin) == 0)
814 	    break;
815 	buf[strlen(buf) - 1] = '\0';
816 	_nc_set_source(buf);
817 
818 	if (tc) {
819 	    char *cp = _nc_infotocap("to termcap", buf, 1);
820 
821 	    if (cp)
822 		(void) fputs(cp, stdout);
823 	} else
824 	    (void) fputs(_nc_captoinfo("to terminfo", buf, 1), stdout);
825 	(void) putchar('\n');
826     }
827     return (0);
828 }
829 #endif /* MAIN */
830 
831 /* captoinfo.c ends here */
832