home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume3 / hc < prev    next >
Internet Message Format  |  1989-02-03  |  18KB

  1. Path: xanth!mcnc!gatech!bloom-beacon!mit-eddie!ll-xn!ames!necntc!ncoast!allbery
  2. From: srcs@frito.UUCP
  3. Newsgroups: comp.sources.misc
  4. Subject: v03i053: hc -- side-by-side file catenator
  5. Message-ID: <8806060300.AA05293@ccicpg>
  6. Date: 4 Jun 88 23:25:32 GMT
  7. Sender: allbery@ncoast.UUCP
  8. Reply-To: srcs@frito.UUCP
  9. Lines: 540
  10. Approved: allbery@ncoast.UUCP
  11.  
  12. comp.sources.misc: Volume 3, Issue 53
  13. Submitted-By: "A. Nonymous" <srcs@frito.UUCP>
  14. Archive-Name: hc
  15.  
  16. ["pr -m -t" with better control over the columns.  ++bsa]
  17.  
  18. Here's a little utility that I wrote, based on the Knowlogy documentation,
  19. originally available only under  CP/M.   The code is original, the  design
  20. is  acknowledged in the header comments.  I have packed the documentation,
  21. test files  and source  into the source file.  No makefile is needed, just
  22.     cc -o hc hc.c        and run the tests.
  23.  
  24. Questions/bugs/flames to Bill Cox, (714)631-4452 (voice)
  25.             or uunet!ccicpg!frito!bill
  26.  
  27. Please let me know when you receive this, and if you have any comments or
  28. suggestions before posting it.  THANKS for all the work that you do to
  29. make stuff like this available to people.
  30.  
  31. thanks,
  32. Bill
  33.  
  34. [Cheap shar from inside the editor, why can't/don't people read?!  ++bsa]
  35. sed 's/^X//' << \EOF > hc.c
  36. X/*
  37. X * SYNOPSIS
  38. X *    hc [-s] [+tab | -col | -l str | filename] ...
  39. X *
  40. X * DESCRIPTION
  41. X *
  42. X *    hc  concatenates files horizontally.   It concatenates correspon-
  43. X *    ding lines from files.   The effects of hc are best visualized by 
  44. X *    imagining  the files to be printed on long strips of  paper  with 
  45. X *    Velcro  strips  on the left and right margins;   hc sticks  these 
  46. X *    files together to make one wide file on the standard output.
  47. X *
  48. X *    Besides file names,  the command line can contain tab  positions, 
  49. X *    column numbers, and literal strings.  A maximum of thirteen files 
  50. X *    can be specified.
  51. X *
  52. X *    a  TAB  POSITION  is denoted by a positive  decimal  number.   It 
  53. X *    specifies  that  the next item should be placed at  a  particular 
  54. X *    column.   The  first column number is one.   For example,  a  tab 
  55. X *    position of five would cause the next item to be placed at column 
  56. X *    5 in the output.   If the last argument is a tab  position,  then 
  57. X *    each  line of output will contain enough trailing spaces to bring 
  58. X *    it to that column.
  59. X *
  60. X *    a START POSITION is denoted by a negative decimal.  It  specifies 
  61. X *    the  left margin of the next file to be concatenated.   The first 
  62. X *    column is number one, and normally the left margin number is one.  
  63. X *    For  a start column number greater than one,  the  first  (col-1) 
  64. X *    characters of each line of the next file are discarded.
  65. X *
  66. X *    a LITERAL STRING is preceded by a '-l' flag. It may be surrounded
  67. X *    by double quotes (under DOS, at least).  It specifies  characters
  68. X *    to  be  inserted in every output line at its relative position in
  69. X *    the argument list.   In an extension to  the  Knowlogy  HC,  this
  70. X *    version interprets the standard C backslashed escape codes.   The
  71. X *    following  command  interleaves lines from the two files, separa-
  72. X *    ting them by a '\n' or newline:
  73. X *            hc file1 -l "\n" file2
  74. X *
  75. X *    Of necessity, all tabs are expanded to spaces upon input, so that 
  76. X *    columns can be determined.  On output, spaces are compressed back 
  77. X *    to  tabs,  and,  unless a TAB POSITION argument follows the  last 
  78. X *    file name, trailing spaces are removed from each line.
  79. X *
  80. X *    All  files but the longest are conceptually padded at the  bottom 
  81. X *    with  zero-length  lines to bring all files to bring to the  same 
  82. X *    length.   That is, concatenation continues until the end of every 
  83. X *    file is encountered.   However,  if there are one or more literal 
  84. X *    strings in the argument list, at least one line of output will be 
  85. X *    written even if all files are empty or no files are specified.
  86. X *
  87. X *    The -s flag prevents the output from including any tabs; that is, 
  88. X *    the normal tab compression of the output line is suppressed.
  89. X *
  90. X * EXAMPLES
  91. X *
  92. X *    Assume the files A, B, and C, whose contents are as follows:
  93. X *
  94. X *    00000000011111111112
  95. X *    12345678901234567890
  96. X *    -- this is file A --
  97. X *    Little Miss Muffet
  98. X *    Sat on her tuffet,
  99. X *    Eating her curds
  100. X *      and whey.
  101. X *    Along came a spider
  102. X *    Who sat down
  103. X *      beside her,
  104. X *    And frightened
  105. X *      Miss Muffet away.
  106. X *
  107. X *    000000000111111111122222222223
  108. X *    123456789012345678901234567890
  109. X *         -- this is file B --
  110. X *    Four score and seven years
  111. X *    ago, our fathers brought forth
  112. X *    upon this continent a new
  113. X *    nation, conceived in liberty,
  114. X *    and dedicated to the
  115. X *    proposition that all men are
  116. X *    created equal.
  117. X *
  118. X *    0000000001
  119. X *    1234567890
  120. X *    - file C -
  121. X *    gimme a U
  122. X *    gimme an N
  123. X *    gimme an I
  124. X *    gimme a C
  125. X *    gimme an A
  126. X *    whazzat
  127. X *      spell?
  128. X *    whazzat
  129. X *      spell?
  130. X *    arright!
  131. X *    
  132. X * Example: hc A B C        simply concatenate the files, making
  133. X *                no attempt to justify any margins.
  134. X *
  135. X *    000000000111111111120000000001111111111222222222230000000001
  136. X *    123456789012345678901234567890123456789012345678901234567890
  137. X *    -- this is file A --     -- this is file B --- file C -
  138. X *    Little Miss MuffetFour score and seven yearsgimme a U
  139. X *    Sat on her tuffet,ago, our fathers brought forthgimme an N
  140. X *    Eating her curdsupon this continent a newgimme an I
  141. X *      and whey.nation, conceived in liberty,gimme a C
  142. X *    Along came a spiderand dedicated to thegimme an A
  143. X *    Who sat downproposition that all men arewhazzat
  144. X *      beside her,created equal.  spell?
  145. X *    And frightenedwhazzat
  146. X *      Miss Muffet away.  spell?
  147. X *    arright!
  148. X *    
  149. X * Example: hc A +21 B +51 C    This concatenates the three files,
  150. X *                with file A starting at column 1,
  151. X *                file B starting at column 21, and
  152. X *                file C starting at column 51.
  153. X *
  154. X *    000000000111111111120000000001111111111222222222230000000001
  155. X *    123456789012345678901234567890123456789012345678901234567890
  156. X *    -- this is file A --     -- this is file B --     - file C -
  157. X *    Little Miss Muffet  Four score and seven years    gimme a U
  158. X *    Sat on her tuffet,  ago, our fathers brought forthgimme an N
  159. X *    Eating her curds    upon this continent a new     gimme an I
  160. X *      and whey.         nation, conceived in liberty, gimme a C
  161. X *    Along came a spider and dedicated to the          gimme an A
  162. X *    Who sat down        proposition that all men are  whazzat
  163. X *      beside her,       created equal.                  spell?
  164. X *    And frightened                                    whazzat
  165. X *      Miss Muffet away.                                 spell?
  166. X *                                                      arright!
  167. X *
  168. X * Example: hc B +40 A        Concatenate files B, starting at column 1,
  169. X *                and A, starting at column 40.  The two files
  170. X *                are displayed side by side for comparison
  171. X *                viewing.
  172. X *
  173. X *    000000000111111111122222222223         00000000011111111112
  174. X *    123456789012345678901234567890         12345678901234567890
  175. X *         -- this is file B --              -- this is file A --
  176. X *    Four score and seven years             Little Miss Muffet
  177. X *    ago, our fathers brought forth         Sat on her tuffet,
  178. X *    upon this continent a new              Eating her curds
  179. X *    nation, conceived in liberty,            and whey.
  180. X *    and dedicated to the                   Along came a spider
  181. X *    proposition that all men are           Who sat down
  182. X *    created equal.                           beside her,
  183. X *                                           And frightened
  184. X *                                             Miss Muffet away.
  185. X *
  186. X *
  187. X * Example: hc -5 B +17        Chop the first 4 and last 5 columns off
  188. X *                of file B.
  189. X *    0000011111111112
  190. X *    5678901234567890
  191. X *     -- this is file
  192. X *     score and seven
  193. X *     our fathers bro
  194. X *     this continent 
  195. X *    on, conceived in
  196. X *    dedicated to the
  197. X *    osition that all
  198. X *    ted equal.      
  199. X *
  200. X *
  201. X * Example: hc -s -5 A +10 +15 -5 C +50    Concatenate file A (starting with
  202. X *                    its fifth column), and cut it off
  203. X *    000001111     000001            when it reaches column 9 (just be-
  204. X *    567890123     567890            fore column 10);  then add file B
  205. X *    his is fi     le C -            (startint with its fifth column)
  206. X *    le Miss M     e a U             beginning at column 15;  then pad
  207. X *    on her tu     e an N            out with spaces through column 49
  208. X *    ng her cu     e an I            (just before column 50).  Don't
  209. X *    d whey.       e a C             compress spaces to tabs.
  210. X *    g came a      e an A                             
  211. X *    sat down      zat                                
  212. X *    side her,     ell?                               
  213. X *    frightene     zat                                
  214. X *    ss Muffet     ell?                               
  215. X *                  ght!                               
  216. X *                                                     
  217. X *
  218. X * Example: hc A "-----" B    Concatenate files A and B, with
  219. X *                five dashes between them.
  220. X *
  221. X *    00000000011111111112-----000000000111111111122222222223
  222. X *    12345678901234567890-----123456789012345678901234567890
  223. X *    -- this is file A -------     -- this is file B --
  224. X *    Little Miss Muffet-----Four score and seven years
  225. X *    Sat on her tuffet,-----ago, our fathers brought forth
  226. X *    Eating her curds-----upon this continent a new
  227. X *      and whey.-----nation, conceived in liberty,
  228. X *    Along came a spider-----and dedicated to the
  229. X *    Who sat down-----proposition that all men are
  230. X *      beside her,-----created equal.
  231. X *    And frightened-----
  232. X *      Miss Muffet away.-----
  233. X *
  234. X *
  235. X * Example: hc "| " A +24 "|| " C +38 "|"    
  236. X *                    Concatenate files A and C,
  237. X *                    with vertical walls sur-
  238. X *                    rounding them.
  239. X *
  240. X *    | 00000000011111111112 || 0000000001 |
  241. X *    | 12345678901234567890 || 1234567890 |
  242. X *    | -- this is file A -- || - file C - |
  243. X *    | Little Miss Muffet   || gimme a U  |
  244. X *    | Sat on her tuffet,   || gimme an N |
  245. X *    | Eating her curds     || gimme an I |
  246. X *    |   and whey.          || gimme a C  |
  247. X *    | Along came a spider  || gimme an A |
  248. X *    | Who sat down         || whazzat    |
  249. X *    |   beside her,        ||   spell?   |
  250. X *    | And frightened       || whazzat    |
  251. X *    |   Miss Muffet away.  ||   spell?   |
  252. X *    |                      || arright!   |
  253. X *
  254. X *
  255. X * Example: ls ? | hc -s "sp " - " | pr" > tmp
  256. X *
  257. X *    sp A  | pr            This example builds a shell
  258. X *    sp B  | pr            script to be run.  The script
  259. X *    sp C  | pr            will run the spelling checker
  260. X *    sp D  | pr            on each file and print its
  261. X *                    output.
  262. X *
  263. X * Example: hc -s input > tmp        Expand tabs to spaces while
  264. X *                    copying input to tmp.
  265. X *
  266. X * Example: ... | hc - > tmp        Compact spaces to tabs while
  267. X *                    copying stdin to tmp.
  268. X *
  269. X * From the Unica hc utility published by Knowlogy, Inc. in 1982, for
  270. X * use under CP/M.  As far as I know, Knowlogy no longer exists.  The
  271. X * above documentation is quoted nearly verbatim from the Unica manual.
  272. X * My thanks to Knowlogy's people for a most useful design.
  273. X *
  274. X * Comments and bugs to:
  275. X * Bill Cox    (714)631-4452 (voice)
  276. X */
  277. X
  278. X#include <stdio.h>
  279. X#include <ctype.h>
  280. X#include <string.h>
  281. X/* #include <stdlib.h>            /* for atoi */
  282. X
  283. X#define TRUE    1
  284. X#define FALSE    0 
  285. X
  286. X#define LINELEN 256
  287. X#define LITLEN  80
  288. X#define NARGS    13
  289. X#define TABINT    8            /* tab interval, in columns */
  290. X
  291. X#define FIL 0                /* remember what type arg */
  292. X#define LIT 1                /* stored in parmtype */
  293. X#define TAB 2
  294. X#define STR 3
  295. X
  296. Xint ch,
  297. X    sflag = 0,                /* TRUE when "-s" flag specified */
  298. X    filesact = 0,            /* count of active files */
  299. X    filecnt = 0,            /* count of specified file names */
  300. X    actarg = 0,                /* actual argument-table index */
  301. X    saweof[NARGS],            /* TRUE when this file reaches EOF */
  302. X    parmtype[NARGS],            /* type of current argument */
  303. X    tabposn[NARGS],            /* TAB POSITION arguments here */
  304. X    startcol[NARGS];            /* START COLUMN arguments here */
  305. Xchar literals[NARGS][LITLEN];        /* LITERAL arguments here */
  306. XFILE *infiles[NARGS];            /* FILE argument handles here */
  307. X
  308. Xextern void exit();
  309. Xvoid usage();                /* forward declaration */
  310. X
  311. Xmain(argc, argv)
  312. Xint argc;
  313. Xchar *argv[];
  314. X    {
  315. X    int argno, i, j, k;
  316. X
  317. X    if (argc == 1)                /* informational prompt */
  318. X    usage();
  319. X/*
  320. X * collect input line arguments, initialize
  321. X */
  322. X    tabposn[0] = startcol[0] = 0;
  323. X    for (argno = 1; argno < argc; argno++) {
  324. X    saweof[argno] = TRUE;
  325. X    startcol[argno] = 0;
  326. X    tabposn[argno] = 0;
  327. X    switch (argv[argno][0]) {
  328. X    /*
  329. X     * tab position - the next literal or input line will 
  330. X     * be placed at this column in the output line.
  331. X     */
  332. X        case '+':            
  333. X        tabposn[actarg] = atoi(&argv[argno][1])-1;
  334. X        parmtype[actarg++] = TAB;
  335. X        break;            
  336. X
  337. X    case '-':
  338. X        switch (argv[argno][1]) {
  339. X        case 's': case 'S':
  340. X        sflag = TRUE;
  341. X        break;
  342. X
  343. X        case 'l': case 'L':
  344. X        argno++;            /* NEXT arg is literal */
  345. X        if (argno >= argc) {
  346. X            (void)fprintf(stderr, "hc: missing literal argument\n");
  347. X            exit(4);
  348. X            }
  349. X        if (strlen(argv[argno]) > LITLEN-1) {
  350. X            (void)fprintf(stderr, "hc: literal %d too long\n", argno-1);
  351. X            exit(3);
  352. X            }
  353. X        /*
  354. X         * copy string literal, interpreting backslash notation
  355. X         */
  356. X        for (i = j = 0; (ch = argv[argno][j++]) != '\0';)
  357. X            if (ch != '\\')
  358. X                literals[actarg][i++] = ch;
  359. X            else
  360. X            switch (argv[argno][j++]) {
  361. X            case 'n':            /* newline */
  362. X                literals[actarg][i++] = '\n'; break;
  363. X            case 't':            /* horizontal tab */
  364. X                literals[actarg][i++] = '\t'; break;
  365. X            case 'v':            /* vertical tab */
  366. X                literals[actarg][i++] = '\v'; break;
  367. X            case 'b':            /* backspace */
  368. X                literals[actarg][i++] = '\b'; break;
  369. X            case 'r':            /* carriage return */
  370. X                literals[actarg][i++] = '\r'; break;
  371. X            case 'f':            /* form feed */
  372. X                literals[actarg][i++] = '\f'; break;
  373. X            case '\\':            /* actual backslash */
  374. X                literals[actarg][i++] = '\\'; break;
  375. X            case '\'':            /* single quote */
  376. X                literals[actarg][i++] = '\''; break;
  377. X            case '\"':            /* double quote */
  378. X                literals[actarg][i++] = '\"'; break;
  379. X            case '\0':            /* '\' at end  */
  380. X                literals[actarg][i++] = '\\'; 
  381. X                --j;            /* let FOR see null */
  382. X                break;
  383. X            default:            /* not recognized */
  384. X                literals[actarg][i++] = '\\';
  385. X                literals[actarg][i++] = ch;
  386. X                break;
  387. X            } /* switch */
  388. X            literals[actarg][i] = '\0';
  389. X            parmtype[actarg++] = LIT;
  390. X            break;
  391. X
  392. X        case '0': case '1': case '2': case '3':
  393. X        case '4': case '5': case '6': case '7':
  394. X        case '8': case '9':        /* col number */
  395. X            startcol[actarg] = atoi(&argv[argno][1])-1;
  396. X            parmtype[actarg++] = STR;
  397. X            break;
  398. X
  399. X        case '\0':            /* lone '-' is ref to stdin */
  400. X            filesact++;
  401. X            filecnt++;
  402. X            infiles[actarg] = stdin;
  403. X            saweof[actarg] = FALSE;
  404. X            parmtype[actarg++] = FIL;
  405. X            break;
  406. X            
  407. X        default:
  408. X            (void)fprintf(stderr, "hc: unrecognized option=%s\n",
  409. X                    argv[argno]);
  410. X            usage();
  411. X            } /* inner switch */
  412. X        break;
  413. X
  414. X        default:            /* open the file */
  415. X        if (argno > NARGS) {
  416. X            (void)fprintf(stderr, "hc: too many file names\n");
  417. X            continue;
  418. X            }
  419. X        if ((infiles[actarg] = fopen(argv[argno], "r")) != NULL) {
  420. X            saweof[actarg] = FALSE;
  421. X            filesact++;
  422. X            filecnt++;
  423. X            }
  424. X        else {
  425. X            (void)fprintf(stderr, "hc: can't access %s\n", argv[actarg]);
  426. X            exit(2);
  427. X            }
  428. X        actarg++;
  429. X        break;
  430. X        } /* outer switch */
  431. X    } /* for */
  432. X/* 
  433. X * process input lines, generate output
  434. X */
  435. X
  436. X   do {
  437. X    char bufin[NARGS][LINELEN],    /* input file line buffers here */
  438. X         intmp[LINELEN],        /* buffer for tab expansion */
  439. X         bufout[LINELEN];        /* output buffer */
  440. X
  441. X     bufout[0] = '\0';            /* ready for output */
  442. X    for (argno = 0; argno < actarg; argno++) {
  443. X        switch (parmtype[argno]) {
  444. X        case TAB:            /* pad/truncate bufout */
  445. X            if ((j = tabposn[argno]-strlen(bufout)) < 0)
  446. X            bufout[tabposn[argno]] = '\0';
  447. X            else 
  448. X            for (; j > 0; j--)
  449. X                strcat(bufout, " ");            
  450. X            break;
  451. X
  452. X        case LIT:            /* output the literal */
  453. X            for (j = strlen(bufout), k = startcol[argno-1];
  454. X            (bufout[j++] = literals[argno][k++]) != 0;);
  455. X            bufout[j] = '\0';
  456. X            break;         
  457. X
  458. X        case FIL:            /* get next line into intmp */
  459. X                if (saweof[argno] == TRUE)
  460. X            intmp[0] = '\0';
  461. X            else if (fgets(intmp, LINELEN, infiles[argno]) != NULL)
  462. X            if (intmp[strlen(intmp)-1] == '\n')
  463. X                intmp[strlen(intmp)-1] = '\0';
  464. X            else {
  465. X                (void)fprintf(stderr, "hc: input line length > ");
  466. X                (void)fprintf(stderr, "%d chars, truncated.\n",
  467. X                    LINELEN);
  468. X                exit(2);
  469. X                }
  470. X            else {
  471. X            intmp[0] = '\0';
  472. X            filesact--;
  473. X            saweof[argno] = TRUE;
  474. X            }
  475. X            /*
  476. X             * expand TABs from intmp into spaces in bufin
  477. X             */            
  478. X            for (i = 0, j = 0; (ch = intmp[j++]) != '\0';)
  479. X            if (ch == '\t')
  480. X                for (k = TABINT - (i % TABINT); k-- > 0;)
  481. X                bufin[argno][i++] = ' ';
  482. X            else
  483. X                bufin[argno][i++] = ch;
  484. X            bufin[argno][i] = '\0';
  485. X        /*
  486. X         * move bufin into bufout, if it's not null string
  487. X         */
  488. X            if (bufin[argno][0] != '\0') {
  489. X            for (j = strlen(bufout), k = startcol[argno-1];
  490. X                (bufout[j++] = bufin[argno][k++]) != 0;);
  491. X            bufout[j] = '\0';
  492. X            }
  493. X            break;
  494. X        } /* switch */
  495. X        } /* for */
  496. X
  497. X    /*
  498. X     * compress tabs in bufout
  499. X     */
  500. X    if (!sflag) {
  501. X        int sp;
  502. X
  503. X        strcpy(intmp, bufout);
  504. X        for (i = 0, j = 0, sp = 0; (ch = intmp[j++]) != '\0';) {
  505. X        if (ch == ' ') {
  506. X            sp++;
  507. X            if ((sp > 1) && ((j % TABINT) == 0)) {
  508. X            bufout[i++] = '\t';
  509. X            sp = 0;
  510. X            }
  511. X            }
  512. X        else {
  513. X            while (sp > 0) {
  514. X                bufout[i++] = ' ';
  515. X            sp--;
  516. X                }
  517. X            bufout[i++] = ch;
  518. X            }
  519. X        }
  520. X        bufout[i] = '\0';
  521. X        }
  522. X
  523. X    /*
  524. X     * remove trailing blanks
  525. X     */
  526. X    if (tabposn[actarg-1] == 0) {
  527. X        j = strlen(bufout);
  528. X        while (bufout[--j] == ' ')
  529. X        ;
  530. X        bufout[j+1] = '\0';
  531. X        }
  532. X/*
  533. X * if there were input files specified, and all files have reached EOF,
  534. X * don't bother to output this line.
  535. X */
  536. X    if ((filecnt != 0) && (filesact != 0)) {
  537. X        fputs(bufout, stdout);
  538. X        fputc('\n', stdout);
  539. X        }
  540. X    } while (filesact > 0);
  541. X     exit(0);                /* successful return */
  542. X     } /* hc routine */
  543. X
  544. X
  545. Xvoid usage() {
  546. X    (void)fprintf(stderr, "usage:");
  547. X    (void)fprintf(stderr, "  hc [-s] [+tab | -col | -l \"str\" | filename] ...\n");
  548. X    exit(2);
  549. X    }
  550. EOF
  551. exit 0
  552.