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