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