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