xref: /illumos-gate/usr/src/cmd/captoinfo/captoinfo.c (revision ed5289f91b9bf164dccd6c75398362be77a4478d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1988 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 /*
33  *  NAME
34  *	captoinfo - convert a termcap description to a terminfo description
35  *
36  *  SYNOPSIS
37  *	captoinfo [-1vV] [-w width] [ filename ... ]
38  *
39  *  AUTHOR
40  *	Tony Hansen, January 22, 1984.
41  */
42 
43 #include "curses.h"
44 #include <ctype.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <string.h>
48 #include "otermcap.h"
49 #include "print.h"
50 
51 #define	trace stderr			/* send trace messages to stderr */
52 
53 /* extra termcap variables no longer in terminfo */
54 char *oboolcodes[] =
55 	{
56 	"bs",	/* Terminal can backspace with "^H" */
57 	"nc",	/* No correctly working carriage return (DM2500,H2000) */
58 	"ns",	/* Terminal is a CRT but does not scroll. */
59 	"pt",	/* Has hardware tabs (may need to be set with "is") */
60 	"MT",	/* Has meta key, alternate code. */
61 	"xr",	/* Return acts like ce \r \n (Delta Data) */
62 	0
63 };
64 int cap_bs = 0, cap_nc = 1, cap_ns = 2, cap_pt = 3, cap_MT = 4, cap_xr = 5;
65 char *onumcodes[] =
66 	{
67 	"dB",	/* Number of millisec of bs delay needed */
68 	"dC",	/* Number of millisec of cr delay needed */
69 	"dF",	/* Number of millisec of ff delay needed */
70 	"dN",	/* Number of millisec of nl delay needed */
71 	"dT",	/* Number of millisec of tab delay needed */
72 	"ug",	/* Number of blank chars left by us or ue */
73 /* Ignore the 'kn' number. It was ill-defined and never used. */
74 	"kn",	/* Number of "other" keys */
75 	0
76 };
77 int cap_dB = 0, cap_dC = 1, cap_dF = 2, cap_dN = 3, cap_dT = 4, cap_ug = 5;
78 
79 char *ostrcodes[] =
80 	{
81 	"bc",	/* Backspace if not "^H" */
82 	"ko",	/* Termcap entries for other non-function keys */
83 	"ma",	/* Arrow key map, used by vi version 2 only */
84 	"nl",	/* Newline character (default "\n") */
85 	"rs",	/* undocumented reset string, like is (info is2) */
86 /* Ignore the 'ml' and 'mu' strings. */
87 	"ml",	/* Memory lock on above cursor. */
88 	"mu",	/* Memory unlock (turn off memory lock). */
89 	0
90 };
91 int cap_bc = 0, cap_ko = 1, cap_ma = 2, cap_nl = 3, cap_rs = 4;
92 
93 #define	numelements(x)	(sizeof (x)/sizeof (x[0]))
94 char oboolval[2][numelements(oboolcodes)];
95 short onumval[2][numelements(onumcodes)];
96 char *ostrval[2][numelements(ostrcodes)];
97 
98 /* externs from libcurses.a */
99 extern char *boolnames[], *boolcodes[];
100 extern char *numnames[], *numcodes[];
101 extern char *strnames[], *strcodes[];
102 
103 /* globals for this file */
104 char *progname;			/* argv [0], the name of the program */
105 static char *term_name;		/* the name of the terminal being worked on */
106 static int uselevel;		/* whether we're dealing with use= info */
107 static int boolcount,		/* the maximum numbers of each name array */
108 	    numcount,
109 	    strcount;
110 
111 /* globals dealing with the environment */
112 extern char **environ;
113 static char TERM[100];
114 #if defined(SYSV) || defined(USG)  /* handle both Sys Vr2 and Vr3 curses */
115 static char dirname[BUFSIZ];
116 #else
117 #include <sys/param.h>
118 static char dirname[MAXPATHLEN];
119 #endif /* SYSV || USG */
120 static char TERMCAP[BUFSIZ+15];
121 static char *newenviron[] = { &TERM[0], &TERMCAP[0], 0 };
122 
123 /* dynamic arrays */
124 static char *boolval[2];	/* dynamic array of boolean values */
125 static short *numval[2];	/* dynamic array of numeric values */
126 static char **strval[2];	/* dynamic array of string pointers */
127 
128 /* data buffers */
129 static char *capbuffer;		/* string table, pointed at by strval */
130 static char *nextstring;	/* pointer into string table */
131 static char *bp;		/* termcap raw string table */
132 static char *buflongname;	/* place to copy the long names */
133 
134 /* flags */
135 static int verbose = 0;		/* debugging printing level */
136 static int copycomments = 0;	/* copy comments from tercap source */
137 
138 #define	ispadchar(c)	(isdigit(c) || (c) == '.' || (c) == '*')
139 
140 static void getlongname(void);
141 static void handleko(void);
142 static void handlema(void);
143 static void print_no_use_entry(void);
144 static void print_use_entry(char *);
145 static void captoinfo(void);
146 static void use_etc_termcap(void);
147 static void initdirname(void);
148 static void setfilename(char *);
149 static void setterm_name(void);
150 static void use_file(char *);
151 static void sorttable(char *[], char *[]);
152 static void inittables(void);
153 
154 /*
155  *  Verify that the names given in the termcap entry are all valid.
156  */
157 
158 int
159 capsearch(char *codes[], char *ocodes[], char *cap)
160 {
161 	for (; *codes; codes++)
162 		if (((*codes)[0] == cap[0]) && ((*codes)[1] == cap[1]))
163 			return (1);
164 
165 	for (; *ocodes; ocodes++)
166 		if (((*ocodes)[0] == cap[0]) && ((*ocodes)[1] == cap[1]))
167 			return (1);
168 
169 	return (0);
170 }
171 
172 void
173 checktermcap()
174 {
175 	char *tbuf = bp;
176 	enum { tbool, tnum, tstr, tcancel, tunknown } type;
177 
178 	for (;;) {
179 		tbuf = tskip(tbuf);
180 		while (*tbuf == '\t' || *tbuf == ' ' || *tbuf == ':')
181 			tbuf++;
182 
183 		if (*tbuf == 0)
184 			return;
185 
186 		/* commented out entry? */
187 		if (*tbuf == '.') {
188 			if (verbose)
189 				(void) fprintf(trace, "termcap string '%c%c' "
190 				    "commented out.\n", tbuf[1], tbuf[2]);
191 			if (!capsearch(boolcodes, oboolcodes, tbuf + 1) &&
192 			    !capsearch(numcodes, onumcodes, tbuf + 1) &&
193 			    !capsearch(strcodes, ostrcodes, tbuf + 1))
194 				(void) fprintf(stderr,
195 				    "%s: TERM=%s: commented out code '%.2s' "
196 				    "is unknown.\n", progname, term_name,
197 				    tbuf+1);
198 			continue;
199 		}
200 
201 		if (verbose)
202 			(void) fprintf(trace, "looking at termcap string "
203 			    "'%.2s'.\n", tbuf);
204 
205 		switch (tbuf[2]) {
206 			case ':': case '\0':	type = tbool;	break;
207 			case '#':			type = tnum;	break;
208 			case '=':			type = tstr;	break;
209 			case '@':			type = tcancel;	break;
210 			default:
211 				(void) fprintf(stderr,
212 				    "%s: TERM=%s: unknown type given for the "
213 				    "termcap code '%.2s'.\n", progname,
214 				    term_name, tbuf);
215 				type = tunknown;
216 		}
217 
218 		if (verbose > 1)
219 			(void) fprintf(trace, "type of '%.2s' is %s.\n", tbuf,
220 			    (type == tbool) ? "boolean" :
221 			    (type == tnum) ? "numeric" :
222 			    (type = tstr) ? "string" :
223 			    (type = tcancel) ? "canceled" : "unknown");
224 
225 		/* look for the name in bools */
226 		if (capsearch(boolcodes, oboolcodes, tbuf)) {
227 			if (type != tbool && type != tcancel)
228 				(void) fprintf(stderr,
229 				    "%s: TERM=%s: wrong type given for the "
230 				    "boolean termcap code '%.2s'.\n", progname,
231 				    term_name, tbuf);
232 				continue;
233 		}
234 
235 		/* look for the name in nums */
236 		if (capsearch(numcodes, onumcodes, tbuf)) {
237 			if (type != tnum && type != tcancel)
238 				(void) fprintf(stderr,
239 				    "%s: TERM=%s: wrong type given for the "
240 				    "numeric termcap code '%.2s'.\n", progname,
241 				    term_name, tbuf);
242 				continue;
243 		}
244 
245 		/* look for the name in strs */
246 		if (capsearch(strcodes, ostrcodes, tbuf)) {
247 			if (type != tstr && type != tcancel)
248 				(void) fprintf(stderr,
249 				    "%s: TERM=%s: wrong type given for the "
250 				    "string termcap code '%.2s'.\n", progname,
251 				    term_name, tbuf);
252 				continue;
253 		}
254 
255 		(void) fprintf(stderr,
256 		    "%s: TERM=%s: the %s termcap code '%.2s' is not a valid "
257 		    "name.\n", progname, term_name,
258 		    (type == tbool) ? "boolean" :
259 		    (type == tnum) ? "numeric" :
260 		    (type = tstr) ? "string" :
261 		    (type = tcancel) ? "canceled" : "(unknown type)", tbuf);
262 	}
263 }
264 
265 /*
266  *  Fill up the termcap tables.
267  */
268 int
269 filltables(void)
270 {
271 	int i, tret;
272 
273 	/* Retrieve the termcap entry. */
274 	if ((tret = otgetent(bp, term_name)) != 1) {
275 		(void) fprintf(stderr,
276 		    "%s: TERM=%s: tgetent failed with return code %d (%s).\n",
277 		    progname, term_name, tret,
278 		    (tret == 0) ? "non-existent or invalid entry" :
279 		    (tret == -1) ? "cannot open $TERMCAP" : "unknown reason");
280 		return (0);
281 	}
282 
283 	if (verbose) {
284 		(void) fprintf(trace, "bp=");
285 		(void) cpr(trace, bp);
286 		(void) fprintf(trace, ".\n");
287 	}
288 
289 	if (uselevel == 0)
290 		checktermcap();
291 
292 	/* Retrieve the values that are in terminfo. */
293 
294 	/* booleans */
295 	for (i = 0; boolcodes[i]; i++) {
296 		boolval[uselevel][i] = otgetflag(boolcodes[i]);
297 		if (verbose > 1) {
298 			(void) fprintf(trace, "boolcodes=%s, ", boolcodes[i]);
299 			(void) fprintf(trace, "boolnames=%s, ", boolnames[i]);
300 			(void) fprintf(trace,
301 			    "flag=%d.\n", boolval[uselevel][i]);
302 		}
303 	}
304 
305 	/* numbers */
306 	for (i = 0; numcodes[i]; i++) {
307 		numval[uselevel][i] = otgetnum(numcodes[i]);
308 		if (verbose > 1) {
309 			(void) fprintf(trace, "numcodes=%s, ", numcodes[i]);
310 			(void) fprintf(trace, "numnames=%s, ", numnames[i]);
311 			(void) fprintf(trace, "num=%d.\n", numval[uselevel][i]);
312 		}
313 	}
314 
315 	if (uselevel == 0)
316 		nextstring = capbuffer;
317 
318 	/* strings */
319 	for (i = 0; strcodes[i]; i++) {
320 		strval[uselevel][i] = otgetstr(strcodes[i], &nextstring);
321 		if (verbose > 1) {
322 			(void) fprintf(trace, "strcodes=%s, ", strcodes [i]);
323 			(void) fprintf(trace, "strnames=%s, ", strnames [i]);
324 			if (strval[uselevel][i]) {
325 				(void) fprintf(trace, "str=");
326 				tpr(trace, strval[uselevel][i]);
327 				(void) fprintf(trace, ".\n");
328 			}
329 		else
330 			(void) fprintf(trace, "str=NULL.\n");
331 		}
332 		/* remove zero length strings */
333 		if (strval[uselevel][i] && (strval[uselevel][i][0] == '\0')) {
334 			(void) fprintf(stderr,
335 			    "%s: TERM=%s: cap %s (info %s) is NULL: REMOVED\n",
336 			    progname, term_name, strcodes[i], strnames[i]);
337 			strval[uselevel][i] = NULL;
338 		}
339 	}
340 
341 	/* Retrieve the values not found in terminfo anymore. */
342 
343 	/* booleans */
344 	for (i = 0; oboolcodes[i]; i++) {
345 		oboolval[uselevel][i] = otgetflag(oboolcodes[i]);
346 		if (verbose > 1) {
347 			(void) fprintf(trace, "oboolcodes=%s, ",
348 			    oboolcodes[i]);
349 			(void) fprintf(trace, "flag=%d.\n",
350 			    oboolval[uselevel][i]);
351 		}
352 	}
353 
354 	/* numbers */
355 	for (i = 0; onumcodes[i]; i++) {
356 		onumval[uselevel][i] = otgetnum(onumcodes[i]);
357 		if (verbose > 1) {
358 			(void) fprintf(trace, "onumcodes=%s, ", onumcodes[i]);
359 			(void) fprintf(trace, "num=%d.\n",
360 			    onumval[uselevel][i]);
361 		}
362 	}
363 
364 	/* strings */
365 	for (i = 0; ostrcodes[i]; i++) {
366 		ostrval[uselevel][i] = otgetstr(ostrcodes[i], &nextstring);
367 		if (verbose > 1) {
368 			(void) fprintf(trace, "ostrcodes=%s, ", ostrcodes[i]);
369 			if (ostrval[uselevel][i]) {
370 				(void) fprintf(trace, "ostr=");
371 				tpr(trace, ostrval[uselevel][i]);
372 				(void) fprintf(trace, ".\n");
373 			}
374 			else
375 				(void) fprintf(trace, "ostr=NULL.\n");
376 		}
377 		/* remove zero length strings */
378 		if (ostrval[uselevel][i] && (ostrval[uselevel][i][0] == '\0')) {
379 			(void) fprintf(stderr,
380 			    "%s: TERM=%s: cap %s (no terminfo name) is NULL: "
381 			    "REMOVED\n", progname, term_name, ostrcodes[i]);
382 			ostrval[uselevel][i] = NULL;
383 		}
384 	}
385 	return (1);
386 }
387 
388 /*
389  *  This routine copies the set of names from the termcap entry into
390  *  a separate buffer, getting rid of the old obsolete two character
391  *  names.
392  */
393 static void
394 getlongname(void)
395 {
396 	char *b = &bp[0],  *l = buflongname;
397 
398 	/* Skip the two character name */
399 	if (bp[2] == '|')
400 		b = &bp[3];
401 
402 	/* Copy the rest of the names */
403 	while (*b && *b != ':')
404 		*l++ = *b++;
405 	*l = '\0';
406 
407 	if (b != &bp[0]) {
408 		(void) fprintf(stderr, "%s: obsolete 2 character name "
409 		    "'%2.2s' removed.\n", progname, bp);
410 		(void) fprintf(stderr, "\tsynonyms are: '%s'\n", buflongname);
411 	}
412 }
413 
414 /*
415  *  Return the value of the termcap string 'capname' as stored in our list.
416  */
417 char *
418 getcapstr(char *capname)
419 {
420 	int i;
421 
422 	if (verbose > 1)
423 		(void) fprintf(trace, "looking for termcap value of %s.\n",
424 		    capname);
425 
426 	/* Check the old termcap list. */
427 	for (i = 0; ostrcodes[i]; i++)
428 		if (strcmp(ostrcodes[i], capname) == 0) {
429 			if (verbose > 1) {
430 				(void) fprintf(trace, "\tvalue is:");
431 				tpr(trace, ostrval[uselevel][i]);
432 				(void) fprintf(trace, ".\n");
433 			}
434 			return (ostrval[uselevel][i]);
435 		}
436 
437 	if (verbose > 1)
438 		(void) fprintf(trace, "termcap name '%s' not found in "
439 		    "ostrcodes.\n", capname);
440 
441 	/* Check the terminfo list. */
442 	for (i = 0; strcodes[i]; i++)
443 		if (strcmp(strcodes[i], capname) == 0) {
444 			if (verbose > 1) {
445 				(void) fprintf(trace, "\tvalue is:");
446 				tpr(trace, strval[uselevel][i]);
447 				(void) fprintf(trace, ".\n");
448 			}
449 			return (strval[uselevel][i]);
450 		}
451 
452 	(void) fprintf(stderr, "%s: TERM=%s: termcap name '%s' not found.\n",
453 	    progname, term_name, capname);
454 
455 	return ((char *)NULL);
456 }
457 
458 /*
459  *  Search for a name in the given table and
460  *  return the index.
461  *  Someday I'll redo this to use bsearch().
462  */
463 /* ARGSUSED */
464 int
465 search(char *names[], int max, char *infoname)
466 {
467 #ifndef BSEARCH
468 	int i;
469 	for (i = 0; names [i] != NULL; i++)
470 		if (strcmp(names [i], infoname) == 0)
471 			return (i);
472 		return (-1);
473 #else				/* this doesn't work for some reason */
474 	char **bret;
475 
476 	bret = (char **)bsearch(infoname, (char *)names, max,
477 	    sizeof (char *), strcmp);
478 	(void) fprintf(trace, "search looking for %s.\n", infoname);
479 	(void) fprintf(trace, "base=%#x, bret=%#x, nel=%d.\n", names,
480 	    bret, max);
481 	(void) fprintf(trace, "returning %d.\n", bret == NULL ? -1 :
482 	    bret - names);
483 	if (bret == NULL)
484 		return (-1);
485 	else
486 		return (bret - names);
487 #endif /* OLD */
488 }
489 
490 /*
491  *  return the value of the terminfo string 'infoname'
492  */
493 char *
494 getinfostr(char *infoname)
495 {
496 	int i;
497 
498 	if (verbose > 1)
499 		(void) fprintf(trace, "looking for terminfo value of %s.\n",
500 		    infoname);
501 
502 	i = search(strnames, strcount, infoname);
503 	if (i != -1) {
504 		if (verbose > 1) {
505 			(void) fprintf(trace, "\tvalue is:");
506 			tpr(trace, strval[uselevel][i]);
507 			(void) fprintf(trace, ".\n");
508 		}
509 		return (strval[uselevel][i]);
510 	}
511 
512 	if (verbose > 1)
513 		(void) fprintf(trace, "terminfo name '%s' not found.\n",
514 		    infoname);
515 
516 	return ((char *)NULL);
517 }
518 
519 /*
520  *  Replace the value stored for the terminfo boolean
521  *  capability 'infoname' with the newvalue.
522  */
523 void
524 putbool(char *infoname, int newvalue)
525 {
526 	int i;
527 
528 	if (verbose > 1)
529 		(void) fprintf(trace, "changing value for %s to %d.\n",
530 		    infoname, newvalue);
531 
532 	i = search(boolnames, boolcount, infoname);
533 	if (i != -1) {
534 		if (verbose > 1)
535 			(void) fprintf(trace, "value was: %d.\n",
536 			    boolval[uselevel][i]);
537 
538 		boolval[uselevel][i] = newvalue;
539 		return;
540 	}
541 
542 	(void) fprintf(stderr, "%s: TERM=%s: the boolean name '%s' was not "
543 	    "found!\n", progname, term_name, infoname);
544 }
545 
546 /*
547  *  Replace the value stored for the terminfo number
548  *  capability 'infoname' with the newvalue.
549  */
550 void
551 putnum(char *infoname, int newvalue)
552 {
553 	int i;
554 
555 	if (verbose > 1)
556 		(void) fprintf(trace, "changing value for %s to %d.\n",
557 		    infoname, newvalue);
558 
559 	i = search(numnames, numcount, infoname);
560 	if (i != -1) {
561 		if (verbose > 1)
562 			(void) fprintf(trace, "value was: %d.\n",
563 			    numval[uselevel][i]);
564 
565 		numval[uselevel][i] = newvalue;
566 		return;
567 	}
568 
569 	(void) fprintf(stderr, "%s: TERM=%s: the numeric name '%s' was not "
570 	    "found!\n",
571 	    progname, term_name, infoname);
572 }
573 
574 /*
575  *  replace the value stored for the terminfo string capability 'infoname'
576  *  with the newvalue.
577  */
578 void
579 putstr(char *infoname, char *newvalue)
580 {
581 	int i;
582 
583 	if (verbose > 1) {
584 		(void) fprintf(trace, "changing value for %s to ", infoname);
585 		tpr(trace, newvalue);
586 		(void) fprintf(trace, ".\n");
587 	}
588 
589 	i = search(strnames, strcount, infoname);
590 	if (i != -1) {
591 		if (verbose > 1) {
592 			(void) fprintf(trace, "value was:");
593 			tpr(trace, strval[uselevel][i]);
594 			(void) fprintf(trace, ".\n");
595 		}
596 		strval[uselevel][i] = nextstring;
597 		while (*newvalue)
598 			*nextstring++ = *newvalue++;
599 		*nextstring++ = '\0';
600 		return;
601 	}
602 
603 	(void) fprintf(stderr, "%s: TERM=%s: the string name '%s' was not "
604 	    "found!\n",
605 	    progname, term_name, infoname);
606 }
607 
608 /*
609  *  Add in extra delays if they are not recorded already.
610  *  This is done before the padding information has been modified by
611  *  changecalculations() below, so the padding information, if there
612  *  already, is still at the beginning of the string in termcap format.
613  */
614 void
615 addpadding(int cappadding, char *infostr)
616 {
617 	char *cap;
618 	char tempbuffer [100];
619 
620 	/* Is there padding to add? */
621 	if (cappadding > 0)
622 	/* Is there a string to add it to? */
623 		if (cap = getinfostr(infostr))
624 		/* Is there any padding info already? */
625 			if (ispadchar(*cap)) {
626 				/* EMPTY */;
627 		/* Assume that the padding info that is there is correct. */
628 			} else {
629 		/* Add the padding at the end of the present string. */
630 				(void) snprintf(tempbuffer, sizeof (tempbuffer),
631 				    "%s$<%d>", cap, cappadding);
632 				putstr(infostr, tempbuffer);
633 		} else {
634 			/* Create a new string that only has the padding. */
635 			(void) sprintf(tempbuffer, "$<%d>", cappadding);
636 			putstr(infostr, tempbuffer);
637 		}
638 }
639 
640 struct
641 	{
642 	char *capname;
643 	char *keyedinfoname;
644 	} ko_map[] = {
645 	"al",		"kil1",
646 	"bs",		"kbs",		/* special addition */
647 	"bt",		"kcbt",
648 	"cd",		"ked",
649 	"ce",		"kel",
650 	"cl",		"kclr",
651 	"ct",		"ktbc",
652 	"dc",		"kdch1",
653 	"dl",		"kdl1",
654 	"do",		"kcud1",
655 	"ei",		"krmir",
656 	"ho",		"khome",
657 	"ic",		"kich1",
658 	"im",		"kich1",	/* special addition */
659 	"le",		"kcub1",
660 	"ll",		"kll",
661 	"nd",		"kcuf1",
662 	"sf",		"kind",
663 	"sr",		"kri",
664 	"st",		"khts",
665 	"up",		"kcuu1",
666 /*	"",		"kctab",	*/
667 /*	"",		"knp",		*/
668 /*	"",		"kpp",		*/
669 	0,		0
670 	};
671 
672 /*
673  *  Work with the ko string. It is a comma separated list of keys for which
674  *  the keyboard has a key by the same name that emits the same sequence.
675  *  For example, ko = dc, im, ei means that there are keys called
676  *  delete-character, enter-insert-mode and exit-insert-mode on the keyboard,
677  *  and they emit the same sequences as specified in the dc, im and ei
678  *  capabilities.
679  */
680 static void
681 handleko(void)
682 {
683 	char capname[3];
684 	char *capstr;
685 	int i, j, found;
686 	char *infostr;
687 
688 	if (verbose > 1)
689 		(void) fprintf(trace, "working on termcap ko string.\n");
690 
691 	if (ostrval[uselevel][cap_ko] == NULL)
692 		return;
693 
694 	capname[2] = '\0';
695 	for (i = 0; ostrval[uselevel][cap_ko][i] != '\0'; ) {
696 		/* isolate the termcap name */
697 		capname[0] = ostrval[uselevel][cap_ko][i++];
698 		if (ostrval[uselevel][cap_ko][i] == '\0')
699 			break;
700 		capname[1] = ostrval[uselevel][cap_ko][i++];
701 		if (ostrval[uselevel][cap_ko][i] == ',')
702 			i++;
703 
704 		if (verbose > 1) {
705 			(void) fprintf(trace, "key termcap name is '");
706 			tpr(trace, capname);
707 			(void) fprintf(trace, "'.\n");
708 		}
709 
710 		/* match it up into our list */
711 		found = 0;
712 		for (j = 0; !found && ko_map[j].keyedinfoname != NULL; j++) {
713 			if (verbose > 1)
714 			(void) fprintf(trace, "looking at termcap name %s.\n",
715 			    ko_map[j].capname);
716 			if (capname[0] == ko_map[j].capname[0] &&
717 			    capname[1] == ko_map[j].capname[1]) {
718 				/* add the value to our database */
719 				if ((capstr = getcapstr(capname)) != NULL) {
720 					infostr = getinfostr
721 					    (ko_map[j].keyedinfoname);
722 				if (infostr == NULL) {
723 					/* skip any possible padding */
724 					/* information */
725 					while (ispadchar(*capstr))
726 						capstr++;
727 					putstr(ko_map[j].keyedinfoname, capstr);
728 				} else
729 					if (strcmp(capstr, infostr) != 0) {
730 						(void) fprintf(stderr,
731 						    "%s: TERM=%s: a function "
732 						    "key for '%s' was "
733 						    "specified with the "
734 						    "value ", progname,
735 						    term_name, capname);
736 						tpr(stderr, capstr);
737 						(void) fprintf(stderr,
738 						    ", but it already has the "
739 						    "value '");
740 						tpr(stderr, infostr);
741 						(void) fprintf(stderr, "'.\n");
742 					}
743 				}
744 				found = 1;
745 			}
746 		}
747 
748 		if (!found) {
749 			(void) fprintf(stderr, "%s: TERM=%s: the unknown "
750 			    "termcap name '%s' was\n", progname, term_name,
751 			    capname);
752 			(void) fprintf(stderr, "specified in the 'ko' "
753 			    "termcap capability.\n");
754 		}
755 	}
756 }
757 
758 #define	CONTROL(x)		((x) & 037)
759 struct
760 	{
761 	char vichar;
762 	char *keyedinfoname;
763 	} ma_map[] = {
764 		CONTROL('J'),	"kcud1",	/* down */
765 		CONTROL('N'),	"kcud1",
766 		'j',		"kcud1",
767 		CONTROL('P'),	"kcuu1",	/* up */
768 		'k',		"kcuu1",
769 		'h',		"kcub1",	/* left */
770 		CONTROL('H'),	"kcub1",
771 		' ',		"kcuf1",	/* right */
772 		'l',		"kcuf1",
773 		'H',		"khome",	/* home */
774 		CONTROL('L'),	"kclr",		/* clear */
775 		0,		0
776 	};
777 
778 /*
779  *  Work with the ma string. This is a list of pairs of characters.
780  *  The first character is the what a function key sends. The second
781  *  character is the equivalent vi function that should be done when
782  *  it receives that character. Note that only function keys that send
783  *  a single character could be defined by this list.
784  */
785 
786 void
787 prchar(FILE *stream, int c)
788 {
789 	char xbuf[2];
790 	xbuf[0] = c;
791 	xbuf[1] = '\0';
792 	(void) fprintf(stream, "%s", iexpand(xbuf));
793 }
794 
795 static void
796 handlema(void)
797 {
798 	char vichar;
799 	char cap[2];
800 	int i, j, found;
801 	char *infostr;
802 
803 	if (verbose > 1)
804 		(void) fprintf(trace, "working on termcap ma string.\n");
805 
806 	if (ostrval[uselevel][cap_ma] == NULL)
807 		return;
808 
809 	cap[1] = '\0';
810 	for (i = 0; ostrval[uselevel][cap_ma][i] != '\0'; ) {
811 		/* isolate the key's value */
812 		cap[0] = ostrval[uselevel][cap_ma][i++];
813 		if (verbose > 1) {
814 			(void) fprintf(trace, "key value is '");
815 			tpr(trace, cap);
816 			(void) fprintf(trace, "'.\n");
817 		}
818 
819 		if (ostrval[uselevel][cap_ma][i] == '\0')
820 			break;
821 
822 		/* isolate the vi key name */
823 		vichar = ostrval[uselevel][cap_ma][i++];
824 		if (verbose > 1) {
825 			(void) fprintf(trace, "the vi key is '");
826 			prchar(trace, vichar);
827 			(void) fprintf(trace, "'.\n");
828 		}
829 
830 		/* match up the vi name in our list */
831 		found = 0;
832 		for (j = 0; !found && ma_map[j].keyedinfoname != NULL; j++) {
833 			if (verbose > 1) {
834 				(void) fprintf(trace, "looking at vi "
835 				    "character '");
836 				prchar(trace, ma_map[j].vichar);
837 				(void) fprintf(trace, "'\n");
838 			}
839 			if (vichar == ma_map[j].vichar) {
840 				infostr = getinfostr(ma_map[j].keyedinfoname);
841 				if (infostr == NULL)
842 					putstr(ma_map[j].keyedinfoname, cap);
843 				else if (strcmp(cap, infostr) != 0) {
844 					(void) fprintf(stderr, "%s: TERM=%s: "
845 					    "the vi character '", progname,
846 					    term_name);
847 					prchar(stderr, vichar);
848 					(void) fprintf(stderr,
849 					    "' (info '%s') has the value '",
850 					    ma_map[j].keyedinfoname);
851 					tpr(stderr, infostr);
852 					(void) fprintf(stderr, "', but 'ma' "
853 					    "gives '");
854 					prchar(stderr, cap[0]);
855 					(void) fprintf(stderr, "'.\n");
856 				}
857 				found = 1;
858 			}
859 		}
860 
861 		if (!found) {
862 			(void) fprintf(stderr, "%s: the unknown vi key '",
863 			    progname);
864 			prchar(stderr, vichar);
865 			(void) fprintf(stderr, "' was\n");
866 			(void) fprintf(stderr, "specified in the 'ma' termcap "
867 			    "capability.\n");
868 		}
869 	}
870 }
871 
872 /*
873  *  Many capabilities were defaulted in termcap which must now be explicitly
874  *  given. We'll assume that the defaults are in effect for this terminal.
875  */
876 void
877 adddefaults(void)
878 {
879 	char *cap;
880 	int sg;
881 
882 	if (verbose > 1)
883 		(void) fprintf(trace, "assigning defaults.\n");
884 
885 	/* cr was assumed to be ^M, unless nc was given, */
886 	/* which meant it could not be done. */
887 	/* Also, xr meant that ^M acted strangely. */
888 	if ((getinfostr("cr") == NULL) && !oboolval[uselevel][cap_nc] &&
889 	    !oboolval[uselevel][cap_xr])
890 		if ((cap = getcapstr("cr")) == NULL)
891 			putstr("cr", "\r");
892 		else
893 			putstr("cr", cap);
894 
895 	/* cursor down was assumed to be ^J if not specified by nl */
896 	if (getinfostr("cud1") == NULL)
897 		if (ostrval[uselevel][cap_nl] != NULL)
898 			putstr("cud1", ostrval[uselevel][cap_nl]);
899 		else
900 			putstr("cud1", "\n");
901 
902 	/* ind was assumed to be ^J, unless ns was given, */
903 	/* which meant it could not be done. */
904 	if ((getinfostr("ind") == NULL) && !oboolval[uselevel][cap_ns])
905 		if (ostrval[uselevel][cap_nl] == NULL)
906 			putstr("ind", "\n");
907 		else
908 			putstr("ind", ostrval[uselevel][cap_nl]);
909 
910 	/* bel was assumed to be ^G */
911 	if (getinfostr("bel") == NULL)
912 		putstr("bel", "\07");
913 
914 	/* if bs, then could do backspacing, */
915 	/* with value of bc, default of ^H */
916 	if ((getinfostr("cub1") == NULL) && oboolval[uselevel][cap_bs])
917 		if (ostrval[uselevel][cap_bc] != NULL)
918 			putstr("cub1", ostrval[uselevel][cap_bc]);
919 		else
920 			putstr("cub1", "\b");
921 
922 	/* default xon to true */
923 	if (!otgetflag("xo"))
924 		putbool("xon", 1);
925 
926 	/* if pt, then hardware tabs are allowed, */
927 	/* with value of ta, default of ^I */
928 	if ((getinfostr("ht") == NULL) && oboolval[uselevel][cap_pt])
929 		if ((cap = getcapstr("ta")) == NULL)
930 			putstr("ht", "\t");
931 		else
932 			putstr("ht", cap);
933 
934 	/* The dX numbers are now stored as padding */
935 	/* in the appropriate terminfo string. */
936 	addpadding(onumval[uselevel][cap_dB], "cub1");
937 	addpadding(onumval[uselevel][cap_dC], "cr");
938 	addpadding(onumval[uselevel][cap_dF], "ff");
939 	addpadding(onumval[uselevel][cap_dN], "cud1");
940 	addpadding(onumval[uselevel][cap_dT], "ht");
941 
942 	/* The ug and sg caps were essentially identical, */
943 	/* so ug almost never got used. We set sg from ug */
944 	/* if it hasn't already been set. */
945 	if (onumval[uselevel][cap_ug] >= 0 && (sg = otgetnum("sg")) < 0)
946 		putnum("xmc", onumval[uselevel][cap_ug]);
947 	else if ((onumval[uselevel][cap_ug] >= 0) &&
948 	    (sg >= 0) && (onumval[uselevel][cap_ug] != sg))
949 		(void) fprintf(stderr,
950 		    "%s: TERM=%s: Warning: termcap sg and ug had different "
951 		    "values (%d<->%d).\n", progname, term_name, sg,
952 		    onumval[uselevel][cap_ug]);
953 
954 	/* The MT boolean was never really part of termcap, */
955 	/* but we can check for it anyways. */
956 	if (oboolval[uselevel][cap_MT] && !otgetflag("km"))
957 		putbool("km", 1);
958 
959 	/* the rs string was renamed r2 (info rs2) */
960 	if ((ostrval[uselevel][cap_rs] != NULL) &&
961 	    (ostrval[uselevel][cap_rs][0] != NULL))
962 		putstr("rs2", ostrval[uselevel][cap_rs]);
963 
964 	handleko();
965 	handlema();
966 }
967 
968 #define	caddch(x) *to++ = (x)
969 
970 /*
971  *  add the string to the string table
972  */
973 char *
974 caddstr(char *to, char *str)
975 {
976 	while (*str)
977 		*to++ = *str++;
978 	return (to);
979 }
980 
981 /* If there is no padding info or parmed strings, */
982 /* then we do not need to copy the string. */
983 int
984 needscopying(char *string)
985 {
986 	/* any string at all? */
987 	if (string == NULL)
988 		return (0);
989 
990 	/* any padding info? */
991 	if (ispadchar(*string))
992 		return (1);
993 
994 	/* any parmed info? */
995 	while (*string)
996 		if (*string++ == '%')
997 			return (1);
998 
999 	return (0);
1000 }
1001 
1002 /*
1003  *  Certain manipulations of the stack require strange manipulations of the
1004  *  values that are on the stack. To handle these, we save the values of the
1005  *  parameters in registers at the very beginning and make the changes in
1006  *  the registers. We don't want to do this in the general case because of the
1007  *  potential performance loss.
1008  */
1009 int
1010 fancycap(char *string)
1011 {
1012 	int parmset = 0;
1013 
1014 	while (*string)
1015 		if (*string++ == '%') {
1016 			switch (*string) {
1017 				/* These manipulate just the top value on */
1018 				/* the stack, so we only have to do */
1019 				/* something strange if a %r follows. */
1020 				case '>': case 'B': case 'D':
1021 					parmset = 1;
1022 					break;
1023 				/* If the parm has already been been */
1024 				/* pushed onto the stack by %>, then we */
1025 				/* can not reverse the parms and must get */
1026 				/* them from the registers. */
1027 				case 'r':
1028 					if (parmset)
1029 						return (1);
1030 					break;
1031 				/* This manipulates both parameters, so we */
1032 				/* cannot just do one and leave the value */
1033 				/* on the stack like we can with %>, */
1034 				/* %B or %D. */
1035 				case 'n':
1036 					return (1);
1037 			}
1038 			string++;
1039 		}
1040 		return (0);
1041 }
1042 
1043 /*
1044  *  Change old style of doing calculations to the new stack style.
1045  *  Note that this will not necessarily produce the most efficient string,
1046  *  but it will work.
1047  */
1048 void
1049 changecalculations()
1050 {
1051 	int i, currentparm;
1052 	char *from, *to = nextstring;
1053 	int ch;
1054 	int parmset, parmsaved;
1055 	char padding[100], *saveto;
1056 
1057 	for (i = 0; strnames[i]; i++)
1058 		if (needscopying(strval[uselevel][i])) {
1059 			if (verbose) {
1060 				(void) fprintf(trace, "%s needs copying, "
1061 				    "was:", strnames [i]);
1062 				tpr(trace, strval[uselevel][i]);
1063 				(void) fprintf(trace, ".\n");
1064 			}
1065 
1066 			from = strval[uselevel][i];
1067 			strval[uselevel][i] = to;
1068 			currentparm = 1;
1069 			parmset = 0;
1070 
1071 	    /* Handle padding information. Save it so that it can be */
1072 	    /* placed at the end of the string where it should */
1073 	    /* have been in the first place. */
1074 			if (ispadchar(*from)) {
1075 				saveto = to;
1076 				to = padding;
1077 				to = caddstr(to, "$<");
1078 				while (isdigit(*from) || *from == '.')
1079 					caddch(*from++);
1080 				if (*from == '*')
1081 					caddch(*from++);
1082 				caddch('>');
1083 				caddch('\0');
1084 				to = saveto;
1085 			} else
1086 				padding[0] = '\0';
1087 
1088 			if (fancycap(from)) {
1089 				to = caddstr(to, "%p1%Pa%p2%Pb");
1090 				parmsaved = 1;
1091 				(void) fprintf(stderr,
1092 				    "%s: TERM=%s: Warning: the string "
1093 				    "produced for '%s' may be inefficient.\n",
1094 				    progname, term_name, strnames[i]);
1095 				(void) fprintf(stderr, "It should be "
1096 				    "looked at by hand.\n");
1097 			} else
1098 				parmsaved = 0;
1099 
1100 			while ((ch = *from++) != '\0')
1101 				if (ch != '%')
1102 					caddch(ch);
1103 				else
1104 				switch (ch = *from++) {
1105 					case '.':	/* %.  -> %p1%c */
1106 					case 'd':	/* %d  -> %p1%d */
1107 					case '2':	/* %2  -> %p1%2.2d */
1108 					case '3':	/* %3  -> %p1%3.3d */
1109 					case '+':
1110 					/* %+x -> %p1%'x'%+%c */
1111 
1112 					case '>':
1113 					/* %>xy -> %p1%Pc%?%'x'%> */
1114 					/* %t%gc%'y'%+ */
1115 					/* if current value > x, then add y. */
1116 					/* No output. */
1117 
1118 					case 'B':
1119 					/* %B: BCD */
1120 					/* (16*(x/10))+(x%10) */
1121 					/* No output. */
1122 					/* (Adds Regent 100) */
1123 
1124 					case 'D':
1125 					/* %D: Reverse coding */
1126 					/* (x-2*(x%16)) */
1127 					/* No output. */
1128 					/* (Delta Data) */
1129 
1130 					if (!parmset)
1131 						if (parmsaved) {
1132 							to = caddstr(to, "%g");
1133 							if (currentparm == 1)
1134 								caddch('a');
1135 							else
1136 								caddch('b');
1137 						} else {
1138 							to = caddstr(to, "%p");
1139 							if (currentparm == 1)
1140 								caddch('1');
1141 							else
1142 								caddch('2');
1143 						}
1144 					currentparm = 3 - currentparm;
1145 					parmset = 0;
1146 					switch (ch) {
1147 						case '.':
1148 							to = caddstr(to, "%c");
1149 							break;
1150 						case 'd':
1151 							to = caddstr(to, "%d");
1152 							break;
1153 						case '2': case '3':
1154 #ifdef USG	/* Vr2==USG, Vr3==SYSV. Use %02d for Vr2, %2.2d for Vr3 */
1155 							caddch('%');
1156 							caddch('0');
1157 #else
1158 							caddch('%');
1159 							caddch(ch);
1160 							caddch('.');
1161 #endif /* USG vs. SYSV */
1162 							caddch(ch);
1163 							caddch('d');
1164 							break;
1165 						case '+':
1166 							to = caddstr(to, "%'");
1167 							caddch(*from++);
1168 							to = caddstr(to,
1169 							    "'%+%c");
1170 							break;
1171 						case '>':
1172 							to = caddstr(to,
1173 							    "%Pc%?%'");
1174 							caddch(*from++);
1175 							to = caddstr(to,
1176 							    "'%>%t%gc%'");
1177 							caddch(*from++);
1178 							to = caddstr(to,
1179 							    "'%+");
1180 							parmset = 1;
1181 							break;
1182 						case 'B':
1183 							to = caddstr(to,
1184 "%Pc%gc%{10}%/%{16}%*%gc%{10}%m%+");
1185 						parmset = 1;
1186 						break;
1187 
1188 						case 'D':
1189 							to = caddstr(to,
1190 "%Pc%gc%gc%{16}%m%{2}%*%-");
1191 							parmset = 1;
1192 							break;
1193 					}
1194 					break;
1195 
1196 					/* %r reverses current parameter */
1197 					case 'r':
1198 						currentparm = 3 - currentparm;
1199 						break;
1200 
1201 					/* %n: exclusive-or row AND column */
1202 					/* with 0140, 96 decimal, no output */
1203 					/* (Datamedia 2500, Exidy Sorceror) */
1204 					case 'n':
1205 						to = caddstr(to,
1206 						    "%ga%'`'%^%Pa");
1207 						to = caddstr(to,
1208 						    "%gb%'`'%^%Pb");
1209 						break;
1210 
1211 					/* assume %x means %x */
1212 					/* this includes %i and %% */
1213 					default:
1214 						caddch('%');
1215 						caddch(ch);
1216 				}
1217 		to = caddstr(to, padding);
1218 		caddch('\0');
1219 
1220 		if (verbose) {
1221 			(void) fprintf(trace, "and has become:");
1222 			tpr(trace, strval[uselevel][i]);
1223 			(void) fprintf(trace, ".\n");
1224 		}
1225 	}
1226 	nextstring = to;
1227 }
1228 
1229 static void
1230 print_no_use_entry(void)
1231 {
1232 	int i;
1233 
1234 	pr_heading("", buflongname);
1235 	pr_bheading();
1236 
1237 	for (i = 0; boolcodes[i]; i++)
1238 		if (boolval[0][i])
1239 			pr_boolean(boolnames[i], (char *)0, (char *)0, 1);
1240 
1241 	pr_bfooting();
1242 	pr_sheading();
1243 
1244 	for (i = 0; numcodes[i]; i++)
1245 		if (numval[0][i] > -1)
1246 			pr_number(numnames[i], (char *)0, (char *)0,
1247 			    numval[0][i]);
1248 
1249 	pr_nfooting();
1250 	pr_sheading();
1251 
1252 	for (i = 0; strcodes[i]; i++)
1253 		if (strval[0][i])
1254 			pr_string(strnames[i], (char *)0, (char *)0,
1255 			    strval[0][i]);
1256 
1257 	pr_sfooting();
1258 }
1259 
1260 static void
1261 print_use_entry(char *usename)
1262 {
1263 	int i;
1264 
1265 	pr_heading("", buflongname);
1266 	pr_bheading();
1267 
1268 	for (i = 0; boolcodes[i]; i++)
1269 		if (boolval[0][i] && !boolval[1][i])
1270 			pr_boolean(boolnames[i], (char *)0, (char *)0, 1);
1271 		else if (!boolval[0][i] && boolval[1][i])
1272 			pr_boolean(boolnames[i], (char *)0, (char *)0, -1);
1273 
1274 	pr_bfooting();
1275 	pr_nheading();
1276 
1277 	for (i = 0; numcodes[i]; i++)
1278 		if ((numval[0][i] > -1) && (numval[0][i] != numval[1][i]))
1279 			pr_number(numnames[i], (char *)0, (char *)0,
1280 			    numval[0][i]);
1281 		else if ((numval [0] [i] == -1) && (numval [1] [i] > -1))
1282 			pr_number(numnames[i], (char *)0, (char *)0, -1);
1283 
1284 	pr_nfooting();
1285 	pr_sheading();
1286 
1287 	for (i = 0; strcodes[i]; i++)
1288 		/* print out str[0] if: */
1289 		/* str[0] != NULL and str[1] == NULL, or str[0] != str[1] */
1290 		if (strval[0][i] && ((strval[1][i] == NULL) ||
1291 		    (strcmp(strval[0][i], strval[1][i]) != 0)))
1292 				pr_string(strnames[i], (char *)0, (char *)0,
1293 				    strval[0][i]);
1294 		/* print out @ if str[0] == NULL and str[1] != NULL */
1295 		else if (strval[0][i] == NULL && strval[1][i] != NULL)
1296 			pr_string(strnames[i], (char *)0, (char *)0,
1297 			    (char *)0);
1298 
1299 	pr_sfooting();
1300 
1301 	(void) printf("\tuse=%s,\n", usename);
1302 }
1303 
1304 static void
1305 captoinfo(void)
1306 {
1307 	char usename[512];
1308 	char *sterm_name;
1309 
1310 	if (term_name == NULL) {
1311 		(void) fprintf(stderr, "%s: Null term_name given.\n",
1312 		    progname);
1313 		return;
1314 	}
1315 
1316 	if (verbose)
1317 		(void) fprintf(trace, "changing cap to info, TERM=%s.\n",
1318 		    term_name);
1319 
1320 	uselevel = 0;
1321 	if (filltables() == 0)
1322 		return;
1323 	getlongname();
1324 	adddefaults();
1325 	changecalculations();
1326 	if (TLHtcfound != 0) {
1327 		uselevel = 1;
1328 		if (verbose)
1329 			(void) fprintf(trace, "use= found, %s uses %s.\n",
1330 			    term_name, TLHtcname);
1331 		(void) strcpy(usename, TLHtcname);
1332 		sterm_name = term_name;
1333 		term_name = usename;
1334 		if (filltables() == 0)
1335 			return;
1336 		adddefaults();
1337 		changecalculations();
1338 		term_name = sterm_name;
1339 		print_use_entry(usename);
1340 	} else
1341 		print_no_use_entry();
1342 }
1343 
1344 
1345 #include <signal.h>   /* use this file to determine if this is SVR4.0 system */
1346 
1347 static void
1348 use_etc_termcap(void)
1349 {
1350 	if (verbose)
1351 #ifdef  SIGSTOP
1352 		(void) fprintf(trace, "reading from /usr/share/lib/termcap\n");
1353 #else   /* SIGSTOP */
1354 		(void) fprintf(trace, "reading from /etc/termcap\n");
1355 #endif  /* SIGSTOP */
1356 		term_name = getenv("TERM");
1357 		captoinfo();
1358 }
1359 
1360 static void
1361 initdirname(void)
1362 {
1363 #if defined(SYSV) || defined(USG)  /* handle both Sys Vr2 and Vr3 curses */
1364 	(void) getcwd(dirname, BUFSIZ-2);
1365 #else
1366 	(void) getwd(dirname);
1367 #endif /* SYSV || USG */
1368 	if (verbose)
1369 		(void) fprintf(trace, "current directory name=%s.\n", dirname);
1370 		environ = newenviron;
1371 }
1372 
1373 static void
1374 setfilename(char *capfile)
1375 {
1376 	if (capfile [0] == '/')
1377 		(void) snprintf(TERMCAP, sizeof (TERMCAP),
1378 		    "TERMCAP=%s", capfile);
1379 	else
1380 		(void) snprintf(TERMCAP, sizeof (TERMCAP),
1381 		    "TERMCAP=%s/%s", dirname, capfile);
1382 	if (verbose)
1383 		(void) fprintf(trace, "setting the environment for %s.\n",
1384 		    TERMCAP);
1385 }
1386 
1387 static void
1388 setterm_name(void)
1389 {
1390 	if (verbose)
1391 		(void) fprintf(trace, "setting the environment "
1392 		    "for TERM=%s.\n", term_name);
1393 	(void) snprintf(TERM, sizeof (TERM), "TERM=%s", term_name);
1394 }
1395 
1396 /* Look at the current line to see if it is a list of names. */
1397 /* If it is, return the first name in the list, else NULL. */
1398 /* As a side-effect, comment lines and blank lines */
1399 /* are copied to standard output. */
1400 
1401 char *
1402 getterm_name(char *line)
1403 {
1404 	char *lineptr = line;
1405 
1406 	if (verbose)
1407 		(void) fprintf(trace, "extracting name from '%s'.\n", line);
1408 
1409 	/* Copy comment lines out. */
1410 	if (*line == '#') {
1411 		if (copycomments)
1412 			(void) printf("%s", line);
1413 	}
1414 	/* Blank lines get copied too. */
1415 	else if (isspace (*line)) {
1416 		if (copycomments) {
1417 			for (; *lineptr; lineptr++)
1418 				if (!isspace(*lineptr))
1419 					break;
1420 			if (*lineptr == '\0')
1421 			(void) printf("\n");
1422 		}
1423 	}
1424 	else
1425 		for (; *lineptr; lineptr++)
1426 			if (*lineptr == '|' || *lineptr == ':') {
1427 				*lineptr = '\0';
1428 				if (verbose)
1429 					(void) fprintf(trace,
1430 					    "returning %s.\n", line);
1431 				return (line);
1432 			}
1433 	if (verbose)
1434 		(void) fprintf(trace, "returning NULL.\n");
1435 	return (NULL);
1436 }
1437 
1438 static void
1439 use_file(char *filename)
1440 {
1441 	FILE *termfile;
1442 	char buffer[BUFSIZ];
1443 
1444 	if (verbose)
1445 		(void) fprintf(trace, "reading from %s.\n", filename);
1446 
1447 	if ((termfile = fopen(filename, "r")) == NULL) {
1448 		(void) fprintf(stderr, "%s: cannot open %s for reading.\n",
1449 		    progname, filename);
1450 		return;
1451 	}
1452 
1453 	copycomments++;
1454 	setfilename(filename);
1455 
1456 	while (fgets(buffer, BUFSIZ, termfile) != NULL) {
1457 		if ((term_name = getterm_name(buffer)) != NULL) {
1458 			setterm_name();
1459 			captoinfo();
1460 		}
1461 	}
1462 }
1463 
1464 /*
1465  *  Sort a name and code table pair according to the name table.
1466  *  Use a simple bubble sort for now. Too bad I can't call qsort(3).
1467  *  At least I only have to do it once for each table.
1468  */
1469 static void
1470 sorttable(char *nametable[], char *codetable[])
1471 {
1472 	int i, j;
1473 	char *c;
1474 
1475 	for (i = 0; nametable[i]; i++)
1476 		for (j = 0; j < i; j++)
1477 			if (strcmp(nametable[i], nametable[j]) < 0) {
1478 				c = nametable[i];
1479 				nametable[i] = nametable[j];
1480 				nametable[j] = c;
1481 				c = codetable[i];
1482 				codetable[i] = codetable[j];
1483 				codetable[j] = c;
1484 			}
1485 }
1486 
1487 /*
1488  *  Initialize and sort the name and code tables. Allocate space for the
1489  *  value tables.
1490  */
1491 static void
1492 inittables(void)
1493 {
1494 	unsigned int i;
1495 
1496 	for (i = 0; boolnames [i]; i++)
1497 		;
1498 	boolval[0] = (char *)malloc(i * sizeof (char));
1499 	boolval[1] = (char *)malloc(i * sizeof (char));
1500 	boolcount = i;
1501 	sorttable(boolnames, boolcodes);
1502 
1503 	for (i = 0; numcodes [i]; i++)
1504 		;
1505 	numval[0] = (short *)malloc(i * sizeof (short));
1506 	numval[1] = (short *)malloc(i * sizeof (short));
1507 	numcount = i;
1508 	sorttable(numnames, numcodes);
1509 
1510 	for (i = 0; strcodes [i]; i++)
1511 		;
1512 	strval[0] = (char **)malloc(i * sizeof (char *));
1513 	strval[1] = (char **)malloc(i * sizeof (char *));
1514 	strcount = i;
1515 	sorttable(strnames, strcodes);
1516 }
1517 
1518 int
1519 main(int argc, char **argv)
1520 {
1521 	int c;
1522 	char _capbuffer [8192];
1523 	char _bp [TBUFSIZE];
1524 	char _buflongname [128];
1525 
1526 	capbuffer = &_capbuffer[0];
1527 	bp = &_bp[0];
1528 	buflongname = &_buflongname[0];
1529 	progname = argv[0];
1530 
1531 	while ((c = getopt(argc, argv, "1vVw:")) != EOF)
1532 		switch (c) {
1533 			case '1':
1534 				pr_onecolumn(1);
1535 				break;
1536 			case 'w':
1537 				pr_width(atoi(optarg));
1538 				break;
1539 			case 'v':
1540 				verbose++;
1541 				break;
1542 			case 'V':
1543 				(void) printf("%s: version %s\n", progname,
1544 				    "@(#)curses:screen/captoinfo.c	1.12");
1545 				(void) fflush(stdout);
1546 				exit(0);
1547 				/* FALLTHROUGH (not really) */
1548 			case '?':
1549 				(void) fprintf(stderr,
1550 				    "usage: %s [-1Vv] [-w width] "
1551 				    "[filename ...]\n", progname);
1552 				(void) fprintf(stderr, "\t-1\tsingle column "
1553 				    "output\n");
1554 				(void) fprintf(stderr,
1555 				    "\t-v\tverbose debugging output\n");
1556 				(void) fprintf(stderr,
1557 				    "\t-V\tprint program version\n");
1558 				exit(-1);
1559 		}
1560 
1561 	/* initialize */
1562 	pr_init(pr_terminfo);
1563 	inittables();
1564 
1565 	if (optind >= argc)
1566 		use_etc_termcap();
1567 	else {
1568 		initdirname();
1569 	for (; optind < argc; optind++)
1570 		use_file(argv [optind]);
1571 	}
1572 
1573 	return (0);
1574 }
1575 
1576 /* fake out the modules in print.c so we don't have to load in */
1577 /* cexpand.c and infotocap.c */
1578 /* ARGSUSED */
1579 int
1580 cpr(FILE *stream, char *string)
1581 {
1582 	return (0);
1583 }
1584