158314Sache/****************************************************************************
2136759Speter * Copyright (c) 1998-2006,2008 Free Software Foundation, Inc.              *
321308Sache *                                                                          *
421308Sache * Permission is hereby granted, free of charge, to any person obtaining a  *
5136759Speter * copy of this software and associated documentation files (the            *
621308Sache * "Software"), to deal in the Software without restriction, including      *
721308Sache * without limitation the rights to use, copy, modify, merge, publish,      *
821308Sache * distribute, distribute with modifications, sublicense, and/or sell       *
921308Sache * copies of the Software, and to permit persons to whom the Software is    *
1021308Sache * furnished to do so, subject to the following conditions:                 *
1121308Sache *                                                                          *
1258314Sache * The above copyright notice and this permission notice shall be included  *
1321308Sache * in all copies or substantial portions of the Software.                   *
1421308Sache *                                                                          *
1521308Sache * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1621308Sache * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1721308Sache * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1821308Sache * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1921308Sache * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2021308Sache * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2121308Sache * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2221308Sache *                                                                          *
2358314Sache * Except as contained in this notice, the name(s) of the above copyright   *
2421308Sache * holders shall not be used in advertising or otherwise to promote the     *
2521308Sache * sale, use or other dealings in this Software without prior written       *
2621308Sache * authorization.                                                           *
2721308Sache ****************************************************************************/
2821308Sache
2921308Sache/****************************************************************************
3021308Sache *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3121308Sache *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
3221308Sache *     and: Thomas E. Dickey                        1996-on                 *
3321308Sache ****************************************************************************/
3421308Sache
3521308Sache/*
3626497Sache *	captoinfo.c --- conversion between termcap and terminfo formats
3726497Sache *
3821308Sache *	The captoinfo() code was swiped from Ross Ridge's mytinfo package,
3921308Sache *	adapted to fit ncurses by Eric S. Raymond <esr@snark.thyrsus.com>.
4021308Sache *
4121308Sache *	There is just one entry point:
4221308Sache *
4321308Sache *	char *_nc_captoinfo(n, s, parameterized)
4426497Sache *
4521308Sache *	Convert value s for termcap string capability named n into terminfo
4621308Sache *	format.
4721308Sache *
48119614Sache *	This code recognizes all the standard 4.4BSD %-escapes:
4921308Sache *
5021308Sache *	%%       output `%'
5121308Sache *	%d       output value as in printf %d
5221308Sache *	%2       output value as in printf %2d
5321308Sache *	%3       output value as in printf %3d
5421308Sache *	%.       output value as in printf %c
5521308Sache *	%+x      add x to value, then do %.
5621308Sache *	%>xy     if value > x then add y, no output
5758314Sache *	%r       reverse order of two parameters, no output
5858314Sache *	%i       increment by one, no output
5958314Sache *	%n       exclusive-or all parameters with 0140 (Datamedia 2500)
6021308Sache *	%B       BCD (16*(value/10)) + (value%10), no output
6121308Sache *	%D       Reverse coding (value - 2*(value%16)), no output (Delta Data).
6221308Sache *
6321308Sache *	Also, %02 and %03 are accepted as synonyms for %2 and %3.
6458314Sache *
6575409Sache *	Besides all the standard termcap escapes, this translator understands
6635486Sache *	the following extended escapes:
6721308Sache *
68119614Sache *	used by GNU Emacs termcap libraries
69119614Sache *		%a[+*-/=][cp]x	GNU arithmetic.
70119614Sache *		%m		xor the first two parameters by 0177
71119614Sache *		%b		backup to previous parameter
72119614Sache *		%f		skip this parameter
7321308Sache *
74119614Sache *	used by the University of Waterloo (MFCF) termcap libraries
75136759Speter *		%-x	 subtract parameter FROM char x and output it as a char
76119614Sache *		%ax	 add the character x to parameter
77119614Sache *
78119614Sache *	If #define WATERLOO is on, also enable these translations:
79119614Sache *
80119614Sache *		%sx	 subtract parameter FROM the character x
8121308Sache *
8258314Sache *	By default, this Waterloo translations are not compiled in, because
8321308Sache *	the Waterloo %s conflicts with the way terminfo uses %s in strings for
8421308Sache *	function programming.
8521308Sache *
8621308Sache *	Note the two definitions of %a: the GNU definition is translated if the
8721308Sache *	characters after the 'a' are valid for it, otherwise the UW definition
8821308Sache *	is translated.
8921308Sache */
9021308Sache
9121308Sache#include <curses.priv.h>
9221308Sache
9321308Sache#include <ctype.h>
9421308Sache#include <tic.h>
9521308Sache
9621308SacheMODULE_ID("$Id: captoinfo.c,v 1.52 2008/08/16 19:24:51 tom Exp $")
9721308Sache
9821308Sache#define MAX_PUSHED	16	/* max # args we can push onto the stack */
9921308Sache
10021308Sachestatic int stack[MAX_PUSHED];	/* the stack */
10121308Sachestatic int stackptr;		/* the next empty place on the stack */
10221308Sachestatic int onstack;		/* the top of stack */
10321308Sachestatic int seenm;		/* seen a %m */
10421308Sachestatic int seenn;		/* seen a %n */
10521308Sachestatic int seenr;		/* seen a %r */
10621308Sachestatic int param;		/* current parameter */
10721308Sachestatic char *dp;		/* pointer to end of the converted string */
10821308Sache
10921308Sachestatic char *my_string;
11021308Sachestatic size_t my_length;
11121308Sache
11221308Sachestatic char *
11321308Sacheinit_string(void)
11421308Sache/* initialize 'my_string', 'my_length' */
11521308Sache{
11675409Sache    if (my_string == 0)
11721308Sache	my_string = typeMalloc(char, my_length = 256);
11821308Sache    if (my_string == 0)
11921308Sache	_nc_err_abort(MSG_NO_MEMORY);
12021308Sache
12121308Sache    *my_string = '\0';
12221308Sache    return my_string;
12321308Sache}
12421308Sache
12521308Sachestatic char *
12621308Sachesave_string(char *d, const char *const s)
12721308Sache{
12821308Sache    size_t have = (d - my_string);
12921308Sache    size_t need = have + strlen(s) + 2;
13021308Sache    if (need > my_length) {
13121308Sache	my_string = (char *) realloc(my_string, my_length = (need + need));
13221308Sache	if (my_string == 0)
13321308Sache	    _nc_err_abort(MSG_NO_MEMORY);
13421308Sache	d = my_string + have;
13521308Sache    }
13621308Sache    (void) strcpy(d, s);
13721308Sache    return d + strlen(d);
13821308Sache}
13921308Sache
14021308Sachestatic NCURSES_INLINE char *
14121308Sachesave_char(char *s, int c)
14221308Sache{
14321308Sache    static char temp[2];
14421308Sache    temp[0] = (char) c;
14521308Sache    return save_string(s, temp);
14621308Sache}
14721308Sache
14821308Sachestatic void
14921308Sachepush(void)
15021308Sache/* push onstack on to the stack */
15121308Sache{
15221308Sache    if (stackptr >= MAX_PUSHED)
15321308Sache	_nc_warning("string too complex to convert");
15421308Sache    else
15575409Sache	stack[stackptr++] = onstack;
15675409Sache}
15775409Sache
15821308Sachestatic void
15975409Sachepop(void)
16021308Sache/* pop the top of the stack into onstack */
16121308Sache{
16221308Sache    if (stackptr == 0) {
16321308Sache	if (onstack == 0)
16421308Sache	    _nc_warning("I'm confused");
16575409Sache	else
16675409Sache	    onstack = 0;
16721308Sache    } else
16821308Sache	onstack = stack[--stackptr];
16975409Sache    param++;
17075409Sache}
17121308Sache
17221308Sachestatic int
17321308Sachecvtchar(register const char *sp)
17421308Sache/* convert a character to a terminfo push */
17521308Sache{
17675409Sache    unsigned char c = 0;
17775409Sache    int len;
17875409Sache
17975409Sache    switch (*sp) {
18075409Sache    case '\\':
18175409Sache	switch (*++sp) {
18275409Sache	case '\'':
183136759Speter	case '$':
184136759Speter	case '\\':
18521308Sache	case '%':
18621308Sache	    c = (unsigned char) (*sp);
18721308Sache	    len = 2;
18875409Sache	    break;
18975409Sache	case '\0':
190136759Speter	    c = '\\';
191136759Speter	    len = 1;
19221308Sache	    break;
19321308Sache	case '0':
19421308Sache	case '1':
19521308Sache	case '2':
19621308Sache	case '3':
19721308Sache	    len = 1;
19821308Sache	    while (isdigit(UChar(*sp))) {
19921308Sache		c = 8 * c + (*sp++ - '0');
20021308Sache		len++;
201136759Speter	    }
20221308Sache	    break;
203136759Speter	default:
20421308Sache	    c = (unsigned char) (*sp);
20521308Sache	    len = 2;
206136759Speter	    break;
20721308Sache	}
20821308Sache	break;
209136759Speter    case '^':
21021308Sache	c = (*++sp & 0x1f);
21121308Sache	len = 2;
21221308Sache	break;
21321308Sache    default:
214136759Speter	c = (unsigned char) (*sp);
215136759Speter	len = 1;
216136759Speter    }
217136759Speter    if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') {
218136759Speter	dp = save_string(dp, "%\'");
219136759Speter	dp = save_char(dp, c);
22021308Sache	dp = save_char(dp, '\'');
22121308Sache    } else {
22221308Sache	dp = save_string(dp, "%{");
22321308Sache	if (c > 99)
224119614Sache	    dp = save_char(dp, c / 100 + '0');
22575409Sache	if (c > 9)
22675409Sache	    dp = save_char(dp, ((int) (c / 10)) % 10 + '0');
227136759Speter	dp = save_char(dp, c % 10 + '0');
22875409Sache	dp = save_char(dp, '}');
229136759Speter    }
23021308Sache    return len;
23121308Sache}
23221308Sache
23321308Sachestatic void
23421308Sachegetparm(int parm, int n)
23521308Sache/* push n copies of param on the terminfo stack if not already there */
23621308Sache{
23721308Sache    if (seenr) {
23821308Sache	if (parm == 1)
23921308Sache	    parm = 2;
24021308Sache	else if (parm == 2)
24121308Sache	    parm = 1;
24221308Sache    }
24321308Sache    if (onstack == parm) {
24421308Sache	if (n > 1) {
24521308Sache	    _nc_warning("string may not be optimal");
246136759Speter	    dp = save_string(dp, "%Pa");
247136759Speter	    while (n--) {
248136759Speter		dp = save_string(dp, "%ga");
249136759Speter	    }
250136759Speter	}
251136759Speter	return;
252136759Speter    }
253136759Speter    if (onstack != 0)
254136759Speter	push();
255136759Speter
256136759Speter    onstack = parm;
257136759Speter
258136759Speter    while (n--) {
259136759Speter	dp = save_string(dp, "%p");
260136759Speter	dp = save_char(dp, '0' + parm);
261136759Speter    }
262136759Speter
26375409Sache    if (seenn && parm < 3) {
264136759Speter	dp = save_string(dp, "%{96}%^");
265136759Speter    }
266136759Speter
267136759Speter    if (seenm && parm < 3) {
268136759Speter	dp = save_string(dp, "%{127}%^");
269136759Speter    }
270136759Speter}
271136759Speter
272136759Speter/*
273136759Speter * Convert a termcap string to terminfo format.
274136759Speter * 'cap' is the relevant terminfo capability index.
275136759Speter * 's' is the string value of the capability.
276136759Speter * 'parameterized' tells what type of translations to do:
277136759Speter *	% translations if 1
278136759Speter *	pad translations if >=0
279136759Speter */
280136759SpeterNCURSES_EXPORT(char *)
28121308Sache_nc_captoinfo(const char *cap, const char *s, int const parameterized)
28221308Sache{
28321308Sache    const char *capstart;
28475409Sache
28575409Sache    stackptr = 0;
28675409Sache    onstack = 0;
28721308Sache    seenm = 0;
28821308Sache    seenn = 0;
28921308Sache    seenr = 0;
29021308Sache    param = 1;
29121308Sache
29275409Sache    dp = init_string();
29375409Sache
294136759Speter    /* skip the initial padding (if we haven't been told not to) */
295136759Speter    capstart = 0;
29621308Sache    if (s == 0)
29721308Sache	s = "";
29821308Sache    if (parameterized >= 0 && isdigit(UChar(*s)))
29958314Sache	for (capstart = s;; s++)
30058314Sache	    if (!(isdigit(UChar(*s)) || *s == '*' || *s == '.'))
30158314Sache		break;
30258314Sache
30358314Sache    while (*s != '\0') {
30458314Sache	switch (*s) {
30558314Sache	case '%':
30658314Sache	    s++;
307136759Speter	    if (parameterized < 1) {
30858314Sache		dp = save_char(dp, '%');
30958314Sache		break;
31058314Sache	    }
31121308Sache	    switch (*s++) {
31221308Sache	    case '%':
31321308Sache		dp = save_char(dp, '%');
31421308Sache		break;
31521308Sache	    case 'r':
31621308Sache		if (seenr++ == 1) {
31721308Sache		    _nc_warning("saw %%r twice in %s", cap);
31821308Sache		}
31975409Sache		break;
32075409Sache	    case 'm':
32121308Sache		if (seenm++ == 1) {
32221308Sache		    _nc_warning("saw %%m twice in %s", cap);
32321308Sache		}
32421308Sache		break;
32521308Sache	    case 'n':
32621308Sache		if (seenn++ == 1) {
32721308Sache		    _nc_warning("saw %%n twice in %s", cap);
32821308Sache		}
32921308Sache		break;
33021308Sache	    case 'i':
33121308Sache		dp = save_string(dp, "%i");
33221308Sache		break;
33321308Sache	    case '6':
33421308Sache	    case 'B':
33521308Sache		getparm(param, 1);
33675409Sache		dp = save_string(dp, "%{10}%/%{16}%*");
33775409Sache		getparm(param, 1);
33875409Sache		dp = save_string(dp, "%{10}%m%+");
33921308Sache		break;
34075409Sache	    case '8':
34121308Sache	    case 'D':
34221308Sache		getparm(param, 2);
34321308Sache		dp = save_string(dp, "%{2}%*%-");
34421308Sache		break;
34521308Sache	    case '>':
34621308Sache		getparm(param, 2);
34721308Sache		/* %?%{x}%>%t%{y}%+%; */
34875409Sache		dp = save_string(dp, "%?");
34975409Sache		s += cvtchar(s);
35075409Sache		dp = save_string(dp, "%>%t");
351136759Speter		s += cvtchar(s);
352136759Speter		dp = save_string(dp, "%+%;");
35321308Sache		break;
35475409Sache	    case 'a':
35521308Sache		if ((*s == '=' || *s == '+' || *s == '-'
35621308Sache		     || *s == '*' || *s == '/')
35721308Sache		    && (s[1] == 'p' || s[1] == 'c')
35821308Sache		    && s[2] != '\0') {
35921308Sache		    int l;
36075409Sache		    l = 2;
36175409Sache		    if (*s != '=')
362136759Speter			getparm(param, 1);
363136759Speter		    if (s[1] == 'p') {
36421308Sache			getparm(param + s[2] - '@', 1);
36521308Sache			if (param != onstack) {
36621308Sache			    pop();
36775409Sache			    param--;
36875409Sache			}
369136759Speter			l++;
370136759Speter		    } else
37121308Sache			l += cvtchar(s + 2);
37275409Sache		    switch (*s) {
37321308Sache		    case '+':
37421308Sache			dp = save_string(dp, "%+");
37521308Sache			break;
37658314Sache		    case '-':
37758314Sache			dp = save_string(dp, "%-");
37858314Sache			break;
37958314Sache		    case '*':
38058314Sache			dp = save_string(dp, "%*");
38158314Sache			break;
38258314Sache		    case '/':
38358314Sache			dp = save_string(dp, "%/");
38458314Sache			break;
38558314Sache		    case '=':
38658314Sache			if (seenr) {
38758314Sache			    if (param == 1)
38858314Sache				onstack = 2;
38958314Sache			    else if (param == 2)
39058314Sache				onstack = 1;
391119614Sache			    else
392119614Sache				onstack = param;
39358314Sache			} else
39458314Sache			    onstack = param;
39558314Sache			break;
39658314Sache		    }
39758314Sache		    s += l;
39858314Sache		    break;
399119614Sache		}
400119614Sache		getparm(param, 1);
40158314Sache		s += cvtchar(s);
40258314Sache		dp = save_string(dp, "%+");
40358314Sache		break;
40458314Sache	    case '+':
40558314Sache		getparm(param, 1);
40658314Sache		s += cvtchar(s);
40758314Sache		dp = save_string(dp, "%+%c");
40858314Sache		pop();
40958314Sache		break;
41058314Sache	    case 's':
41158314Sache#ifdef WATERLOO
41258314Sache		s += cvtchar(s);
41358314Sache		getparm(param, 1);
41458314Sache		dp = save_string(dp, "%-");
415119614Sache#else
416119614Sache		getparm(param, 1);
417119614Sache		dp = save_string(dp, "%s");
41858314Sache		pop();
41958314Sache#endif /* WATERLOO */
42058314Sache		break;
42158314Sache	    case '-':
42221308Sache		s += cvtchar(s);
42321308Sache		getparm(param, 1);
42421308Sache		dp = save_string(dp, "%-%c");
42521308Sache		pop();
42621308Sache		break;
42721308Sache	    case '.':
42821308Sache		getparm(param, 1);
429136759Speter		dp = save_string(dp, "%c");
43021308Sache		pop();
431119614Sache		break;
432119614Sache	    case '0':		/* not clear any of the historical termcaps did this */
433119614Sache		if (*s == '3')
434119614Sache		    goto see03;
435119614Sache		else if (*s != '2')
436119614Sache		    goto invalid;
437119614Sache		/* FALLTHRU */
43821308Sache	    case '2':
43921308Sache		getparm(param, 1);
44021308Sache		dp = save_string(dp, "%2d");
44121308Sache		pop();
44221308Sache		break;
44321308Sache	    case '3':
44421308Sache	      see03:
44521308Sache		getparm(param, 1);
44621308Sache		dp = save_string(dp, "%3d");
44758314Sache		pop();
44821308Sache		break;
44921308Sache	    case 'd':
45021308Sache		getparm(param, 1);
45121308Sache		dp = save_string(dp, "%d");
45221308Sache		pop();
45321308Sache		break;
45421308Sache	    case 'f':
45521308Sache		param++;
45621308Sache		break;
45721308Sache	    case 'b':
45821308Sache		param--;
459136759Speter		break;
46021308Sache	    case '\\':
46121308Sache		dp = save_string(dp, "%\\");
46221308Sache		break;
46321308Sache	    default:
464136759Speter	      invalid:
46521308Sache		dp = save_char(dp, '%');
46621308Sache		s--;
46721308Sache		_nc_warning("unknown %% code %s (%#x) in %s",
46821308Sache			    unctrl((chtype) *s), UChar(*s), cap);
46921308Sache		break;
47021308Sache	    }
47121308Sache	    break;
47221308Sache#ifdef REVISIBILIZE
47321308Sache	case '\\':
47421308Sache	    dp = save_char(dp, *s++);
47521308Sache	    dp = save_char(dp, *s++);
47621308Sache	    break;
47721308Sache	case '\n':
47821308Sache	    dp = save_string(dp, "\\n");
47921308Sache	    s++;
48021308Sache	    break;
48121308Sache	case '\t':
48221308Sache	    dp = save_string(dp, "\\t");
48321308Sache	    s++;
48421308Sache	    break;
48547563Sache	case '\r':
48647563Sache	    dp = save_string(dp, "\\r");
48747563Sache	    s++;
48847563Sache	    break;
489119614Sache	case '\200':
490119614Sache	    dp = save_string(dp, "\\0");
49147563Sache	    s++;
49221308Sache	    break;
49321308Sache	case '\f':
49421308Sache	    dp = save_string(dp, "\\f");
49521308Sache	    s++;
49675409Sache	    break;
49721308Sache	case '\b':
49821308Sache	    dp = save_string(dp, "\\b");
49921308Sache	    s++;
50021308Sache	    break;
50121308Sache	case ' ':
50221308Sache	    dp = save_string(dp, "\\s");
50321308Sache	    s++;
50421308Sache	    break;
50521308Sache	case '^':
50621308Sache	    dp = save_string(dp, "\\^");
50758314Sache	    s++;
50821308Sache	    break;
50921308Sache	case ':':
51058314Sache	    dp = save_string(dp, "\\:");
51121308Sache	    s++;
51221308Sache	    break;
51358314Sache	case ',':
51421308Sache	    dp = save_string(dp, "\\,");
51521308Sache	    s++;
51621308Sache	    break;
51721308Sache	default:
518136759Speter	    if (*s == '\033') {
51947563Sache		dp = save_string(dp, "\\E");
52047563Sache		s++;
52147563Sache	    } else if (*s > 0 && *s < 32) {
52247563Sache		dp = save_char(dp, '^');
523119614Sache		dp = save_char(dp, *s + '@');
524119614Sache		s++;
52547563Sache	    } else if (*s <= 0 || *s >= 127) {
52621308Sache		dp = save_char(dp, '\\');
52721308Sache		dp = save_char(dp, ((*s & 0300) >> 6) + '0');
52821308Sache		dp = save_char(dp, ((*s & 0070) >> 3) + '0');
52975409Sache		dp = save_char(dp, (*s & 0007) + '0');
53021308Sache		s++;
53121308Sache	    } else
53258314Sache		dp = save_char(dp, *s++);
53358314Sache	    break;
53458314Sache#else
53558314Sache	default:
53658314Sache	    dp = save_char(dp, *s++);
53758314Sache	    break;
53858314Sache#endif
53958314Sache	}
540119614Sache    }
541119614Sache
54221308Sache    /*
54321308Sache     * Now, if we stripped off some leading padding, add it at the end
54447563Sache     * of the string as mandatory padding.
54575409Sache     */
54647563Sache    if (capstart) {
54758314Sache	dp = save_string(dp, "$<");
54858314Sache	for (s = capstart;; s++)
54958314Sache	    if (isdigit(UChar(*s)) || *s == '*' || *s == '.')
55058314Sache		dp = save_char(dp, *s);
551119614Sache	    else
55258314Sache		break;
55347563Sache	dp = save_string(dp, "/>");
554119614Sache    }
55547563Sache
55647563Sache    (void) save_char(dp, '\0');
55721308Sache    return (my_string);
558119614Sache}
559119614Sache
560119614Sache/*
561119614Sache * Check for an expression that corresponds to "%B" (BCD):
562119614Sache *	(parameter / 10) * 16 + (parameter % 10)
563119614Sache */
564119614Sachestatic int
565119614Sachebcd_expression(const char *str)
566119614Sache{
567119614Sache    /* leave this non-const for HPUX */
568119614Sache    static char fmt[] = "%%p%c%%{10}%%/%%{16}%%*%%p%c%%{10}%%m%%+";
569119614Sache    int len = 0;
570119614Sache    char ch1, ch2;
571119614Sache
572119614Sache    if (sscanf(str, fmt, &ch1, &ch2) == 2
573119614Sache	&& isdigit(UChar(ch1))
57421308Sache	&& isdigit(UChar(ch2))
57521308Sache	&& (ch1 == ch2)) {
57621308Sache	len = 28;
577136759Speter#ifndef NDEBUG
57826497Sache	{
579136759Speter	    char buffer[80];
580136759Speter	    int tst;
581136759Speter	    sprintf(buffer, fmt, ch1, ch2);
582136759Speter	    tst = strlen(buffer) - 1;
583119614Sache	    assert(len == tst);
584119614Sache	}
585136759Speter#endif
586119614Sache    }
58721308Sache    return len;
58875409Sache}
58975409Sache
59075409Sachestatic char *
59175409Sachesave_tc_char(char *bufptr, int c1)
59275409Sache{
59375409Sache    char temp[80];
59426497Sache
59575409Sache    if (is7bits(c1) && isprint(c1)) {
59621308Sache	if (c1 == ':' || c1 == '\\')
59775409Sache	    bufptr = save_char(bufptr, '\\');
59875409Sache	bufptr = save_char(bufptr, c1);
59975409Sache    } else {
60075409Sache	if (c1 == (c1 & 0x1f))	/* iscntrl() returns T on 255 */
60175409Sache	    (void) strcpy(temp, unctrl((chtype) c1));
602136759Speter	else
603136759Speter	    (void) sprintf(temp, "\\%03o", c1);
604136759Speter	bufptr = save_string(bufptr, temp);
605136759Speter    }
606136759Speter    return bufptr;
607136759Speter}
608136759Speter
609136759Speterstatic char *
610136759Spetersave_tc_inequality(char *bufptr, int c1, int c2)
611136759Speter{
612136759Speter    bufptr = save_string(bufptr, "%>");
613136759Speter    bufptr = save_tc_char(bufptr, c1);
614136759Speter    bufptr = save_tc_char(bufptr, c2);
615136759Speter    return bufptr;
616136759Speter}
617136759Speter
61875409Sache/*
619136759Speter * Here are the capabilities infotocap assumes it can translate to:
620136759Speter *
621136759Speter *     %%       output `%'
622136759Speter *     %d       output value as in printf %d
623136759Speter *     %2       output value as in printf %2d
624136759Speter *     %3       output value as in printf %3d
625136759Speter *     %.       output value as in printf %c
626136759Speter *     %+c      add character c to value, then do %.
627119614Sache *     %>xy     if value > x then add y, no output
628136759Speter *     %r       reverse order of two parameters, no output
629119614Sache *     %i       increment by one, no output
63026497Sache *     %n       exclusive-or all parameters with 0140 (Datamedia 2500)
63126497Sache *     %B       BCD (16*(value/10)) + (value%10), no output
63275409Sache *     %D       Reverse coding (value - 2*(value%16)), no output (Delta Data).
63375409Sache *     %m       exclusive-or all parameters with 0177 (not in 4.4BSD)
63475409Sache */
63575409Sache
63675409Sache/*
63775409Sache * Convert a terminfo string to termcap format.  Parameters are as in
63875409Sache * _nc_captoinfo().
63926497Sache */
640119614SacheNCURSES_EXPORT(char *)
641119614Sache_nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameterized)
642119614Sache{
643119614Sache    int seenone = 0, seentwo = 0, saw_m = 0, saw_n = 0;
644119614Sache    const char *padding;
645119614Sache    const char *trimmed = 0;
646119614Sache    char ch1 = 0, ch2 = 0;
647119614Sache    char *bufptr = init_string();
648119614Sache    int len;
649119614Sache    bool syntax_error = FALSE;
650119614Sache
65126497Sache    /* we may have to move some trailing mandatory padding up front */
652119614Sache    padding = str + strlen(str) - 1;
65326497Sache    if (padding > str && *padding == '>' && *--padding == '/') {
65421308Sache	--padding;
65521308Sache	while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*')
656119614Sache	    padding--;
657119614Sache	if (padding > str && *padding == '<' && *--padding == '$')
658119614Sache	    trimmed = padding;
659136759Speter	padding += 2;
660119614Sache
661119614Sache	while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*')
662119614Sache	    bufptr = save_char(bufptr, *padding++);
663119614Sache    }
664119614Sache
665119614Sache    for (; *str && str != trimmed; str++) {
666119614Sache	int c1, c2;
667119614Sache	char *cp = 0;
668136759Speter
669119614Sache	if (str[0] == '\\' && (str[1] == '^' || str[1] == ',')) {
670119614Sache	    bufptr = save_char(bufptr, *++str);
671119614Sache	} else if (str[0] == '$' && str[1] == '<') {	/* discard padding */
672119614Sache	    str += 2;
673136759Speter	    while (isdigit(UChar(*str))
674119614Sache		   || *str == '.'
675119614Sache		   || *str == '*'
676119614Sache		   || *str == '/'
677119614Sache		   || *str == '>')
67821308Sache		str++;
67921308Sache	    --str;
68021308Sache	} else if (str[0] == '%' && str[1] == '%') {	/* escaped '%' */
681119614Sache	    bufptr = save_string(bufptr, "%%");
682119614Sache	    ++str;
68321308Sache	} else if (*str != '%' || (parameterized < 1)) {
68421308Sache	    bufptr = save_char(bufptr, *str);
68521308Sache	} else if (sscanf(str, "%%?%%{%d}%%>%%t%%{%d}%%+%%;", &c1, &c2) == 2) {
68621308Sache	    str = strchr(str, ';');
68721308Sache	    bufptr = save_tc_inequality(bufptr, c1, c2);
68821308Sache	} else if (sscanf(str, "%%?%%{%d}%%>%%t%%'%c'%%+%%;", &c1, &ch2) == 2) {
68921308Sache	    str = strchr(str, ';');
69021308Sache	    bufptr = save_tc_inequality(bufptr, c1, c2);
69121308Sache	} else if (sscanf(str, "%%?%%'%c'%%>%%t%%{%d}%%+%%;", &ch1, &c2) == 2) {
692119614Sache	    str = strchr(str, ';');
693119614Sache	    bufptr = save_tc_inequality(bufptr, c1, c2);
694119614Sache	} else if (sscanf(str, "%%?%%'%c'%%>%%t%%'%c'%%+%%;", &ch1, &ch2) == 2) {
69521308Sache	    str = strchr(str, ';');
696119614Sache	    bufptr = save_tc_inequality(bufptr, c1, c2);
69721308Sache	} else if ((len = bcd_expression(str)) != 0) {
69821308Sache	    str += len;
69921308Sache	    bufptr = save_string(bufptr, "%B");
70021308Sache	} else if ((sscanf(str, "%%{%d}%%+%%c", &c1) == 1
70121308Sache		    || sscanf(str, "%%'%c'%%+%%c", &ch1) == 1)
70275409Sache		   && (cp = strchr(str, '+'))) {
70321308Sache	    str = cp + 2;
70475409Sache	    bufptr = save_string(bufptr, "%+");
70558314Sache
70621308Sache	    if (ch1)
70721308Sache		c1 = ch1;
70821308Sache	    bufptr = save_tc_char(bufptr, c1);
70921308Sache	}
71021308Sache	/* FIXME: this "works" for 'delta' */
71121308Sache	else if (strncmp(str, "%{2}%*%-", 8) == 0) {
71221308Sache	    str += 7;
71321308Sache	    bufptr = save_string(bufptr, "%D");
71421308Sache	} else if (strncmp(str, "%{96}%^", 7) == 0) {
71521308Sache	    str += 6;
71621308Sache	    if (saw_m++ == 0) {
71721308Sache		bufptr = save_string(bufptr, "%n");
71821308Sache	    }
71921308Sache	} else if (strncmp(str, "%{127}%^", 8) == 0) {
72021308Sache	    str += 7;
72121308Sache	    if (saw_n++ == 0) {
72221308Sache		bufptr = save_string(bufptr, "%m");
72375409Sache	    }
72447563Sache	} else {		/* cm-style format element */
72547563Sache	    str++;
72621308Sache	    switch (*str) {
72747563Sache	    case '%':
72847563Sache		bufptr = save_char(bufptr, '%');
72947563Sache		break;
73021308Sache
73175409Sache	    case '0':
73221308Sache	    case '1':
73321308Sache	    case '2':
73475409Sache	    case '3':
73558314Sache	    case '4':
73621308Sache	    case '5':
73721308Sache	    case '6':
73821308Sache	    case '7':
73921308Sache	    case '8':
74021308Sache	    case '9':
74121308Sache		bufptr = save_char(bufptr, '%');
74221308Sache		while (isdigit(UChar(*str)))
74321308Sache		    bufptr = save_char(bufptr, *str++);
74421308Sache		if (strchr("doxX.", *str)) {
74521308Sache		    if (*str != 'd')	/* termcap doesn't have octal, hex */
74621308Sache			return 0;
74721308Sache		}
74821308Sache		break;
74975409Sache
75047563Sache	    case 'd':
75147563Sache		bufptr = save_string(bufptr, "%d");
75258314Sache		break;
75347563Sache
75447563Sache	    case 'c':
75547563Sache		bufptr = save_string(bufptr, "%.");
75621308Sache		break;
75721308Sache
75821308Sache		/*
75921308Sache		 * %s isn't in termcap, but it's convenient to pass it through
76021308Sache		 * so we can represent things like terminfo pfkey strings in
76121308Sache		 * termcap notation.
76221308Sache		 */
76321308Sache	    case 's':
76421308Sache		bufptr = save_string(bufptr, "%s");
765119614Sache		break;
766119614Sache
767119614Sache	    case 'p':
768119614Sache		str++;
769119614Sache		if (*str == '1')
770119614Sache		    seenone = 1;
771119614Sache		else if (*str == '2') {
772119614Sache		    if (!seenone && !seentwo) {
773119614Sache			bufptr = save_string(bufptr, "%r");
774119614Sache			seentwo++;
775119614Sache		    }
776119614Sache		} else if (*str >= '3')
777119614Sache		    return (0);
778119614Sache		break;
779119614Sache
780119614Sache	    case 'i':
781119614Sache		bufptr = save_string(bufptr, "%i");
782119614Sache		break;
783119614Sache
784119614Sache	    default:
785119614Sache		bufptr = save_char(bufptr, *str);
786119614Sache		syntax_error = TRUE;
787119614Sache		break;
788119614Sache	    }			/* endswitch (*str) */
789119614Sache	}			/* endelse (*str == '%') */
790119614Sache
791119614Sache	/*
792119614Sache	 * 'str' always points to the end of what was scanned in this step,
793119614Sache	 * but that may not be the end of the string.
794119614Sache	 */
795119614Sache	assert(str != 0);
79621308Sache	if (*str == '\0')
79721308Sache	    break;
798119614Sache
79921308Sache    }				/* endwhile (*str) */
800119614Sache
801119614Sache    return (syntax_error ? NULL : my_string);
802119614Sache}
803119614Sache
804119614Sache#ifdef MAIN
805119614Sache
806119614Sacheint curr_line;
807119614Sache
808119614Sacheint
809119614Sachemain(int argc, char *argv[])
810119614Sache{
81121308Sache    int c, tc = FALSE;
81221308Sache
81321308Sache    while ((c = getopt(argc, argv, "c")) != EOF)
81421308Sache	switch (c) {
81521308Sache	case 'c':
81621308Sache	    tc = TRUE;
81721308Sache	    break;
81821308Sache	}
81921308Sache
82058314Sache    curr_line = 0;
82121308Sache    for (;;) {
82221308Sache	char buf[BUFSIZ];
82321308Sache
82475409Sache	++curr_line;
82575409Sache	if (fgets(buf, sizeof(buf), stdin) == 0)
82621308Sache	    break;
82721308Sache	buf[strlen(buf) - 1] = '\0';
82821308Sache	_nc_set_source(buf);
82921308Sache
83021308Sache	if (tc) {
83121308Sache	    char *cp = _nc_infotocap("to termcap", buf, 1);
83221308Sache
83321308Sache	    if (cp)
83421308Sache		(void) fputs(cp, stdout);
83521308Sache	} else
83675409Sache	    (void) fputs(_nc_captoinfo("to terminfo", buf, 1), stdout);
83721308Sache	(void) putchar('\n');
83821308Sache    }
83921308Sache    return (0);
84021308Sache}
84121308Sache#endif /* MAIN */
84221308Sache
84321308Sache#if NO_LEAKS
84421308SacheNCURSES_EXPORT(void)
84521308Sache_nc_captoinfo_leaks(void)
84621308Sache{
84775409Sache    if (my_string != 0) {
848119614Sache	FreeAndNull(my_string);
849119614Sache    }
850119614Sache    my_length = 0;
851119614Sache}
852119614Sache#endif
853119614Sache