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