1 /* $NetBSD: rs.c,v 1.12 2008/07/21 14:19:25 lukem 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 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)rs.c 8.1 (Berkeley) 6/6/93"; 41 #else 42 __RCSID("$NetBSD: rs.c,v 1.12 2008/07/21 14:19:25 lukem Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 /* 47 * rs - reshape a data array 48 * Author: John Kunze, Office of Comp. Affairs, UCB 49 * BEWARE: lots of unfinished edges 50 */ 51 52 #include <ctype.h> 53 #include <err.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <stdarg.h> 58 59 long flags; 60 #define TRANSPOSE 000001 61 #define MTRANSPOSE 000002 62 #define ONEPERLINE 000004 63 #define ONEISEPONLY 000010 64 #define ONEOSEPONLY 000020 65 #define NOTRIMENDCOL 000040 66 #define SQUEEZE 000100 67 #define SHAPEONLY 000200 68 #define DETAILSHAPE 000400 69 #define RIGHTADJUST 001000 70 #define NULLPAD 002000 71 #define RECYCLE 004000 72 #define SKIPPRINT 010000 73 #define ICOLBOUNDS 020000 74 #define OCOLBOUNDS 040000 75 #define ONEPERCHAR 0100000 76 #define NOARGS 0200000 77 78 short *colwidths; 79 short *cord; 80 short *icbd; 81 short *ocbd; 82 int nelem; 83 char **elem; 84 char **endelem; 85 char *curline; 86 int allocsize = BUFSIZ; 87 int curlen; 88 int irows, icols; 89 int orows, ocols; 90 int maxlen; 91 int skip; 92 int propgutter; 93 char isep = ' ', osep = ' '; 94 int owidth = 80, gutter = 2; 95 96 void usage __P((char *, ...)) 97 __attribute__((__format__(__printf__, 1, 2))); 98 void getargs __P((int, char *[])); 99 void getfile __P((void)); 100 int getline __P((void)); 101 char *getlist __P((short **, char *)); 102 char *getnum __P((int *, char *, int)); 103 char **getptrs __P((char **)); 104 int main __P((int, char **)); 105 void prepfile __P((void)); 106 void prints __P((char *, int)); 107 void putfile __P((void)); 108 109 #define INCR(ep) do { \ 110 if (++ep >= endelem) \ 111 ep = getptrs(ep); \ 112 } while(0) 113 114 int 115 main(argc, argv) 116 int argc; 117 char *argv[]; 118 { 119 getargs(argc, argv); 120 getfile(); 121 if (flags & SHAPEONLY) { 122 printf("%d %d\n", irows, icols); 123 exit(0); 124 } 125 prepfile(); 126 putfile(); 127 exit(0); 128 } 129 130 void 131 getfile() 132 { 133 char *p; 134 char *endp; 135 char **ep = 0; 136 int multisep = (flags & ONEISEPONLY ? 0 : 1); 137 int nullpad = flags & NULLPAD; 138 char **padto; 139 140 while (skip--) { 141 getline(); 142 if (flags & SKIPPRINT) 143 puts(curline); 144 } 145 getline(); 146 if (flags & NOARGS && curlen < owidth) 147 flags |= ONEPERLINE; 148 if (flags & ONEPERLINE) 149 icols = 1; 150 else /* count cols on first line */ 151 for (p = curline, endp = curline + curlen; p < endp; p++) { 152 if (*p == isep && multisep) 153 continue; 154 icols++; 155 while (*p && *p != isep) 156 p++; 157 } 158 ep = getptrs(elem); 159 p = curline; 160 do { 161 if (flags & ONEPERLINE) { 162 *ep = curline; 163 INCR(ep); /* prepare for next entry */ 164 if (maxlen < curlen) 165 maxlen = curlen; 166 irows++; 167 continue; 168 } 169 for (p = curline, endp = curline + curlen; p < endp; p++) { 170 if (*p == isep && multisep) 171 continue; /* eat up column separators */ 172 if (*p == isep) /* must be an empty column */ 173 *ep = ""; 174 else /* store column entry */ 175 *ep = p; 176 while (p < endp && *p != isep) 177 p++; /* find end of entry */ 178 *p = '\0'; /* mark end of entry */ 179 if (maxlen < p - *ep) /* update maxlen */ 180 maxlen = p - *ep; 181 INCR(ep); /* prepare for next entry */ 182 } 183 irows++; /* update row count */ 184 if (nullpad) { /* pad missing entries */ 185 padto = elem + irows * icols; 186 while (ep < padto) { 187 *ep = ""; 188 INCR(ep); 189 } 190 } 191 } while (getline() != EOF); 192 *ep = 0; /* mark end of pointers */ 193 nelem = ep - elem; 194 } 195 196 void 197 putfile() 198 { 199 char **ep; 200 int i, j, n; 201 202 ep = elem; 203 if (flags & TRANSPOSE) { 204 for (i = 0; i < orows; i++) { 205 for (j = i; j < nelem; j += orows) 206 prints(ep[j], (j - i) / orows); 207 putchar('\n'); 208 } 209 } else { 210 for (n = 0, i = 0; i < orows && n < nelem; i++) { 211 for (j = 0; j < ocols; j++) { 212 if (n++ >= nelem) 213 break; 214 prints(*ep++, j); 215 } 216 putchar('\n'); 217 } 218 } 219 } 220 221 void 222 prints(s, col) 223 char *s; 224 int col; 225 { 226 int n; 227 char *p = s; 228 229 while (*p) 230 p++; 231 n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - (p - s)); 232 if (flags & RIGHTADJUST) 233 while (n-- > 0) 234 putchar(osep); 235 for (p = s; *p; p++) 236 putchar(*p); 237 while (n-- > 0) 238 putchar(osep); 239 } 240 241 void 242 usage(char *msg, ...) 243 { 244 va_list ap; 245 246 va_start(ap, msg); 247 vwarnx(msg, ap); 248 va_end(ap); 249 fprintf(stderr, 250 "usage: rs [ -[csCS][x][kKgGw][N]tTeEnyjhHm ] [ rows [ cols ] ]\n"); 251 exit(1); 252 } 253 254 void 255 prepfile() 256 { 257 char **ep; 258 int i; 259 int j; 260 char **lp; 261 int colw; 262 int max = 0; 263 int n; 264 265 ep = NULL; 266 if (!nelem) 267 exit(0); 268 gutter += maxlen * propgutter / 100.0; 269 colw = maxlen + gutter; 270 if (flags & MTRANSPOSE) { 271 orows = icols; 272 ocols = irows; 273 } 274 else if (orows == 0 && ocols == 0) { /* decide rows and cols */ 275 ocols = owidth / colw; 276 if (ocols == 0) { 277 warnx("Display width %d is less than column width %d", owidth, colw); 278 ocols = 1; 279 } 280 if (ocols > nelem) 281 ocols = nelem; 282 orows = nelem / ocols + (nelem % ocols ? 1 : 0); 283 } 284 else if (orows == 0) /* decide on rows */ 285 orows = nelem / ocols + (nelem % ocols ? 1 : 0); 286 else if (ocols == 0) /* decide on cols */ 287 ocols = nelem / orows + (nelem % orows ? 1 : 0); 288 lp = elem + orows * ocols; 289 while (lp > endelem) { 290 getptrs(elem + nelem); 291 lp = elem + orows * ocols; 292 } 293 if (flags & RECYCLE) { 294 for (ep = elem + nelem; ep < lp; ep++) 295 *ep = *(ep - nelem); 296 nelem = lp - elem; 297 } 298 if (!(colwidths = (short *) malloc(ocols * sizeof(short)))) 299 errx(1, "malloc: No gutter space"); 300 if (flags & SQUEEZE) { 301 if (flags & TRANSPOSE) 302 for (ep = elem, i = 0; i < ocols; i++) { 303 for (j = 0; j < orows; j++) 304 if ((n = strlen(*ep++)) > max) 305 max = n; 306 colwidths[i] = max + gutter; 307 } 308 else 309 for (ep = elem, i = 0; i < ocols; i++) { 310 for (j = i; j < nelem; j += ocols) 311 if ((n = strlen(ep[j])) > max) 312 max = n; 313 colwidths[i] = max + gutter; 314 } 315 } 316 /* for (i = 0; i < orows; i++) { 317 for (j = i; j < nelem; j += orows) 318 prints(ep[j], (j - i) / orows); 319 putchar('\n'); 320 } 321 else 322 for (i = 0; i < orows; i++) { 323 for (j = 0; j < ocols; j++) 324 prints(*ep++, j); 325 putchar('\n'); 326 }*/ 327 else 328 for (i = 0; i < ocols; i++) 329 colwidths[i] = colw; 330 if (!(flags & NOTRIMENDCOL)) { 331 if (flags & RIGHTADJUST) 332 colwidths[0] -= gutter; 333 else 334 colwidths[ocols - 1] = 0; 335 } 336 n = orows * ocols; 337 if (n > nelem && (flags & RECYCLE)) 338 nelem = n; 339 /*for (i = 0; i < ocols; i++) 340 fprintf(stderr, "%d ",colwidths[i]); 341 fprintf(stderr, "is colwidths, nelem %d\n", nelem);*/ 342 } 343 344 #define BSIZE 2048 345 char ibuf[BSIZE]; /* two screenfuls should do */ 346 347 int 348 getline() /* get line; maintain curline, curlen; manage storage */ 349 { 350 static int putlength; 351 static char *endblock = ibuf + BSIZE; 352 char *p; 353 int c, i; 354 355 if (!irows) { 356 curline = ibuf; 357 putlength = flags & DETAILSHAPE; 358 } 359 else if (skip <= 0) { /* don't waste storage */ 360 curline += curlen + 1; 361 if (putlength) /* print length, recycle storage */ 362 printf(" %d line %d\n", curlen, irows); 363 } 364 if (!putlength && endblock - curline < BUFSIZ) { /* need storage */ 365 /*ww = endblock-curline; tt += ww;*/ 366 /*printf("#wasted %d total %d\n",ww,tt);*/ 367 if (!(curline = (char *) malloc(BSIZE))) 368 errx(1, "File too large"); 369 endblock = curline + BSIZE; 370 /*printf("#endb %d curline %d\n",endblock,curline);*/ 371 } 372 for (p = curline, i = 1; i < BUFSIZ; *p++ = c, i++) 373 if ((c = getchar()) == EOF || c == '\n') 374 break; 375 *p = '\0'; 376 curlen = i - 1; 377 return(c); 378 } 379 380 char ** 381 getptrs(sp) 382 char **sp; 383 { 384 char **p; 385 386 allocsize += allocsize; 387 p = (char **)realloc(elem, allocsize * sizeof(char *)); 388 if (p == (char **)0) 389 err(1, "no memory"); 390 391 sp += (p - elem); 392 endelem = (elem = p) + allocsize; 393 return(sp); 394 } 395 396 void 397 getargs(ac, av) 398 int ac; 399 char *av[]; 400 { 401 char *p; 402 403 if (ac == 1) { 404 flags |= NOARGS | TRANSPOSE; 405 } 406 while (--ac && **++av == '-') 407 for (p = *av+1; *p; p++) 408 switch (*p) { 409 case 'T': 410 flags |= MTRANSPOSE; 411 case 't': 412 flags |= TRANSPOSE; 413 break; 414 case 'c': /* input col. separator */ 415 flags |= ONEISEPONLY; 416 case 's': /* one or more allowed */ 417 if (p[1]) 418 isep = *++p; 419 else 420 isep = '\t'; /* default is ^I */ 421 break; 422 case 'C': 423 flags |= ONEOSEPONLY; 424 case 'S': 425 if (p[1]) 426 osep = *++p; 427 else 428 osep = '\t'; /* default is ^I */ 429 break; 430 case 'w': /* window width, default 80 */ 431 p = getnum(&owidth, p, 0); 432 if (owidth <= 0) 433 usage("Width must be a positive integer"); 434 break; 435 case 'K': /* skip N lines */ 436 flags |= SKIPPRINT; 437 case 'k': /* skip, do not print */ 438 p = getnum(&skip, p, 0); 439 if (!skip) 440 skip = 1; 441 break; 442 case 'm': 443 flags |= NOTRIMENDCOL; 444 break; 445 case 'g': /* gutter space */ 446 p = getnum(&gutter, p, 0); 447 break; 448 case 'G': 449 p = getnum(&propgutter, p, 0); 450 break; 451 case 'e': /* each line is an entry */ 452 flags |= ONEPERLINE; 453 break; 454 case 'E': 455 flags |= ONEPERCHAR; 456 break; 457 case 'j': /* right adjust */ 458 flags |= RIGHTADJUST; 459 break; 460 case 'n': /* null padding for missing values */ 461 flags |= NULLPAD; 462 break; 463 case 'y': 464 flags |= RECYCLE; 465 break; 466 case 'H': /* print shape only */ 467 flags |= DETAILSHAPE; 468 case 'h': 469 flags |= SHAPEONLY; 470 break; 471 case 'z': /* squeeze col width */ 472 flags |= SQUEEZE; 473 break; 474 /*case 'p': 475 ipagespace = atoi(++p); (default is 1) 476 break;*/ 477 case 'o': /* col order */ 478 p = getlist(&cord, p); 479 break; 480 case 'b': 481 flags |= ICOLBOUNDS; 482 p = getlist(&icbd, p); 483 break; 484 case 'B': 485 flags |= OCOLBOUNDS; 486 p = getlist(&ocbd, p); 487 break; 488 default: 489 usage("Bad flag: %.1s", p); 490 } 491 /*if (!osep) 492 osep = isep;*/ 493 switch (ac) { 494 /*case 3: 495 opages = atoi(av[2]);*/ 496 case 2: 497 ocols = atoi(av[1]); 498 case 1: 499 orows = atoi(av[0]); 500 case 0: 501 break; 502 default: 503 usage("Too many arguments."); 504 } 505 } 506 507 char * 508 getlist(list, p) 509 short **list; 510 char *p; 511 { 512 int count = 1; 513 char *t; 514 515 for (t = p + 1; *t; t++) { 516 if (!isdigit((unsigned char)*t)) 517 usage("Option %.1s requires a list of unsigned numbers separated by commas", t); 518 count++; 519 while (*t && isdigit((unsigned char)*t)) 520 t++; 521 if (*t != ',') 522 break; 523 } 524 if (!(*list = (short *) malloc(count * sizeof(short)))) 525 errx(1, "No list space"); 526 count = 0; 527 for (t = p + 1; *t; t++) { 528 (*list)[count++] = atoi(t); 529 printf("++ %d ", (*list)[count-1]); 530 fflush(stdout); 531 while (*t && isdigit((unsigned char)*t)) 532 t++; 533 if (*t != ',') 534 break; 535 } 536 (*list)[count] = 0; 537 return(t - 1); 538 } 539 540 char * 541 getnum(num, p, strict) /* num = number p points to; if (strict) complain */ 542 int *num, strict; /* returns pointer to end of num */ 543 char *p; 544 { 545 char *t = p; 546 547 if (!isdigit((unsigned char)*++t)) { 548 if (strict || *t == '-' || *t == '+') 549 usage("Option %.1s requires an unsigned integer", p); 550 *num = 0; 551 return(p); 552 } 553 *num = atoi(t); 554 while (*++t) 555 if (!isdigit((unsigned char)*t)) 556 break; 557 return(--t); 558 } 559