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