1 /* $OpenBSD: rs.c,v 1.30 2015/12/03 12:23:15 schwarze Exp $ */ 2 3 /*- 4 * Copyright (c) 1993 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 /* 33 * rs - reshape a data array 34 * Author: John Kunze, Office of Comp. Affairs, UCB 35 * BEWARE: lots of unfinished edges 36 */ 37 38 #include <ctype.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <limits.h> 42 #include <locale.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 48 struct entry { 49 int w; /* Display width. */ 50 char *s; /* Multibyte string. */ 51 }; 52 53 long flags; 54 #define TRANSPOSE 000001 55 #define MTRANSPOSE 000002 56 #define ONEPERLINE 000004 57 #define ONEISEPONLY 000010 58 #define ONEOSEPONLY 000020 59 #define NOTRIMENDCOL 000040 60 #define SQUEEZE 000100 61 #define SHAPEONLY 000200 62 #define DETAILSHAPE 000400 63 #define RIGHTADJUST 001000 64 #define NULLPAD 002000 65 #define RECYCLE 004000 66 #define SKIPPRINT 010000 67 #define ONEPERCHAR 0100000 68 #define NOARGS 0200000 69 70 short *colwidths; 71 int nelem; 72 struct entry *elem; 73 struct entry *endelem; 74 char *curline; 75 int allocsize = BUFSIZ; 76 int irows, icols; 77 int orows, ocols; 78 int maxwidth; 79 int skip; 80 int propgutter; 81 char isep = ' ', osep = ' '; 82 int owidth = 80, gutter = 2; 83 84 int mbsavis(char **, const char *); 85 86 void usage(void); 87 void getargs(int, char *[]); 88 void getfile(void); 89 int get_line(void); 90 struct entry *getptrs(struct entry *); 91 void prepfile(void); 92 void prints(struct entry *, int); 93 void putfile(void); 94 95 #define INCR(ep) do { \ 96 if (++ep >= endelem) \ 97 ep = getptrs(ep); \ 98 } while(0) 99 100 int 101 main(int argc, char *argv[]) 102 { 103 setlocale(LC_CTYPE, ""); 104 105 if (pledge("stdio", NULL) == -1) 106 err(1, "pledge"); 107 108 getargs(argc, argv); 109 getfile(); 110 if (flags & SHAPEONLY) { 111 printf("%d %d\n", irows, icols); 112 exit(0); 113 } 114 prepfile(); 115 putfile(); 116 exit(0); 117 } 118 119 void 120 getfile(void) 121 { 122 const char delim[2] = { isep, '\0' }; 123 char *p; 124 struct entry *ep; 125 int multisep = (flags & ONEISEPONLY ? 0 : 1); 126 int nullpad = flags & NULLPAD; 127 struct entry *padto; 128 129 curline = NULL; 130 while (skip--) { 131 if (get_line() == EOF) 132 return; 133 if (flags & SKIPPRINT) 134 puts(curline); 135 } 136 if (get_line() == EOF) 137 return; 138 if (flags & NOARGS && strlen(curline) < (size_t)owidth) 139 flags |= ONEPERLINE; 140 if (flags & ONEPERLINE) 141 icols = 1; 142 else /* count cols on first line */ 143 for (p = curline; *p != '\0'; p++) { 144 if (*p == isep && multisep) 145 continue; 146 icols++; 147 while (*p && *p != isep) 148 p++; 149 } 150 ep = getptrs(NULL); 151 p = curline; 152 do { 153 if (flags & ONEPERLINE) { 154 ep->w = mbsavis(&ep->s, curline); 155 if (maxwidth < ep->w) 156 maxwidth = ep->w; 157 INCR(ep); /* prepare for next entry */ 158 irows++; 159 continue; 160 } 161 p = curline; 162 while (p != NULL && *p != '\0') { 163 if (*p == isep) { 164 p++; 165 if (multisep) 166 continue; 167 ep->s = ""; /* empty column */ 168 ep->w = 0; 169 } else 170 ep->w = mbsavis(&ep->s, strsep(&p, delim)); 171 if (maxwidth < ep->w) 172 maxwidth = ep->w; 173 INCR(ep); /* prepare for next entry */ 174 } 175 irows++; /* update row count */ 176 if (nullpad) { /* pad missing entries */ 177 padto = elem + irows * icols; 178 while (ep < padto) { 179 ep->s = ""; 180 ep->w = 0; 181 INCR(ep); 182 } 183 } 184 } while (get_line() != EOF); 185 nelem = ep - elem; 186 } 187 188 void 189 putfile(void) 190 { 191 struct entry *ep; 192 int i, j, n; 193 194 ep = elem; 195 if (flags & TRANSPOSE) { 196 for (i = 0; i < orows; i++) { 197 for (j = i; j < nelem; j += orows) 198 prints(ep + j, (j - i) / orows); 199 putchar('\n'); 200 } 201 } else { 202 for (n = 0, i = 0; i < orows && n < nelem; i++) { 203 for (j = 0; j < ocols; j++) { 204 if (n++ >= nelem) 205 break; 206 prints(ep++, j); 207 } 208 putchar('\n'); 209 } 210 } 211 } 212 213 void 214 prints(struct entry *ep, int col) 215 { 216 int n; 217 218 n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - ep->w); 219 if (flags & RIGHTADJUST) 220 while (n-- > 0) 221 putchar(osep); 222 fputs(ep->s, stdout); 223 while (n-- > 0) 224 putchar(osep); 225 } 226 227 void 228 usage(void) 229 { 230 extern char *__progname; 231 232 fprintf(stderr, 233 "usage: %s [-CcSs[x]] [-GgKkw N] [-EeHhjmnTtyz] [rows [cols]]\n", 234 __progname); 235 exit(1); 236 } 237 238 void 239 prepfile(void) 240 { 241 struct entry *ep; 242 int i; 243 int j; 244 struct entry *lp; 245 int colw; 246 int max = 0; 247 int n; 248 249 if (!nelem) 250 exit(0); 251 gutter += maxwidth * propgutter / 100.0; 252 colw = maxwidth + gutter; 253 if (flags & MTRANSPOSE) { 254 orows = icols; 255 ocols = irows; 256 } 257 else if (orows == 0 && ocols == 0) { /* decide rows and cols */ 258 ocols = owidth / colw; 259 if (ocols == 0) { 260 warnx("Display width %d is less than column width %d", 261 owidth, colw); 262 ocols = 1; 263 } 264 if (ocols > nelem) 265 ocols = nelem; 266 orows = nelem / ocols + (nelem % ocols ? 1 : 0); 267 } 268 else if (orows == 0) /* decide on rows */ 269 orows = nelem / ocols + (nelem % ocols ? 1 : 0); 270 else if (ocols == 0) /* decide on cols */ 271 ocols = nelem / orows + (nelem % orows ? 1 : 0); 272 while ((lp = elem + orows * ocols) > endelem) 273 (void)getptrs(NULL); 274 if (flags & RECYCLE) { 275 for (ep = elem + nelem; ep < lp; ep++) 276 memcpy(ep, ep - nelem, sizeof(*ep)); 277 nelem = lp - elem; 278 } 279 if (!(colwidths = calloc(ocols, sizeof(short)))) 280 errx(1, "malloc: No gutter space"); 281 if (flags & SQUEEZE) { 282 for (ep = elem, i = 0; i < ocols; i++) { 283 max = 0; 284 if (flags & TRANSPOSE) { 285 for (j = 0; j < orows; j++, ep++) 286 if (ep->w > max) 287 max = ep->w; 288 } else { 289 for (j = i; j < nelem; j += ocols) 290 if (ep[j].w > max) 291 max = ep[j].w; 292 } 293 colwidths[i] = max + gutter; 294 } 295 } else { 296 for (i = 0; i < ocols; i++) 297 colwidths[i] = colw; 298 } 299 if (!(flags & NOTRIMENDCOL)) { 300 if (flags & RIGHTADJUST) 301 colwidths[0] -= gutter; 302 else 303 colwidths[ocols - 1] = 0; 304 } 305 n = orows * ocols; 306 if (n > nelem && (flags & RECYCLE)) 307 nelem = n; 308 } 309 310 int 311 get_line(void) 312 { 313 static size_t cursz; 314 static ssize_t curlen; 315 316 if (irows > 0 && flags & DETAILSHAPE) 317 printf(" %zd line %d\n", curlen, irows); 318 319 if ((curlen = getline(&curline, &cursz, stdin)) == EOF) { 320 if (ferror(stdin)) 321 err(1, NULL); 322 return EOF; 323 } 324 if (curlen > 0 && curline[curlen - 1] == '\n') 325 curline[--curlen] = '\0'; 326 327 return 0; 328 } 329 330 struct entry * 331 getptrs(struct entry *sp) 332 { 333 struct entry *p; 334 int newsize; 335 336 newsize = allocsize * 2; 337 p = reallocarray(elem, newsize, sizeof(*p)); 338 if (p == NULL) 339 err(1, "no memory"); 340 341 allocsize = newsize; 342 sp = sp == NULL ? p : p + (sp - elem); 343 elem = p; 344 endelem = elem + allocsize; 345 return(sp); 346 } 347 348 void 349 getargs(int ac, char *av[]) 350 { 351 int ch; 352 const char *errstr; 353 354 if (ac == 1) 355 flags |= NOARGS | TRANSPOSE; 356 while ((ch = getopt(ac, av, "c::C::s::S::k:K:g:G:w:tTeEnyjhHmz")) != -1) { 357 switch (ch) { 358 case 'T': 359 flags |= MTRANSPOSE; 360 /* FALLTHROUGH */ 361 case 't': 362 flags |= TRANSPOSE; 363 break; 364 case 'c': /* input col. separator */ 365 flags |= ONEISEPONLY; 366 /* FALLTHROUGH */ 367 case 's': /* one or more allowed */ 368 if (optarg == NULL) 369 isep = '\t'; /* default is ^I */ 370 else if (optarg[1] != '\0') 371 usage(); /* single char only */ 372 else 373 isep = *optarg; 374 break; 375 case 'C': 376 flags |= ONEOSEPONLY; 377 /* FALLTHROUGH */ 378 case 'S': 379 if (optarg == NULL) 380 osep = '\t'; /* default is ^I */ 381 else if (optarg[1] != '\0') 382 usage(); /* single char only */ 383 else 384 osep = *optarg; 385 break; 386 case 'w': /* window width, default 80 */ 387 owidth = strtonum(optarg, 1, INT_MAX, &errstr); 388 if (errstr) { 389 warnx("width %s", errstr); 390 usage(); 391 } 392 break; 393 case 'K': /* skip N lines */ 394 flags |= SKIPPRINT; 395 /* FALLTHROUGH */ 396 case 'k': /* skip, do not print */ 397 skip = strtonum(optarg, 0, INT_MAX, &errstr); 398 if (errstr) { 399 warnx("skip value %s", errstr); 400 usage(); 401 } 402 if (skip == 0) 403 skip = 1; 404 break; 405 case 'm': 406 flags |= NOTRIMENDCOL; 407 break; 408 case 'g': /* gutter width */ 409 gutter = strtonum(optarg, 0, INT_MAX, &errstr); 410 if (errstr) { 411 warnx("gutter width %s", errstr); 412 usage(); 413 } 414 break; 415 case 'G': 416 propgutter = strtonum(optarg, 0, INT_MAX, &errstr); 417 if (errstr) { 418 warnx("gutter proportion %s", errstr); 419 usage(); 420 } 421 break; 422 case 'e': /* each line is an entry */ 423 flags |= ONEPERLINE; 424 break; 425 case 'E': 426 flags |= ONEPERCHAR; 427 break; 428 case 'j': /* right adjust */ 429 flags |= RIGHTADJUST; 430 break; 431 case 'n': /* null padding for missing values */ 432 flags |= NULLPAD; 433 break; 434 case 'y': 435 flags |= RECYCLE; 436 break; 437 case 'H': /* print shape only */ 438 flags |= DETAILSHAPE; 439 /* FALLTHROUGH */ 440 case 'h': 441 flags |= SHAPEONLY; 442 break; 443 case 'z': /* squeeze col width */ 444 flags |= SQUEEZE; 445 break; 446 default: 447 usage(); 448 } 449 } 450 ac -= optind; 451 av += optind; 452 453 switch (ac) { 454 case 2: 455 ocols = strtonum(av[1], 0, INT_MAX, &errstr); 456 if (errstr) { 457 warnx("columns value %s", errstr); 458 usage(); 459 } 460 /* FALLTHROUGH */ 461 case 1: 462 orows = strtonum(av[0], 0, INT_MAX, &errstr); 463 if (errstr) { 464 warnx("columns value %s", errstr); 465 usage(); 466 } 467 /* FALLTHROUGH */ 468 case 0: 469 break; 470 default: 471 usage(); 472 } 473 } 474