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