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