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 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 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 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 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 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 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 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