1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 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/types.h> 33 #include <sys/ioctl.h> 34 #include <sys/param.h> 35 36 #include <err.h> 37 #include <limits.h> 38 #include <locale.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <wchar.h> 44 #include <wctype.h> 45 46 #define TAB 8 47 48 static void c_columnate(void); 49 static void input(FILE *); 50 static void maketbl(void); 51 static void print(void); 52 static void r_columnate(void); 53 static void usage(void); 54 static int width(const wchar_t *); 55 56 static int termwidth = 80; /* default terminal width */ 57 58 static int entries; /* number of records */ 59 static int eval; /* exit value */ 60 static int maxlength; /* longest record */ 61 static wchar_t **list; /* array of pointers to records */ 62 static const wchar_t *separator = L"\t "; /* field separator for table option */ 63 64 int 65 main(int argc, char **argv) 66 { 67 struct winsize win; 68 FILE *fp; 69 int ch, tflag, xflag; 70 char *p; 71 const char *src; 72 wchar_t *newsep; 73 size_t seplen; 74 75 setlocale(LC_ALL, ""); 76 77 if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) { 78 if ((p = getenv("COLUMNS"))) 79 termwidth = atoi(p); 80 } else 81 termwidth = win.ws_col; 82 83 tflag = xflag = 0; 84 while ((ch = getopt(argc, argv, "c:s:tx")) != -1) 85 switch(ch) { 86 case 'c': 87 termwidth = atoi(optarg); 88 break; 89 case 's': 90 src = optarg; 91 seplen = mbsrtowcs(NULL, &src, 0, NULL); 92 if (seplen == (size_t)-1) 93 err(1, "bad separator"); 94 newsep = malloc((seplen + 1) * sizeof(wchar_t)); 95 if (newsep == NULL) 96 err(1, NULL); 97 mbsrtowcs(newsep, &src, seplen + 1, NULL); 98 separator = newsep; 99 break; 100 case 't': 101 tflag = 1; 102 break; 103 case 'x': 104 xflag = 1; 105 break; 106 case '?': 107 default: 108 usage(); 109 } 110 argc -= optind; 111 argv += optind; 112 113 if (!*argv) 114 input(stdin); 115 else for (; *argv; ++argv) 116 if ((fp = fopen(*argv, "r"))) { 117 input(fp); 118 (void)fclose(fp); 119 } else { 120 warn("%s", *argv); 121 eval = 1; 122 } 123 124 if (!entries) 125 exit(eval); 126 127 maxlength = roundup(maxlength + 1, TAB); 128 if (tflag) 129 maketbl(); 130 else if (maxlength >= termwidth) 131 print(); 132 else if (xflag) 133 c_columnate(); 134 else 135 r_columnate(); 136 exit(eval); 137 } 138 139 static void 140 c_columnate(void) 141 { 142 int chcnt, col, cnt, endcol, numcols; 143 wchar_t **lp; 144 145 numcols = termwidth / maxlength; 146 endcol = maxlength; 147 for (chcnt = col = 0, lp = list;; ++lp) { 148 wprintf(L"%ls", *lp); 149 chcnt += width(*lp); 150 if (!--entries) 151 break; 152 if (++col == numcols) { 153 chcnt = col = 0; 154 endcol = maxlength; 155 putwchar('\n'); 156 } else { 157 while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) { 158 (void)putwchar('\t'); 159 chcnt = cnt; 160 } 161 endcol += maxlength; 162 } 163 } 164 if (chcnt) 165 putwchar('\n'); 166 } 167 168 static void 169 r_columnate(void) 170 { 171 int base, chcnt, cnt, col, endcol, numcols, numrows, row; 172 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 wprintf(L"%ls", list[base]); 182 chcnt += width(list[base]); 183 if ((base += numrows) >= entries) 184 break; 185 while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) { 186 (void)putwchar('\t'); 187 chcnt = cnt; 188 } 189 endcol += maxlength; 190 } 191 putwchar('\n'); 192 } 193 } 194 195 static void 196 print(void) 197 { 198 int cnt; 199 wchar_t **lp; 200 201 for (cnt = entries, lp = list; cnt--; ++lp) 202 (void)wprintf(L"%ls\n", *lp); 203 } 204 205 typedef struct _tbl { 206 wchar_t **list; 207 int cols, *len; 208 } TBL; 209 #define DEFCOLS 25 210 211 static void 212 maketbl(void) 213 { 214 TBL *t; 215 int coloff, cnt; 216 wchar_t *p, **lp; 217 int *lens, maxcols; 218 TBL *tbl; 219 wchar_t **cols; 220 wchar_t *last; 221 222 if ((t = tbl = calloc(entries, sizeof(TBL))) == NULL) 223 err(1, NULL); 224 if ((cols = calloc((maxcols = DEFCOLS), sizeof(*cols))) == NULL) 225 err(1, NULL); 226 if ((lens = calloc(maxcols, sizeof(int))) == NULL) 227 err(1, NULL); 228 for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) { 229 for (coloff = 0, p = *lp; 230 (cols[coloff] = wcstok(p, separator, &last)); 231 p = NULL) 232 if (++coloff == maxcols) { 233 if (!(cols = realloc(cols, ((u_int)maxcols + 234 DEFCOLS) * sizeof(wchar_t *))) || 235 !(lens = realloc(lens, 236 ((u_int)maxcols + DEFCOLS) * sizeof(int)))) 237 err(1, NULL); 238 memset((char *)lens + maxcols * sizeof(int), 239 0, DEFCOLS * sizeof(int)); 240 maxcols += DEFCOLS; 241 } 242 if ((t->list = calloc(coloff, sizeof(*t->list))) == NULL) 243 err(1, NULL); 244 if ((t->len = calloc(coloff, sizeof(int))) == NULL) 245 err(1, NULL); 246 for (t->cols = coloff; --coloff >= 0;) { 247 t->list[coloff] = cols[coloff]; 248 t->len[coloff] = width(cols[coloff]); 249 if (t->len[coloff] > lens[coloff]) 250 lens[coloff] = t->len[coloff]; 251 } 252 } 253 for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) { 254 for (coloff = 0; coloff < t->cols - 1; ++coloff) 255 (void)wprintf(L"%ls%*ls", t->list[coloff], 256 lens[coloff] - t->len[coloff] + 2, L" "); 257 (void)wprintf(L"%ls\n", t->list[coloff]); 258 free(t->list); 259 free(t->len); 260 } 261 free(lens); 262 free(cols); 263 free(tbl); 264 } 265 266 #define DEFNUM 1000 267 #define MAXLINELEN (LINE_MAX + 1) 268 269 static void 270 input(FILE *fp) 271 { 272 static int maxentry; 273 int len; 274 wchar_t *p, buf[MAXLINELEN]; 275 276 if (!list) 277 if ((list = calloc((maxentry = DEFNUM), sizeof(*list))) == 278 NULL) 279 err(1, NULL); 280 while (fgetws(buf, MAXLINELEN, fp)) { 281 for (p = buf; *p && iswspace(*p); ++p); 282 if (!*p) 283 continue; 284 if (!(p = wcschr(p, L'\n'))) { 285 warnx("line too long"); 286 eval = 1; 287 continue; 288 } 289 *p = L'\0'; 290 len = width(buf); 291 if (maxlength < len) 292 maxlength = len; 293 if (entries == maxentry) { 294 maxentry += DEFNUM; 295 if (!(list = realloc(list, 296 (u_int)maxentry * sizeof(*list)))) 297 err(1, NULL); 298 } 299 list[entries] = malloc((wcslen(buf) + 1) * sizeof(wchar_t)); 300 if (list[entries] == NULL) 301 err(1, NULL); 302 wcscpy(list[entries], buf); 303 entries++; 304 } 305 } 306 307 /* Like wcswidth(), but ignores non-printing characters. */ 308 static int 309 width(const wchar_t *wcs) 310 { 311 int w, cw; 312 313 for (w = 0; *wcs != L'\0'; wcs++) 314 if ((cw = wcwidth(*wcs)) > 0) 315 w += cw; 316 return (w); 317 } 318 319 static void 320 usage(void) 321 { 322 323 (void)fprintf(stderr, 324 "usage: column [-tx] [-c columns] [-s sep] [file ...]\n"); 325 exit(1); 326 } 327