home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume16 / colm next >
Text File  |  1989-01-17  |  34KB  |  1,145 lines

  1. Subject:  v16i087:  A columnation program
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4.  
  5. Submitted-by: Gary Oberbrunner <masscomp!garyo>
  6. Posting-number: Volume 16, Issue 87
  7. Archive-name: colm
  8.  
  9. I got sick of the fact that ls(1) will sort its columns down when given
  10. no arguments, but not in any other case.  And I figured the world needed
  11. a decent columnation tool.  So here is one I just whipped up; it does
  12. fixed and variable width columns, selectable gutter widths, it automatically
  13. figures out how many columns to use (unlike pr -#), and it allows various
  14. formatting options such as selectable leader character, selectable tabbing
  15. with selectable tab size, and your choice of leaders or a separation string.
  16. Colm should compile on any machine that has getopt(3C).
  17. The hooks are in there for multi-page columnation, but it's not done yet.
  18. Any volunteers?
  19.  
  20. A man page is included.  To make, unpack the shar and type 'make'.  Enjoy!
  21.  
  22. ----------------------------------------------------------------------------
  23. Remember,            Truth is not beauty;      (617)692-6200x2445
  24. Information is not knowledge;    Beauty is not love;      Gary   Oberbrunner
  25. Knowledge is not wisdom;    Love is not music;      ...!masscomp!garyo
  26. Wisdom is not truth;        Music is the best. - FZ   ....garyo@masscomp
  27.  
  28. #--------------------------------- cut here ----------------------------------
  29. # To unpack, cut out all lines above the dotted line, and any lines
  30. # after the similar line near the end of the file.
  31. # Then run the remaining file with /bin/sh (NOT /bin/csh) in the
  32. # appropriate directory.  It will create the following files:
  33. #
  34. #    colm.1
  35. #    makefile
  36. #    colm.h
  37. #    std.h
  38. #    colm.c
  39. #    column.c
  40. #    lists.c
  41. #    output.c
  42. #    readin.c
  43. #    vcolumn.c
  44. #########################################################
  45. if (test -d colm.1) then
  46.   echo colm.1 is an existing directory.  Cannot overwrite it.
  47.   exit 2
  48. fi
  49. if (test -f colm.1) then
  50.   echo 'Should I overwrite the existing version of colm.1 (y/n)?  \c'
  51.   read response
  52.   if (test $response != y) then exit 99
  53.   fi
  54. fi
  55. if (test -f colm.1) then
  56.   echo Overwriting colm.1...
  57.   else
  58.   echo Creating colm.1...
  59. fi
  60. sed 's/-//;s/%$//' << 'END.colm.1' > colm.1
  61. -.\"@(#)colm.1    1.1 6/3/88 22:48:54%
  62. -.RL "MASSCOMP"%
  63. -.TH LS 1%
  64. -.UC%
  65. -.SH NAME%
  66. -colm \- columnate lines of text%
  67. -.SH SYNOPSIS%
  68. -.B colm%
  69. -[%
  70. -.B  -cglnstvwLT%
  71. -] name ...%
  72. -.SH DESCRIPTION%
  73. -.I colm%
  74. -produces multi-column output from single-column input.%
  75. -It is sort of a cross between%
  76. -.I pr%
  77. --<n> (which prints multi-column output sorted across)%
  78. -and the column-sorting function%
  79. -of%
  80. -.I ls%
  81. -(1) (which sorts down, but only if given no filename arguments).%
  82. -It can produce fixed or variable width columns, and it figures out any%
  83. -options not explicitly specified.%
  84. -.PP%
  85. -.I colm%
  86. -uses getopt(3C) to parse its options, so options may be strung%
  87. -together, as in%
  88. -.IR -vw ,%
  89. -and values may either adhere to their option letters or follow as%
  90. -the next argument.%
  91. -The options are as follows:%
  92. -.TP%
  93. -.B \-v%
  94. -Produce variable-width columns.%
  95. -.I colm%
  96. -uses an iterative algorithm to find the maximum number of columns that%
  97. -allows at least the gutter-width between the widest element and the next%
  98. -column.%
  99. -The -v option is incompatible with -c.%
  100. -.TP%
  101. -.BI \-c n%
  102. -Use fixed-width columns of width%
  103. -.IR n .%
  104. -Data will be truncated if necessary to fit the specified column width.%
  105. -If%
  106. -.B -c%
  107. -is not used,%
  108. -.I colm%
  109. -expands the column width(s) to fit the output width.%
  110. -.TP%
  111. -.BI \-g n%
  112. -Use a "gutter" of%
  113. -.I n%
  114. -characters between columns.%
  115. -The default is 1.%
  116. -.TP%
  117. -.BI \-n n%
  118. -Use%
  119. -.I n%
  120. -fixed-width columns.%
  121. -Without%
  122. -.BR -n ,%
  123. -.I colm%
  124. -fits as many columns in the given output width as it can.%
  125. -.TP%
  126. -.B -s%
  127. -Spread the columns to fit the output width.%
  128. -Otherwise columns are made as narrow as possible,%
  129. -while leaving the gutter space between columns.%
  130. -With fixed-width columns, the columns will be spread as far as%
  131. -possible while keeping their widths even.%
  132. -WIth variable-width columns, the columns will be spread so that there%
  133. -is an even amount of white space between them.%
  134. -.TP%
  135. -.BI \-w n%
  136. -Sets the output width to%
  137. -.I n%
  138. -characters.%
  139. -The output lines may be shorter, but cannot be longer than%
  140. -.IR n .%
  141. -.TP%
  142. -.BI \-l n%
  143. -Sets the page length to%
  144. -.I n%
  145. -lines.%
  146. -This option is not implemented.%
  147. -An error message will result if you try to use it.%
  148. -.TP%
  149. -.BI \-L c%
  150. -Sets the%
  151. -.I leader character%
  152. -to%
  153. -.IR c .%
  154. -This character is used repeatedly to fill in the space between columns.%
  155. -The default is to use as many spaces and tabs as are needed.%
  156. --L ' ' will cause%
  157. -.I colm%
  158. -not to use tabs.%
  159. -Watch out for shell metacharacters.%
  160. -.TP%
  161. -.BI \-t s%
  162. -Use the literal string "s" to separate the columns,%
  163. -rather than a repeated leader character.%
  164. -Watch out for shell metacharacters.%
  165. -.br%
  166. -There are two main differences between -L and -t:%
  167. --L only allows a single character, while -t allows a string;%
  168. -and the leader character specified by -L is repeated to fill the%
  169. -space, whereas the string specified with -t is used only once between%
  170. -columns.%
  171. -Generally, -t will not produce even columns, while -L always will.%
  172. -.TP%
  173. -.BI \-T n%
  174. -Specifies the output tab width as%
  175. -.I n%
  176. -characters.%
  177. -The default is eight.%
  178. -.SH DIAGNOSTICS%
  179. -Several errors and warnings can be produced by%
  180. -.IR colm :%
  181. -.br%
  182. -A usage message is produced for illegal options.%
  183. -.br%
  184. -If any single input line is longer than the output width,%
  185. -.I colm%
  186. -terminates with an error.%
  187. -.br%
  188. -Illegal combinations of arguments and impossible conditions,%
  189. -such as 20 columns of 30 characters%
  190. -in an output width of 79 columns, or selecting spreading with a fixed%
  191. -separator string, cause errors.%
  192. -.br%
  193. -Certain combinations of total number of lines and specified number of%
  194. -columns cannot produce balanced columns, even with the last column%
  195. -allowed to be short.%
  196. -If this occurs,%
  197. -.I colm%
  198. -prints an explanatory message on stderr and uses a smaller number%
  199. -of columns than were specified.%
  200. -.br%
  201. -Attempting to do multi-page columnation (with -l) produces an error.%
  202. -.br%
  203. -Nonexistent or unreadable files produce an error.%
  204. -.SH SEE ALSO%
  205. -pr(1), ls(1)%
  206. -.SH BUGS%
  207. -Multi-page columnation is not implemented.%
  208. -.XX "Text manipulation"%
  209. -.XX "Columns, creating"%
  210. END.colm.1
  211. if (test -d makefile) then
  212.   echo makefile is an existing directory.  Cannot overwrite it.
  213.   exit 2
  214. fi
  215. if (test -f makefile) then
  216.   echo 'Should I overwrite the existing version of makefile (y/n)?  \c'
  217.   read response
  218.   if (test $response != y) then exit 99
  219.   fi
  220. fi
  221. if (test -f makefile) then
  222.   echo Overwriting makefile...
  223.   else
  224.   echo Creating makefile...
  225. fi
  226. sed 's/-//;s/%$//' << 'END.makefile' > makefile
  227. -#%
  228. -# Makefile for colm - auto-columnation filter%
  229. -# 5-31-88%
  230. -#%
  231. -SCCSID="@(#)makefile    1.1 6/3/8822:49:03"%
  232. -CFLAGS=-g -I/usr/garyo/lib%
  233. -LIBS=%
  234. -LDFLAGS=-g%
  235. -OBJECTS = colm.o lists.o column.o vcolumn.o readin.o output.o%
  236. -%
  237. -colm: $(OBJECTS)%
  238. -    cc $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS)%
  239. -%
  240. -$(OBJECTS): colm.h%
  241. END.makefile
  242. if (test -d colm.h) then
  243.   echo colm.h is an existing directory.  Cannot overwrite it.
  244.   exit 2
  245. fi
  246. if (test -f colm.h) then
  247.   echo 'Should I overwrite the existing version of colm.h (y/n)?  \c'
  248.   read response
  249.   if (test $response != y) then exit 99
  250.   fi
  251. fi
  252. if (test -f colm.h) then
  253.   echo Overwriting colm.h...
  254.   else
  255.   echo Creating colm.h...
  256. fi
  257. sed 's/-//;s/%$//' << 'END.colm.h' > colm.h
  258. -/*----------------------------------------------------------------------%
  259. - * Header file for auto-columnator%
  260. - * Copyright c. 1988, Gary Oberbrunner.%
  261. - * This program may be freely redistributed providing that this source code%
  262. - * and the accompanying copyright notice are preserved intact.%
  263. - * SCCS ID: @(#)colm.h    1.1 6/3/88 22:47:12%
  264. - *----------------------------------------------------------------------%
  265. - */%
  266. -%
  267. -#define MAX_COLS 1000        /* maximum number of columns */%
  268. -%
  269. -#define TRUE 1%
  270. -#define FALSE 0%
  271. -%
  272. -typedef int BOOLEAN;%
  273. -%
  274. -/* A doubly-linked list of text lines */%
  275. -typedef struct NODE {%
  276. -    struct NODE *next;%
  277. -    struct NODE *prev;%
  278. -    int length;%
  279. -    char *string;%
  280. -} NODE;%
  281. -%
  282. -extern NODE *findn();%
  283. -%
  284. -extern NODE Lines;        /* the head of the list */%
  285. -                /* defined in lists.c */%
  286. -%
  287. -extern int TotalLines;        /* total # of lines read */%
  288. -                /* defined in readin.c */%
  289. -%
  290. -/* Command-line options: */%
  291. -typedef struct OPTS {%
  292. -    int Vflg;            /* variable col width */%
  293. -    int Sflg;            /* stretch output columns to fit */%
  294. -    int Gutter;            /* blank space between columns */%
  295. -    int ColWidth;        /* fixed col width */%
  296. -    int Ncols;            /* number of cols */%
  297. -    int Width;            /* output width */%
  298. -    int PageLen;        /* page length */%
  299. -    int Tabsize;        /* tab size (normally 8) */%
  300. -    char ColSep[100];        /* separator string */%
  301. -    char Leader;        /* leader char for column separation */%
  302. -} OPTS;%
  303. -%
  304. -extern OPTS Opts;        /* defined in colm.c */%
  305. END.colm.h
  306. if (test -d std.h) then
  307.   echo std.h is an existing directory.  Cannot overwrite it.
  308.   exit 2
  309. fi
  310. if (test -f std.h) then
  311.   echo 'Should I overwrite the existing version of std.h (y/n)?  \c'
  312.   read response
  313.   if (test $response != y) then exit 99
  314.   fi
  315. fi
  316. if (test -f std.h) then
  317.   echo Overwriting std.h...
  318.   else
  319.   echo Creating std.h...
  320. fi
  321. sed 's/-//;s/%$//' << 'END.std.h' > std.h
  322. -/* Standard include file with lots of generally useful stuff */%
  323. -/* Copyright c. 1988, Gary Oberbrunner. */%
  324. -/* This program may be freely redistributed providing that this source code */%
  325. -/* and the accompanying copyright notice are preserved intact. */%
  326. -/* SCCS Id: @(#)std.h    1.2 1/21/88 19:03:14 */%
  327. -%
  328. -#define STDIN 0%
  329. -#define STDOUT 1%
  330. -#define STDERR 2%
  331. -%
  332. -#define min(x,y) ((x) < (y) ? (x) : (y))%
  333. -#define max(x,y) ((x) > (y) ? (x) : (y))%
  334. -#define clamp(x, mn, mx) (((x) <= (mn)) ? (mn) : (((x) >= (mx)) ? (mx) : (x)))%
  335. -%
  336. -#define SECURITY%
  337. -#ifdef SECURITY%
  338. -#define ASSERT(exp) { if (!(exp)) { fprintf(stderr,\%
  339. -    "Assertion error: %s, line %d. Assertion exp failed.\n",\%
  340. -    __FILE__, __LINE__);\%
  341. -    exit(69); } }%
  342. -#else%
  343. -#define ASSERT(exp)%
  344. -#endif%
  345. -%
  346. -#define dbug0(str) \%
  347. -    if (debug) fprintf(stderr, str);%
  348. -#define dbug1(str, arg1) \%
  349. -    if (debug) fprintf(stderr, str, arg1);%
  350. -#define dbug2(str, arg1, arg2) \%
  351. -    if (debug) fprintf(stderr, str, arg1, arg2);%
  352. -#define dbug3(str, arg1, arg2, arg3) \%
  353. -    if (debug) fprintf(stderr, str, arg1, arg2, arg3);%
  354. -#define dbug4(str, arg1, arg2, arg3, arg4) \%
  355. -    if (debug) fprintf(stderr, str, arg1, arg2, arg3, arg4);%
  356. -%
  357. -extern char *malloc();%
  358. -/*%
  359. - * NEW is a macro to malloc 'n' new variables of type 'type'.%
  360. - */%
  361. -#define NEW(var, type, num) \%
  362. -    if ((var = (type *) malloc((num) * sizeof(type)))==NULL) \%
  363. -    {    fprintf(stderr,\%
  364. -        "ERROR: Out of memory.\nLast request:\n");\%
  365. -    fprintf(stderr,\%
  366. -        "\tNumber: num\n\tType  : type\n\tName  : var.\n");\%
  367. -    fprintf(stderr,"File %s, line %d\n", __FILE__, __LINE__);\%
  368. -    fprintf(stderr,\%
  369. -    "\tTotal requested: num=%d x sizeof(type)=%d\n\t =%d bytes.\n",\%
  370. -        num, sizeof(type), (num) * sizeof(type));\%
  371. -    exit(99); }%
  372. END.std.h
  373. if (test -d colm.c) then
  374.   echo colm.c is an existing directory.  Cannot overwrite it.
  375.   exit 2
  376. fi
  377. if (test -f colm.c) then
  378.   echo 'Should I overwrite the existing version of colm.c (y/n)?  \c'
  379.   read response
  380.   if (test $response != y) then exit 99
  381.   fi
  382. fi
  383. if (test -f colm.c) then
  384.   echo Overwriting colm.c...
  385.   else
  386.   echo Creating colm.c...
  387. fi
  388. sed 's/-//;s/%$//' << 'END.colm.c' > colm.c
  389. -/*----------------------------------------------------------------------%
  390. - * colm: auto-columnate, with various options.%
  391. - * by Gary Oberbrunner  5-31-88%
  392. - *%
  393. - * -v        variable-width columns (default: fixed-width)%
  394. - * -s        spread output columns to fit output width (default: don't)%
  395. - * -g #        Separate columns by a #-column gutter%
  396. - * -c #        column width (size) (default: max line length of input)%
  397. - * -n #        use # columns (default: program picks max # that will fit)%
  398. - * -w #        output width is # characters/line (default: 80)%
  399. - * -l #        page length is # lines (default: infinite)%
  400. - * -L c        leader character is 'c' (default: space)%
  401. - * -t s        use string "s" as separator; (default: enough space to line up)%
  402. - * -T #        tab size # chars (default 8)%
  403. - *%
  404. - * Copyright c. 1988, Gary Oberbrunner.%
  405. - * This program may be freely redistributed providing that this source code%
  406. - * and the accompanying copyright notice are preserved intact.%
  407. - *----------------------------------------------------------------------%
  408. - */%
  409. -%
  410. -#include <stdio.h>%
  411. -#include "std.h"%
  412. -#include "colm.h"%
  413. -%
  414. -int debug = 1;%
  415. -static char SCCSId[] = "@(#)colm.c    1.2 6/3/88 (MASSCOMP) 23:17:12";%
  416. -static char Copyright[] = "Copyright c. 1988, Gary Oberbrunner.";%
  417. -%
  418. -OPTS Opts;            /* global copy of original options */%
  419. -main (argc, argv)%
  420. -int argc;%
  421. -char **argv;%
  422. -{%
  423. -    int    c;%
  424. -    extern int optind;%
  425. -    extern char    *optarg;%
  426. -    int errflg = 0;%
  427. -    int totlines = 0;%
  428. -%
  429. -    /* Command-line options: */%
  430. -    OPTS opts;%
  431. -    opts.Vflg = FALSE;        /* variable col width */%
  432. -    opts.Sflg = FALSE;        /* stretch output columns to fit */%
  433. -    opts.Gutter = 1;        /* gutter width */%
  434. -    opts.ColWidth = 0;        /* fixed col width */%
  435. -    opts.Ncols = 0;        /* number of cols */%
  436. -    opts.Width = 79;        /* output width */%
  437. -    opts.Tabsize = 8;        /* tab size */%
  438. -    opts.PageLen = 0;        /* page length */%
  439. -    opts.ColSep[0] = '\0';    /* column separator string */%
  440. -    opts.Leader = '\0';        /* leader char for column separation */%
  441. -%
  442. -    while ((c =    getopt (argc, argv, "vsg:c:n:w:l:L:t:T:")) != EOF) {%
  443. -    switch    (c) {%
  444. -      case 'v':%
  445. -        if (opts.ColWidth != 0) errflg++;%
  446. -        opts.Vflg = TRUE;%
  447. -        break;%
  448. -      case 's':%
  449. -        opts.Sflg = TRUE;%
  450. -        break;%
  451. -      case 'c':%
  452. -        if (opts.Vflg == TRUE) errflg++;%
  453. -        opts.ColWidth = atoi(optarg);%
  454. -        if (opts.ColWidth < 1) errflg++;%
  455. -        break;%
  456. -      case 'g':%
  457. -        opts.Gutter = atoi(optarg);%
  458. -        if (opts.Gutter < 0) errflg++;%
  459. -        break;%
  460. -      case 'n':%
  461. -        opts.Ncols = atoi(optarg);%
  462. -        if (opts.Ncols < 0) errflg++;%
  463. -        break;%
  464. -      case 'w':%
  465. -        opts.Width = atoi(optarg);%
  466. -        if (opts.Width < 1) errflg++;%
  467. -        break;%
  468. -      case 'l':%
  469. -        opts.PageLen = atoi(optarg);%
  470. -        if (opts.PageLen < 1) errflg++;%
  471. -        break;%
  472. -      case 'T':%
  473. -        opts.Tabsize = atoi(optarg);%
  474. -        if (opts.Tabsize < 1) errflg++;%
  475. -        break;%
  476. -      case 'L':%
  477. -        opts.Leader = optarg[0];%
  478. -        break;%
  479. -      case 't':%
  480. -        strcpy(opts.ColSep, optarg);%
  481. -        break;%
  482. -      case '?':%
  483. -        errflg++;%
  484. -    }%
  485. -    }%
  486. -    Opts = opts;        /* copy to global, so we know what was */%
  487. -                /* actually user-specified and what we */%
  488. -                /* computed */%
  489. -    if (errflg)    {%
  490. -    Usage();%
  491. -    exit (2);%
  492. -    }%
  493. -    if (optind >= argc)%
  494. -    totlines = ReadIn("-", &Lines); /* read stdin */%
  495. -    else {%
  496. -    for( ; optind < argc; optind++) {%
  497. -        totlines += ReadIn(argv[optind], &Lines); /* read text files */%
  498. -    }%
  499. -    }%
  500. -    if (totlines > 0)%
  501. -    Columnate(&Lines, &opts, totlines);%
  502. -}%
  503. -%
  504. -Usage()%
  505. -{%
  506. -    fprintf(stderr,"\n\%
  507. -Usage: %s [-cglnstvwLT] <files>\n\%
  508. - Use '-' for stdin.\n\%
  509. - Options:\n\%
  510. - -v    variable-width columns (default: fixed-width)\n\%
  511. - -s    spread columns to fit output width (default: don't spread)\n\%
  512. - -g #    separate columns by a #-character 'gutter'.\n\%
  513. - -c #    column width (size) (default: max line length of input)\n\%
  514. - -n #    use # columns (default: program picks max # that will fit)\n\%
  515. - -w #    output width is # characters/line (default: 80)\n\%
  516. - -l #    page length is # lines (default: infinite)\n\%
  517. - -L c    leader character is 'c' (default: space)\n\%
  518. - -t s    use string \"s\" as separator (default: enough space to line up)\n\%
  519. - -T #    use #-space tabs (default: 8)\n");%
  520. -}%
  521. END.colm.c
  522. if (test -d column.c) then
  523.   echo column.c is an existing directory.  Cannot overwrite it.
  524.   exit 2
  525. fi
  526. if (test -f column.c) then
  527.   echo 'Should I overwrite the existing version of column.c (y/n)?  \c'
  528.   read response
  529.   if (test $response != y) then exit 99
  530.   fi
  531. fi
  532. if (test -f column.c) then
  533.   echo Overwriting column.c...
  534.   else
  535.   echo Creating column.c...
  536. fi
  537. sed 's/-//;s/%$//' << 'END.column.c' > column.c
  538. -/*----------------------------------------------------------------------%
  539. - * column.c - does the actual work of columnating the list of lines%
  540. - * Copyright c. 1988, Gary Oberbrunner.%
  541. - * This program may be freely redistributed providing that this source code%
  542. - * and the accompanying copyright notice are preserved intact.%
  543. - *----------------------------------------------------------------------%
  544. - */%
  545. -%
  546. -#include <stdio.h>%
  547. -#include "std.h"%
  548. -#include "colm.h"%
  549. -%
  550. -extern int debug;%
  551. -%
  552. -static char SCCSId[] = "@(#)column.c    1.1 6/3/88 (MASSCOMP) 22:44:43";%
  553. -%
  554. -/* Returns success or failure. */%
  555. -BOOLEAN%
  556. -Columnate(list, opts, totlines)%
  557. -NODE *list;%
  558. -OPTS *opts;%
  559. -int totlines;            /* total number of text lines to columnate */%
  560. -{%
  561. -    /* First, check to make sure it's possible to columnate the output. */%
  562. -    if (findmax(list, totlines) > opts->Width) {%
  563. -    fprintf(stderr, "\%
  564. -Error: Some input line is longer than the output width of %d chars.\n",%
  565. -        opts->Width);%
  566. -    exit(1);%
  567. -    }%
  568. -    if (!SinglePageCol(list, opts, totlines))%
  569. -    return MultiPageCol(list, opts, totlines);%
  570. -    else return TRUE;%
  571. -}%
  572. -%
  573. -/* SinglePageCol returns TRUE or FALSE, according to whether the text */%
  574. -/* will fit on the page. */%
  575. -static BOOLEAN%
  576. -SinglePageCol(list, opts, totlines)%
  577. -NODE *list;%
  578. -OPTS *opts;%
  579. -int totlines;%
  580. -{%
  581. -    int try_again = TRUE;%
  582. -    while (try_again) {%
  583. -    try_again = FALSE;%
  584. -    %
  585. -    if (opts->ColWidth > 0 && opts->Ncols > 0)%
  586. -        return FixedColumns(list, opts, totlines);%
  587. -    if (opts->Vflg == TRUE)%
  588. -        return VarColumns(list, opts, totlines);%
  589. -    if (opts->Ncols > 0 && opts->Sflg == TRUE  && opts->ColWidth == 0) {%
  590. -        opts->ColWidth = opts->Width / opts->Ncols;%
  591. -        return FixedColumns(list, opts, totlines);%
  592. -    }%
  593. -    /* We can figure out the column width by finding the largest */%
  594. -    /* string.  Once we find that, we process again to pick up any */%
  595. -    /* other options. */%
  596. -    if (opts->ColWidth == 0) {%
  597. -        opts->ColWidth = findmax(list, -1) + opts->Gutter;%
  598. -        try_again = TRUE;%
  599. -    }%
  600. -    /* If we know the column width and the output width, we can */%
  601. -    /* figure out the number of columns. */%
  602. -    if (opts->ColWidth > 0 && opts->Width > 0 && opts->Ncols == 0) {%
  603. -        opts->Ncols = opts->Width / opts->ColWidth;%
  604. -        try_again = TRUE;%
  605. -    }%
  606. -    }%
  607. -}%
  608. -%
  609. -/* Same return value as for SinglePageCol. */%
  610. -/* FixedColumns() requires that ColWidth, NCols and Width be set. */%
  611. -BOOLEAN%
  612. -static FixedColumns(list, opts, totlines)%
  613. -NODE *list;%
  614. -OPTS *opts;%
  615. -int totlines;%
  616. -{%
  617. -    NODE *cols[MAX_COLS];    /* NODE pointers for each column */%
  618. -    register int i,j;%
  619. -    int outlines;        /* total # of output lines */%
  620. -%
  621. -    /* This is the easy case - just put out the lines. */%
  622. -%
  623. -    ASSERT(opts->ColWidth > 0 && opts->Ncols > 0);%
  624. -    /* We only print an error message here if the user actually */%
  625. -    /* specified (via capital-O Opts) a number of columns and width. */%
  626. -    if (Opts.Ncols > 0 && Opts.ColWidth > 0 &&%
  627. -    opts->ColWidth * opts->Ncols > opts->Width) {%
  628. -    fprintf(stderr,%
  629. -        "%d columns of %d characters won't fit in output width",%
  630. -        opts->Ncols, opts->ColWidth);%
  631. -    fprintf(stderr," of %d chars.\n", opts->Width);%
  632. -    exit(3);%
  633. -    }%
  634. -    /* It's not always possible to form up proper columns from a given */%
  635. -    /* number of input lines; 'proper' here means all columns even */%
  636. -    /* except the last, which can be as short as one element.  For */%
  637. -    /* instance, five text lines can't be formed into four columns. */%
  638. -    /* But they can be formed into three columns of two each, except */%
  639. -    /* the last column which will contain only one line. */%
  640. -    /* Here we always opt for using fewer columns instead. */%
  641. -    /* We spread the extra space (if any) out over all the columns. */%
  642. -%
  643. -    outlines = (totlines + opts->Ncols - 1) / opts->Ncols;%
  644. -%
  645. -    if ((totlines + outlines-1) / outlines != opts->Ncols) {%
  646. -    int extraspace = opts->ColWidth%
  647. -        * (opts->Ncols - (totlines + outlines-1) / outlines);%
  648. -    /* Here again, we only print the error message if the user */%
  649. -    /* explicitly requested (via Opts) a number of columns.  If we */%
  650. -    /* figured it out, we don't need to tell her that we guessed */%
  651. -    /* wrong. */%
  652. -    if (Opts.Ncols > 0) {%
  653. -        fprintf(stderr,"Can't form %d text lines into %d proper columns; ",%
  654. -            totlines, opts->Ncols);%
  655. -        fprintf(stderr,"using %d columns instead.\n",%
  656. -            (totlines + outlines-1) / outlines);%
  657. -    }%
  658. -    opts->Ncols = (totlines + outlines-1) / outlines;%
  659. -    /* Only spread columns if cwidth wasn't explicitly specified. */%
  660. -    if (Opts.ColWidth == 0 && opts->Sflg == TRUE)%
  661. -        opts->ColWidth += extraspace / opts->Ncols;%
  662. -    return FixedColumns(list, opts, totlines);%
  663. -    }%
  664. -    if (opts->PageLen > 0 && outlines > opts->PageLen) return FALSE;%
  665. -    for (i = 0; i < opts->Ncols; i++)%
  666. -    cols[i] = findn(list, i * outlines);%
  667. -%
  668. -    for (j = 0; j < outlines; j++) {%
  669. -    for (i = 0; i < opts->Ncols; i++) {%
  670. -        if (cols[i] != list) {%
  671. -        Output(cols[i]->string, opts->ColWidth);%
  672. -        if (i != opts->Ncols - 1 && cols[i+1] != list)%
  673. -            ToCol(opts->ColWidth * (i+1), opts);%
  674. -        cols[i] = cols[i]->next;%
  675. -        }%
  676. -    }%
  677. -    OutNL();%
  678. -    }%
  679. -}%
  680. -%
  681. -BOOLEAN%
  682. -static MultiPageCol(list, opts, totlines)%
  683. -NODE *list;%
  684. -OPTS *opts;%
  685. -int totlines;%
  686. -{%
  687. -    fprintf(stderr,"Can't do multi-page columnation (yet).\n");%
  688. -    return FALSE;%
  689. -}%
  690. END.column.c
  691. if (test -d lists.c) then
  692.   echo lists.c is an existing directory.  Cannot overwrite it.
  693.   exit 2
  694. fi
  695. if (test -f lists.c) then
  696.   echo 'Should I overwrite the existing version of lists.c (y/n)?  \c'
  697.   read response
  698.   if (test $response != y) then exit 99
  699.   fi
  700. fi
  701. if (test -f lists.c) then
  702.   echo Overwriting lists.c...
  703.   else
  704.   echo Creating lists.c...
  705. fi
  706. sed 's/-//;s/%$//' << 'END.lists.c' > lists.c
  707. -/*----------------------------------------------------------------------%
  708. - * These functions append to and search a doubly-linked, non-circular%
  709. - * list of text lines.%
  710. - * Copyright c. 1988, Gary Oberbrunner.%
  711. - * This program may be freely redistributed providing that this source code%
  712. - * and the accompanying copyright notice are preserved intact.%
  713. - *----------------------------------------------------------------------%
  714. - */%
  715. -%
  716. -#include <stdio.h>%
  717. -#include "std.h"%
  718. -#include "colm.h"%
  719. -%
  720. -extern int debug;%
  721. -%
  722. -static char SCCSId[] = "@(#)lists.c    1.1 6/3/88 (MASSCOMP) 22:44:45";%
  723. -%
  724. -/* Declare the empty line list: */%
  725. -/* Note that this header node is NOT part of the list. */%
  726. -NODE Lines = { &Lines, &Lines, 0, NULL };%
  727. -%
  728. -/* Append a new string to the list: */%
  729. -append(str, list)%
  730. -char *str;%
  731. -NODE *list;%
  732. -{%
  733. -    register NODE *new;%
  734. -%
  735. -    ASSERT(str != NULL);%
  736. -    ASSERT(list != NULL);%
  737. -%
  738. -    NEW(new, NODE, 1);        /* get a node */%
  739. -    NEW(new->string, char, strlen(str) + 1); /* get string storage */%
  740. -%
  741. -    strcpy(new->string, str);    /* copy the string into the storage */%
  742. -    new->length = strlen(str);    /* store the length, for speed */%
  743. -%
  744. -    new->next = list;        /* link it onto the end of the list. */%
  745. -    new->prev = list->prev;%
  746. -    new->prev->next = new;%
  747. -    list->prev = new;%
  748. -}%
  749. -%
  750. -/* Linearly search the next 'n' items of the list for longest string */%
  751. -/* If n is negative, the search goes forever. */%
  752. -int%
  753. -findmax(list, n)%
  754. -NODE *list;%
  755. -int n;%
  756. -{%
  757. -    register NODE *current = list->next;%
  758. -    NODE *longest = current;%
  759. -%
  760. -    ASSERT(list != NULL);%
  761. -    ASSERT(list->next != NULL);%
  762. -    ASSERT(list->prev != NULL);%
  763. -%
  764. -    for ( ; current != list && n != 0; current = current->next) {%
  765. -        if (current->length > longest->length)%
  766. -        longest = current;%
  767. -    if (n > 0) n--;%
  768. -    }%
  769. -    ASSERT(longest != NULL);%
  770. -    return longest->length;%
  771. -}%
  772. -%
  773. -/* Add up the lengths of all the strings in the list. */%
  774. -/* If n > 0, look only at the next 'n' items. */%
  775. -/* If n < 0, look through to the end. */%
  776. -int%
  777. -total_chars(list, n)%
  778. -NODE *list;%
  779. -int n;%
  780. -{%
  781. -    register NODE *current;%
  782. -    int total = 0;%
  783. -%
  784. -    ASSERT(list != NULL);%
  785. -    ASSERT(list->next != NULL);%
  786. -    ASSERT(list->prev != NULL);%
  787. -%
  788. -    for (current = list->next; current != list && n != 0;%
  789. -     current = current->next) {%
  790. -    total += current->length;%
  791. -    if (n > 0) n--;%
  792. -    }%
  793. -    return total;%
  794. -}%
  795. -%
  796. -NODE *%
  797. -findn(list, n)%
  798. -NODE *list;%
  799. -int n;%
  800. -{%
  801. -    register NODE *current = list->next;%
  802. -%
  803. -    ASSERT(list != NULL);%
  804. -    ASSERT(list->next != NULL);%
  805. -    ASSERT(list->prev != NULL);%
  806. -%
  807. -    for ( ; current != list && n != 0; current = current->next, n--)%
  808. -    ;%
  809. -    ASSERT(current != list);%
  810. -    return current;%
  811. -}%
  812. END.lists.c
  813. if (test -d output.c) then
  814.   echo output.c is an existing directory.  Cannot overwrite it.
  815.   exit 2
  816. fi
  817. if (test -f output.c) then
  818.   echo 'Should I overwrite the existing version of output.c (y/n)?  \c'
  819.   read response
  820.   if (test $response != y) then exit 99
  821.   fi
  822. fi
  823. if (test -f output.c) then
  824.   echo Overwriting output.c...
  825.   else
  826.   echo Creating output.c...
  827. fi
  828. sed 's/-//;s/%$//' << 'END.output.c' > output.c
  829. -/*----------------------------------------------------------------------%
  830. - * output - output routines for auto-columnator%
  831. - * Copyright c. 1988, Gary Oberbrunner.%
  832. - * This program may be freely redistributed providing that this source code%
  833. - * and the accompanying copyright notice are preserved intact.%
  834. - *----------------------------------------------------------------------%
  835. - */%
  836. -%
  837. -#include "stdio.h"%
  838. -#include "std.h"%
  839. -#include "colm.h"%
  840. -%
  841. -static char SCCSId[] = "@(#)output.c    1.1 6/3/88 (MASSCOMP) 22:44:46";%
  842. -%
  843. -static OutCol = 0;        /* current output column */%
  844. -%
  845. -Output(s, maxlen)%
  846. -char *s;%
  847. -int maxlen;%
  848. -{%
  849. -    OutCol += printf("%.*s", maxlen, s);%
  850. -}%
  851. -%
  852. -/* This ignores the opts right now. */%
  853. -ToCol(tocol, opts)%
  854. -int tocol;            /* column to move to (org 0) */%
  855. -OPTS *opts;            /* how to move there */%
  856. -{%
  857. -    if (opts->ColSep[0] != '\0')%
  858. -    printf("%s", opts->ColSep);%
  859. -    if (opts->Leader == '\0') {    /* no leader, use spaces & tabs */%
  860. -    while (OutCol < (tocol - tocol % opts->Tabsize)) {%
  861. -        putchar('\t');%
  862. -        OutCol += opts->Tabsize;%
  863. -        OutCol -= OutCol % opts->Tabsize;%
  864. -    }%
  865. -    }%
  866. -    while (OutCol < tocol) {%
  867. -    putchar(opts->Leader == '\0' ? ' ' : opts->Leader);%
  868. -    OutCol++;%
  869. -    }%
  870. -}%
  871. -%
  872. -OutNL()%
  873. -{%
  874. -    putchar('\n');%
  875. -    OutCol = 0;%
  876. -}%
  877. END.output.c
  878. if (test -d readin.c) then
  879.   echo readin.c is an existing directory.  Cannot overwrite it.
  880.   exit 2
  881. fi
  882. if (test -f readin.c) then
  883.   echo 'Should I overwrite the existing version of readin.c (y/n)?  \c'
  884.   read response
  885.   if (test $response != y) then exit 99
  886.   fi
  887. fi
  888. if (test -f readin.c) then
  889.   echo Overwriting readin.c...
  890.   else
  891.   echo Creating readin.c...
  892. fi
  893. sed 's/-//;s/%$//' << 'END.readin.c' > readin.c
  894. -/*----------------------------------------------------------------------%
  895. - * ReadIn - read a file into a list of lines.%
  896. - * Copyright c. 1988, Gary Oberbrunner.%
  897. - * This program may be freely redistributed providing that this source code%
  898. - * and the accompanying copyright notice are preserved intact.%
  899. - *----------------------------------------------------------------------%
  900. - */%
  901. -%
  902. -#include <stdio.h>%
  903. -#include "std.h"%
  904. -#include "colm.h"%
  905. -%
  906. -#define MAX_LEN 20000        /* maximum string length */%
  907. -%
  908. -extern int debug;%
  909. -%
  910. -static char SCCSId[] = "@(#)readin.c    1.1 6/3/88 (MASSCOMP) 22:44:47";%
  911. -%
  912. -ReadIn(fname, list)%
  913. -char *fname;%
  914. -NODE *list;%
  915. -{%
  916. -    char Instr[MAX_LEN];%
  917. -    FILE *infile = NULL;%
  918. -    int TotalLines = 0;        /* total # of lines read */%
  919. -%
  920. -    if (!strcmp(fname, "-"))%
  921. -    infile = stdin;%
  922. -    else if ((infile = fopen(fname, "r")) == NULL) {%
  923. -    fprintf(stderr,"Can't open file %s\n.", fname);%
  924. -    exit(9);%
  925. -    }%
  926. -    while (fgets(Instr, MAX_LEN, infile)) {%
  927. -    if (*Instr == '\0') {%
  928. -        fprintf(stderr,%
  929. -            "Error: Null found in supposedly line-oriented data.\n");%
  930. -        exit(2);%
  931. -    }%
  932. -    TotalLines++;%
  933. -    Instr[strlen(Instr)-1] = '\0'; /* strip newline */%
  934. -    append(Instr, list); /* add it to the list */%
  935. -    ASSERT(list->prev != NULL);%
  936. -    }%
  937. -    return TotalLines;%
  938. -}%
  939. END.readin.c
  940. if (test -d vcolumn.c) then
  941.   echo vcolumn.c is an existing directory.  Cannot overwrite it.
  942.   exit 2
  943. fi
  944. if (test -f vcolumn.c) then
  945.   echo 'Should I overwrite the existing version of vcolumn.c (y/n)?  \c'
  946.   read response
  947.   if (test $response != y) then exit 99
  948.   fi
  949. fi
  950. if (test -f vcolumn.c) then
  951.   echo Overwriting vcolumn.c...
  952.   else
  953.   echo Creating vcolumn.c...
  954. fi
  955. sed 's/-//;s/%$//' << 'END.vcolumn.c' > vcolumn.c
  956. -/*----------------------------------------------------------------------%
  957. - * vcolumn.c - columnates with variable-size columns%
  958. - * Copyright c. 1988, Gary Oberbrunner.%
  959. - * This program may be freely redistributed providing that this source code%
  960. - * and the accompanying copyright notice are preserved intact.%
  961. - *----------------------------------------------------------------------%
  962. - */%
  963. -%
  964. -#include <stdio.h>%
  965. -#include "std.h"%
  966. -#include "colm.h"%
  967. -%
  968. -extern int debug;%
  969. -void ClearWidths();%
  970. -%
  971. -static char SCCSId[] = "@(#)vcolumn.c    1.2 6/3/88 (MASSCOMP) 23:11:49";%
  972. -%
  973. -BOOLEAN%
  974. -    VarColumns(list, opts, totlines)%
  975. -NODE *list;%
  976. -OPTS *opts;%
  977. -int totlines;%
  978. -{%
  979. -    ASSERT(Opts.ColWidth == 0);%
  980. -    /* Only auto-columnate if she didn't say how many columns, or if */%
  981. -    /* the requested number of columns fails. */%
  982. -    if (Opts.Ncols == 0 || ManVarColumns(list, opts, totlines) == FALSE)%
  983. -    AutoVarColumns(list, opts, totlines);%
  984. -}%
  985. -%
  986. -BOOLEAN%
  987. -    ManVarColumns(list, opts, totlines)%
  988. -NODE *list;%
  989. -OPTS *opts;%
  990. -int totlines;%
  991. -{%
  992. -    int outlines;%
  993. -    int widths[MAX_COLS];    /* column-width table */%
  994. -    register NODE *current;%
  995. -    register int line, col;%
  996. -    int twidth = 0;%
  997. -    %
  998. -    ASSERT(opts->Ncols > 0);%
  999. -    outlines = (totlines + opts->Ncols - 1) / opts->Ncols;%
  1000. -    %
  1001. -    ClearWidths(widths, MAX_COLS);%
  1002. -    for (current = list->next, col = 0, line=0;%
  1003. -     current != list && totlines != 0;%
  1004. -     current = current->next, totlines--) {%
  1005. -    if (current->length > widths[col])%
  1006. -        widths[col] = current->length;%
  1007. -    /* Do next line, and if at bottom start next column */%
  1008. -    if (++line >= outlines) {%
  1009. -        col++;%
  1010. -        line = 0;%
  1011. -    }%
  1012. -    }%
  1013. -    for (col = 0; col < opts->Ncols; col++)%
  1014. -    if (col != opts->Ncols -1)%
  1015. -        widths[col] += opts->Gutter;%
  1016. -    twidth += widths[col];%
  1017. -    if (twidth > opts->Width) {%
  1018. -    fprintf(stderr,"\%
  1019. -%d text columns won't fit into output width of %d chars; %d chars too wide.\n",%
  1020. -        opts->Ncols, opts->Width, twidth - opts->Width);%
  1021. -    return FALSE;%
  1022. -    }%
  1023. -    OutVCols(list, opts, totlines, outlines, widths);%
  1024. -    return TRUE;%
  1025. -}%
  1026. -%
  1027. -BOOLEAN%
  1028. -    AutoVarColumns(list, opts, totlines)%
  1029. -NODE *list;%
  1030. -OPTS *opts;%
  1031. -int totlines;%
  1032. -{%
  1033. -    int widths[MAX_COLS];    /* column-width table */%
  1034. -    register int outlines;%
  1035. -    int avglen;%
  1036. -    %
  1037. -    /* We start with the smallest possible number of output lines and */%
  1038. -    /* successively increase by ones until it works. */%
  1039. -    %
  1040. -    /* First, compute the smallest possible outlines: */%
  1041. -    outlines = max(1, total_chars(list, totlines) / opts->Width);%
  1042. -    %
  1043. -    while (1) {%
  1044. -    ASSERT(outlines <= totlines); /* runaway check */%
  1045. -    ClearWidths(widths, MAX_COLS);%
  1046. -    if (VariCol(list, opts, totlines, outlines, widths) == TRUE)%
  1047. -        break;%
  1048. -    outlines++;%
  1049. -    }%
  1050. -    /* Now opts->Ncols is correct, and VariCol has filled in widths[]. */%
  1051. -    OutVCols(list, opts, totlines, outlines, widths);%
  1052. -    return TRUE;%
  1053. -}%
  1054. -static void%
  1055. -    ClearWidths(w, nc)%
  1056. -int w[];%
  1057. -int nc;%
  1058. -{%
  1059. -    ASSERT (nc > 0);%
  1060. -    while (nc--)%
  1061. -    w[nc] = 0;%
  1062. -}%
  1063. -%
  1064. -static BOOLEAN%
  1065. -    VariCol(list, opts, textlines, outlines, widths)%
  1066. -NODE *list;%
  1067. -OPTS *opts;%
  1068. -int textlines;%
  1069. -int outlines;%
  1070. -int widths[];%
  1071. -{%
  1072. -    register int col;        /* current column */%
  1073. -    register int line;        /* current out line */%
  1074. -    register int twidth = 0;%
  1075. -    register NODE *current;%
  1076. -    %
  1077. -    for (current = list->next, col = 0, line=0;%
  1078. -     current != list && textlines != 0;%
  1079. -     current = current->next, textlines--) {%
  1080. -    if (current->length > widths[col])%
  1081. -        widths[col] = current->length;%
  1082. -    /* Do next line, and if at bottom start next column */%
  1083. -    if (++line >= outlines) {%
  1084. -        col++;%
  1085. -        line = 0;%
  1086. -    }%
  1087. -    }%
  1088. -    if (line != 0) col++;%
  1089. -    opts->Ncols = col;%
  1090. -    for (col = 0; col < opts->Ncols; col++) {%
  1091. -    if (col != opts->Ncols -1)%
  1092. -        widths[col] += opts->Gutter;%
  1093. -    twidth += widths[col];%
  1094. -    }%
  1095. -    if (twidth <= opts->Width) { /* we made it */%
  1096. -    /* Partition the extra space as evenly as possible */%
  1097. -    int extra_chars = opts->Width - twidth;%
  1098. -    while (extra_chars > 0 && opts->Ncols > 1 && opts->Sflg == TRUE)%
  1099. -        for (col = 0; col < opts->Ncols-1 && extra_chars > 0; col++) {%
  1100. -        widths[col]++;%
  1101. -        extra_chars--;%
  1102. -        }%
  1103. -    return (TRUE);%
  1104. -    }%
  1105. -    return FALSE;%
  1106. -}%
  1107. -%
  1108. -static OutVCols(list, opts, textlines, outlines, widths)%
  1109. -NODE *list;%
  1110. -OPTS *opts;%
  1111. -int textlines;%
  1112. -int outlines;%
  1113. -int widths[];%
  1114. -{%
  1115. -    register int col, line;%
  1116. -    int ccol;            /* current character column position */%
  1117. -    NODE *cols[MAX_COLS];%
  1118. -    %
  1119. -    /* Get pointers to text lines, one for each column */%
  1120. -    for (col = 0; col < opts->Ncols; col++) {%
  1121. -    cols[col] = findn(list, outlines * col);%
  1122. -    }%
  1123. -    for (line = 0; line < outlines; line++) {%
  1124. -    ccol = 0;%
  1125. -    for (col = 0; col < opts->Ncols; col++) {%
  1126. -        if (cols[col] != list) {%
  1127. -        Output(cols[col]->string, widths[col]);%
  1128. -        if (col != opts->Ncols - 1 && cols[col+1] != list)%
  1129. -            ToCol(ccol += widths[col], opts);%
  1130. -        cols[col] = cols[col]->next;%
  1131. -        }%
  1132. -    }%
  1133. -    OutNL();%
  1134. -    }%
  1135. -}%
  1136. END.vcolumn.c
  1137. exit 0
  1138. #-------------------------------- end of archive; cut here -------------------
  1139. -- 
  1140. Remember,            Truth is not beauty;      (617)692-6200x2445
  1141. Information is not knowledge;    Beauty is not love;      Gary   Oberbrunner
  1142. Knowledge is not wisdom;    Love is not music;      ...!masscomp!garyo
  1143. Wisdom is not truth;        Music is the best. - FZ   ....garyo@masscomp
  1144.  
  1145.