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