1 /*- 2 * Copyright (c) 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#) Copyright (c) 1993 The Regents of the University of California. All rights reserved. 34 * @(#)rs.c 8.1 (Berkeley) 6/6/93 35 * $FreeBSD: src/usr.bin/rs/rs.c,v 1.5.2.2 2002/08/03 00:48:43 tjr Exp $ 36 */ 37 38 /* 39 * rs - reshape a data array 40 * Author: John Kunze, Office of Comp. Affairs, UCB 41 * BEWARE: lots of unfinished edges 42 */ 43 44 #include <err.h> 45 #include <ctype.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 50 long flags; 51 #define TRANSPOSE 000001 52 #define MTRANSPOSE 000002 53 #define ONEPERLINE 000004 54 #define ONEISEPONLY 000010 55 #define ONEOSEPONLY 000020 56 #define NOTRIMENDCOL 000040 57 #define SQUEEZE 000100 58 #define SHAPEONLY 000200 59 #define DETAILSHAPE 000400 60 #define RIGHTADJUST 001000 61 #define NULLPAD 002000 62 #define RECYCLE 004000 63 #define SKIPPRINT 010000 64 #define ICOLBOUNDS 020000 65 #define OCOLBOUNDS 040000 66 #define ONEPERCHAR 0100000 67 #define NOARGS 0200000 68 69 short *colwidths; 70 short *cord; 71 short *icbd; 72 short *ocbd; 73 int nelem; 74 char **elem; 75 char **endelem; 76 char *curline; 77 int allocsize = BUFSIZ; 78 int curlen; 79 int irows, icols; 80 int orows, ocols; 81 int maxlen; 82 int skip; 83 int propgutter; 84 char isep = ' ', osep = ' '; 85 char blank[] = ""; 86 int owidth = 80, gutter = 2; 87 88 void getargs(int, char *[]); 89 void getfile(void); 90 int getline(void); 91 char *getlist(short **, char *); 92 char *getnum(int *, char *, int); 93 char **getptrs(char **); 94 void prepfile(void); 95 void prints(char *, int); 96 void putfile(void); 97 static void usage(void); 98 99 #define INCR(ep) do { \ 100 if (++ep >= endelem) \ 101 ep = getptrs(ep); \ 102 } while(0) 103 104 int 105 main(int argc, char *argv[]) 106 { 107 getargs(argc, argv); 108 getfile(); 109 if (flags & SHAPEONLY) { 110 printf("%d %d\n", irows, icols); 111 exit(0); 112 } 113 prepfile(); 114 putfile(); 115 exit(0); 116 } 117 118 void 119 getfile(void) 120 { 121 char *p; 122 char *endp; 123 char **ep; 124 int multisep = (flags & ONEISEPONLY ? 0 : 1); 125 int nullpad = flags & NULLPAD; 126 char **padto; 127 128 while (skip--) { 129 getline(); 130 if (flags & SKIPPRINT) 131 puts(curline); 132 } 133 getline(); 134 if (flags & NOARGS && curlen < owidth) 135 flags |= ONEPERLINE; 136 if (flags & ONEPERLINE) 137 icols = 1; 138 else /* count cols on first line */ 139 for (p = curline, endp = curline + curlen; p < endp; p++) { 140 if (*p == isep && multisep) 141 continue; 142 icols++; 143 while (*p && *p != isep) 144 p++; 145 } 146 ep = getptrs(elem); 147 p = curline; 148 do { 149 if (flags & ONEPERLINE) { 150 *ep = curline; 151 INCR(ep); /* prepare for next entry */ 152 if (maxlen < curlen) 153 maxlen = curlen; 154 irows++; 155 continue; 156 } 157 for (p = curline, endp = curline + curlen; p < endp; p++) { 158 if (*p == isep && multisep) 159 continue; /* eat up column separators */ 160 if (*p == isep) /* must be an empty column */ 161 *ep = blank; 162 else /* store column entry */ 163 *ep = p; 164 while (p < endp && *p != isep) 165 p++; /* find end of entry */ 166 *p = '\0'; /* mark end of entry */ 167 if (maxlen < p - *ep) /* update maxlen */ 168 maxlen = p - *ep; 169 INCR(ep); /* prepare for next entry */ 170 } 171 irows++; /* update row count */ 172 if (nullpad) { /* pad missing entries */ 173 padto = elem + irows * icols; 174 while (ep < padto) { 175 *ep = blank; 176 INCR(ep); 177 } 178 } 179 } while (getline() != EOF); 180 *ep = NULL; /* mark end of pointers */ 181 nelem = ep - elem; 182 } 183 184 void 185 putfile(void) 186 { 187 char **ep; 188 int i, j, k; 189 190 ep = elem; 191 if (flags & TRANSPOSE) 192 for (i = 0; i < orows; i++) { 193 for (j = i; j < nelem; j += orows) 194 prints(ep[j], (j - i) / orows); 195 putchar('\n'); 196 } 197 else 198 for (i = k = 0; i < orows; i++) { 199 for (j = 0; j < ocols; j++, k++) 200 if (k < nelem) 201 prints(ep[k], j); 202 putchar('\n'); 203 } 204 } 205 206 void 207 prints(char *s, int col) 208 { 209 int n; 210 char *p = s; 211 212 while (*p) 213 p++; 214 n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - (p - s)); 215 if (flags & RIGHTADJUST) 216 while (n-- > 0) 217 putchar(osep); 218 for (p = s; *p; p++) 219 putchar(*p); 220 while (n-- > 0) 221 putchar(osep); 222 } 223 224 static void 225 usage(void) 226 { 227 fprintf(stderr, 228 "usage: rs [-[csCS][x][kKgGw][N]tTeEnyjhHmz] [rows [cols]]\n"); 229 exit(1); 230 } 231 232 void 233 prepfile(void) 234 { 235 char **ep; 236 int i; 237 int j; 238 char **lp; 239 int colw; 240 int max; 241 int n; 242 243 if (!nelem) 244 exit(0); 245 gutter += maxlen * propgutter / 100.0; 246 colw = maxlen + gutter; 247 if (flags & MTRANSPOSE) { 248 orows = icols; 249 ocols = irows; 250 } 251 else if (orows == 0 && ocols == 0) { /* decide rows and cols */ 252 ocols = owidth / colw; 253 if (ocols == 0) { 254 warnx("display width %d is less than column width %d", 255 owidth, colw); 256 ocols = 1; 257 } 258 if (ocols > nelem) 259 ocols = nelem; 260 orows = nelem / ocols + (nelem % ocols ? 1 : 0); 261 } 262 else if (orows == 0) /* decide on rows */ 263 orows = nelem / ocols + (nelem % ocols ? 1 : 0); 264 else if (ocols == 0) /* decide on cols */ 265 ocols = nelem / orows + (nelem % orows ? 1 : 0); 266 lp = elem + orows * ocols; 267 while (lp > endelem) { 268 getptrs(elem + nelem); 269 lp = elem + orows * ocols; 270 } 271 if (flags & RECYCLE) { 272 for (ep = elem + nelem; ep < lp; ep++) 273 *ep = *(ep - nelem); 274 nelem = lp - elem; 275 } 276 if (!(colwidths = (short *) malloc(ocols * sizeof(short)))) 277 errx(1, "malloc"); 278 if (flags & SQUEEZE) { 279 ep = elem; 280 if (flags & TRANSPOSE) 281 for (i = 0; i < ocols; i++) { 282 max = 0; 283 for (j = 0; *ep != NULL && j < orows; j++) 284 if ((n = strlen(*ep++)) > max) 285 max = n; 286 colwidths[i] = max + gutter; 287 } 288 else 289 for (i = 0; i < ocols; i++) { 290 max = 0; 291 for (j = i; j < nelem; j += ocols) 292 if ((n = strlen(ep[j])) > max) 293 max = n; 294 colwidths[i] = max + gutter; 295 } 296 } 297 /* for (i = 0; i < orows; i++) { 298 for (j = i; j < nelem; j += orows) 299 prints(ep[j], (j - i) / orows); 300 putchar('\n'); 301 } 302 else 303 for (i = 0; i < orows; i++) { 304 for (j = 0; j < ocols; j++) 305 prints(*ep++, j); 306 putchar('\n'); 307 }*/ 308 else 309 for (i = 0; i < ocols; i++) 310 colwidths[i] = colw; 311 if (!(flags & NOTRIMENDCOL)) { 312 if (flags & RIGHTADJUST) 313 colwidths[0] -= gutter; 314 else 315 colwidths[ocols - 1] = 0; 316 } 317 n = orows * ocols; 318 if (n > nelem && (flags & RECYCLE)) 319 nelem = n; 320 /*for (i = 0; i < ocols; i++) 321 warnx("%d is colwidths, nelem %d", colwidths[i], nelem);*/ 322 } 323 324 #define BSIZE 2048 325 char ibuf[BSIZE]; /* two screenfuls should do */ 326 327 int 328 getline(void) /* get line; maintain curline, curlen; manage storage */ 329 { 330 static int putlength; 331 static char *endblock = ibuf + BSIZE; 332 char *p; 333 int c, i; 334 335 if (!irows) { 336 curline = ibuf; 337 putlength = flags & DETAILSHAPE; 338 } 339 else if (skip <= 0) { /* don't waste storage */ 340 curline += curlen + 1; 341 if (putlength) { /* print length, recycle storage */ 342 printf(" %d line %d\n", curlen, irows); 343 curline = ibuf; 344 } 345 } 346 if (!putlength && endblock - curline < BUFSIZ) { /* need storage */ 347 /*ww = endblock-curline; tt += ww;*/ 348 /*printf("#wasted %d total %d\n",ww,tt);*/ 349 if (!(curline = (char *) malloc(BSIZE))) 350 errx(1, "file too large"); 351 endblock = curline + BSIZE; 352 /*printf("#endb %d curline %d\n",endblock,curline);*/ 353 } 354 c = EOF; 355 for (p = curline, i = 1; i < BUFSIZ; *p++ = c, i++) 356 if ((c = getchar()) == EOF || c == '\n') 357 break; 358 *p = '\0'; 359 curlen = i - 1; 360 return(c); 361 } 362 363 char ** 364 getptrs(char **sp) 365 { 366 char **p; 367 368 allocsize += allocsize; 369 p = (char **)realloc(elem, allocsize * sizeof(char *)); 370 if (p == NULL) 371 err(1, "no memory"); 372 373 sp += (p - elem); 374 endelem = (elem = p) + allocsize; 375 return(sp); 376 } 377 378 void 379 getargs(int ac, char *av[]) 380 { 381 char *p; 382 383 if (ac == 1) { 384 flags |= NOARGS | TRANSPOSE; 385 } 386 while (--ac && **++av == '-') 387 for (p = *av+1; *p; p++) 388 switch (*p) { 389 case 'T': 390 flags |= MTRANSPOSE; 391 case 't': 392 flags |= TRANSPOSE; 393 break; 394 case 'c': /* input col. separator */ 395 flags |= ONEISEPONLY; 396 case 's': /* one or more allowed */ 397 if (p[1]) 398 isep = *++p; 399 else 400 isep = '\t'; /* default is ^I */ 401 break; 402 case 'C': 403 flags |= ONEOSEPONLY; 404 case 'S': 405 if (p[1]) 406 osep = *++p; 407 else 408 osep = '\t'; /* default is ^I */ 409 break; 410 case 'w': /* window width, default 80 */ 411 p = getnum(&owidth, p, 0); 412 if (owidth <= 0) 413 errx(1, "width must be a positive integer"); 414 break; 415 case 'K': /* skip N lines */ 416 flags |= SKIPPRINT; 417 case 'k': /* skip, do not print */ 418 p = getnum(&skip, p, 0); 419 if (!skip) 420 skip = 1; 421 break; 422 case 'm': 423 flags |= NOTRIMENDCOL; 424 break; 425 case 'g': /* gutter space */ 426 p = getnum(&gutter, p, 0); 427 break; 428 case 'G': 429 p = getnum(&propgutter, p, 0); 430 break; 431 case 'e': /* each line is an entry */ 432 flags |= ONEPERLINE; 433 break; 434 case 'E': 435 flags |= ONEPERCHAR; 436 break; 437 case 'j': /* right adjust */ 438 flags |= RIGHTADJUST; 439 break; 440 case 'n': /* null padding for missing values */ 441 flags |= NULLPAD; 442 break; 443 case 'y': 444 flags |= RECYCLE; 445 break; 446 case 'H': /* print shape only */ 447 flags |= DETAILSHAPE; 448 case 'h': 449 flags |= SHAPEONLY; 450 break; 451 case 'z': /* squeeze col width */ 452 flags |= SQUEEZE; 453 break; 454 /*case 'p': 455 ipagespace = atoi(++p); (default is 1) 456 break;*/ 457 case 'o': /* col order */ 458 p = getlist(&cord, p); 459 break; 460 case 'b': 461 flags |= ICOLBOUNDS; 462 p = getlist(&icbd, p); 463 break; 464 case 'B': 465 flags |= OCOLBOUNDS; 466 p = getlist(&ocbd, p); 467 break; 468 default: 469 usage(); 470 } 471 /*if (!osep) 472 osep = isep;*/ 473 switch (ac) { 474 /*case 3: 475 opages = atoi(av[2]);*/ 476 case 2: 477 ocols = atoi(av[1]); 478 case 1: 479 orows = atoi(av[0]); 480 case 0: 481 break; 482 default: 483 errx(1, "too many arguments"); 484 } 485 } 486 487 char * 488 getlist(short **list, char *p) 489 { 490 int count = 1; 491 char *t; 492 493 for (t = p + 1; *t; t++) { 494 if (!isdigit(*t)) 495 errx(1, 496 "option %.1s requires a list of unsigned numbers separated by commas", t); 497 count++; 498 while (*t && isdigit(*t)) 499 t++; 500 if (*t != ',') 501 break; 502 } 503 if (!(*list = (short *) malloc(count * sizeof(short)))) 504 errx(1, "no list space"); 505 count = 0; 506 for (t = p + 1; *t; t++) { 507 (*list)[count++] = atoi(t); 508 printf("++ %d ", (*list)[count-1]); 509 fflush(stdout); 510 while (*t && isdigit(*t)) 511 t++; 512 if (*t != ',') 513 break; 514 } 515 (*list)[count] = 0; 516 return(t - 1); 517 } 518 519 /* 520 * num = number p points to; if (strict) complain 521 * returns pointer to end of num 522 */ 523 char * 524 getnum(int *num, char *p, int strict) 525 { 526 char *t = p; 527 528 if (!isdigit(*++t)) { 529 if (strict || *t == '-' || *t == '+') 530 errx(1, "option %.1s requires an unsigned integer", p); 531 *num = 0; 532 return(p); 533 } 534 *num = atoi(t); 535 while (*++t) 536 if (!isdigit(*t)) 537 break; 538 return(--t); 539 } 540