1 /* $OpenBSD: rs.c,v 1.20 2009/10/27 23:59:42 deraadt 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 46 long flags; 47 #define TRANSPOSE 000001 48 #define MTRANSPOSE 000002 49 #define ONEPERLINE 000004 50 #define ONEISEPONLY 000010 51 #define ONEOSEPONLY 000020 52 #define NOTRIMENDCOL 000040 53 #define SQUEEZE 000100 54 #define SHAPEONLY 000200 55 #define DETAILSHAPE 000400 56 #define RIGHTADJUST 001000 57 #define NULLPAD 002000 58 #define RECYCLE 004000 59 #define SKIPPRINT 010000 60 #define ONEPERCHAR 0100000 61 #define NOARGS 0200000 62 63 short *colwidths; 64 int nelem; 65 char **elem; 66 char **endelem; 67 char *curline; 68 int allocsize = BUFSIZ; 69 int curlen; 70 int irows, icols; 71 int orows, ocols; 72 int maxlen; 73 int skip; 74 int propgutter; 75 char isep = ' ', osep = ' '; 76 int owidth = 80, gutter = 2; 77 78 void usage(void); 79 void getargs(int, char *[]); 80 void getfile(void); 81 int getline(void); 82 char **getptrs(char **); 83 void prepfile(void); 84 void prints(char *, int); 85 void putfile(void); 86 87 #define INCR(ep) do { \ 88 if (++ep >= endelem) \ 89 ep = getptrs(ep); \ 90 } while(0) 91 92 int 93 main(int argc, char *argv[]) 94 { 95 getargs(argc, argv); 96 getfile(); 97 if (flags & SHAPEONLY) { 98 printf("%d %d\n", irows, icols); 99 exit(0); 100 } 101 prepfile(); 102 putfile(); 103 exit(0); 104 } 105 106 void 107 getfile(void) 108 { 109 char *p; 110 char *endp; 111 char **ep = NULL; 112 int multisep = (flags & ONEISEPONLY ? 0 : 1); 113 int nullpad = flags & NULLPAD; 114 char **padto; 115 116 while (skip--) { 117 getline(); 118 if (flags & SKIPPRINT) 119 puts(curline); 120 } 121 getline(); 122 if (flags & NOARGS && curlen < owidth) 123 flags |= ONEPERLINE; 124 if (flags & ONEPERLINE) 125 icols = 1; 126 else /* count cols on first line */ 127 for (p = curline, endp = curline + curlen; p < endp; p++) { 128 if (*p == isep && multisep) 129 continue; 130 icols++; 131 while (*p && *p != isep) 132 p++; 133 } 134 ep = getptrs(elem); 135 p = curline; 136 do { 137 if (flags & ONEPERLINE) { 138 *ep = curline; 139 INCR(ep); /* prepare for next entry */ 140 if (maxlen < curlen) 141 maxlen = curlen; 142 irows++; 143 continue; 144 } 145 for (p = curline, endp = curline + curlen; p < endp; p++) { 146 if (*p == isep && multisep) 147 continue; /* eat up column separators */ 148 if (*p == isep) /* must be an empty column */ 149 *ep = ""; 150 else /* store column entry */ 151 *ep = p; 152 while (p < endp && *p != isep) 153 p++; /* find end of entry */ 154 *p = '\0'; /* mark end of entry */ 155 if (maxlen < p - *ep) /* update maxlen */ 156 maxlen = p - *ep; 157 INCR(ep); /* prepare for next entry */ 158 } 159 irows++; /* update row count */ 160 if (nullpad) { /* pad missing entries */ 161 padto = elem + irows * icols; 162 while (ep < padto) { 163 *ep = ""; 164 INCR(ep); 165 } 166 } 167 } while (getline() != EOF); 168 *ep = NULL; /* mark end of pointers */ 169 nelem = ep - elem; 170 } 171 172 void 173 putfile(void) 174 { 175 char **ep; 176 int i, j, n; 177 178 ep = elem; 179 if (flags & TRANSPOSE) { 180 for (i = 0; i < orows; i++) { 181 for (j = i; j < nelem; j += orows) 182 prints(ep[j], (j - i) / orows); 183 putchar('\n'); 184 } 185 } else { 186 for (n = 0, i = 0; i < orows && n < nelem; i++) { 187 for (j = 0; j < ocols; j++) { 188 if (n++ >= nelem) 189 break; 190 prints(*ep++, j); 191 } 192 putchar('\n'); 193 } 194 } 195 } 196 197 void 198 prints(char *s, int col) 199 { 200 int n; 201 char *p = s; 202 203 while (*p) 204 p++; 205 n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - (p - s)); 206 if (flags & RIGHTADJUST) 207 while (n-- > 0) 208 putchar(osep); 209 for (p = s; *p; p++) 210 putchar(*p); 211 while (n-- > 0) 212 putchar(osep); 213 } 214 215 void 216 usage(void) 217 { 218 extern char *__progname; 219 220 fprintf(stderr, 221 "usage: %s [-CcSs[x]] [-GgKkw N] [-EeHhjmnTtyz] [rows [cols]]\n", 222 __progname); 223 exit(1); 224 } 225 226 void 227 prepfile(void) 228 { 229 char **ep; 230 int i; 231 int j; 232 char **lp; 233 int colw; 234 int max = 0; 235 int n; 236 237 if (!nelem) 238 exit(0); 239 gutter += maxlen * propgutter / 100.0; 240 colw = maxlen + gutter; 241 if (flags & MTRANSPOSE) { 242 orows = icols; 243 ocols = irows; 244 } 245 else if (orows == 0 && ocols == 0) { /* decide rows and cols */ 246 ocols = owidth / colw; 247 if (ocols == 0) { 248 warnx("Display width %d is less than column width %d", 249 owidth, colw); 250 ocols = 1; 251 } 252 if (ocols > nelem) 253 ocols = nelem; 254 orows = nelem / ocols + (nelem % ocols ? 1 : 0); 255 } 256 else if (orows == 0) /* decide on rows */ 257 orows = nelem / ocols + (nelem % ocols ? 1 : 0); 258 else if (ocols == 0) /* decide on cols */ 259 ocols = nelem / orows + (nelem % orows ? 1 : 0); 260 lp = elem + orows * ocols; 261 while (lp > endelem) { 262 getptrs(elem + nelem); 263 lp = elem + orows * ocols; 264 } 265 if (flags & RECYCLE) { 266 for (ep = elem + nelem; ep < lp; ep++) 267 *ep = *(ep - nelem); 268 nelem = lp - elem; 269 } 270 if (!(colwidths = (short *) calloc(ocols, sizeof(short)))) 271 errx(1, "malloc: No gutter space"); 272 if (flags & SQUEEZE) { 273 if (flags & TRANSPOSE) 274 for (ep = elem, i = 0; i < ocols; i++) { 275 for (j = 0; j < orows; j++) 276 if ((n = strlen(*ep++)) > max) 277 max = n; 278 colwidths[i] = max + gutter; 279 } 280 else 281 for (ep = elem, i = 0; i < ocols; i++) { 282 for (j = i; j < nelem; j += ocols) 283 if ((n = strlen(ep[j])) > max) 284 max = n; 285 colwidths[i] = max + gutter; 286 } 287 } else { 288 for (i = 0; i < ocols; i++) 289 colwidths[i] = colw; 290 } 291 if (!(flags & NOTRIMENDCOL)) { 292 if (flags & RIGHTADJUST) 293 colwidths[0] -= gutter; 294 else 295 colwidths[ocols - 1] = 0; 296 } 297 n = orows * ocols; 298 if (n > nelem && (flags & RECYCLE)) 299 nelem = n; 300 } 301 302 #define BSIZE 2048 303 char ibuf[BSIZE]; /* two screenfuls should do */ 304 305 int 306 getline(void) /* get line; maintain curline, curlen; manage storage */ 307 { 308 static int putlength; 309 static char *endblock = ibuf + BSIZE; 310 char *p; 311 int c, i; 312 313 if (!irows) { 314 curline = ibuf; 315 putlength = flags & DETAILSHAPE; 316 } 317 else if (skip <= 0) { /* don't waste storage */ 318 curline += curlen + 1; 319 if (putlength) /* print length, recycle storage */ 320 printf(" %d line %d\n", curlen, irows); 321 } 322 if (!putlength && endblock - curline < BUFSIZ) { /* need storage */ 323 if (!(curline = (char *) malloc(BSIZE))) 324 errx(1, "File too large"); 325 endblock = curline + BSIZE; 326 } 327 for (p = curline, i = 1; i < BUFSIZ; *p++ = c, i++) 328 if ((c = getchar()) == EOF || c == '\n') 329 break; 330 *p = '\0'; 331 curlen = i - 1; 332 return(c); 333 } 334 335 char ** 336 getptrs(char **sp) 337 { 338 char **p; 339 int newsize, gap; 340 341 newsize = allocsize * 2; 342 p = realloc(elem, newsize * sizeof(char *)); 343 if (p == NULL) 344 err(1, "no memory"); 345 346 gap = p - elem; 347 elem = p; 348 allocsize = newsize; 349 sp += gap; 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