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