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