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