1/****************************************************************************
2 * Copyright (c) 1998-2011,2012 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 *     and: Thomas E. Dickey                        1996-on                 *
33 ****************************************************************************/
34
35/*
36 *	captoinfo.c --- conversion between termcap and terminfo formats
37 *
38 *	The captoinfo() code was swiped from Ross Ridge's mytinfo package,
39 *	adapted to fit ncurses by Eric S. Raymond <esr@snark.thyrsus.com>.
40 *
41 *	There is just one entry point:
42 *
43 *	char *_nc_captoinfo(n, s, parameterized)
44 *
45 *	Convert value s for termcap string capability named n into terminfo
46 *	format.
47 *
48 *	This code recognizes all the standard 4.4BSD %-escapes:
49 *
50 *	%%       output `%'
51 *	%d       output value as in printf %d
52 *	%2       output value as in printf %2d
53 *	%3       output value as in printf %3d
54 *	%.       output value as in printf %c
55 *	%+x      add x to value, then do %.
56 *	%>xy     if value > x then add y, no output
57 *	%r       reverse order of two parameters, no output
58 *	%i       increment by one, no output
59 *	%n       exclusive-or all parameters with 0140 (Datamedia 2500)
60 *	%B       BCD (16*(value/10)) + (value%10), no output
61 *	%D       Reverse coding (value - 2*(value%16)), no output (Delta Data).
62 *
63 *	Also, %02 and %03 are accepted as synonyms for %2 and %3.
64 *
65 *	Besides all the standard termcap escapes, this translator understands
66 *	the following extended escapes:
67 *
68 *	used by GNU Emacs termcap libraries
69 *		%a[+*-/=][cp]x	GNU arithmetic.
70 *		%m		xor the first two parameters by 0177
71 *		%b		backup to previous parameter
72 *		%f		skip this parameter
73 *
74 *	used by the University of Waterloo (MFCF) termcap libraries
75 *		%-x	 subtract parameter FROM char x and output it as a char
76 *		%ax	 add the character x to parameter
77 *
78 *	If #define WATERLOO is on, also enable these translations:
79 *
80 *		%sx	 subtract parameter FROM the character x
81 *
82 *	By default, this Waterloo translations are not compiled in, because
83 *	the Waterloo %s conflicts with the way terminfo uses %s in strings for
84 *	function programming.
85 *
86 *	Note the two definitions of %a: the GNU definition is translated if the
87 *	characters after the 'a' are valid for it, otherwise the UW definition
88 *	is translated.
89 */
90
91#include <curses.priv.h>
92
93#include <ctype.h>
94#include <tic.h>
95
96MODULE_ID("$Id: captoinfo.c,v 1.77 2012/12/30 00:50:40 tom Exp $")
97
98#define MAX_PUSHED	16	/* max # args we can push onto the stack */
99
100static int stack[MAX_PUSHED];	/* the stack */
101static int stackptr;		/* the next empty place on the stack */
102static int onstack;		/* the top of stack */
103static int seenm;		/* seen a %m */
104static int seenn;		/* seen a %n */
105static int seenr;		/* seen a %r */
106static int param;		/* current parameter */
107static char *dp;		/* pointer to end of the converted string */
108
109static char *my_string;
110static size_t my_length;
111
112static char *
113init_string(void)
114/* initialize 'my_string', 'my_length' */
115{
116    if (my_string == 0)
117	TYPE_MALLOC(char, my_length = 256, my_string);
118
119    *my_string = '\0';
120    return my_string;
121}
122
123static char *
124save_string(char *d, const char *const s)
125{
126    size_t have = (size_t) (d - my_string);
127    size_t need = have + strlen(s) + 2;
128    if (need > my_length) {
129	my_string = (char *) _nc_doalloc(my_string, my_length = (need + need));
130	if (my_string == 0)
131	    _nc_err_abort(MSG_NO_MEMORY);
132	d = my_string + have;
133    }
134    _nc_STRCPY(d, s, my_length - have);
135    return d + strlen(d);
136}
137
138static NCURSES_INLINE char *
139save_char(char *s, int c)
140{
141    static char temp[2];
142    temp[0] = (char) c;
143    return save_string(s, temp);
144}
145
146static void
147push(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
156static void
157pop(void)
158/* pop the top of the stack into onstack */
159{
160    if (stackptr == 0) {
161	if (onstack == 0)
162	    _nc_warning("I'm confused");
163	else
164	    onstack = 0;
165    } else
166	onstack = stack[--stackptr];
167    param++;
168}
169
170static int
171cvtchar(register const char *sp)
172/* convert a character to a terminfo push */
173{
174    unsigned char c = 0;
175    int len;
176
177    switch (*sp) {
178    case '\\':
179	switch (*++sp) {
180	case '\'':
181	case '$':
182	case '\\':
183	case '%':
184	    c = (unsigned char) (*sp);
185	    len = 2;
186	    break;
187	case '\0':
188	    c = '\\';
189	    len = 1;
190	    break;
191	case '0':
192	case '1':
193	case '2':
194	case '3':
195	    len = 1;
196	    while (isdigit(UChar(*sp))) {
197		c = (unsigned char) (8 * c + (*sp++ - '0'));
198		len++;
199	    }
200	    break;
201	default:
202	    c = (unsigned char) (*sp);
203	    len = 2;
204	    break;
205	}
206	break;
207    case '^':
208	c = (unsigned char) (*++sp & 0x1f);
209	len = 2;
210	break;
211    default:
212	c = (unsigned char) (*sp);
213	len = 1;
214    }
215    if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') {
216	dp = save_string(dp, "%\'");
217	dp = save_char(dp, c);
218	dp = save_char(dp, '\'');
219    } else {
220	dp = save_string(dp, "%{");
221	if (c > 99)
222	    dp = save_char(dp, c / 100 + '0');
223	if (c > 9)
224	    dp = save_char(dp, ((int) (c / 10)) % 10 + '0');
225	dp = save_char(dp, c % 10 + '0');
226	dp = save_char(dp, '}');
227    }
228    return len;
229}
230
231static void
232getparm(int parm, int n)
233/* push n copies of param on the terminfo stack if not already there */
234{
235    if (seenr) {
236	if (parm == 1)
237	    parm = 2;
238	else if (parm == 2)
239	    parm = 1;
240    }
241
242    while (n--) {
243	dp = save_string(dp, "%p");
244	dp = save_char(dp, '0' + parm);
245    }
246
247    if (onstack == parm) {
248	if (n > 1) {
249	    _nc_warning("string may not be optimal");
250	    dp = save_string(dp, "%Pa");
251	    while (n--) {
252		dp = save_string(dp, "%ga");
253	    }
254	}
255	return;
256    }
257    if (onstack != 0)
258	push();
259
260    onstack = parm;
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 * 'parameterized' tells what type of translations to do:
276 *	% translations if 1
277 *	pad translations if >=0
278 */
279NCURSES_EXPORT(char *)
280_nc_captoinfo(const char *cap, const char *s, int const parameterized)
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 (parameterized >= 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 (parameterized < 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	default:
472	    dp = save_char(dp, *s++);
473	    break;
474	}
475    }
476
477    /*
478     * Now, if we stripped off some leading padding, add it at the end
479     * of the string as mandatory padding.
480     */
481    if (capstart) {
482	dp = save_string(dp, "$<");
483	for (s = capstart;; s++)
484	    if (isdigit(UChar(*s)) || *s == '*' || *s == '.')
485		dp = save_char(dp, *s);
486	    else
487		break;
488	dp = save_string(dp, "/>");
489    }
490
491    (void) save_char(dp, '\0');
492    return (my_string);
493}
494
495/*
496 * Check for an expression that corresponds to "%B" (BCD):
497 *	(parameter / 10) * 16 + (parameter % 10)
498 */
499static int
500bcd_expression(const char *str)
501{
502    /* leave this non-const for HPUX */
503    static char fmt[] = "%%p%c%%{10}%%/%%{16}%%*%%p%c%%{10}%%m%%+";
504    int len = 0;
505    char ch1, ch2;
506
507    if (sscanf(str, fmt, &ch1, &ch2) == 2
508	&& isdigit(UChar(ch1))
509	&& isdigit(UChar(ch2))
510	&& (ch1 == ch2)) {
511	len = 28;
512#ifndef NDEBUG
513	{
514	    char buffer[80];
515	    int tst;
516	    _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) fmt, ch1, ch2);
517	    tst = strlen(buffer) - 1;
518	    assert(len == tst);
519	}
520#endif
521    }
522    return len;
523}
524
525static char *
526save_tc_char(char *bufptr, int c1)
527{
528    char temp[80];
529
530    if (is7bits(c1) && isprint(c1)) {
531	if (c1 == ':' || c1 == '\\')
532	    bufptr = save_char(bufptr, '\\');
533	bufptr = save_char(bufptr, c1);
534    } else {
535	if (c1 == (c1 & 0x1f)) {	/* iscntrl() returns T on 255 */
536	    _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
537			"%.20s", unctrl((chtype) c1));
538	} else {
539	    _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
540			"\\%03o", c1);
541	}
542	bufptr = save_string(bufptr, temp);
543    }
544    return bufptr;
545}
546
547static char *
548save_tc_inequality(char *bufptr, int c1, int c2)
549{
550    bufptr = save_string(bufptr, "%>");
551    bufptr = save_tc_char(bufptr, c1);
552    bufptr = save_tc_char(bufptr, c2);
553    return bufptr;
554}
555
556/*
557 * Here are the capabilities infotocap assumes it can translate to:
558 *
559 *     %%       output `%'
560 *     %d       output value as in printf %d
561 *     %2       output value as in printf %2d
562 *     %3       output value as in printf %3d
563 *     %.       output value as in printf %c
564 *     %+c      add character c to value, then do %.
565 *     %>xy     if value > x then add y, no output
566 *     %r       reverse order of two parameters, no output
567 *     %i       increment by one, no output
568 *     %n       exclusive-or all parameters with 0140 (Datamedia 2500)
569 *     %B       BCD (16*(value/10)) + (value%10), no output
570 *     %D       Reverse coding (value - 2*(value%16)), no output (Delta Data).
571 *     %m       exclusive-or all parameters with 0177 (not in 4.4BSD)
572 */
573
574/*
575 * Convert a terminfo string to termcap format.  Parameters are as in
576 * _nc_captoinfo().
577 */
578NCURSES_EXPORT(char *)
579_nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameterized)
580{
581    int seenone = 0, seentwo = 0, saw_m = 0, saw_n = 0;
582    const char *padding;
583    const char *trimmed = 0;
584    int in0, in1, in2;
585    char ch1 = 0, ch2 = 0;
586    char *bufptr = init_string();
587    char octal[4];
588    int len;
589    bool syntax_error = FALSE;
590
591    /* we may have to move some trailing mandatory padding up front */
592    padding = str + strlen(str) - 1;
593    if (padding > str && *padding == '>') {
594	if (*--padding == '/')
595	    --padding;
596	while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*')
597	    padding--;
598	if (padding > str && *padding == '<' && *--padding == '$')
599	    trimmed = padding;
600	padding += 2;
601
602	while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*')
603	    bufptr = save_char(bufptr, *padding++);
604    }
605
606    for (; *str && ((trimmed == 0) || (str < trimmed)); str++) {
607	int c1, c2;
608	char *cp = 0;
609
610	if (str[0] == '^') {
611	    if (str[1] == '\0' || (str + 1) == trimmed) {
612		bufptr = save_string(bufptr, "\\136");
613		++str;
614	    } else {
615		bufptr = save_char(bufptr, *str++);
616		bufptr = save_char(bufptr, *str);
617	    }
618	} else if (str[0] == '\\') {
619	    if (str[1] == '\0' || (str + 1) == trimmed) {
620		bufptr = save_string(bufptr, "\\134");
621		++str;
622	    } else if (str[1] == '^') {
623		bufptr = save_string(bufptr, "\\136");
624		++str;
625	    } else if (str[1] == ',') {
626		bufptr = save_char(bufptr, *++str);
627	    } else {
628		int xx1, xx2;
629
630		bufptr = save_char(bufptr, *str++);
631		xx1 = *str;
632		if (_nc_strict_bsd) {
633		    if (isdigit(UChar(xx1))) {
634			int pad = 0;
635
636			if (!isdigit(UChar(str[1])))
637			    pad = 2;
638			else if (str[1] && !isdigit(UChar(str[2])))
639			    pad = 1;
640
641			/*
642			 * Test for "\0", "\00" or "\000" and transform those
643			 * into "\200".
644			 */
645			if (xx1 == '0'
646			    && ((pad == 2) || (str[1] == '0'))
647			    && ((pad >= 1) || (str[2] == '0'))) {
648			    xx2 = '2';
649			} else {
650			    xx2 = '0';
651			    pad = 0;	/* FIXME - optionally pad to 3 digits */
652			}
653			while (pad-- > 0) {
654			    bufptr = save_char(bufptr, xx2);
655			    xx2 = '0';
656			}
657		    } else if (strchr("E\\nrtbf", xx1) == 0) {
658			switch (xx1) {
659			case 'e':
660			    xx1 = 'E';
661			    break;
662			case 'l':
663			    xx1 = 'n';
664			    break;
665			case 's':
666			    bufptr = save_char(bufptr, '0');
667			    bufptr = save_char(bufptr, '4');
668			    xx1 = '0';
669			    break;
670			case ':':
671			    /*
672			     * Note: termcap documentation claims that ":"
673			     * must be escaped as "\072", however the
674			     * documentation is incorrect - read the code.
675			     * The replacement does not work reliably,
676			     * so the advice is not helpful.
677			     */
678			    bufptr = save_char(bufptr, '0');
679			    bufptr = save_char(bufptr, '7');
680			    xx1 = '2';
681			    break;
682			default:
683			    /* should not happen, but handle this anyway */
684			    _nc_SPRINTF(octal, _nc_SLIMIT(sizeof(octal))
685					"%03o", UChar(xx1));
686			    bufptr = save_char(bufptr, octal[0]);
687			    bufptr = save_char(bufptr, octal[1]);
688			    xx1 = octal[2];
689			    break;
690			}
691		    }
692		}
693		bufptr = save_char(bufptr, xx1);
694	    }
695	} else if (str[0] == '$' && str[1] == '<') {	/* discard padding */
696	    str += 2;
697	    while (isdigit(UChar(*str))
698		   || *str == '.'
699		   || *str == '*'
700		   || *str == '/'
701		   || *str == '>')
702		str++;
703	    --str;
704	} else if (sscanf(str,
705			  "[%%?%%p1%%{8}%%<%%t%d%%p1%%d%%e%%p1%%{16}%%<%%t%d%%p1%%{8}%%-%%d%%e%d;5;%%p1%%d%%;m",
706			  &in0, &in1, &in2) == 3
707		   && ((in0 == 4 && in1 == 10 && in2 == 48)
708		       || (in0 == 3 && in1 == 9 && in2 == 38))) {
709	    /* dumb-down an optimized case from xterm-256color for termcap */
710	    if ((str = strstr(str, ";m")) == 0)
711		break;		/* cannot happen */
712	    ++str;
713	    if (in2 == 48) {
714		bufptr = save_string(bufptr, "[48;5;%dm");
715	    } else {
716		bufptr = save_string(bufptr, "[38;5;%dm");
717	    }
718	} else if (str[0] == '%' && str[1] == '%') {	/* escaped '%' */
719	    bufptr = save_string(bufptr, "%%");
720	    ++str;
721	} else if (*str != '%' || (parameterized < 1)) {
722	    bufptr = save_char(bufptr, *str);
723	} else if (sscanf(str, "%%?%%{%d}%%>%%t%%{%d}%%+%%;", &c1, &c2) == 2) {
724	    str = strchr(str, ';');
725	    bufptr = save_tc_inequality(bufptr, c1, c2);
726	} else if (sscanf(str, "%%?%%{%d}%%>%%t%%'%c'%%+%%;", &c1, &ch2) == 2) {
727	    str = strchr(str, ';');
728	    bufptr = save_tc_inequality(bufptr, c1, ch2);
729	} else if (sscanf(str, "%%?%%'%c'%%>%%t%%{%d}%%+%%;", &ch1, &c2) == 2) {
730	    str = strchr(str, ';');
731	    bufptr = save_tc_inequality(bufptr, ch1, c2);
732	} else if (sscanf(str, "%%?%%'%c'%%>%%t%%'%c'%%+%%;", &ch1, &ch2) == 2) {
733	    str = strchr(str, ';');
734	    bufptr = save_tc_inequality(bufptr, ch1, ch2);
735	} else if ((len = bcd_expression(str)) != 0) {
736	    str += len;
737	    bufptr = save_string(bufptr, "%B");
738	} else if ((sscanf(str, "%%{%d}%%+%%c", &c1) == 1
739		    || sscanf(str, "%%'%c'%%+%%c", &ch1) == 1)
740		   && (cp = strchr(str, '+'))) {
741	    str = cp + 2;
742	    bufptr = save_string(bufptr, "%+");
743
744	    if (ch1)
745		c1 = ch1;
746	    bufptr = save_tc_char(bufptr, c1);
747	}
748	/* FIXME: this "works" for 'delta' */
749	else if (strncmp(str, "%{2}%*%-", (size_t) 8) == 0) {
750	    str += 7;
751	    bufptr = save_string(bufptr, "%D");
752	} else if (strncmp(str, "%{96}%^", (size_t) 7) == 0) {
753	    str += 6;
754	    if (saw_m++ == 0) {
755		bufptr = save_string(bufptr, "%n");
756	    }
757	} else if (strncmp(str, "%{127}%^", (size_t) 8) == 0) {
758	    str += 7;
759	    if (saw_n++ == 0) {
760		bufptr = save_string(bufptr, "%m");
761	    }
762	} else {		/* cm-style format element */
763	    str++;
764	    switch (*str) {
765	    case '%':
766		bufptr = save_char(bufptr, '%');
767		break;
768
769	    case '0':
770	    case '1':
771	    case '2':
772	    case '3':
773	    case '4':
774	    case '5':
775	    case '6':
776	    case '7':
777	    case '8':
778	    case '9':
779		bufptr = save_char(bufptr, '%');
780		ch1 = 0;
781		ch2 = 0;
782		while (isdigit(UChar(*str))) {
783		    ch2 = ch1;
784		    ch1 = *str++;
785		    if (_nc_strict_bsd) {
786			if (ch1 > '3')
787			    return 0;
788		    } else {
789			bufptr = save_char(bufptr, ch1);
790		    }
791		}
792		if (_nc_strict_bsd) {
793		    if (ch2 != 0 && ch2 != '0')
794			return 0;
795		    if (ch1 < '2')
796			ch1 = 'd';
797		    bufptr = save_char(bufptr, ch1);
798		}
799		if (strchr("doxX.", *str)) {
800		    if (*str != 'd')	/* termcap doesn't have octal, hex */
801			return 0;
802		}
803		break;
804
805	    case 'd':
806		bufptr = save_string(bufptr, "%d");
807		break;
808
809	    case 'c':
810		bufptr = save_string(bufptr, "%.");
811		break;
812
813		/*
814		 * %s isn't in termcap, but it's convenient to pass it through
815		 * so we can represent things like terminfo pfkey strings in
816		 * termcap notation.
817		 */
818	    case 's':
819		if (_nc_strict_bsd)
820		    return 0;
821		bufptr = save_string(bufptr, "%s");
822		break;
823
824	    case 'p':
825		str++;
826		if (*str == '1')
827		    seenone = 1;
828		else if (*str == '2') {
829		    if (!seenone && !seentwo) {
830			bufptr = save_string(bufptr, "%r");
831			seentwo++;
832		    }
833		} else if (*str >= '3')
834		    return (0);
835		break;
836
837	    case 'i':
838		bufptr = save_string(bufptr, "%i");
839		break;
840
841	    default:
842		bufptr = save_char(bufptr, *str);
843		syntax_error = TRUE;
844		break;
845	    }			/* endswitch (*str) */
846	}			/* endelse (*str == '%') */
847
848	/*
849	 * 'str' always points to the end of what was scanned in this step,
850	 * but that may not be the end of the string.
851	 */
852	assert(str != 0);
853	if (str == 0 || *str == '\0')
854	    break;
855
856    }				/* endwhile (*str) */
857
858    return (syntax_error ? NULL : my_string);
859}
860
861#ifdef MAIN
862
863int curr_line;
864
865int
866main(int argc, char *argv[])
867{
868    int c, tc = FALSE;
869
870    while ((c = getopt(argc, argv, "c")) != EOF)
871	switch (c) {
872	case 'c':
873	    tc = TRUE;
874	    break;
875	}
876
877    curr_line = 0;
878    for (;;) {
879	char buf[BUFSIZ];
880
881	++curr_line;
882	if (fgets(buf, sizeof(buf), stdin) == 0)
883	    break;
884	buf[strlen(buf) - 1] = '\0';
885	_nc_set_source(buf);
886
887	if (tc) {
888	    char *cp = _nc_infotocap("to termcap", buf, 1);
889
890	    if (cp)
891		(void) fputs(cp, stdout);
892	} else
893	    (void) fputs(_nc_captoinfo("to terminfo", buf, 1), stdout);
894	(void) putchar('\n');
895    }
896    return (0);
897}
898#endif /* MAIN */
899
900#if NO_LEAKS
901NCURSES_EXPORT(void)
902_nc_captoinfo_leaks(void)
903{
904    if (my_string != 0) {
905	FreeAndNull(my_string);
906    }
907    my_length = 0;
908}
909#endif
910