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