197033Stjr/*- 297033Stjr * Copyright (c) 2002 Tim J. Robbins. 397033Stjr * All rights reserved. 497033Stjr * 597033Stjr * Redistribution and use in source and binary forms, with or without 697033Stjr * modification, are permitted provided that the following conditions 797033Stjr * are met: 897033Stjr * 1. Redistributions of source code must retain the above copyright 997033Stjr * notice, this list of conditions and the following disclaimer. 1097033Stjr * 2. Redistributions in binary form must reproduce the above copyright 1197033Stjr * notice, this list of conditions and the following disclaimer in the 1297033Stjr * documentation and/or other materials provided with the distribution. 1397033Stjr * 1497033Stjr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1597033Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1697033Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1797033Stjr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1897033Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1997033Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2097033Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2197033Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2297033Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2397033Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2497033Stjr * SUCH DAMAGE. 2597033Stjr */ 2697033Stjr 2797033Stjr/* 2897033Stjr * tabs -- set terminal tabs 2997033Stjr * 3097033Stjr * This utility displays a series of characters that clears the terminal 3197033Stjr * hardware tab settings, then initialises them to specified values, 3297033Stjr * and optionally sets a soft margin. 3397033Stjr */ 3497033Stjr 3597033Stjr#include <sys/cdefs.h> 3697033Stjr__FBSDID("$FreeBSD$"); 3797033Stjr 3897033Stjr#include <sys/types.h> 3997033Stjr#include <sys/tty.h> 4097033Stjr 4197033Stjr#include <ctype.h> 4297033Stjr#include <err.h> 4397033Stjr#include <errno.h> 4497033Stjr#include <locale.h> 4597033Stjr#include <stdio.h> 4697033Stjr#include <stdlib.h> 4797033Stjr#include <string.h> 4897033Stjr#include <term.h> 4997033Stjr#include <unistd.h> 5097033Stjr 5197033Stjr/* Maximum number of tab stops allowed in table. */ 5297033Stjr#define NSTOPS 20 5397033Stjr 5497033Stjr#define NELEMS(a) (sizeof(a) / sizeof(a[0])) 5597033Stjr 5697033Stjr/* Predefined formats, taken from IEEE Std 1003.1-2001. */ 5797033Stjrstatic const struct { 5897033Stjr const char *name; /* Format name used on cmd. line */ 5997033Stjr long stops[NSTOPS]; /* Column positions */ 6097033Stjr} formats[] = { 6197033Stjr { "a", { 1, 10, 16, 36, 72 } }, 6297033Stjr { "a2", { 1, 10, 16, 40, 72 } }, 6397033Stjr { "c", { 1, 8, 12, 16, 20, 55 } }, 6497033Stjr { "c2", { 1, 6, 10, 14, 49 } }, 6597033Stjr { "c3", { 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 6697033Stjr 62, 67 } }, 6797033Stjr { "f", { 1, 7, 11, 15, 19, 23 } }, 6897033Stjr { "p", { 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 6997033Stjr 61 } }, 7097033Stjr { "s", { 1, 10, 55 } }, 7197033Stjr { "u", { 1, 12, 20, 44 } } 7297033Stjr}; 7397033Stjr 7497033Stjrstatic void gettabs(char *, long *, long *); 7597033Stjrstatic int ttywidth(void); 7697033Stjrstatic void usage(void); 7797033Stjr 7897033Stjrint 7997036Stjrmain(int argc __unused, char *argv[]) 8097033Stjr{ 8197033Stjr long cols, i, inc, j, margin, nstops, stops[NSTOPS]; 8297033Stjr const char *cr, *ct, *st, *ML; 8397033Stjr char area[1024], *ap, *arg, *end; 8497033Stjr 8597033Stjr setlocale(LC_ALL, ""); 8697033Stjr 8797033Stjr inc = 8; 8897033Stjr margin = 0; 8997033Stjr nstops = -1; 9097033Stjr while ((arg = *++argv) != NULL && (*arg == '-' || *arg == '+')) { 9197033Stjr if (*arg == '+') { 9297033Stjr /* +m[n] or +[n] */ 9397033Stjr if (*++arg == 'm') 9497033Stjr arg++; 9597033Stjr if (*arg != '\0') { 9697033Stjr errno = 0; 9797033Stjr margin = strtol(arg, &end, 10); 9897033Stjr if (errno != 0 || *end != '\0' || margin < 0) 9997033Stjr errx(1, "%s: invalid margin width", 10097033Stjr arg); 10197033Stjr } else 10297033Stjr margin = 10; 10397033Stjr } else if (isdigit(arg[1])) { 10497033Stjr /* -n */ 10597033Stjr errno = 0; 10697033Stjr inc = strtol(arg + 1, &end, 10); 10797033Stjr if (errno != 0 || *end != '\0' || inc < 0) 10897033Stjr errx(1, "%s: invalid increment", arg + 1); 10997033Stjr } else if (arg[1] == 'T') { 11097033Stjr /* -Ttype or -T type */ 11197033Stjr if (arg[2] != '\0') 11297033Stjr setenv("TERM", arg + 2, 1); 11397033Stjr else { 11497033Stjr if ((arg = *++argv) == NULL) 11597033Stjr usage(); 11697033Stjr setenv("TERM", arg, 1); 11797033Stjr } 11898048Stjr } else if (arg[1] == '-') { 11998048Stjr arg = *++argv; 12098048Stjr break; 12197033Stjr } else { 12297033Stjr /* Predefined format */ 12397033Stjr for (i = 0; i < (int)NELEMS(formats); i++) 12497033Stjr if (strcmp(formats[i].name, arg + 1) == 0) 12597033Stjr break; 12697033Stjr if (i == NELEMS(formats)) 12797033Stjr usage(); 12897033Stjr for (j = nstops = 0; j < NSTOPS && 12997033Stjr formats[i].stops[j] != 0; j++) 13097033Stjr stops[nstops++] = formats[i].stops[j]; 13197033Stjr } 13297033Stjr } 13397033Stjr 13497033Stjr if (arg != NULL) { 13597033Stjr if (nstops != -1) 13697033Stjr usage(); 13797033Stjr gettabs(arg, stops, &nstops); 13897033Stjr } 13997033Stjr 14097033Stjr /* Initialise terminal, get the strings we need */ 14197033Stjr setupterm(NULL, 1, NULL); 14297033Stjr ap = area; 14397033Stjr if ((ct = tgetstr("ct", &ap)) == NULL) 14497033Stjr errx(1, "terminal cannot clear tabs"); 14597033Stjr if ((st = tgetstr("st", &ap)) == NULL) 14697033Stjr errx(1, "terminal cannot set tabs"); 14797033Stjr if ((cr = tgetstr("cr", &ap)) == NULL) 14897033Stjr cr = "\r"; 14997033Stjr ML = tgetstr("ML", &ap); 15097033Stjr cols = ttywidth(); 15197033Stjr 15297033Stjr /* Clear all tabs. */ 15397033Stjr putp(cr); 15497033Stjr putp(ct); 15597033Stjr 15697033Stjr /* 15797033Stjr * Set soft margin. 15897033Stjr * XXX Does this actually work? 15997033Stjr */ 16097033Stjr if (ML != NULL) { 16197033Stjr printf("%*s", (int)margin, ""); 16297033Stjr putp(ML); 16397033Stjr } else if (margin != 0) 16497033Stjr warnx("terminal cannot set left margin"); 16597033Stjr 16697033Stjr /* Optionally output new tab stops. */ 16797033Stjr if (nstops >= 0) { 16897033Stjr printf("%*s", (int)stops[0] - 1, ""); 16997033Stjr putp(st); 17097033Stjr for (i = 1; i < nstops; i++) { 17197033Stjr printf("%*s", (int)(stops[i] - stops[i - 1]), ""); 17297033Stjr putp(st); 17397033Stjr } 17497033Stjr } else if (inc > 0) { 17597033Stjr for (i = 0; i < cols / inc; i++) { 17697033Stjr putp(st); 17797033Stjr printf("%*s", (int)inc, ""); 17897033Stjr } 17997033Stjr putp(st); 18097033Stjr } 18197033Stjr putp(cr); 18297033Stjr 18397033Stjr exit(0); 18497033Stjr} 18597033Stjr 18697033Stjrstatic void 18797033Stjrusage(void) 18897033Stjr{ 18997033Stjr 19097033Stjr fprintf(stderr, 19197033Stjr"usage: tabs [-n|-a|-a2|-c|-c2|-c3|-f|-p|-s|-u] [+m[n]] [-T type]\n"); 19297033Stjr fprintf(stderr, 19397033Stjr" tabs [-T type] [+[n]] n1,[n2,...]\n"); 19497033Stjr exit(1); 19597033Stjr} 19697033Stjr 19797033Stjrstatic void 19897033Stjrgettabs(char *arg, long stops[], long *nstops) 19997033Stjr{ 20097033Stjr char *tok, *end; 20197033Stjr long last, stop; 20297033Stjr 20397033Stjr for (last = *nstops = 0, tok = strtok(arg, ","); tok != NULL; 20497033Stjr tok = strtok(NULL, ",")) { 20597033Stjr if (*nstops >= NSTOPS) 20697033Stjr errx(1, "too many tab stops (limit %d)", NSTOPS); 20797033Stjr errno = 0; 20897033Stjr stop = strtol(tok, &end, 10); 20997033Stjr if (errno != 0 || *end != '\0' || stop <= 0) 21097033Stjr errx(1, "%s: invalid tab stop", tok); 21197033Stjr if (*tok == '+') { 21297033Stjr if (tok == arg) 21397033Stjr errx(1, "%s: first tab may not be relative", 21497033Stjr tok); 21597033Stjr stop += last; 21697033Stjr } 21797033Stjr if (last > stop) 21897033Stjr errx(1, "cannot go backwards"); 21997033Stjr last = stops[(*nstops)++] = stop; 22097033Stjr } 22197033Stjr} 22297033Stjr 22397033Stjrstatic int 22497033Stjrttywidth(void) 22597033Stjr{ 22697033Stjr struct winsize ws; 22797033Stjr int width; 22897033Stjr 22997033Stjr if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) 23097033Stjr width = ws.ws_col; 23197033Stjr else if ((width = tgetnum("co")) == 0) { 23297033Stjr width = 80; 23397033Stjr warnx("cannot find terminal width; defaulted to %d", width); 23497033Stjr } 23597033Stjr 23697033Stjr return (width); 23797033Stjr} 238