home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d1xx / d162 / flow2troff.lha / Flow2Troff / Flow2Troff.c < prev    next >
C/C++ Source or Header  |  1988-10-02  |  16KB  |  581 lines

  1. /**********************************************************************
  2. * FLOW2TROFF:    Translate a "Flow" file into troff code.
  3. *        Daniel J. Barrett, 1988.  barrett@cs.jhu.edu (ARPAnet).
  4. *        PUBLIC DOMAIN.
  5. *
  6. * Usage:    Flow2Troff [options] Flow_file [Troff_File]
  7. *
  8. * Options:    See *usage_string[].
  9. *
  10. * Compiling:    (MANX Aztec C, 16-bit integers)
  11. *        (You also need a version of "getopt()".)
  12. *        cc Flow2Troff.c
  13. *        ln Flow2Troff.o -lc
  14. **********************************************************************/
  15.     
  16. #include <stdio.h>
  17. #ifndef UNIX
  18. #include <getopt.h>            /* I assume that UNIX/ULTRIX has   */
  19. #endif                    /* a built-in getopt() function.   */
  20.     
  21. #define FILE_END        -2    /* My own error flag.              */
  22. #define NUM_HEADER_BYTES    42L    /* How many header bytes to skip.  */
  23.  
  24. #define EQUAL        !strcmp        /* 2 useful functions.             */
  25. #define EXISTS(f)    !access(f,0)
  26.     
  27. #define UNDERLINE    1        /* The 5th byte of HSTL data has   */
  28. #define    BOLD        2        /* the font information.  Lowest 3 */
  29. #define ITALICS        4        /* bits are U, B, and I on/off.    */
  30.  
  31. #define    TRUE        1
  32. #define    FALSE        0
  33. #define    RIGHT        1
  34. #define LEFT        0
  35.     
  36. long indent;                /* Number of times to indent.      */
  37. int fontChange;                /* FALSE=plain, TRUE=bold/italics. */
  38. int printing;                /* Used with dFlag.  TRUE if the   */
  39.                     /* current line indent is less     */
  40.                     /* dFlag.                          */
  41. int quotes;
  42. int underlined;
  43.  
  44. extern char *optarg;            /* Necessary getopt() variables.   */
  45. extern int optind;
  46. char optstring[] = "htp:v:i:d:";
  47.  
  48. int pFlag, vFlag, iFlag, hFlag, dFlag, tFlag;
  49.  
  50. /**********************************************************************
  51. *            Usage information
  52. **********************************************************************/
  53.  
  54. static char *usage_string[] = {
  55. "",
  56. "Flow2Troff V1.0 by Daniel J. Barrett.  PUBLIC DOMAIN.",
  57. "Convert from New Horizons Software \"Flow\" files to Troff files.",
  58. "",
  59. "Usage:        Flow2Troff [options] Flow_file [Troff_file]",
  60. "",
  61. "Options:    -p#    : Set troff point size (default 12)",
  62. "        -v#    : Set troff vertical spacing (default 13)",
  63. "        -i#    : Set indentation (default 3)",
  64. "        -d#    : Print only to specified depth (default all)",
  65. "        -h    : Permit hyphenation (default is none)",
  66. "        -t    : Title the outline with \"Flow_file\"",
  67. "",
  68. "If not specified, Troff_file is written on standard output.",
  69. "",
  70. NULL };
  71.  
  72.     
  73. /**********************************************************************
  74. *            M A I N    P R O G R A M
  75. **********************************************************************/
  76.  
  77. main(argc,argv)
  78. int argc; char *argv[];
  79. {
  80.     FILE *infile=NULL, *outfile=NULL;    /* infile = FLOW file.  */
  81.     char c;                    /* outfile = TROFF file */
  82.  
  83.     /* Set default values for our flags. */
  84.     pFlag=12, vFlag=13, iFlag=3, hFlag=FALSE, dFlag=0, tFlag=FALSE;
  85.  
  86.     /* Parse the command line, getting and setting all options. */
  87.     while ((c = getopt(argc, argv, optstring)) != EOF)
  88.         switch (c) {
  89.             case 'p':    pFlag = atoi(optarg);
  90.                     CheckPositive(pFlag, 'p', ">");
  91.                     break;
  92.             case 'v':    vFlag = atoi(optarg);
  93.                     CheckPositive(vFlag, 'v', ">");
  94.                     break;
  95.             case 'i':    iFlag = atoi(optarg);
  96.                     CheckPositive(iFlag, 'i', ">");
  97.                     break;
  98.             case 'd':    dFlag = atoi(optarg);
  99.                     CheckPositive(dFlag, 'd', ">=");
  100.                     break;
  101.             case 'h':    hFlag = TRUE;
  102.                     break;
  103.             case 't':    tFlag = TRUE;
  104.                     break;
  105.             case '?':    Usage();
  106.                     break;
  107.         }
  108.  
  109.     /* Open infile and outfile. */
  110.     if (!OpenTheFiles(argc, argv, optind, &infile, &outfile))
  111.         Cleanup(infile, outfile, "File opening failed... bye!");
  112.  
  113.     /* If infile is the wrong type, quit. */
  114.     if (NotFlowFile(infile, outfile))
  115.         Cleanup(infile, outfile, "Invalid input file... bye!");
  116.  
  117.     /* Skip the header bytes, convert to troff, and quit. */
  118.     SkipBytes(infile, outfile, NUM_HEADER_BYTES);
  119.     Flow2Troff(infile, outfile, argv[optind]);
  120.     Cleanup(infile, outfile, NULL);
  121. }
  122.  
  123.     
  124. /**********************************************************************
  125. *        Indentifying FLOW commands
  126. **********************************************************************/
  127.  
  128. Flow2Troff(infile, outfile, title)
  129. /* Continually read commands from the Flow file.  GetCommand() finds each
  130.  * TEXT, NEST, or HSTL command and stores it in "command".  Then Convert()
  131.  * processes that command and its data.  "title" is the name of the infile. */
  132. FILE *infile, *outfile;
  133. char *title;
  134. {
  135.     char command[5];
  136.     int error;
  137.  
  138.     indent = 0;            /* Initialize global variables. */
  139.     fontChange = FALSE;        /* No indent, plain font, and   */
  140.     printing = TRUE;        /* printing turned on.          */
  141.     underlined = FALSE;        /* Is current text underlined?  */
  142.     quotes = LEFT;            /* Print left or right quotes?  */
  143.     
  144.     TroffHeader(outfile, title);    /* Output mandatory troff header. */
  145.  
  146.     do {
  147.         if (GetCommand(infile, outfile, command) == FILE_END)
  148.             return(FILE_END);
  149.         error = Convert(infile, outfile, command);
  150.     } while (!error);
  151.  
  152.     return(error);
  153. }
  154.  
  155.     
  156. GetCommand(infile, outfile, command)
  157. /* Get the four-letter formatting command from infile. */
  158. FILE *infile, *outfile;
  159. char command[];
  160. {
  161.     int n=4;
  162.     char c;
  163.  
  164.     StupidHack(infile, outfile);        /* Yeccchh. */
  165.  
  166.     /* Read a four-character command, one byte at a time. */
  167.     while (n && ((c = getc(infile)) != EOF)) {
  168.         command[4-n] = c;
  169.         n--;
  170.     }
  171.  
  172.     /* Did we get the whole command? */
  173.     if (n)                /* We must have hit EOF...       */
  174.         return(FILE_END);    /* ... so complain.              */
  175.     else {
  176.         command[4] = '\0';    /* Terminate command with a null */
  177.         return(0);        /* so we can use it as a string. */
  178.     }
  179. }
  180.  
  181.     
  182. Convert(infile, outfile, command)
  183. /* Depending on what kind of command we have, run the appropriate 
  184.  * Flow --> Troff conversion routine. */
  185. FILE *infile, *outfile;
  186. char *command;
  187. {
  188.     if (EQUAL(command, "TEXT"))        /* Actual text. */
  189.         return(DumpText(infile, outfile));
  190.     else if (EQUAL(command, "NEST"))    /* Indentation data. */
  191.         return(Indent(infile, outfile));
  192.     else if (EQUAL(command, "HSTL"))    /* Text style change. */
  193.         return(ChangeStyle(infile, outfile));
  194.     else {                    /* Error! */
  195.         fprintf(stderr, "Error found in file!\n");
  196.         return(FILE_END);
  197.     }
  198. }
  199.  
  200.     
  201. /**********************************************************************
  202. *            The actual translation routines
  203. **********************************************************************/
  204.  
  205. TroffHeader(outfile, title)
  206. /* Output some mandatory Troff code into the outfile. */
  207. FILE *outfile;
  208. char *title;
  209. {
  210.     DefineUnderline(outfile);
  211.     fprintf(outfile, ".ps %d\n", pFlag);    /* Point size.           */
  212.     fprintf(outfile, ".vs %d\n", vFlag);    /* Vertical spacing.     */
  213.     if (!hFlag)
  214.         fprintf(outfile, ".nh\n");    /* No hyphenation.       */
  215.     fprintf(outfile, ".ad b\n");        /* Left & right justify. */
  216.  
  217.     /* If we have a title, print it.  Else, just print 5 newlines. */
  218.     if (tFlag)
  219.         fprintf(outfile, ".sp 5\n.ft B\n.ce 1\n%s\n.ft R\n.sp 3\n",
  220.             title);
  221.     else
  222.         fprintf(outfile, ".sp 5\n");
  223. }
  224.  
  225.     
  226. DefineUnderline(outfile)
  227. /* Define a troff "underline string" command, ".us".  This is from the
  228.  * NROFF/TROFF USER'S MANUAL, page 20,  in Volume 2 of THE UNIX PROGRAMMER'S
  229.  * MANUAL. */
  230. FILE *outfile;
  231. {
  232.     fprintf(outfile, ".de us\n\\\\$1\\l'|0\\(ul'\n..\n");
  233. }
  234.  
  235.     
  236. DumpText(infile, outfile)
  237. /* For a TEXT command, find the length of its data, and then output that
  238.  * data. */
  239. FILE *infile, *outfile;
  240. {
  241.     int i;
  242.     unsigned char len[4];
  243.     long textLength=0L;
  244.     char c;
  245.  
  246.     /* TEXT data is stored in a variable length field.  The first
  247.      * 4 bytes are a longword; they store the length of the text
  248.      * string immediately following. */
  249.  
  250.     /* Get length of text, in characters.  The length is stored as
  251.      * a 4-byte field.  We must convert this to a long. */
  252.  
  253.     for (i=0; i<4; i++)
  254.         len[i] = getc(infile);
  255.  
  256.     textLength = (long)     ((len[0] << 24)
  257.                 + (len[1] << 16)
  258.                 + (len[2] << 8 )
  259.                 +  len[3]);
  260.  
  261.     /* If we are printing (not indented past dFlag), print the text.
  262.      * If we were printing in an alternate font, return to plain.
  263.      * If we were not printing, just skip all the text data entirely. */
  264.  
  265.     if (printing) {
  266.         for (i=0; i<textLength; i++) {
  267.             c = getc(infile);
  268.             if (underlined && c == '"') {
  269.                 if (quotes == LEFT) {
  270.                     fprintf(outfile, "``");
  271.                     quotes = RIGHT;
  272.                 }
  273.                 else {
  274.                     fprintf(outfile, "''");
  275.                     quotes = LEFT;
  276.                 }
  277.             }
  278.             else
  279.                 putc(c, outfile);
  280.         }
  281.         if (underlined) {        /* Terminate underlining. */
  282.             putc('"', outfile);
  283.             underlined = FALSE;
  284.         }
  285.         fprintf(outfile, "\n");
  286.         if (fontChange) {
  287.             fprintf(outfile, ".ft R\n");
  288.             fontChange = FALSE;
  289.         }
  290.         fprintf(outfile, ".br\n");
  291.     }
  292.     else
  293.         SkipBytes(infile, outfile, textLength);
  294.  
  295.     return(0);
  296. }
  297.  
  298.     
  299. Indent(infile, outfile)
  300. /* Print the proper troff ".in" indenting information.  This algorithm
  301.  * is not as straightforward as I thought it would be. */
  302. FILE *infile, *outfile;
  303. {
  304.     long newIndent=0;        /* New indent value, to be read. */
  305.     long Abs();            /* Absolute value.               */
  306.     unsigned char temp[2];        /* Two bytes of newIndent value. */
  307.     char plusMinus;            /* Holds either a '+' or a '-'.  */
  308.     int i;
  309.  
  310.     /* NEST data is 6 bytes long.  The first four bytes are 0 0 0 2, and
  311.      * I don't know their meaning.  The last two bytes represent the
  312.      * absolute indentation from the left margin. */
  313.  
  314.     SkipBytes(infile, outfile, 4L);
  315.     for (i=0; i<2; i++) {
  316.         temp[i] = getc(infile);
  317.         if (temp[i] == EOF)
  318.            Cleanup(infile, outfile, "Bad indentation data.. bye!");
  319.     }
  320.     newIndent = (long)(temp[1] + (temp[0] << 8));  /* New indent value. */
  321.  
  322.     /* INDENTATION ALGORITHM.
  323.      *
  324.      * Assume we are currently printing.
  325.      *     If the -d flag is not specified, we simply do the indent.
  326.      *    Same deal if we DO have -d, but we're not indented past dFlag.
  327.      *    But if we used -d AND we indented too far, we turn off
  328.      *     printing.
  329.      *
  330.      * Alternatively, assume we are NOT currently printing.  We could
  331.      *  get here ONLY if the -d flag has been set.
  332.      *    If the new indent value is greater than or equal to the -d
  333.      *     value, then we do nothing... we still should not print.
  334.      *    Otherwise, the new indent value is less than the -d value.
  335.      *     Turn printing back on.
  336.      *     For all intents and purposes, we may now pretend that our
  337.      *      current indent value was the maximum printable:  dFlag-1.
  338.      *     If the newIndent value is ALSO dFlag-1, we do not need to
  339.      *      do any indents... just stay where we are.  Otherwise, do
  340.      *      an indent (which MUST be negative) backwards from dFlag.
  341.      *
  342.      * Simple, eh?                            */
  343.  
  344.     if (printing) {
  345.         if (dFlag==0 || (newIndent < dFlag)) {
  346.             plusMinus = ((newIndent - indent) >= 0)
  347.                   ? '+'
  348.                   : '-';
  349.             fprintf(outfile, ".in %c%ld\n", plusMinus, 
  350.                 Abs((newIndent-indent)*iFlag));
  351.         }
  352.         else
  353.             printing = FALSE;
  354.     }
  355.     else if (newIndent < dFlag) {
  356.         printing = TRUE;
  357.         if (newIndent != (dFlag-1))
  358.             fprintf(outfile, ".in -%ld\n",
  359.              Abs((newIndent-(dFlag-1))*iFlag));
  360.     }
  361.     indent = newIndent;        /* Keep the new indent value. */
  362.     return(0);
  363. }
  364.  
  365.     
  366. ChangeStyle(infile, outfile)
  367. /* Change to bold, italics, underline. Troff cannot do both bold & italics
  368.  * simultaneously, so bold takes precedence here.  Underlining is a real
  369.  * hack. */
  370. FILE *infile, *outfile;
  371. {
  372.     char style=0;
  373.  
  374.     /* HSTL data is 6 bytes.  The 5th byte contains style change info.
  375.      * The lowest bit is underline on/off, the next is bold on/off, and
  376.      * the third is italics on/off.  I don't know what the other 5 bytes
  377.      * stand for. */
  378.  
  379.     /* If we are printing, print the appropriate troff style change
  380.      * info.  Else, just skip the 6 bytes of HSTL data. */
  381.  
  382.     if (printing) {
  383.         SkipBytes(infile, outfile, 4L);
  384.         style = getc(infile);
  385.  
  386.         if (style & BOLD) {
  387.             fprintf(outfile, ".ft B\n");
  388.             fontChange = TRUE;
  389.         }
  390.         else if (style & ITALICS) {
  391.             fprintf(outfile, ".ft I\n");
  392.             fontChange = TRUE;
  393.         }
  394.         if (style & UNDERLINE) {
  395.             underlined = TRUE;
  396.             fprintf(outfile, ".us \"");   /* quote before text */
  397.         }
  398.         SkipBytes(infile, outfile, 1L);
  399.     }
  400.     else
  401.         SkipBytes(infile, outfile, 6L);
  402.  
  403.     return(0);
  404. }
  405.  
  406. /**********************************************************************
  407. *            File opening routines
  408. **********************************************************************/
  409.     
  410. OpenTheFiles(argc, argv, optind, infile, outfile)
  411. /* Open input and output files, return their pointers in infile and
  412.  * outfile.  If no outfile specified, use stdout. */
  413. int argc;
  414. char *argv[];
  415. int optind;
  416. FILE **infile, **outfile;
  417. {
  418.     int argsLeft = argc - optind;
  419.  
  420.     if (argsLeft == 2) {        /* infile & outfile were specified. */
  421.         if ((*infile = fopen(argv[optind], "r")) == NULL) {
  422.             perror(argv[optind]);
  423.             return(FALSE);
  424.         }
  425.         optind++;
  426.         if (DontOverwriteExistingFile(argv[optind]))
  427.             return(FALSE);
  428.         if ((*outfile = fopen(argv[optind], "w")) == NULL) {
  429.             perror(argv[optind]);
  430.             return(FALSE);
  431.         }
  432.     }
  433.     else if (argsLeft == 1) {    /* Only infile specified. */
  434.         if ((*infile = fopen(argv[optind], "r")) == NULL) {
  435.             perror(argv[optind]); 
  436.             return(FALSE);
  437.         }
  438.         *outfile = stdout;
  439.     }
  440.     else                     /* Bad syntax */
  441.         Usage();
  442. }
  443.  
  444.     
  445. DontOverwriteExistingFile(filename)
  446. /* If filename already exists, inform the user, who may choose to 
  447.  * continue or quit. */
  448. char *filename;
  449. {
  450.     static char *ex = "File \"%s\" already exists; overwrite it? (n/y): ";
  451.     if (!EXISTS(filename))
  452.         return(FALSE);
  453.     else {
  454.         fprintf(stderr, ex, filename);
  455.         if (getchar() != 'y')
  456.             return(TRUE);
  457.         else
  458.             return(FALSE);
  459.     }
  460. }
  461.  
  462.     
  463. NotFlowFile(infile, outfile)
  464. /* If file is not a FLOW file, return TRUE.  Otherwise, return FALSE.
  465.  * We assume that infile points to the beginning of the file. */
  466. FILE *infile, *outfile;
  467. {
  468.     int i;
  469.     unsigned char buf[5];
  470.  
  471.     /* Check if the file is a custom IFF "FORM" file. */
  472.  
  473.     for (i=0; i<4; i++) {
  474.         buf[i] = getc(infile);
  475.         if (buf[i] == EOF)
  476.             return(TRUE);
  477.     }
  478.     buf[4] = '\0';
  479.     if (strcmp(buf, "FORM")) {
  480.         fprintf(stderr, "Not an IFF FORM file.\n");
  481.         return(TRUE);
  482.     }
  483.  
  484.     /* Check if the type of the FORM file is "HEAD". */
  485.  
  486.     SkipBytes(infile, outfile, 4L);
  487.     for (i=0; i<4; i++) {
  488.         buf[i] = getc(infile);
  489.         if (buf[i] == EOF)
  490.             return(TRUE);
  491.     }
  492.     buf[4] = '\0';
  493.     if (strcmp(buf, "HEAD")) {
  494.         fprintf(stderr, "Infile is IFF FORM, but wrong type.\n");
  495.         return(TRUE);
  496.     }
  497.  
  498.     /* If we got here, then the file must be OK. */
  499.  
  500.     fseek(infile, 0L, 0);        /* Return to beginning of file. */
  501.     return(FALSE);
  502. }
  503.     
  504. /**********************************************************************
  505. *            Miscellaneous little routines    
  506. **********************************************************************/
  507.  
  508. SkipBytes(infile, outfile, n)
  509. /* Skip over the next n bytes in file pointed to by infile.
  510.  * If we reach EOF, quit. */
  511. FILE *infile, *outfile;
  512. long n;
  513. {
  514.     while (n && (getc(infile) != EOF))
  515.         n--;
  516.     if (n)
  517.         Cleanup(infile, outfile, "File ended before I was done!");
  518. }
  519.  
  520.     
  521. Usage()
  522. /* Print a program usage message, then exit. */
  523. {
  524.     char **str = usage_string;
  525.     while (*str)
  526.         fprintf(stderr, "%s\n", *(str++));
  527.     exit(5);
  528. }
  529.  
  530.     
  531. Cleanup(infile, outfile, s)
  532. /* Exit the program gracefully. */
  533. FILE *infile, *outfile;
  534. char *s;
  535. {
  536.     if (infile)
  537.         fclose(infile);
  538.     if (outfile)
  539.         fclose(outfile);
  540.     if (s)
  541.         fprintf(stderr, "%s\n", s);
  542.     exit(0);
  543. }
  544.  
  545.     
  546. StupidHack(infile, outfile)
  547. /* Sometimes, there is a zero immediately following TEXT data.  I
  548.  * have no idea why it is there.  Since it seems to contribute no
  549.  * information useful for troff, I just skip it. */
  550. FILE *infile, *outfile;
  551. {
  552.     char c;
  553.  
  554.     c = getc(infile);
  555.     if (c == EOF)
  556.         Cleanup(infile, outfile, NULL);
  557.     else if (c != 0)
  558.         ungetc(c, infile);
  559. }
  560.  
  561.     
  562. long Abs(x)
  563. /* Return the absolute value of x. */
  564. long x;
  565. {
  566.     return((x<0) ? -x : x);
  567. }
  568.  
  569.     
  570. CheckPositive(value, flag, sign)
  571. /* Print an error message if the value of the flag is out of range. */
  572. int value;
  573. char flag, *sign;
  574. {
  575.     static char *message = "ERROR: -%c value must be %s 0.\n";
  576.  
  577.     if ((EQUAL(sign, ">") && (value <= 0))
  578.     ||  (EQUAL(sign, ">=") && (value < 0)))
  579.         fprintf(stderr, message, flag, sign), exit(5);
  580. }
  581.