1279377Simp/****************************************************************************
2279377Simp * Copyright (c) 1998-2006,2008 Free Software Foundation, Inc.              *
3279377Simp *                                                                          *
4279377Simp * Permission is hereby granted, free of charge, to any person obtaining a  *
5279377Simp * copy of this software and associated documentation files (the            *
6279377Simp * "Software"), to deal in the Software without restriction, including      *
7279377Simp * without limitation the rights to use, copy, modify, merge, publish,      *
8279377Simp * distribute, distribute with modifications, sublicense, and/or sell       *
9279377Simp * copies of the Software, and to permit persons to whom the Software is    *
10279377Simp * furnished to do so, subject to the following conditions:                 *
11279377Simp *                                                                          *
12279377Simp * The above copyright notice and this permission notice shall be included  *
13279377Simp * in all copies or substantial portions of the Software.                   *
14279377Simp *                                                                          *
15279377Simp * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16279377Simp * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17279377Simp * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18279377Simp * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19279377Simp * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20279377Simp * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21279377Simp * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22279377Simp *                                                                          *
23279377Simp * Except as contained in this notice, the name(s) of the above copyright   *
24279377Simp * holders shall not be used in advertising or otherwise to promote the     *
25279377Simp * sale, use or other dealings in this Software without prior written       *
26279377Simp * authorization.                                                           *
27279377Simp ****************************************************************************/
28279377Simp
29279377Simp/****************************************************************************
30279377Simp *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31279377Simp *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32279377Simp *     and: Thomas E. Dickey                        1996-on                 *
33279377Simp ****************************************************************************/
34279377Simp
35279377Simp/*
36279377Simp *	captoinfo.c --- conversion between termcap and terminfo formats
37279377Simp *
38279377Simp *	The captoinfo() code was swiped from Ross Ridge's mytinfo package,
39279377Simp *	adapted to fit ncurses by Eric S. Raymond <esr@snark.thyrsus.com>.
40279377Simp *
41279377Simp *	There is just one entry point:
42279377Simp *
43279377Simp *	char *_nc_captoinfo(n, s, parameterized)
44279377Simp *
45279377Simp *	Convert value s for termcap string capability named n into terminfo
46279377Simp *	format.
47279377Simp *
48279377Simp *	This code recognizes all the standard 4.4BSD %-escapes:
49279377Simp *
50279377Simp *	%%       output `%'
51279377Simp *	%d       output value as in printf %d
52279377Simp *	%2       output value as in printf %2d
53279377Simp *	%3       output value as in printf %3d
54279377Simp *	%.       output value as in printf %c
55279377Simp *	%+x      add x to value, then do %.
56279377Simp *	%>xy     if value > x then add y, no output
57279377Simp *	%r       reverse order of two parameters, no output
58279377Simp *	%i       increment by one, no output
59279377Simp *	%n       exclusive-or all parameters with 0140 (Datamedia 2500)
60279377Simp *	%B       BCD (16*(value/10)) + (value%10), no output
61279377Simp *	%D       Reverse coding (value - 2*(value%16)), no output (Delta Data).
62279377Simp *
63279377Simp *	Also, %02 and %03 are accepted as synonyms for %2 and %3.
64279377Simp *
65279377Simp *	Besides all the standard termcap escapes, this translator understands
66279377Simp *	the following extended escapes:
67279377Simp *
68279377Simp *	used by GNU Emacs termcap libraries
69279377Simp *		%a[+*-/=][cp]x	GNU arithmetic.
70279377Simp *		%m		xor the first two parameters by 0177
71279377Simp *		%b		backup to previous parameter
72279377Simp *		%f		skip this parameter
73279377Simp *
74279377Simp *	used by the University of Waterloo (MFCF) termcap libraries
75279377Simp *		%-x	 subtract parameter FROM char x and output it as a char
76279377Simp *		%ax	 add the character x to parameter
77279377Simp *
78279377Simp *	If #define WATERLOO is on, also enable these translations:
79279377Simp *
80279377Simp *		%sx	 subtract parameter FROM the character x
81279377Simp *
82279377Simp *	By default, this Waterloo translations are not compiled in, because
83279377Simp *	the Waterloo %s conflicts with the way terminfo uses %s in strings for
84279377Simp *	function programming.
85279377Simp *
86279377Simp *	Note the two definitions of %a: the GNU definition is translated if the
87279377Simp *	characters after the 'a' are valid for it, otherwise the UW definition
88279377Simp *	is translated.
89279377Simp */
90279377Simp
91279377Simp#include <curses.priv.h>
92279377Simp
93279377Simp#include <ctype.h>
94279377Simp#include <tic.h>
95279377Simp
96279377SimpMODULE_ID("$Id: captoinfo.c,v 1.52 2008/08/16 19:24:51 tom Exp $")
97279377Simp
98279377Simp#define MAX_PUSHED	16	/* max # args we can push onto the stack */
99279377Simp
100279377Simpstatic int stack[MAX_PUSHED];	/* the stack */
101279377Simpstatic int stackptr;		/* the next empty place on the stack */
102279377Simpstatic int onstack;		/* the top of stack */
103279377Simpstatic int seenm;		/* seen a %m */
104279377Simpstatic int seenn;		/* seen a %n */
105279377Simpstatic int seenr;		/* seen a %r */
106279377Simpstatic int param;		/* current parameter */
107279377Simpstatic char *dp;		/* pointer to end of the converted string */
108279377Simp
109279377Simpstatic char *my_string;
110279377Simpstatic size_t my_length;
111279377Simp
112279377Simpstatic char *
113279377Simpinit_string(void)
114279377Simp/* initialize 'my_string', 'my_length' */
115279377Simp{
116279377Simp    if (my_string == 0)
117279377Simp	my_string = typeMalloc(char, my_length = 256);
118279377Simp    if (my_string == 0)
119279377Simp	_nc_err_abort(MSG_NO_MEMORY);
120279377Simp
121279377Simp    *my_string = '\0';
122279377Simp    return my_string;
123279377Simp}
124279377Simp
125279377Simpstatic char *
126279377Simpsave_string(char *d, const char *const s)
127279377Simp{
128279377Simp    size_t have = (d - my_string);
129279377Simp    size_t need = have + strlen(s) + 2;
130279377Simp    if (need > my_length) {
131279377Simp	my_string = (char *) realloc(my_string, my_length = (need + need));
132279377Simp	if (my_string == 0)
133279377Simp	    _nc_err_abort(MSG_NO_MEMORY);
134279377Simp	d = my_string + have;
135279377Simp    }
136279377Simp    (void) strcpy(d, s);
137279377Simp    return d + strlen(d);
138279377Simp}
139279377Simp
140279377Simpstatic NCURSES_INLINE char *
141279377Simpsave_char(char *s, int c)
142279377Simp{
143279377Simp    static char temp[2];
144279377Simp    temp[0] = (char) c;
145279377Simp    return save_string(s, temp);
146279377Simp}
147279377Simp
148279377Simpstatic void
149279377Simppush(void)
150279377Simp/* push onstack on to the stack */
151279377Simp{
152279377Simp    if (stackptr >= MAX_PUSHED)
153279377Simp	_nc_warning("string too complex to convert");
154279377Simp    else
155279377Simp	stack[stackptr++] = onstack;
156279377Simp}
157279377Simp
158279377Simpstatic void
159279377Simppop(void)
160279377Simp/* pop the top of the stack into onstack */
161279377Simp{
162279377Simp    if (stackptr == 0) {
163279377Simp	if (onstack == 0)
164279377Simp	    _nc_warning("I'm confused");
165279377Simp	else
166279377Simp	    onstack = 0;
167279377Simp    } else
168279377Simp	onstack = stack[--stackptr];
169279377Simp    param++;
170279377Simp}
171279377Simp
172279377Simpstatic int
173279377Simpcvtchar(register const char *sp)
174279377Simp/* convert a character to a terminfo push */
175279377Simp{
176279377Simp    unsigned char c = 0;
177279377Simp    int len;
178279377Simp
179279377Simp    switch (*sp) {
180279377Simp    case '\\':
181279377Simp	switch (*++sp) {
182279377Simp	case '\'':
183279377Simp	case '$':
184279377Simp	case '\\':
185279377Simp	case '%':
186279377Simp	    c = (unsigned char) (*sp);
187279377Simp	    len = 2;
188279377Simp	    break;
189279377Simp	case '\0':
190279377Simp	    c = '\\';
191279377Simp	    len = 1;
192279377Simp	    break;
193279377Simp	case '0':
194279377Simp	case '1':
195279377Simp	case '2':
196279377Simp	case '3':
197279377Simp	    len = 1;
198279377Simp	    while (isdigit(UChar(*sp))) {
199279377Simp		c = 8 * c + (*sp++ - '0');
200279377Simp		len++;
201279377Simp	    }
202279377Simp	    break;
203279377Simp	default:
204279377Simp	    c = (unsigned char) (*sp);
205279377Simp	    len = 2;
206279377Simp	    break;
207279377Simp	}
208279377Simp	break;
209279377Simp    case '^':
210279377Simp	c = (*++sp & 0x1f);
211279377Simp	len = 2;
212279377Simp	break;
213279377Simp    default:
214279377Simp	c = (unsigned char) (*sp);
215279377Simp	len = 1;
216279377Simp    }
217279377Simp    if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') {
218279377Simp	dp = save_string(dp, "%\'");
219279377Simp	dp = save_char(dp, c);
220279377Simp	dp = save_char(dp, '\'');
221279377Simp    } else {
222279377Simp	dp = save_string(dp, "%{");
223279377Simp	if (c > 99)
224279377Simp	    dp = save_char(dp, c / 100 + '0');
225279377Simp	if (c > 9)
226279377Simp	    dp = save_char(dp, ((int) (c / 10)) % 10 + '0');
227279377Simp	dp = save_char(dp, c % 10 + '0');
228279377Simp	dp = save_char(dp, '}');
229279377Simp    }
230279377Simp    return len;
231279377Simp}
232279377Simp
233279377Simpstatic void
234279377Simpgetparm(int parm, int n)
235279377Simp/* push n copies of param on the terminfo stack if not already there */
236279377Simp{
237279377Simp    if (seenr) {
238279377Simp	if (parm == 1)
239279377Simp	    parm = 2;
240279377Simp	else if (parm == 2)
241279377Simp	    parm = 1;
242279377Simp    }
243279377Simp    if (onstack == parm) {
244279377Simp	if (n > 1) {
245279377Simp	    _nc_warning("string may not be optimal");
246279377Simp	    dp = save_string(dp, "%Pa");
247279377Simp	    while (n--) {
248279377Simp		dp = save_string(dp, "%ga");
249279377Simp	    }
250279377Simp	}
251279377Simp	return;
252279377Simp    }
253279377Simp    if (onstack != 0)
254279377Simp	push();
255279377Simp
256279377Simp    onstack = parm;
257279377Simp
258279377Simp    while (n--) {
259279377Simp	dp = save_string(dp, "%p");
260279377Simp	dp = save_char(dp, '0' + parm);
261279377Simp    }
262279377Simp
263279377Simp    if (seenn && parm < 3) {
264279377Simp	dp = save_string(dp, "%{96}%^");
265279377Simp    }
266279377Simp
267279377Simp    if (seenm && parm < 3) {
268279377Simp	dp = save_string(dp, "%{127}%^");
269279377Simp    }
270279377Simp}
271279377Simp
272279377Simp/*
273279377Simp * Convert a termcap string to terminfo format.
274279377Simp * 'cap' is the relevant terminfo capability index.
275279377Simp * 's' is the string value of the capability.
276279377Simp * 'parameterized' tells what type of translations to do:
277279377Simp *	% translations if 1
278279377Simp *	pad translations if >=0
279279377Simp */
280279377SimpNCURSES_EXPORT(char *)
281279377Simp_nc_captoinfo(const char *cap, const char *s, int const parameterized)
282279377Simp{
283279377Simp    const char *capstart;
284279377Simp
285279377Simp    stackptr = 0;
286279377Simp    onstack = 0;
287279377Simp    seenm = 0;
288279377Simp    seenn = 0;
289279377Simp    seenr = 0;
290279377Simp    param = 1;
291279377Simp
292279377Simp    dp = init_string();
293279377Simp
294279377Simp    /* skip the initial padding (if we haven't been told not to) */
295279377Simp    capstart = 0;
296279377Simp    if (s == 0)
297279377Simp	s = "";
298279377Simp    if (parameterized >= 0 && isdigit(UChar(*s)))
299279377Simp	for (capstart = s;; s++)
300279377Simp	    if (!(isdigit(UChar(*s)) || *s == '*' || *s == '.'))
301279377Simp		break;
302279377Simp
303279377Simp    while (*s != '\0') {
304279377Simp	switch (*s) {
305279377Simp	case '%':
306279377Simp	    s++;
307279377Simp	    if (parameterized < 1) {
308279377Simp		dp = save_char(dp, '%');
309279377Simp		break;
310279377Simp	    }
311279377Simp	    switch (*s++) {
312279377Simp	    case '%':
313279377Simp		dp = save_char(dp, '%');
314279377Simp		break;
315279377Simp	    case 'r':
316279377Simp		if (seenr++ == 1) {
317279377Simp		    _nc_warning("saw %%r twice in %s", cap);
318279377Simp		}
319279377Simp		break;
320279377Simp	    case 'm':
321279377Simp		if (seenm++ == 1) {
322279377Simp		    _nc_warning("saw %%m twice in %s", cap);
323279377Simp		}
324279377Simp		break;
325279377Simp	    case 'n':
326279377Simp		if (seenn++ == 1) {
327279377Simp		    _nc_warning("saw %%n twice in %s", cap);
328279377Simp		}
329279377Simp		break;
330279377Simp	    case 'i':
331279377Simp		dp = save_string(dp, "%i");
332279377Simp		break;
333279377Simp	    case '6':
334279377Simp	    case 'B':
335		getparm(param, 1);
336		dp = save_string(dp, "%{10}%/%{16}%*");
337		getparm(param, 1);
338		dp = save_string(dp, "%{10}%m%+");
339		break;
340	    case '8':
341	    case 'D':
342		getparm(param, 2);
343		dp = save_string(dp, "%{2}%*%-");
344		break;
345	    case '>':
346		getparm(param, 2);
347		/* %?%{x}%>%t%{y}%+%; */
348		dp = save_string(dp, "%?");
349		s += cvtchar(s);
350		dp = save_string(dp, "%>%t");
351		s += cvtchar(s);
352		dp = save_string(dp, "%+%;");
353		break;
354	    case 'a':
355		if ((*s == '=' || *s == '+' || *s == '-'
356		     || *s == '*' || *s == '/')
357		    && (s[1] == 'p' || s[1] == 'c')
358		    && s[2] != '\0') {
359		    int l;
360		    l = 2;
361		    if (*s != '=')
362			getparm(param, 1);
363		    if (s[1] == 'p') {
364			getparm(param + s[2] - '@', 1);
365			if (param != onstack) {
366			    pop();
367			    param--;
368			}
369			l++;
370		    } else
371			l += cvtchar(s + 2);
372		    switch (*s) {
373		    case '+':
374			dp = save_string(dp, "%+");
375			break;
376		    case '-':
377			dp = save_string(dp, "%-");
378			break;
379		    case '*':
380			dp = save_string(dp, "%*");
381			break;
382		    case '/':
383			dp = save_string(dp, "%/");
384			break;
385		    case '=':
386			if (seenr) {
387			    if (param == 1)
388				onstack = 2;
389			    else if (param == 2)
390				onstack = 1;
391			    else
392				onstack = param;
393			} else
394			    onstack = param;
395			break;
396		    }
397		    s += l;
398		    break;
399		}
400		getparm(param, 1);
401		s += cvtchar(s);
402		dp = save_string(dp, "%+");
403		break;
404	    case '+':
405		getparm(param, 1);
406		s += cvtchar(s);
407		dp = save_string(dp, "%+%c");
408		pop();
409		break;
410	    case 's':
411#ifdef WATERLOO
412		s += cvtchar(s);
413		getparm(param, 1);
414		dp = save_string(dp, "%-");
415#else
416		getparm(param, 1);
417		dp = save_string(dp, "%s");
418		pop();
419#endif /* WATERLOO */
420		break;
421	    case '-':
422		s += cvtchar(s);
423		getparm(param, 1);
424		dp = save_string(dp, "%-%c");
425		pop();
426		break;
427	    case '.':
428		getparm(param, 1);
429		dp = save_string(dp, "%c");
430		pop();
431		break;
432	    case '0':		/* not clear any of the historical termcaps did this */
433		if (*s == '3')
434		    goto see03;
435		else if (*s != '2')
436		    goto invalid;
437		/* FALLTHRU */
438	    case '2':
439		getparm(param, 1);
440		dp = save_string(dp, "%2d");
441		pop();
442		break;
443	    case '3':
444	      see03:
445		getparm(param, 1);
446		dp = save_string(dp, "%3d");
447		pop();
448		break;
449	    case 'd':
450		getparm(param, 1);
451		dp = save_string(dp, "%d");
452		pop();
453		break;
454	    case 'f':
455		param++;
456		break;
457	    case 'b':
458		param--;
459		break;
460	    case '\\':
461		dp = save_string(dp, "%\\");
462		break;
463	    default:
464	      invalid:
465		dp = save_char(dp, '%');
466		s--;
467		_nc_warning("unknown %% code %s (%#x) in %s",
468			    unctrl((chtype) *s), UChar(*s), cap);
469		break;
470	    }
471	    break;
472#ifdef REVISIBILIZE
473	case '\\':
474	    dp = save_char(dp, *s++);
475	    dp = save_char(dp, *s++);
476	    break;
477	case '\n':
478	    dp = save_string(dp, "\\n");
479	    s++;
480	    break;
481	case '\t':
482	    dp = save_string(dp, "\\t");
483	    s++;
484	    break;
485	case '\r':
486	    dp = save_string(dp, "\\r");
487	    s++;
488	    break;
489	case '\200':
490	    dp = save_string(dp, "\\0");
491	    s++;
492	    break;
493	case '\f':
494	    dp = save_string(dp, "\\f");
495	    s++;
496	    break;
497	case '\b':
498	    dp = save_string(dp, "\\b");
499	    s++;
500	    break;
501	case ' ':
502	    dp = save_string(dp, "\\s");
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	case ',':
514	    dp = save_string(dp, "\\,");
515	    s++;
516	    break;
517	default:
518	    if (*s == '\033') {
519		dp = save_string(dp, "\\E");
520		s++;
521	    } else if (*s > 0 && *s < 32) {
522		dp = save_char(dp, '^');
523		dp = save_char(dp, *s + '@');
524		s++;
525	    } else if (*s <= 0 || *s >= 127) {
526		dp = save_char(dp, '\\');
527		dp = save_char(dp, ((*s & 0300) >> 6) + '0');
528		dp = save_char(dp, ((*s & 0070) >> 3) + '0');
529		dp = save_char(dp, (*s & 0007) + '0');
530		s++;
531	    } else
532		dp = save_char(dp, *s++);
533	    break;
534#else
535	default:
536	    dp = save_char(dp, *s++);
537	    break;
538#endif
539	}
540    }
541
542    /*
543     * Now, if we stripped off some leading padding, add it at the end
544     * of the string as mandatory padding.
545     */
546    if (capstart) {
547	dp = save_string(dp, "$<");
548	for (s = capstart;; s++)
549	    if (isdigit(UChar(*s)) || *s == '*' || *s == '.')
550		dp = save_char(dp, *s);
551	    else
552		break;
553	dp = save_string(dp, "/>");
554    }
555
556    (void) save_char(dp, '\0');
557    return (my_string);
558}
559
560/*
561 * Check for an expression that corresponds to "%B" (BCD):
562 *	(parameter / 10) * 16 + (parameter % 10)
563 */
564static int
565bcd_expression(const char *str)
566{
567    /* leave this non-const for HPUX */
568    static char fmt[] = "%%p%c%%{10}%%/%%{16}%%*%%p%c%%{10}%%m%%+";
569    int len = 0;
570    char ch1, ch2;
571
572    if (sscanf(str, fmt, &ch1, &ch2) == 2
573	&& isdigit(UChar(ch1))
574	&& isdigit(UChar(ch2))
575	&& (ch1 == ch2)) {
576	len = 28;
577#ifndef NDEBUG
578	{
579	    char buffer[80];
580	    int tst;
581	    sprintf(buffer, fmt, ch1, ch2);
582	    tst = strlen(buffer) - 1;
583	    assert(len == tst);
584	}
585#endif
586    }
587    return len;
588}
589
590static char *
591save_tc_char(char *bufptr, int c1)
592{
593    char temp[80];
594
595    if (is7bits(c1) && isprint(c1)) {
596	if (c1 == ':' || c1 == '\\')
597	    bufptr = save_char(bufptr, '\\');
598	bufptr = save_char(bufptr, c1);
599    } else {
600	if (c1 == (c1 & 0x1f))	/* iscntrl() returns T on 255 */
601	    (void) strcpy(temp, unctrl((chtype) c1));
602	else
603	    (void) sprintf(temp, "\\%03o", c1);
604	bufptr = save_string(bufptr, temp);
605    }
606    return bufptr;
607}
608
609static char *
610save_tc_inequality(char *bufptr, int c1, int c2)
611{
612    bufptr = save_string(bufptr, "%>");
613    bufptr = save_tc_char(bufptr, c1);
614    bufptr = save_tc_char(bufptr, c2);
615    return bufptr;
616}
617
618/*
619 * Here are the capabilities infotocap assumes it can translate to:
620 *
621 *     %%       output `%'
622 *     %d       output value as in printf %d
623 *     %2       output value as in printf %2d
624 *     %3       output value as in printf %3d
625 *     %.       output value as in printf %c
626 *     %+c      add character c to value, then do %.
627 *     %>xy     if value > x then add y, no output
628 *     %r       reverse order of two parameters, no output
629 *     %i       increment by one, no output
630 *     %n       exclusive-or all parameters with 0140 (Datamedia 2500)
631 *     %B       BCD (16*(value/10)) + (value%10), no output
632 *     %D       Reverse coding (value - 2*(value%16)), no output (Delta Data).
633 *     %m       exclusive-or all parameters with 0177 (not in 4.4BSD)
634 */
635
636/*
637 * Convert a terminfo string to termcap format.  Parameters are as in
638 * _nc_captoinfo().
639 */
640NCURSES_EXPORT(char *)
641_nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameterized)
642{
643    int seenone = 0, seentwo = 0, saw_m = 0, saw_n = 0;
644    const char *padding;
645    const char *trimmed = 0;
646    char ch1 = 0, ch2 = 0;
647    char *bufptr = init_string();
648    int len;
649    bool syntax_error = FALSE;
650
651    /* we may have to move some trailing mandatory padding up front */
652    padding = str + strlen(str) - 1;
653    if (padding > str && *padding == '>' && *--padding == '/') {
654	--padding;
655	while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*')
656	    padding--;
657	if (padding > str && *padding == '<' && *--padding == '$')
658	    trimmed = padding;
659	padding += 2;
660
661	while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*')
662	    bufptr = save_char(bufptr, *padding++);
663    }
664
665    for (; *str && str != trimmed; str++) {
666	int c1, c2;
667	char *cp = 0;
668
669	if (str[0] == '\\' && (str[1] == '^' || str[1] == ',')) {
670	    bufptr = save_char(bufptr, *++str);
671	} else if (str[0] == '$' && str[1] == '<') {	/* discard padding */
672	    str += 2;
673	    while (isdigit(UChar(*str))
674		   || *str == '.'
675		   || *str == '*'
676		   || *str == '/'
677		   || *str == '>')
678		str++;
679	    --str;
680	} else if (str[0] == '%' && str[1] == '%') {	/* escaped '%' */
681	    bufptr = save_string(bufptr, "%%");
682	    ++str;
683	} else if (*str != '%' || (parameterized < 1)) {
684	    bufptr = save_char(bufptr, *str);
685	} else if (sscanf(str, "%%?%%{%d}%%>%%t%%{%d}%%+%%;", &c1, &c2) == 2) {
686	    str = strchr(str, ';');
687	    bufptr = save_tc_inequality(bufptr, c1, c2);
688	} else if (sscanf(str, "%%?%%{%d}%%>%%t%%'%c'%%+%%;", &c1, &ch2) == 2) {
689	    str = strchr(str, ';');
690	    bufptr = save_tc_inequality(bufptr, c1, c2);
691	} else if (sscanf(str, "%%?%%'%c'%%>%%t%%{%d}%%+%%;", &ch1, &c2) == 2) {
692	    str = strchr(str, ';');
693	    bufptr = save_tc_inequality(bufptr, c1, c2);
694	} else if (sscanf(str, "%%?%%'%c'%%>%%t%%'%c'%%+%%;", &ch1, &ch2) == 2) {
695	    str = strchr(str, ';');
696	    bufptr = save_tc_inequality(bufptr, c1, c2);
697	} else if ((len = bcd_expression(str)) != 0) {
698	    str += len;
699	    bufptr = save_string(bufptr, "%B");
700	} else if ((sscanf(str, "%%{%d}%%+%%c", &c1) == 1
701		    || sscanf(str, "%%'%c'%%+%%c", &ch1) == 1)
702		   && (cp = strchr(str, '+'))) {
703	    str = cp + 2;
704	    bufptr = save_string(bufptr, "%+");
705
706	    if (ch1)
707		c1 = ch1;
708	    bufptr = save_tc_char(bufptr, c1);
709	}
710	/* FIXME: this "works" for 'delta' */
711	else if (strncmp(str, "%{2}%*%-", 8) == 0) {
712	    str += 7;
713	    bufptr = save_string(bufptr, "%D");
714	} else if (strncmp(str, "%{96}%^", 7) == 0) {
715	    str += 6;
716	    if (saw_m++ == 0) {
717		bufptr = save_string(bufptr, "%n");
718	    }
719	} else if (strncmp(str, "%{127}%^", 8) == 0) {
720	    str += 7;
721	    if (saw_n++ == 0) {
722		bufptr = save_string(bufptr, "%m");
723	    }
724	} else {		/* cm-style format element */
725	    str++;
726	    switch (*str) {
727	    case '%':
728		bufptr = save_char(bufptr, '%');
729		break;
730
731	    case '0':
732	    case '1':
733	    case '2':
734	    case '3':
735	    case '4':
736	    case '5':
737	    case '6':
738	    case '7':
739	    case '8':
740	    case '9':
741		bufptr = save_char(bufptr, '%');
742		while (isdigit(UChar(*str)))
743		    bufptr = save_char(bufptr, *str++);
744		if (strchr("doxX.", *str)) {
745		    if (*str != 'd')	/* termcap doesn't have octal, hex */
746			return 0;
747		}
748		break;
749
750	    case 'd':
751		bufptr = save_string(bufptr, "%d");
752		break;
753
754	    case 'c':
755		bufptr = save_string(bufptr, "%.");
756		break;
757
758		/*
759		 * %s isn't in termcap, but it's convenient to pass it through
760		 * so we can represent things like terminfo pfkey strings in
761		 * termcap notation.
762		 */
763	    case 's':
764		bufptr = save_string(bufptr, "%s");
765		break;
766
767	    case 'p':
768		str++;
769		if (*str == '1')
770		    seenone = 1;
771		else if (*str == '2') {
772		    if (!seenone && !seentwo) {
773			bufptr = save_string(bufptr, "%r");
774			seentwo++;
775		    }
776		} else if (*str >= '3')
777		    return (0);
778		break;
779
780	    case 'i':
781		bufptr = save_string(bufptr, "%i");
782		break;
783
784	    default:
785		bufptr = save_char(bufptr, *str);
786		syntax_error = TRUE;
787		break;
788	    }			/* endswitch (*str) */
789	}			/* endelse (*str == '%') */
790
791	/*
792	 * 'str' always points to the end of what was scanned in this step,
793	 * but that may not be the end of the string.
794	 */
795	assert(str != 0);
796	if (*str == '\0')
797	    break;
798
799    }				/* endwhile (*str) */
800
801    return (syntax_error ? NULL : my_string);
802}
803
804#ifdef MAIN
805
806int curr_line;
807
808int
809main(int argc, char *argv[])
810{
811    int c, tc = FALSE;
812
813    while ((c = getopt(argc, argv, "c")) != EOF)
814	switch (c) {
815	case 'c':
816	    tc = TRUE;
817	    break;
818	}
819
820    curr_line = 0;
821    for (;;) {
822	char buf[BUFSIZ];
823
824	++curr_line;
825	if (fgets(buf, sizeof(buf), stdin) == 0)
826	    break;
827	buf[strlen(buf) - 1] = '\0';
828	_nc_set_source(buf);
829
830	if (tc) {
831	    char *cp = _nc_infotocap("to termcap", buf, 1);
832
833	    if (cp)
834		(void) fputs(cp, stdout);
835	} else
836	    (void) fputs(_nc_captoinfo("to terminfo", buf, 1), stdout);
837	(void) putchar('\n');
838    }
839    return (0);
840}
841#endif /* MAIN */
842
843#if NO_LEAKS
844NCURSES_EXPORT(void)
845_nc_captoinfo_leaks(void)
846{
847    if (my_string != 0) {
848	FreeAndNull(my_string);
849    }
850    my_length = 0;
851}
852#endif
853