xref: /illumos-gate/usr/src/lib/libxcurses/src/tabs/tabs.c (revision 1da57d55)
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 (c) 1996, by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 /*
28  *	tabs.c
29  *
30  *	Copyright 1990, 1992 by Mortice Kern Systems Inc.  All rights reserved.
31  *
32  *	PORTABILITY:
33  *	POSIX.2a UPE full support
34  *	SVID 3 full support except +m option is stubbed
35  *	XPG full support except +m option is stubbed
36  *
37  *	SYNOPSIS:
38  *	tabs [-T term] [+m[n]] [-n]
39  *	tabs [-T term] [+m[n]] -t tablist
40  *	tabs [-T term] [+m[n]] n1[,n2,...]
41  *	tabs [-T term] [+m[n]] tabspec
42  *
43  *	DESCRIPTION:
44  *	The tabs utility shall display a series of characters that first clears
45  *	the hardware terminal tab settings and then initializes the tab stops
46  *	at the specified positions.
47  *
48  *	The phrase "tab-stop position N" shall be taken to mean that, from the
49  *	start of a line of output, tabbing to position N shall cause the next
50  *	character output to be in the (N+1)th column position on that line.
51  *	The maximum number of tab stops allowed is terminal dependent.
52  *
53  *	'tabspec' is one of the following:
54  *
55  *	Assembler:
56  *	-a	1,10,16,36,72
57  *	-a2	1,10,16,40,72
58  *	-u	1,12,20,44
59  *
60  *	COBOL:
61  *	-c	1,8,12,16,20,55
62  *	-c2	1,6,10,14,49
63  *	-c3	1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67
64  *
65  *	FORTRAN:
66  *	-f	1,7,11,15,19,23
67  *
68  *	PL/I:
69  *	-p	1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61
70  *
71  *	SNOBOL:
72  *	-s	1,10,55
73  *
74  *
75  *	EXIT STATUS:
76  *	0	successful completion.
77  *
78  *	>0	An error occured.
79  */
80 #ifdef M_RCSID
81 #ifndef lint
82 static char rcsID[] = "$Id: tabs.c 1.20 1995/09/21 21:00:28 ant Exp $";
83 #endif
84 #endif
85 
86 #include <mks.h>
87 #include <curses.h>
88 #define SINGLE	1		/* only one terminal to be concerned about */
89 #include <term.h>
90 #include <ctype.h>
91 #include <stdarg.h>
92 #include <stdio.h>
93 #include <stdlib.h>
94 #include <string.h>
95 
96 extern char *_cmdname;
97 
98 
99 /* Exit Status */
100 #define SUCCESS		0
101 #define NOT_DEFINED	1
102 #define USAGE		2
103 #define BAD_TERMINAL	3
104 #define NOT_VALID	4
105 #define ERROR		5
106 
107 #define NO_FORM	0
108 #define N_FORM	1	/* tabs [-T term] [+m[n]] [-<n>] */
109 #define T_FORM	2	/* tabs [-T term] [+m[n]] -t tablist */
110 #define P_FORM	3	/* tabs [-T term] [+m[n]] n1[,n2,...]  and
111 			 * tabs [-T term] [+m[n]] tabspec
112 			 */
113 
114 
115 static int form = NO_FORM;
116 static int n_width = 8;
117 static int margin = 0;
118 static wchar_t *tablist;
119 
120 typedef struct {
121 	char *option;
122 	char *list;
123 } predefined;
124 
125 static predefined tabspec[] = {
126 	{ "a", "1,10,16,36,72" },
127 	{ "a2", "1,10,16,40,72" },
128 	{ "u", "1,12,20,44" },
129 	{ "c", "1,8,12,16,20,55" },
130 	{ "c2", "1,6,10,14,49" },
131 	{ "c3", "1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67" },
132 	{ "f", "1,7,11,15,19,23" },
133 	{ "p", "1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61" },
134 	{ "s", "1,10,55" },
135 	{ NULL, NULL }
136 };
137 
138 static char *term_name;
139 static char dumb_term[] = "dumb";
140 static char missing_tablist[] = m_textstr(1828, "Missing tab list after -t.\n", "E");
141 static char missing_terminal[] = m_textstr(1829, "Missing terminal type after -T.\n", "E");
142 static char unknown_option[] = m_textstr(433, "Unknown option \"-%s\".\n", "E option");
143 static char bad_list[] = m_textstr(1830, "Illegal tabs in \"%s\".\n", "E tablist");
144 static char no_margins[] = m_textstr(1831, "Cannot set margins on terminal \"%s\".\n", "E term");
145 static char no_tabs[] = m_textstr(1832, "Cannot set tabs on terminal \"%s\".\n", "E term");
146 static char not_ascending[] = m_textstr(1833, "\"%s\" are not in ascending order.\n", "E tablist");
147 static char usage_msg[] = m_textstr(1834, "\
148 Usage: tabs [-T term] [+m[n]] [-n]\n\
149        tabs [-T term] [+m[n]] -t <tablist>\n\
150        tabs [-T term] [+m[n]] n1[,n2,...]\n\
151        tabs [-T term] [+m[n]] -a|a2|u|c|c2|c3|f|p|s\n", "U");
152 
153 
154 STATREF int do_tabs ANSI((void));
155 STATREF void err_msg ANSI((char *fmt, ...));	/* GENTEXT: err_msg */
156 STATREF void mvcol ANSI((int oc, int nc));
157 STATREF void set_every ANSI((int n));
158 STATREF void set_tab_at ANSI((int x));
159 STATREF int usage ANSI((void));
160 
161 
162 /*f
163  * mainline for tabs
164  */
165 int
main(argc,argv)166 main(argc, argv)
167 int argc;
168 char **argv;
169 {
170 	char *ap;
171 	int i;
172 	int err_code;
173 	predefined *p;
174 	setlocale(LC_ALL, "");
175 	_cmdname = m_cmdname(*argv);
176 	if ((term_name = getenv("TERM")) == NULL)
177 		term_name = dumb_term;
178 	while (0 < --argc && (**++argv == '-' || **argv == '+')) {
179 		ap = &argv[0][1];
180 
181 		/* Check for standard io '-' */
182 		if (*ap == '\0')
183 			break;
184 		/* End option list '--'? */
185 		if (*ap == '-' && ap[1] == '\0') {
186 			++argv;
187 			--argc;
188 			break;
189 		}
190 		if (**argv == '-') {
191 			/* '-xyz...' or '-xyzF<parm>' or '-xyzF <parm>' */
192 			for (;*ap != '\0'; ++ap) {
193 				switch (*ap) {
194 				case 't':
195 					if (form != NO_FORM)
196 						return (usage());
197 					form = T_FORM;
198 					if (*++ap != '\0') {
199 						tablist = m_mbstowcsdup(ap);
200 						break;
201 					} else if (1 < argc) {
202 						tablist = m_mbstowcsdup(*++argv);
203 						--argc;
204 						break;
205 					}
206 					err_msg(missing_tablist);
207 					return (usage());
208 					break;
209 				case 'T':
210 					/* '-T<term>' or '-T <term>' */
211 					if (*++ap != '\0') {
212 						term_name = ap;
213 						break;
214 					} else if (1 < argc) {
215 						term_name = *++argv;
216 						--argc;
217 						break;
218 					}
219 					err_msg(missing_terminal);
220 					return (usage());
221 				default:
222 					if (isdigit(*ap)) {
223 						if (form != NO_FORM)
224 							return (usage());
225 						form = N_FORM;
226 						n_width =  *ap - '0';
227 						continue;
228 					}
229 					for (p = tabspec;
230 					     p->option != NULL
231 					     && strcmp(p->option, ap) != 0;
232 					     ++p)
233 						;
234 					if (p->option != NULL) {
235 						form = P_FORM;
236 						tablist = m_mbstowcsdup(p->list);
237 						break;
238 					}
239 					err_msg(unknown_option, ap);
240 					return (usage());
241 				}
242 				break;
243 			}
244 		} else {
245 			/* All '+' options. */
246 			if (*ap == 'm') {
247 				margin = (int) strtol(++ap, NULL, 0);
248 				if (margin == 0)
249 					margin = 10;
250 			} else {
251 				err_msg(unknown_option, ap);
252 				return (usage());
253 			}
254 		}
255 	}
256 	if (form == NO_FORM) {
257 		switch (argc) {
258 		case 0:
259 			form = N_FORM;
260 			break;
261 		case 1:
262 			form = P_FORM;
263 			tablist = m_mbstowcsdup(*argv);
264 			break;
265 		default:
266 			return (usage());
267 		}
268 	} else if (0 < argc) {
269 		return (usage());
270 	}
271 	(void) setupterm(term_name, fileno(stdout), &err_code);
272 	switch (err_code) {
273 	case 1:
274 		break;
275 	case 0:
276 		err_msg(m_textstr(202, "Unknown terminal \"%s\".\n", "E term"), term_name);
277 		return (BAD_TERMINAL);
278 	case -1:
279 		err_msg(m_textstr(203, "No terminfo database.\n", "E"));
280 		return (BAD_TERMINAL);
281 	}
282 	if (save_cursor != NULL)
283 		putp(save_cursor);
284 	err_code = do_tabs();
285 	if (restore_cursor != NULL)
286 		putp(restore_cursor);
287 	else
288 		mvcol(0, 0);
289 	return (err_code);
290 }
291 
292 /*f
293  * actually do tabs
294  */
295 STATIC int
do_tabs()296 do_tabs()
297 {
298 	int oc = 0;
299 	int nc = 0;
300 	wchar_t *p = tablist;
301 	if (clear_all_tabs == NULL || set_tab == NULL) {
302 		err_msg(no_tabs, term_name);
303 		return (NOT_DEFINED);
304 	}
305 	mvcol(0, 0);
306 	putp(clear_all_tabs);
307 #if 0	/* margins are not yet supported in terminfo */
308 	if (clear_margins == NULL || set_left_margin == NULL) {
309 		err_msg(no_margins, term_name);
310 		return (NOT_DEFINED);
311 	} else {
312 		putp(clear_margins);
313 		mvcol(0, margin);
314 		putp(set_left_margin);
315 	}
316 #endif
317 	switch (form) {
318 	case N_FORM:
319 		if (0 < n_width)
320 			set_every(n_width);
321 		break;
322 	case T_FORM:
323 		nc = (int) wcstol(p, &p, 0);
324 		if (p == tablist || nc < 0) {
325 			err_msg(bad_list, tablist);
326 			return (NOT_VALID);
327 		}
328 		if (*p == '\0') {
329 			set_every(nc);
330 			break;
331 		}
332 		do {
333 			if (nc <= oc) {
334 				err_msg(not_ascending, tablist);
335 				return (NOT_VALID);
336 			}
337 			if (*p != '\0' && *p != ',' && !iswblank(*p)) {
338 				err_msg(bad_list, tablist);
339 				return (NOT_VALID);
340 			}
341 			++p;
342 			oc = nc;
343 			set_tab_at(nc);
344 			nc = (int) wcstol(p, &p, 0);
345 		} while (nc != 0);
346 		break;
347 	case P_FORM:
348 		if (*p == '+' || *p == '-') {
349 			err_msg(bad_list, tablist);
350 			return (NOT_VALID);
351 		}
352 		for (;;) {
353 			nc += (int) wcstol(p, &p, 0);
354 			if (nc == 0)
355 				break;
356 			if (nc <= oc) {
357 				err_msg(not_ascending, tablist);
358 				return (NOT_VALID);
359 			}
360 			if (*p != '\0' && *p != ',' && !iswblank(*p)) {
361 				err_msg(bad_list, tablist);
362 				return (NOT_VALID);
363 			}
364 			++p;
365 			oc = nc;
366 			set_tab_at(nc);
367 			if (*p == '+')
368 				++p;
369 			else
370 				nc = 0;
371 		}
372 		break;
373 	}
374 	return (SUCCESS);
375 }
376 
377 /*f
378  *	Set a tab every n columns starting with column 0.
379  */
380 STATIC void
set_every(n)381 set_every(n)
382 int n;
383 {
384 	int x;
385 	for (x = 0; x < columns; x += n)
386 		set_tab_at(x);
387 }
388 
389 /*f
390  *	Set tab at column x. Assume that cursor has been positioned at the
391  *	start of the line before settiing the first tab.
392  */
393 STATIC void
set_tab_at(x)394 set_tab_at(x)
395 int x;
396 {
397 	static int col = 0;
398 	mvcol(col, x);
399 	putp(set_tab);
400 	col = x;
401 }
402 
403 /*f
404  *	Move the cursor on the current row from column 'col' to column 'x'.
405  *	We can't use mvcur() because we have no idea what row we're on.
406  */
407 STATIC void
mvcol(oc,nc)408 mvcol(oc, nc)
409 int oc, nc;
410 {
411 	int diff = nc - oc;
412 	if (nc == 0) {
413 		putchar('\r');
414 	} else if (column_address != NULL) {
415 		putp(tparm(column_address, nc, 0, 0, 0, 0, 0, 0, 0, 0));
416 	} else if (parm_right_cursor != NULL) {
417 		putp(tparm(parm_right_cursor, diff, 0, 0, 0, 0, 0, 0, 0, 0));
418 	} else if (cursor_right != NULL) {
419 		while (diff--)
420 			putp(cursor_right);
421 	} else {
422 		while (diff--)
423 			putchar(' ');
424 	}
425 }
426 
427 /*f
428  * usage message for tabs
429  */
430 STATIC int
usage()431 usage()
432 {
433 	(void) fprintf(stderr, m_strmsg(usage_msg));
434 	return (USAGE);
435 }
436 
437 /*f
438  * display error message
439  */
440 STATIC void
VARARG1(char *,fmt)441 err_msg VARARG1(char*, fmt)
442 {
443 	va_list ap;
444 	(void) fprintf(stderr, "%s: ", _cmdname);
445 	va_start(ap, fmt);
446 	(void) vfprintf(stderr, m_strmsg(fmt), ap);
447 	va_end(ap);
448 }
449