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