1/* $NetBSD: tabs.c,v 1.6 2021/08/27 18:28:41 rillig Exp $ */ 2 3/*- 4 * Copyright (c) 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Roy Marples. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34__COPYRIGHT("@(#) Copyright (c) 2008 \ 35The NetBSD Foundation, inc. All rights reserved."); 36__RCSID("$NetBSD: tabs.c,v 1.6 2021/08/27 18:28:41 rillig Exp $"); 37#endif /* not lint */ 38 39#include <sys/ioctl.h> 40#include <sys/types.h> 41 42#include <ctype.h> 43#include <err.h> 44#include <errno.h> 45#include <limits.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49#include <term.h> 50#include <unistd.h> 51 52#define NSTOPS 20 53 54struct tabspec { 55 const char *opt; 56 const char *spec; 57}; 58static const struct tabspec tabspecs[] = { 59 {"a", "1,10,16,36,72"}, 60 {"a2", "1,10,16,40,72"}, 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 {"f", "1,7,11,15,19,23"}, 65 {"p", "1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61"}, 66 {"s", "1,10,55"}, 67 {"u", "1,12,20,44"} 68}; 69static const size_t ntabspecs = sizeof(tabspecs) / sizeof(tabspecs[0]); 70 71__dead static void 72usage(void) 73{ 74 fprintf(stderr, 75 "usage: tabs [-n|-a|-a2|-c|-c2|-c3|-f|-p|-s|-u] [+m[n]]" 76 " [-T type]\n" 77 " tabs [-T type] [+[n]] n1[,n2,...]\n"); 78 exit(EXIT_FAILURE); 79 /* NOTREACHED */ 80} 81 82int 83main(int argc, char **argv) 84{ 85 char *term, *arg, *token, *end, *tabs = NULL, *p; 86 const char *cr, *spec = NULL; 87 int i, n, inc = 8, stops[NSTOPS], nstops, last, cols, margin = 0; 88 long num = 0; 89 size_t j; 90 struct winsize ws; 91 92 term = getenv("TERM"); 93 for (i = 1; i < argc; i++) { 94 if (argv[i][0] == '+') { 95 arg = argv[i] + 1; 96 if (arg[0] == 'm') 97 arg++; 98 if (arg[0] == '\0') 99 margin = 10; 100 else { 101 errno = 0; 102 num = strtol(arg, &end, 10); 103 if (errno != 0 || *end != '\0' || 104 num < 0 || num > INT_MAX) 105 errx(EXIT_FAILURE, 106 "%s: invalid margin", arg); 107 margin = (int)num; 108 } 109 continue; 110 } 111 if (argv[i][0] != '-') { 112 tabs = argv[i]; 113 break; 114 } 115 arg = argv[i] + 1; 116 if (arg[0] == '\0') 117 usage(); 118 if (arg[0] == '-' && arg[1] == '\0') { 119 if (argv[i + 1] != NULL) 120 tabs = argv[i + 1]; 121 break; 122 } 123 if (arg[0] == 'T' && arg[1] == '\0') { 124 term = argv[++i]; 125 if (term == NULL) 126 usage(); 127 continue; 128 } 129 if (isdigit((unsigned char)arg[0])) { 130 if (arg[1] != '\0') 131 errx(EXIT_FAILURE, 132 "%s: invalid increment", arg); 133 inc = arg[0] - '0'; 134 continue; 135 } 136 for (j = 0; j < ntabspecs; j++) { 137 if (arg[0] == tabspecs[j].opt[0] && 138 arg[1] == tabspecs[j].opt[1]) { 139 spec = tabspecs[j].spec; 140 break; 141 } 142 } 143 if (j == ntabspecs) 144 usage(); 145 } 146 if (tabs == NULL && spec != NULL) 147 tabs = strdup(spec); 148 149 if (tabs != NULL) 150 last = nstops = 0; 151 else 152 nstops = -1; 153 p = tabs; 154 while ((token = strsep(&p, ", ")) != NULL) { 155 if (*token == '\0') 156 continue; 157 if (nstops >= NSTOPS) 158 errx(EXIT_FAILURE, 159 "too many tab stops (max %d)", NSTOPS); 160 errno = 0; 161 num = strtol(token, &end, 10); 162 if (errno != 0 || *end != '\0' || num <= 0 || num > INT_MAX) 163 errx(EXIT_FAILURE, "%s: invalid tab stop", token); 164 n = (int)num; 165 if (*token == '+') { 166 if (nstops == 0) 167 errx(EXIT_FAILURE, 168 "first tab stop may not be relative"); 169 n += last; 170 } 171 if (last > n) 172 errx(EXIT_FAILURE, "tab stops may not go backwards"); 173 last = stops[nstops++] = n; 174 } 175 176 if (term == NULL) 177 errx(EXIT_FAILURE, "no value for $TERM and -T not given"); 178 if (setupterm(term, STDOUT_FILENO, NULL) != 0) 179 err(EXIT_FAILURE, "setupterm:"); 180 cr = carriage_return; 181 if (cr == NULL) 182 cr = "\r"; 183 if (clear_all_tabs == NULL) 184 errx(EXIT_FAILURE, "terminal cannot clear tabs"); 185 if (set_tab == NULL) 186 errx(EXIT_FAILURE, "terminal cannot set tabs"); 187 188 /* Clear existing tabs */ 189 putp(cr); 190 putp(clear_all_tabs); 191 putp(cr); 192 193 if (set_lr_margin != NULL) { 194 printf("%*s", margin, ""); 195 putp(set_lr_margin); 196 } else if (margin != 0) 197 warnx("terminal cannot set left margin"); 198 199 if (nstops >= 0) { 200 printf("%*s", stops[0] - 1, ""); 201 putp(set_tab); 202 for (i = 1; i < nstops; i++) { 203 printf("%*s", stops[i] - stops[i - 1], ""); 204 putp(set_tab); 205 } 206 } else if (inc > 0) { 207 cols = 0; 208 term = getenv("COLUMNS"); 209 if (term != NULL) { 210 errno = 0; 211 num = strtol(term, &end, 10); 212 if (errno == 0 && *end == '\0' && 213 0 <= cols && cols <= INT_MAX) 214 cols = (int)num; 215 } 216 if (cols == 0) { 217 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) 218 cols = ws.ws_col; 219 else { 220 cols = tigetnum("cols"); 221 if (cols == 0) { 222 cols = 80; 223 warnx("terminal does not specify number" 224 "columns; defaulting to %d", 225 cols); 226 } 227 } 228 } 229 for (i = 0; i < cols / inc; i++) { 230 printf("%*s", inc, ""); 231 putp(set_tab); 232 } 233 } 234 putp(cr); 235 236 exit(EXIT_SUCCESS); 237 /* NOTREACHED */ 238} 239