1 /* $OpenBSD: column.c,v 1.19 2014/05/22 19:50:34 millert Exp $ */ 2 /* $NetBSD: column.c,v 1.4 1995/09/02 05:53:03 jtc Exp $ */ 3 4 /* 5 * Copyright (c) 1989, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/ioctl.h> 35 36 #include <ctype.h> 37 #include <err.h> 38 #include <limits.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 44 void c_columnate(void); 45 void *ereallocarray(void *, size_t, size_t); 46 void *ecalloc(size_t, size_t); 47 void input(FILE *); 48 void maketbl(void); 49 void print(void); 50 void r_columnate(void); 51 void usage(void); 52 53 int termwidth = 80; /* default terminal width */ 54 55 int entries; /* number of records */ 56 int eval; /* exit value */ 57 int maxlength; /* longest record */ 58 char **list; /* array of pointers to records */ 59 char *separator = "\t "; /* field separator for table option */ 60 61 int 62 main(int argc, char *argv[]) 63 { 64 struct winsize win; 65 FILE *fp; 66 int ch, tflag, xflag; 67 char *p; 68 const char *errstr; 69 70 if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) { 71 if ((p = getenv("COLUMNS")) && *p != '\0') { 72 termwidth = strtonum(p, 1, INT_MAX, &errstr); 73 if (errstr != NULL) 74 errx(1, "%s: %s", errstr, p); 75 } 76 } else 77 termwidth = win.ws_col; 78 79 tflag = xflag = 0; 80 while ((ch = getopt(argc, argv, "c:s:tx")) != -1) 81 switch(ch) { 82 case 'c': 83 termwidth = strtonum(optarg, 1, INT_MAX, &errstr); 84 if (errstr != NULL) 85 errx(1, "%s: %s", errstr, optarg); 86 break; 87 case 's': 88 separator = optarg; 89 break; 90 case 't': 91 tflag = 1; 92 break; 93 case 'x': 94 xflag = 1; 95 break; 96 case '?': 97 default: 98 usage(); 99 } 100 argc -= optind; 101 argv += optind; 102 103 if (!*argv) 104 input(stdin); 105 else for (; *argv; ++argv) 106 if ((fp = fopen(*argv, "r"))) { 107 input(fp); 108 (void)fclose(fp); 109 } else { 110 warn("%s", *argv); 111 eval = 1; 112 } 113 114 if (!entries) 115 exit(eval); 116 117 if (tflag) 118 maketbl(); 119 else if (maxlength >= termwidth) 120 print(); 121 else if (xflag) 122 c_columnate(); 123 else 124 r_columnate(); 125 exit(eval); 126 } 127 128 #define TAB 8 129 void 130 c_columnate(void) 131 { 132 int chcnt, col, cnt, endcol, numcols; 133 char **lp; 134 135 maxlength = (maxlength + TAB) & ~(TAB - 1); 136 numcols = termwidth / maxlength; 137 endcol = maxlength; 138 for (chcnt = col = 0, lp = list;; ++lp) { 139 chcnt += printf("%s", *lp); 140 if (!--entries) 141 break; 142 if (++col == numcols) { 143 chcnt = col = 0; 144 endcol = maxlength; 145 putchar('\n'); 146 } else { 147 while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) { 148 (void)putchar('\t'); 149 chcnt = cnt; 150 } 151 endcol += maxlength; 152 } 153 } 154 if (chcnt) 155 putchar('\n'); 156 } 157 158 void 159 r_columnate(void) 160 { 161 int base, chcnt, cnt, col, endcol, numcols, numrows, row; 162 163 maxlength = (maxlength + TAB) & ~(TAB - 1); 164 numcols = termwidth / maxlength; 165 if (numcols == 0) 166 numcols = 1; 167 numrows = entries / numcols; 168 if (entries % numcols) 169 ++numrows; 170 171 for (row = 0; row < numrows; ++row) { 172 endcol = maxlength; 173 for (base = row, chcnt = col = 0; col < numcols; ++col) { 174 chcnt += printf("%s", list[base]); 175 if ((base += numrows) >= entries) 176 break; 177 while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) { 178 (void)putchar('\t'); 179 chcnt = cnt; 180 } 181 endcol += maxlength; 182 } 183 putchar('\n'); 184 } 185 } 186 187 void 188 print(void) 189 { 190 int cnt; 191 char **lp; 192 193 for (cnt = entries, lp = list; cnt--; ++lp) 194 (void)printf("%s\n", *lp); 195 } 196 197 typedef struct _tbl { 198 char **list; 199 int cols, *len; 200 } TBL; 201 #define DEFCOLS 25 202 203 void 204 maketbl(void) 205 { 206 TBL *t; 207 int coloff, cnt; 208 char *p, **lp; 209 int *lens, maxcols = DEFCOLS; 210 TBL *tbl; 211 char **cols; 212 213 t = tbl = ecalloc(entries, sizeof(TBL)); 214 cols = ereallocarray(NULL, maxcols, sizeof(char *)); 215 lens = ecalloc(maxcols, sizeof(int)); 216 for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) { 217 for (coloff = 0, p = *lp; (cols[coloff] = strtok(p, separator)); 218 p = NULL) 219 if (++coloff == maxcols) { 220 maxcols += DEFCOLS; 221 cols = ereallocarray(cols, maxcols, 222 sizeof(char *)); 223 lens = ereallocarray(lens, maxcols, 224 sizeof(int)); 225 memset(lens + coloff, 0, DEFCOLS * sizeof(int)); 226 } 227 if (coloff == 0) 228 continue; 229 t->list = ecalloc(coloff, sizeof(char *)); 230 t->len = ecalloc(coloff, sizeof(int)); 231 for (t->cols = coloff; --coloff >= 0;) { 232 t->list[coloff] = cols[coloff]; 233 t->len[coloff] = strlen(cols[coloff]); 234 if (t->len[coloff] > lens[coloff]) 235 lens[coloff] = t->len[coloff]; 236 } 237 } 238 for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) { 239 if (t->cols > 0) { 240 for (coloff = 0; coloff < t->cols - 1; ++coloff) 241 (void)printf("%s%*s", t->list[coloff], 242 lens[coloff] - t->len[coloff] + 2, " "); 243 (void)printf("%s\n", t->list[coloff]); 244 } 245 } 246 free(tbl); 247 free(lens); 248 free(cols); 249 } 250 251 #define DEFNUM 1000 252 #define MAXLINELEN (LINE_MAX + 1) 253 254 void 255 input(FILE *fp) 256 { 257 static size_t maxentry = DEFNUM; 258 int len; 259 char *p, buf[MAXLINELEN]; 260 261 if (!list) 262 list = ecalloc(maxentry, sizeof(char *)); 263 while (fgets(buf, MAXLINELEN, fp)) { 264 for (p = buf; isspace((unsigned char)*p); ++p); 265 if (!*p) 266 continue; 267 if (!(p = strchr(p, '\n'))) { 268 warnx("line too long"); 269 eval = 1; 270 continue; 271 } 272 *p = '\0'; 273 len = p - buf; 274 if (maxlength < len) 275 maxlength = len; 276 if (entries == maxentry) { 277 maxentry += DEFNUM; 278 list = ereallocarray(list, maxentry, sizeof(char *)); 279 memset(list + entries, 0, DEFNUM * sizeof(char *)); 280 } 281 if (!(list[entries++] = strdup(buf))) 282 err(1, NULL); 283 } 284 } 285 286 void * 287 ereallocarray(void *oldp, size_t sz1, size_t sz2) 288 { 289 void *p; 290 291 if (!(p = reallocarray(oldp, sz1, sz2))) 292 err(1, NULL); 293 return (p); 294 } 295 296 void * 297 ecalloc(size_t sz1, size_t sz2) 298 { 299 void *p; 300 301 if (!(p = calloc(sz1, sz2))) 302 err(1, NULL); 303 return (p); 304 } 305 306 void 307 usage(void) 308 { 309 310 (void)fprintf(stderr, 311 "usage: column [-tx] [-c columns] [-s sep] [file ...]\n"); 312 exit(1); 313 } 314