home *** CD-ROM | disk | FTP | other *** search
/ APDL Public Domain 1 / APDL_PD1A.iso / deskutils / _a_l / boota / C-source / c / 1stMake next >
Encoding:
Text File  |  1993-03-03  |  52.7 KB  |  1,452 lines

  1. /* > c.1stMake  - (c) Paul Witheridge - Version 2.01 - 03 Mar 1993   */
  2.  
  3. /*===================================================================*/
  4. /*                                                                   */
  5. /*  1stMake - converts text files to 1stWord+ files                  */
  6. /*  -------                                                          */
  7. /*                                                                   */
  8. /*  This utility converts plain text files to 1stWord+ files in an   */
  9. /*  intelligent manner, so that (hopefully) the resulting file can   */
  10. /*  be re-formatted using 1stWord+ without losing indents, hanging   */
  11. /*  indents, etc.                                                    */
  12. /*                                                                   */
  13. /*-------------------------------------------------------------------*/
  14. /*                                                                   */
  15. /*  COPYRIGHT NOTICE                                                 */
  16. /*                                                                   */
  17. /*  1stMake is subject to Copyright.                                 */
  18. /*                                                                   */
  19. /*  Permission is granted by the author to any recipient of this     */
  20. /*  material to use and make/disseminate copies of the application   */
  21. /*  provided that no charges are made for doing so (other than to    */
  22. /*  cover any cost of media or postage) and that this notice is      */
  23. /*  included with all copies.                                        */
  24. /*                                                                   */
  25. /*===================================================================*/
  26.  
  27. #include "kernel.h"                     /* ARC specifics             */
  28. #include <ctype.h>                      /* Character handling        */
  29. #include <limits.h>                     /* Implementation limits     */
  30. #include <stddef.h>                     /* Standard definitions      */
  31. #include <stdio.h>                      /* Input/output              */
  32. #include <stdlib.h>                     /* General utilities         */
  33. #include <string.h>                     /* String handling           */
  34. #include "Beep.h"                       /* Beep header               */
  35. #include "GetDirs.h"                    /* GetDirs header            */
  36. #include "ArgFuncs.h"                   /* Arg functions etc         */
  37. #include "Useful.h"                     /* Useful definitions        */
  38.  
  39. /*-------------------------------------------------------------------*/
  40. /*  Global data declarations and definitions.                        */
  41. /*-------------------------------------------------------------------*/
  42.  
  43. static unsigned char flags = '\0' ;     /* Processing flags          */
  44.   #define flgfnd 0x01                   /* Found file to process     */
  45.   #define flglfo 0x02                   /* List file is open         */
  46.   #define flglst 0x10                   /* List mode                 */
  47.   #define flgovr 0x20                   /* Overwrite mode            */
  48.   #define flgrcs 0x40                   /* Recursive mode            */
  49.   #define flgtst 0x80                   /* Test mode                 */
  50.  
  51. static int filecount = 0 ;              /* Count of files processed. */
  52.  
  53. static const char *argptr = NULL ;      /* Pointer to file spec.     */
  54. static const char *pfxptr = "_"  ;      /* Pointer to output prefix  */
  55. static const char *lstptr = NULL ;      /* Pointer to list file spec */
  56. static FILE *listfile = NULL ;          /* Pointer to list file      */
  57.  
  58. /*-------------------------------------------------------------------*/
  59. /*  Define settings for line flags.                                  */
  60. /*-------------------------------------------------------------------*/
  61.  
  62. #define brkflg 0x01                     /* Break at end of this line */
  63. #define hinflg 0x02                     /* Hanging indent            */
  64. #define itmflg 0x04                     /* List item                 */
  65. #define pinflg 0x08                     /* Paragraph indent flag     */
  66. #define hrdflg 0x10                     /* Hard spaces flag          */
  67.  
  68. /*-------------------------------------------------------------------*/
  69. /*  Define 1stWord+ standard file header.                            */
  70. /*-------------------------------------------------------------------*/
  71.  
  72. static const char docheader[] =
  73. {
  74.   0x1F, 0x30,                     /* File Start                      */
  75.   0x36, 0x36,                     /* Form length                     */
  76.   0x30, 0x31,                     /* Top of form margin              */
  77.   0x30, 0x33,                     /* Header Margin                   */
  78.   0x30, 0x33,                     /* Footer Margin                   */
  79.   0x30, 0x35,                     /* Bottom of form margin           */
  80.   0x38, 0x30, 0x30,
  81.   0x0A,                           /* End of first line               */
  82.  
  83.   0x1F, 0x31,                     /* Left hdg text would go here     */
  84.   0x1F,                           /* Centre hdg text would go here   */
  85.   0x1F,                           /* Right hdg text would go here    */
  86.   0x0A,                           /* End of hdg line                 */
  87.  
  88.   0x1F, 0x32,                     /* Left ftg text would go here     */
  89.   0x1F,                           /* Centre ftg text would go here   */
  90.   0x1F,                           /* Right ftg text would go here    */
  91.   0x0A,                           /* End of ftg line                 */
  92.  
  93.   0x1F, 0x46, 0x30,               /* Start of footnote line          */
  94.   0x31,                           /* Lines before footnote           */
  95.   0x31,                           /* Lines after footnote            */
  96.   0x30, 0x30,
  97.   0x33, 0x30,                     /* Length of separator line        */
  98.   0x0A,                           /* End of footnote line            */
  99.  
  100.   0x1F, 0x39,                     /* Start of default ruler          */
  101.   0x5B,                           /* Start of TABS definition        */
  102.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  1st TAB                        */
  103.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  2nd TAB                        */
  104.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  3rd TAB                        */
  105.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  4th TAB                        */
  106.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  5th TAB                        */
  107.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  6th TAB                        */
  108.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  7th TAB                        */
  109.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  8th TAB                        */
  110.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /*  9th TAB                        */
  111.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /* 10th TAB                        */
  112.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /* 11th TAB                        */
  113.   0x2E, 0x2E, 0x2E, 0x2E, 0x7F,   /* 12th TAB                        */
  114.   0x2E, 0x2E, 0x2E,
  115.   0x5D,                           /* End of tabs                     */
  116.   0x30,                           /* Pitch Pica/Elite/Cndnsd/Expnded */
  117.   0x30,                           /* Justification off/on            */
  118.   0x31,                           /* Line spacing                    */
  119.   0x0A,                           /* End of ruler line               */
  120.   0x1B, 0x80
  121. } ;
  122.  
  123. /*-------------------------------------------------------------------*/
  124. /*  Declare functions local in scope to this file.                   */
  125. /*-------------------------------------------------------------------*/
  126.  
  127. static enum boolean cnvtfunc(           /* Convert function          */
  128.   const char *path,
  129.   direntry *ptr) ;
  130.  
  131. static int leadblanks(                  /* Count leading blanks      */
  132.   const char *lineptr) ;
  133.  
  134. static enum boolean isitem(             /* Tests for list item       */
  135.   const char *lineptr,
  136.   const int indent) ;
  137.  
  138. static enum boolean isnumbered(         /* Test for 1-2 digit number */
  139.   const char *p,
  140.   const int l) ;
  141.  
  142. static enum boolean isroman(            /* Test for roman numeral    */
  143.   const char *p,
  144.   const int l) ;
  145.  
  146. static enum boolean isterm(             /* Tests for definition term */
  147.   const char *lineptr,
  148.   int len,
  149.   int indent,
  150.   int nextindent) ;
  151.  
  152. static enum boolean istag(              /* Test for certain GML tags */
  153.   const char *lineptr) ;
  154.  
  155. static enum boolean isshort(            /* Test for short line       */
  156.   const char *lineptr,
  157.   const int len,
  158.   const int brklen) ;
  159.  
  160. static void sethangingindent(           /* Set hanging indent        */
  161.   int line,
  162.   int nlines,
  163.   int nextindent,
  164.   char *(*lineptrs)[],
  165.   char (*lineflgs)[]) ;
  166.  
  167. static void putdup(                     /* Repeat character to file  */
  168.   const int c,
  169.   const int n,
  170.   FILE *fileptr) ;
  171.  
  172. /*===================================================================*/
  173. /*                                                                   */
  174. /*  Main program                                                     */
  175. /*  ------------                                                     */
  176. /*                                                                   */
  177. /*  Analyse arguments. Then call the "GetDirentrys" function to      */
  178. /*  read all the file-names in the specified directory. Provide      */
  179. /*  pointer to function to process the specified files.              */
  180. /*                                                                   */
  181. /*===================================================================*/
  182.  
  183. int main(
  184.   const int argc,                       /* Number of arguments       */
  185.   const char *argv[])                   /* Array of pointers to args */
  186. {
  187.   /*-----------------------------------------------------------------*/
  188.   /*  Local definitions.                                             */
  189.   /*-----------------------------------------------------------------*/
  190.  
  191.   int returncode ;
  192.  
  193.   static const char **posval[] = /* Ptrs to ptrs to posit'nal values */ 
  194.   {
  195.     &argptr
  196.   } ;
  197.   static const char **(*posargs)[] = &posval ;    /* Ptr to array    */
  198.  
  199.   static const char options[] =  /* Used to match option argument    */
  200.   {
  201.     'L',                            /* List mode                     */
  202.     'O',                            /* Overwrite mode                */
  203.     'P',                            /* Output prefix                 */
  204.     'R',                            /* Recursive mode                */
  205.     'T',                            /* Test mode                     */
  206.     '0'                             /* Null byte terminator          */
  207.   } ;
  208.  
  209.   static const unsigned char optflags[] = /* Used to set option flgs */
  210.   {
  211.     flglst,                         /* List mode                     */
  212.     flgovr,                         /* Overwrite mode                */
  213.     0,                              /* Output prefix                 */
  214.     flgrcs,                         /* Recursive mode                */
  215.     flgtst,                         /* Test mode                     */
  216.   } ;
  217.  
  218.   static const char **optval[] = /* Ptrs to ptrs to option values    */
  219.   {
  220.     &lstptr,                        /* Optional list file name       */
  221.     NULL,                           /* No value allowed              */
  222.     &pfxptr,                        /* Output file prefix            */
  223.     NULL,                           /* No value allowed              */
  224.     NULL,                           /* No value allowed              */
  225.   } ;
  226.  
  227.   static const char helpdata[] = /* Help text to be displayed        */
  228.  
  229.     "1stMake converts plain ASCII text files into 1stWord+ "
  230.     "documents. It attempts to insert the correct 1stWord+ "
  231.     "format control characters for hanging indents and lists "
  232.     "so that they can be reflowed correctly when reformatting "
  233.     " with 1stWord+.\x1f"
  234.     "\x1f"
  235.     "WARNING:\x01" "This utility converts files IN-PLACE if the "
  236.     "'-o' option is used to replace the input text file with "
  237.     "the output 1stWord+ document.\x1f"
  238.     "\x1f"
  239.     "Syntax:\x01" "*1stMake  [path.]object  [options]\x1f"
  240.     "\x1f"
  241.     "object:\x01" "Specifies one of the following:\x1f"
  242.     "\x01" "(a) a single non-wildcarded file name\x1f"
  243.     "\x01" "(b) a single non-wildcarded directory name\x1f"
  244.     "\x01" "(c) a wildcarded name.\x1f"
  245.     "\x1f"
  246.     "\x01" "In case (a) the file is converted provided that "
  247.     "it has a file-type of TEXT.\x1f"
  248.     "\x1f"
  249.     "\x01" "In case (b) all TEXT files in the specified directory "
  250.     "are converted. If the RECURSION option is specified all TEXT "
  251.     "files in all subdirectories are also converted.\x1f"
  252.     "\x1f"
  253.     "\x01" "In case (c) all matching TEXT files are converted. "
  254.     "If no matching files are found, then the first matching "
  255.     "directory name is taken and all TEXT files therein are "
  256.     "converted. If the RECURSION option is specified, all "
  257.     "matching TEXT files are converted, plus all TEXT files "
  258.     "in all subdirectories.\x1f"
  259.     "\x1f"
  260.     "\x01" "In all cases, only files which have a filetype of "
  261.     "TEXT (hex FFF) will be converted.\x1f"
  262.     "\x1f"
  263.     "path:\x01" "Specifies the directory to be searched for the "
  264.     "object file/directory. If omitted the current directory "
  265.     "is searched.\x1f"
  266.     "\x1f"
  267.     "options:\x01" "Specifies processing options which can be "
  268.     "one or more of the following:\x1f"
  269.     "\x1f"
  270.     "\x01" "-l xxx\x02" "-\x03" "LIST mode; the input file "
  271.     "contents are listed to file 'xxx', each line prefixed by "
  272.     "flags showing the conversion action for that line; if 'xxx' "
  273.     "is omitted the list is written to the standard output "
  274.     "stream.\x1f"
  275.     "\x1f"
  276.     "\x01" "-o\x02" "-\x03" "OVERWRITE mode; output file will "
  277.     "have same name and will replace input file.\x1f"
  278.     "\x1f"
  279.     "\x01" "-p xxx\x02" "-\x03" "specify PREFIX which will be "
  280.     "used to create name for output file if '-o' is not specified; "
  281.     "'xxx' can be one or more characters including directory "
  282.     "specification (e.g. 'txtdir.'); if not specified a default "
  283.     "of '_' is used.\x1f"
  284.     "\x1f"
  285.     "\x01" "-r\x02" "-\x03" "RECURSION mode which causes all eligible "
  286.     "files in all subdirectories to be converted.\x1f"
  287.     "\x1f"
  288.     "\x01" "-t\x02" "-\x03" "TEST mode; a list of files to be "
  289.     "converted is displayed but no output is actually written; "
  290.     "useful for checking when specifying a directory or wildcarded "
  291.     "filename.\x1f"
  292.     "\x1f"
  293.     "If option '-l' (list mode) is specified, the input file contents "
  294.     "are listed with each line prefixed by flag characters indicating "
  295.     "how 1stMake will convert that line. The flag possible flag "
  296.     "characters are as follows:\x1f"
  297.     "\x1f"
  298.     "\x04" "B\x05" "-\x06" "line assumed to be 'break' in formatting "
  299.     "(end of paragraph) and no space will be inserted before new "
  300.     "line character at end of line.\x1f"
  301.     "\x1f"
  302.     "\x04" "P\x05" "-\x06" "line assumed to be start of paragraph "
  303.     "and will be indented with fixed spaces (as produced by TAB or "
  304.     "FIXED SPACE key).\x1f"
  305.     "\x1f"
  306.     "\x04" "L\x05" "-\x06" "line assumed to be the start of an item "
  307.     "in a list and the initial item identification (e.g. paragraph "
  308.     "number) will be followed by an 'indent' space plus 'stretch' "
  309.     "spaces (as produced by INDENT key)\x1f"
  310.     "\x1f"
  311.     "\x04" "I\x05" "-\x06" "line will be indented with an 'indent' "
  312.     "space plus 'stretch' spaces (as produced by INDENT key)\x1f"
  313.     "\x1f"
  314.     "\x04" "A\x05" "-\x06" "line is assumed to be non-formattable "
  315.     "('as is') and all spaces will be converted to fixed spaces.\x1f"
  316.     "\x1f"
  317.     "1stMake - copyright Paul Witheridge, 1993\x1f"
  318.     "\x1f" ;
  319.  
  320.   static const unsigned char helptabs[] =  /* Help text tab settings */
  321.   {
  322.     1,10,17,19,2,4,6
  323.   } ;
  324.  
  325.   /*-----------------------------------------------------------------*/
  326.   /*  Executable statements                                          */
  327.   /*                                                                 */
  328.   /*  First display sign-on message with version/date ids.           */
  329.   /*-----------------------------------------------------------------*/
  330.  
  331.   puts("\n1stMake Version 2.01 - 03 March 1993\n") ;
  332.   
  333.   /*-----------------------------------------------------------------*/
  334.   /*  Analyse arguments for file-name and option flags.              */
  335.   /*-----------------------------------------------------------------*/
  336.  
  337.   analargs(argc,argv,1,&posargs,options,&flags,optflags,optval) ;
  338.  
  339.   /*-----------------------------------------------------------------*/
  340.   /*  Set paged scrolling mode                                       */
  341.   /*-----------------------------------------------------------------*/
  342.  
  343.   _kernel_oswrch(12) ;
  344.   _kernel_oswrch(14) ;
  345.  
  346.   puts(" PRESS SHIFT KEY TO ALLOW WINDOW TO SCROLL\n") ;
  347.   
  348.   /*-----------------------------------------------------------------*/
  349.   /*  If no file name operand just display help text.                */
  350.   /*-----------------------------------------------------------------*/
  351.  
  352.   if ( argptr == NULL )
  353.   {
  354.     displaytext(helpdata,helptabs) ;
  355.     _kernel_oswrch(15) ;
  356.     return 0 ;
  357.   }
  358.  
  359.   /*-----------------------------------------------------------------*/
  360.   /*  If TEST MODE issue message.                                    */
  361.   /*-----------------------------------------------------------------*/
  362.  
  363.   if ( flags & flgtst )
  364.   {
  365.     puts("TEST MODE (files will be identified, not converted).\n") ;
  366.   }
  367.  
  368.   /*-----------------------------------------------------------------*/
  369.   /*  Invoke GetDirs function to read the directory entry(s) of the  */
  370.   /*  file(s) to be processed. Pass it a pointer of a processing     */
  371.   /*  function to be called.                                         */
  372.   /*-----------------------------------------------------------------*/
  373.  
  374.   returncode = 4 ;
  375.  
  376.   if ( getdirentrys(argptr,
  377.        ( flags & flgrcs ? RECURSE_ALWAYS : RECURSE_ONCE ),cnvtfunc) )
  378.   {
  379.     if ( flags & flgfnd )
  380.     {
  381.       printf("\n%d text file(s) ",filecount) ;
  382.       if ( flags & flgtst )
  383.       {
  384.         printf("would be ") ;
  385.       }
  386.       puts("converted.\n") ;
  387.       returncode = 0 ;
  388.     }
  389.     else
  390.     {
  391.       printf("No text files found matching '%s'\n",argptr) ;
  392.       beep() ;
  393.     }
  394.   }
  395.   
  396.   /*-----------------------------------------------------------------*/
  397.   /*  Close list file if open.                                       */
  398.   /*-----------------------------------------------------------------*/
  399.  
  400.   if ( flags & flglfo )
  401.   {
  402.     fclose(listfile) ;
  403.   }
  404.   
  405.   /*-----------------------------------------------------------------*/
  406.   /*  Return to caller. All done.                                    */
  407.   /*-----------------------------------------------------------------*/
  408.  
  409.   _kernel_oswrch(15) ;
  410.   return returncode ;
  411. }
  412.  
  413. /*===================================================================*/
  414. /*                                                                   */
  415. /*  cnvtfunc  -  perform actual file conversion                      */
  416. /*  --------                                                         */
  417. /*                                                                   */
  418. /*  This function is called by the "getdirentrys" function for each  */
  419. /*  file-name that it encounters.                                    */
  420. /*                                                                   */
  421. /*  Only files which have a filetype of "text" (0xFFF) will be       */
  422. /*  processed.                                                       */
  423. /*                                                                   */
  424. /*===================================================================*/
  425.  
  426. static enum boolean cnvtfunc(
  427.   const char *path,                     /* Pointer to path name.     */
  428.   direntry *ptr)                        /* Pointer to direntry info. */
  429. {
  430.   /*-----------------------------------------------------------------*/
  431.   /*  Local definitions.                                             */
  432.   /*-----------------------------------------------------------------*/
  433.  
  434.   char *infile ;                      /* Ptr to path + leafname      */
  435.   char *oufile ;                      /* Ptr tp path + leafname      */
  436.   int result ;                        /* Result from OS-File SWI     */
  437.   char *workarea ;                    /* Ptr to start of work area   */
  438.   char *workend ;                     /* Ptr to end of work area     */
  439.   char *iptr ;                        /* Working pointer             */
  440.   char *optr ;                        /* Working pointer             */
  441.   _kernel_osfile_block osfileblk ;    /* OS_File parameter block     */
  442.   char *(*lineptrs)[] ;               /* Ptr to line ptr array       */
  443.   char *lineptr ;                     /* Ptr to current line         */
  444.   char (*lineflgs)[] ;                /* Ptr to line flags array     */
  445.   int lineflg ;                       /* Copy of current line flags  */
  446.   int len ;                           /* Length of current line      */
  447.   int maxlen ;                        /* Maximum length of any line  */
  448.   int brklen ;                        /* Short line threshold        */
  449.   int line ;                          /* Current line number         */
  450.   int nlines ;                        /* Number of lines             */
  451.   int nblanks ;                       /* Working blanks counter      */
  452.   int indent ;                        /* Indent of current line      */
  453.   int nextindent ;                    /* Indent of next line         */
  454.   int minindent ;                     /* Minimum indent of any line  */
  455.   FILE *fileptr ;                     /* Output file pointer         */
  456.   FILE *lstfptr ;                     /* List file pointer           */
  457.   int i,j,k ;                         /* Working integers            */
  458.   int c ;                             /* Working character           */
  459.   char flagprnt[8] ;                  /* Flag display work area      */
  460.  
  461.   static const char toomanylines[] =
  462.     "'%s' contains too many lines to process" ;
  463.  
  464.   /*-----------------------------------------------------------------*/
  465.   /*  Executable statements                                          */
  466.   /*                                                                 */
  467.   /*  Return immediatley if not a text file.                         */
  468.   /*-----------------------------------------------------------------*/
  469.  
  470.   if ( ptr->type != 0xfff )
  471.   {
  472.     return TRUE ;
  473.   }
  474.  
  475.   /*-----------------------------------------------------------------*/
  476.   /*  If test mode without "-l" option, just list file name.         */
  477.   /*-----------------------------------------------------------------*/
  478.  
  479.   if ( (flags & (flgtst | flglst)) == flgtst )
  480.   {
  481.     if ( !(flags & flgfnd) )
  482.     {
  483.       flags |= flgfnd ;
  484.       puts("The following files would be converted "
  485.            "to 1stWord+ documents:\n") ;
  486.     }
  487.     printf("%s%s\n",path,ptr->name) ;
  488.     filecount++ ;
  489.     return TRUE ;
  490.   }
  491.  
  492.   /*-----------------------------------------------------------------*/
  493.   /*  Create input and output filespecs                              */
  494.   /*-----------------------------------------------------------------*/
  495.   
  496.   if ( ( infile = malloc(2 * (strlen(path) + strlen(ptr->name) + 1)
  497.                          + strlen(pfxptr) ) ) == NULL )
  498.   {
  499.     puts("Out of memory") ;
  500.     return FALSE ;
  501.   } 
  502.   
  503.   oufile = infile + sprintf(infile,"%s%s",path,ptr->name) + 1 ;
  504.   sprintf(oufile,"%s%s%s",path,flags & flgovr ? "" : pfxptr,ptr->name) ;
  505.   
  506.   /*-----------------------------------------------------------------*/
  507.   /*  Check input file has non-zero length.                          */
  508.   /*-----------------------------------------------------------------*/
  509.  
  510.   if ( ptr->length == 0 )
  511.   {
  512.     printf("'%s' is empty\n",infile) ;
  513.   }
  514.  
  515.   /*-----------------------------------------------------------------*/
  516.   /*  Allocate memory for file and load it.                          */
  517.   /*-----------------------------------------------------------------*/
  518.  
  519.   if ( (workarea = malloc((ptr->length)+1)) == NULL )
  520.   {
  521.     printf("'%s' too large to load",infile) ;
  522.     goto error4 ;
  523.   }
  524.  
  525.   workend = workarea + ptr->length ;
  526.  
  527.   osfileblk.load  = (int)workarea ;
  528.   osfileblk.exec  = 0 ;
  529.   result = _kernel_osfile(255,infile,&osfileblk) ;
  530.  
  531.   if ( result == _kernel_ERROR )
  532.   {
  533.     printf("'%s' load failed - %s\n",infile,
  534.           _kernel_last_oserror()->errmess) ;
  535.     goto error3 ;
  536.   }
  537.  
  538.   /*-----------------------------------------------------------------*/
  539.   /*  If missing new-line at end of file, add one (provided that     */
  540.   /*  file is non-zero length).                                      */
  541.   /*-----------------------------------------------------------------*/
  542.  
  543.   if ( workend > workarea && *(workend-1) != '\n' )
  544.   {
  545.     *(workend++) = '\n' ;
  546.     printf("No newline character at end of '%s' - inserted\n",infile) ;
  547.     beep() ;
  548.   }
  549.  
  550.   /*-----------------------------------------------------------------*/
  551.   /*  Count number of newline characters in file. Substitute null    */
  552.   /*  bytes for the newline characters. Also determine the minimum   */
  553.   /*  indentation of any line.                                       */
  554.   /*-----------------------------------------------------------------*/
  555.  
  556.   indent = minindent = INT_MAX ;
  557.   nblanks = nlines = 0 ;
  558.  
  559.   for ( iptr = workarea ;iptr < workend ; iptr++ )
  560.   {
  561.     switch (*iptr)
  562.     {
  563.       /*-------------------------------------------------------------*/
  564.       /*  Process newline character.                                 */
  565.       /*-------------------------------------------------------------*/
  566.  
  567.       case '\n' :
  568.         
  569.         *iptr = '\0' ;
  570.         nlines++ ;
  571.         if ( indent < minindent )
  572.         {
  573.           minindent = indent ;
  574.         }
  575.         nblanks = 0 ;
  576.         indent  = INT_MAX ;
  577.         break ;
  578.  
  579.       /*-------------------------------------------------------------*/
  580.       /*  Process blank.                                             */
  581.       /*-------------------------------------------------------------*/
  582.  
  583.       case ' ' :
  584.         
  585.         nblanks++ ;
  586.         break ;
  587.  
  588.       /*-------------------------------------------------------------*/
  589.       /*  Process other characters                                   */
  590.       /*-------------------------------------------------------------*/
  591.  
  592.       default :
  593.         if ( indent == INT_MAX )
  594.         {
  595.           indent = nblanks ;
  596.         }
  597.     }
  598.   }
  599.  
  600.   /*-----------------------------------------------------------------*/
  601.   /*  Allocate storage for pointer array. Perform second pass        */
  602.   /*  through loaded file setting up pointers, shifting each line    */
  603.   /*  left by minimum indentation amount, truncating any trailing    */
  604.   /*  blanks and computing maximum line length.                      */
  605.   /*-----------------------------------------------------------------*/
  606.  
  607.   if ( (lineptrs = malloc((nlines+2) * sizeof(char *))) == NULL )
  608.   {
  609.     printf(toomanylines,infile) ;
  610.     goto error3 ;
  611.   }
  612.  
  613.   iptr = optr = workarea ;
  614.   maxlen = 0 ;
  615.   (*lineptrs)[0] = (*lineptrs)[nlines+1] = "" ;
  616.  
  617.   for ( line = 1 ; line <= nlines ; line++ )
  618.   {
  619.     (*lineptrs)[line] = lineptr = optr ;
  620.     len = strlen(iptr) ;
  621.     if ( len < minindent )
  622.     {
  623.       iptr += len + 1 ;
  624.     }
  625.     else
  626.     {
  627.       iptr += minindent ;
  628.       for ( ; (*optr = *(iptr++)) != '\0' ; optr++ ) ;
  629.       while ( optr > lineptr )
  630.       {
  631.         if ( *(--optr) != ' ' )
  632.         {
  633.           optr++ ;
  634.           break ;
  635.         }
  636.       }
  637.       if ( (len = optr - lineptr) > maxlen )
  638.       {
  639.         maxlen = len ;
  640.       }
  641.     }
  642.     *(optr++) = '\0' ;
  643.  
  644.   }
  645.  
  646.   brklen = 2 * maxlen / 3 ;
  647.  
  648.   /*-----------------------------------------------------------------*/
  649.   /*  Allocate storage for line flag array                           */
  650.   /*-----------------------------------------------------------------*/
  651.  
  652.   if ( (lineflgs = malloc(nlines+2)) == NULL )
  653.   {
  654.     printf(toomanylines,infile) ;
  655.     goto error2 ;
  656.   }
  657.   memset(lineflgs,0,nlines+2) ;
  658.  
  659.   /*-----------------------------------------------------------------*/
  660.   /*  Perform pass through file setting flags to indicate how the    */
  661.   /*  line should be converted.                                      */
  662.   /*-----------------------------------------------------------------*/
  663.  
  664.   nextindent = leadblanks((*lineptrs)[1]) ;
  665.  
  666.   for ( line = 1 ; line <= nlines ; line++ )
  667.   {
  668.     lineptr = (*lineptrs)[line] ;
  669.     lineflg = (*lineflgs)[line] ;
  670.     len     = strlen(lineptr) ;
  671.     indent  = nextindent ;
  672.     nextindent = leadblanks((*lineptrs)[line+1]) ;
  673.  
  674.     /*---------------------------------------------------------------*/
  675.     /*  If null line encountered or if this line starts with a       */
  676.     /*  period (i.e. is SCRIPT/VS control word) set break flag on    */
  677.     /*  this and on previous line.                                   */
  678.     /*---------------------------------------------------------------*/
  679.  
  680.     if ( *lineptr == '\0' || *lineptr == '.' )
  681.     {
  682.       (*lineflgs)[line-1] |= brkflg ;
  683.       lineflg |= brkflg ;
  684.     }
  685.  
  686.     /*---------------------------------------------------------------*/
  687.     /*  If not a null line then it must be analysed to determine     */
  688.     /*  any special formatting requirements.                         */
  689.     /*---------------------------------------------------------------*/
  690.  
  691.     else
  692.     {
  693.       /*-------------------------------------------------------------*/
  694.       /*  If line begins with a colon (i.e. is a SCRIPT/VS GML       */
  695.       /*  tag) then set break flag on previous line (provided        */
  696.       /*  that it is not one of the GML tags that I prefer NOT       */
  697.       /*  to be treated as a break).                                 */
  698.       /*-------------------------------------------------------------*/
  699.  
  700.       if ( *lineptr == ':' && !istag(lineptr) )
  701.       {
  702.         (*lineflgs)[line-1] |= brkflg ;
  703.       }
  704.  
  705.       /*-------------------------------------------------------------*/
  706.       /*  Count number of punctuation characters in line. If         */
  707.       /*  they make up more than two thirds of the non-blank         */
  708.       /*  characters on the line, treat it as an 'asis' line.        */
  709.       /*-------------------------------------------------------------*/
  710.  
  711.       for ( i = j = k = 0 ; i < len ; i++ )
  712.       {
  713.         c = lineptr[i] ;
  714.         if ( ispunct(c) )
  715.         {
  716.           j++ ;
  717.         }
  718.         else if ( c == ' ' )
  719.         {
  720.           k++ ;
  721.         }
  722.       }
  723.  
  724.       if ( j > 2 * (len - k) / 3 )
  725.       {
  726.         (*lineflgs)[line-1] |= brkflg ;
  727.         lineflg |= brkflg | hrdflg ;
  728.       }
  729.  
  730.       /*-------------------------------------------------------------*/
  731.       /*  Current line indented more than next line.                 */
  732.       /*                                                             */
  733.       /*  If current line is part of hanging indent, assume end      */
  734.       /*  of list item, so set break flag on current line.           */
  735.       /*  If line starts with bullet-like format assume a one        */
  736.       /*  line list item and set break flag on current line and      */
  737.       /*  on previous line. Otherwise assume start of new            */
  738.       /*  paragraph.                                                 */
  739.       /*-------------------------------------------------------------*/
  740.  
  741.       else if ( indent > nextindent )
  742.       {
  743.         if ( lineflg & hinflg )
  744.         {
  745.           lineflg |= brkflg ;
  746.           if ( isitem(lineptr,indent) )
  747.           {
  748.             (*lineflgs)[line-1] |= brkflg ;
  749.             lineflg |= itmflg ;
  750.           }
  751.         }
  752.         else
  753.         {
  754.           (*lineflgs)[line-1] |= brkflg ;
  755.           if( isitem(lineptr,indent) )
  756.           {
  757.             lineflg |= brkflg | itmflg ;
  758.           }
  759.           else
  760.           {
  761.             lineflg |= pinflg ;
  762.             if ( isshort(lineptr,len,brklen) )
  763.             {
  764.               lineflg |= brkflg ;
  765.             }
  766.           }
  767.         }
  768.       }
  769.  
  770.       /*-------------------------------------------------------------*/
  771.       /*  Current line at same indentation as next.                  */
  772.       /*                                                             */
  773.       /*  Set break flag if short line. If line starts with          */
  774.       /*  bullet-like format assume one line list item.              */
  775.       /*-------------------------------------------------------------*/
  776.  
  777.       else if ( indent == nextindent )
  778.       {
  779.         if ( isshort(lineptr,len,brklen) )
  780.         {
  781.           lineflg |= brkflg ;
  782.         }
  783.         if ( isitem(lineptr,indent) )
  784.         {
  785.           (*lineflgs)[line-1] |= brkflg ;
  786.           lineflg |= itmflg | brkflg ;
  787.         }
  788.         if ( !(lineflg & hinflg) )
  789.         {
  790.           if ( indent > 0 )
  791.           {
  792.             (*lineflgs)[line-1] |= brkflg ;
  793.             lineflg |= hinflg ;
  794.             sethangingindent(line,nlines,nextindent,
  795.                   lineptrs,lineflgs) ;
  796.           }
  797.         }
  798.       }
  799.  
  800.       /*-------------------------------------------------------------*/
  801.       /*  Current line is less indented than next line.              */
  802.       /*                                                             */
  803.       /*  Assume start of hanging indent. Set item flag if line      */
  804.       /*  appears to start with bullet-like format or list term.     */
  805.       /*                                                             */
  806.       /*  Set hanging indent flag on all following lines of same     */
  807.       /*  or greater indent than next line.                          */
  808.       /*-------------------------------------------------------------*/
  809.  
  810.       else
  811.       {
  812.         if ( isitem(lineptr,indent) ||
  813.             isterm(lineptr,len,indent,nextindent) )
  814.         {
  815.           (*lineflgs)[line-1] |= brkflg ;
  816.           lineflg |= itmflg ;
  817.         }
  818.         if ( isshort(lineptr,len,brklen) )
  819.         {
  820.           lineflg |= brkflg ;
  821.         }
  822.         sethangingindent(line,nlines,nextindent,lineptrs,lineflgs) ;
  823.       }
  824.     }
  825.     (*lineflgs)[line] = lineflg ;
  826.   }
  827.  
  828.   /*-----------------------------------------------------------------*/
  829.   /*  For debug purposes list document showing flag settings.        */
  830.   /*-----------------------------------------------------------------*/
  831.  
  832.   if ( flags & flglst )
  833.   {
  834.     if ( !(flags & flglfo) )
  835.     {
  836.       if ( lstptr != NULL )
  837.       {
  838.         if ( (listfile = fopen(lstptr,"w")) == NULL )
  839.         {
  840.           printf("Cannot open list file '%s'\n"
  841.                  "Listing will be written to "
  842.                  "standard output stream\n",lstptr) ;
  843.         }
  844.       }
  845.       flags |= flglfo ;
  846.     }
  847.     lstfptr = ( listfile == NULL ) ? stdout : listfile ;
  848.  
  849.     putdup('*',70,lstfptr) ;
  850.     fprintf(lstfptr,"\nConversion listing for file: %s\n",infile) ;
  851.     putdup('*',70,lstfptr) ;
  852.     fputc('\n',lstfptr) ;
  853.  
  854.     for ( line = 1 ; line <= nlines ; line++ )
  855.     {
  856.       lineflg = (*lineflgs)[line] ;
  857.       strcpy(flagprnt,".....  ") ;
  858.       if ( lineflg & brkflg )
  859.       {
  860.         flagprnt[0] = 'B' ;
  861.       }
  862.       if ( lineflg & pinflg )
  863.       {
  864.         flagprnt[1] = 'P' ;
  865.       }
  866.       if ( lineflg & itmflg )
  867.       {
  868.         flagprnt[2] = 'L' ;
  869.       }
  870.       if ( lineflg & hinflg )
  871.       {
  872.         flagprnt[3] = 'I' ;
  873.       }
  874.       if ( lineflg & hrdflg )
  875.       {
  876.         flagprnt[4] = 'A' ;
  877.       }
  878.       fprintf(lstfptr,"%s%s\n",flagprnt,(*lineptrs)[line]) ;
  879.     }
  880.     putdup('*',70,lstfptr) ;
  881.     fputs("\n\n",lstfptr) ;
  882.   }
  883.  
  884.   /*-----------------------------------------------------------------*/
  885.   /*  Unless test mode convert document and replace original file.   */
  886.   /*-----------------------------------------------------------------*/
  887.  
  888.   if ( !(flags & flgtst) )
  889.   {
  890.     /*---------------------------------------------------------------*/
  891.     /*  Open target file for output.                                 */
  892.     /*---------------------------------------------------------------*/
  893.  
  894.     if ( (fileptr = fopen(oufile,"w")) == NULL )
  895.     {
  896.       printf("Cannot open file '%s' - %s\n",oufile,
  897.              _kernel_last_oserror()->errmess) ;
  898.       goto error1 ;
  899.     }
  900.  
  901.     /*---------------------------------------------------------------*/
  902.     /*  Copy 1stWord+ header to output file.                         */
  903.     /*---------------------------------------------------------------*/
  904.  
  905.     for ( i = 0 ; i < sizeof(docheader) ; i++ )
  906.     {
  907.       fputc(docheader[i],fileptr) ;
  908.     }
  909.  
  910.     /*---------------------------------------------------------------*/
  911.     /*  Copy document to output file.                                */
  912.     /*---------------------------------------------------------------*/
  913.  
  914.     nextindent = leadblanks((*lineptrs)[1]) ;
  915.  
  916.     for ( line = 1 ; line <= nlines ; line++ )
  917.     {
  918.       lineptr = (*lineptrs)[line] ;
  919.       lineflg = (*lineflgs)[line] ;
  920.       len     = strlen(lineptr) ;
  921.       indent  = nextindent ;
  922.       nextindent = leadblanks((*lineptrs)[line+1]) ;
  923.  
  924.       i = 0 ;
  925.  
  926.       /*-------------------------------------------------------------*/
  927.       /*  If 'asis' line copy to output file asis.                   */
  928.       /*-------------------------------------------------------------*/
  929.  
  930.       if ( lineflg & hrdflg )
  931.       {
  932.         for ( ; (c = lineptr[i]) != '\0' ; i++ )
  933.         {
  934.           fputc(c,fileptr) ;
  935.         }
  936.       }
  937.  
  938.       /*-------------------------------------------------------------*/
  939.       /*  If 'paragraph' indent, indent with hard spaces.            */
  940.       /*-------------------------------------------------------------*/
  941.  
  942.       else if ( lineflg & pinflg )
  943.       {
  944.         putdup(' ',indent,fileptr) ;
  945.         i = indent ;
  946.       }
  947.  
  948.       /*-------------------------------------------------------------*/
  949.       /*  If hanging indent, indent with indent spaces.              */
  950.       /*-------------------------------------------------------------*/
  951.  
  952.       else if ( lineflg & hinflg )
  953.       {
  954.         fputc(0x1d,fileptr) ;
  955.         putdup(0x1c,indent-1,fileptr) ;
  956.         i = indent ;
  957.       }
  958.  
  959.       /*-------------------------------------------------------------*/
  960.       /*  If start of list item indent with indent spaces.           */
  961.       /*-------------------------------------------------------------*/
  962.  
  963.       if ( lineflg & itmflg )
  964.       {
  965.         j = 0 ;
  966.         c = lineptr[i] ;
  967.         while ( (i < nextindent || j < 2) && c != '\0' )
  968.         {
  969.           if (c == ' ')
  970.           {
  971.             fputc(0x1d,fileptr) ;
  972.             i++ ;
  973.             while ( (i < nextindent || j < 2) &&
  974.                   (c = lineptr[i]) == ' ' )
  975.             {
  976.               fputc(0x1c,fileptr) ;
  977.               i++ ;
  978.             }
  979.           }
  980.           else
  981.           {
  982.             j++ ;
  983.             while ( (c = lineptr[i]) != ' ' && c != '\0' )
  984.             {
  985.               fputc(c,fileptr) ;
  986.               i++ ;
  987.             }
  988.           }
  989.         }
  990.       }
  991.  
  992.       /*-------------------------------------------------------------*/
  993.       /*  Copy rest of line to output, converting blanks to          */
  994.       /*  'soft' blanks.                                             */
  995.       /*-------------------------------------------------------------*/
  996.  
  997.       for ( ; (c = lineptr[i]) != '\0' ; i++ )
  998.       {
  999.         if ( c == ' ' )
  1000.         {
  1001.           c = 0x1e ;
  1002.         }
  1003.         fputc(c,fileptr) ;
  1004.       }
  1005.  
  1006.       /*-------------------------------------------------------------*/
  1007.       /*  Unless break expected, add a final soft blank to the       */
  1008.       /*  end of the line.                                           */
  1009.       /*-------------------------------------------------------------*/
  1010.  
  1011.       if ( !(lineflg & brkflg) )
  1012.       {
  1013.         fputc(0x1e,fileptr) ;
  1014.       }
  1015.  
  1016.       /*-------------------------------------------------------------*/
  1017.       /*  Finish off with newline character.                         */
  1018.       /*-------------------------------------------------------------*/
  1019.  
  1020.       fputc('\n',fileptr) ;
  1021.     }
  1022.  
  1023.     /*---------------------------------------------------------------*/
  1024.     /*  Close file.                                                  */
  1025.     /*---------------------------------------------------------------*/
  1026.  
  1027.     fclose(fileptr) ;
  1028.   }
  1029.  
  1030.   /*-----------------------------------------------------------------*/
  1031.   /*  Free work area, line pointers, etc.                            */
  1032.   /*-----------------------------------------------------------------*/
  1033.  
  1034.   free(lineflgs) ;
  1035.   free(lineptrs) ;
  1036.   free(workarea) ;
  1037.  
  1038.   /*-----------------------------------------------------------------*/
  1039.   /*  Set record type to that for 1stWord+ Document.                 */
  1040.   /*-----------------------------------------------------------------*/
  1041.  
  1042.   if ( !(flags & flgtst) )
  1043.   {
  1044.     osfileblk.load  = 0xaf8 ;
  1045.     result = _kernel_osfile(18,oufile,&osfileblk) ;
  1046.  
  1047.     if ( result == _kernel_ERROR )
  1048.     {
  1049.       printf("'%s' SETTYPE failed - %s\n",oufile,
  1050.              _kernel_last_oserror()->errmess) ;
  1051.       goto error4 ;
  1052.     }
  1053.  
  1054.     printf("'%s' converted",infile) ;
  1055.     if ( !(flags & flgovr) )
  1056.     {
  1057.       printf(" to '%s'",oufile) ;
  1058.     }
  1059.     putchar('\n') ;
  1060.   }
  1061.  
  1062.   /*-----------------------------------------------------------------*/
  1063.   /*  Return to caller with RC=TRUE.                                 */
  1064.   /*-----------------------------------------------------------------*/
  1065.  
  1066.   free(infile) ;
  1067.   flags |= flgfnd ;
  1068.   filecount++ ;
  1069.   return TRUE ;
  1070.  
  1071.   /*-----------------------------------------------------------------*/
  1072.   /*  Error exits.                                                   */
  1073.   /*-----------------------------------------------------------------*/
  1074.  
  1075.   error1: free(lineflgs) ;
  1076.   error2: free(lineptrs) ;
  1077.   error3: free(workarea) ;
  1078.   error4: free(infile) ;
  1079.           beep() ;
  1080.  
  1081.   return FALSE ;
  1082. }
  1083.  
  1084. /*===================================================================*/
  1085. /*                                                                   */
  1086. /*  leadblanks  -  count number of leading blanks in line            */
  1087. /*  ----------                                                       */
  1088. /*                                                                   */
  1089. /*  This function determines the indent of the line passed as an     */
  1090. /*  argument.                                                        */
  1091. /*                                                                   */
  1092. /*===================================================================*/
  1093.  
  1094. static int leadblanks(
  1095.   const char *lineptr)                  /* Ptr to line text          */
  1096. {
  1097.   int i = 0 ;
  1098.   while ( lineptr[i] == ' ' )
  1099.   {
  1100.     i++ ;
  1101.   }
  1102.   return i ;
  1103. }
  1104.  
  1105. /*===================================================================*/
  1106. /*                                                                   */
  1107. /*  isitem  -  test for list item format                             */
  1108. /*  ------                                                           */
  1109. /*                                                                   */
  1110. /*  Check for line staring with:                                     */
  1111. /*                                                                   */
  1112. /*  -  a bullet ("*", "-", or "o")                                   */
  1113. /*  -  an alphabetic id in form "(x)" or "x)"                        */
  1114. /*  -  a numeric id in form "(n)", "(nn)", "n)", "nn)", "n.", "nn."  */
  1115. /*  -  a roman numeral in form "(r)", "r)",  "r." or "r"             */
  1116. /*                                                                   */
  1117. /*===================================================================*/
  1118.  
  1119. static enum boolean isitem(
  1120.   const char *lineptr,                 /* Ptr to line text           */
  1121.   const int indent)                    /* Count of leading blanks    */
  1122. {
  1123.   const char *p ;
  1124.   int l ;
  1125.  
  1126.   lineptr += indent ;
  1127.  
  1128.   if ( (p = strchr(lineptr,' ')) == NULL )
  1129.   {
  1130.     return FALSE ;
  1131.   }
  1132.  
  1133.   if ( (l = p - lineptr) == 1 )
  1134.   {
  1135.     if ( strchr("*-o",*lineptr) != NULL )
  1136.     {
  1137.       return TRUE ;
  1138.     }
  1139.   }
  1140.   else
  1141.   {
  1142.     p-- ;
  1143.  
  1144.     if ( *lineptr == '(' && *p == ')' )
  1145.     {
  1146.       lineptr++ ;
  1147.       l -= 2 ;
  1148.       if ( l == 1 && isalpha(*lineptr) )
  1149.       {
  1150.         return TRUE ;
  1151.       }
  1152.       return (isnumbered(lineptr,l) | isroman(lineptr,l)) ;
  1153.     }
  1154.  
  1155.     if ( *p == ')' )
  1156.     {
  1157.       l-- ;
  1158.       if ( l == 1 && isalpha(*lineptr) )
  1159.       {
  1160.         return TRUE ;
  1161.       }
  1162.       return (isnumbered(lineptr,l) | isroman(lineptr,l)) ;
  1163.     }
  1164.  
  1165.     if ( *p == '.' )
  1166.     {
  1167.       l-- ;
  1168.       return (isnumbered(lineptr,l) | isroman(lineptr,l)) ;
  1169.     }
  1170.   }
  1171.   
  1172.   return isroman(lineptr,l) ;
  1173. }
  1174.  
  1175. /*===================================================================*/
  1176. /*                                                                   */
  1177. /*  isnumbered  -  check for one or two digit number                 */
  1178. /*  ----------                                                       */
  1179. /*                                                                   */
  1180. /*  This function checks that the argument passed is either a one    */
  1181. /*  or two digit number.                                             */
  1182. /*                                                                   */
  1183. /*===================================================================*/
  1184.  
  1185. static enum boolean isnumbered(
  1186.   const char *p,                        /* Ptr to item number        */
  1187.   const int l)                          /* Length of item number     */
  1188. {
  1189.   if ( l == 1 )
  1190.   {
  1191.     if ( isdigit(*p) )
  1192.     {
  1193.       return TRUE ;
  1194.     }
  1195.   }
  1196.   else if ( l == 2 )
  1197.   {
  1198.     if ( isdigit(*p) && isdigit(*(p+1)) )
  1199.     {
  1200.       return TRUE ;
  1201.     }
  1202.   }
  1203.   return FALSE ;
  1204. }
  1205.  
  1206. /*===================================================================*/
  1207. /*                                                                   */
  1208. /*  isroman  -  check for roman numeral                              */
  1209. /*  -------                                                          */
  1210. /*                                                                   */
  1211. /*  This function checks that the argument passed is a roman numeral */
  1212. /*  in the range one to twenty (i - xx).                             */
  1213. /*                                                                   */
  1214. /*===================================================================*/
  1215.  
  1216. static enum boolean isroman(
  1217.   const char *p,                        /* Ptr to item number        */
  1218.   const int l)                          /* Length of item number     */
  1219. {
  1220.   static const char *const roman[] =
  1221.   {
  1222.     "i",
  1223.     "v",
  1224.     "x",
  1225.     "ii",
  1226.     "iv",
  1227.     "vi",
  1228.     "ix",
  1229.     "xi",
  1230.     "xv",
  1231.     "xx",
  1232.     "vii",
  1233.     "iii",
  1234.     "xii",
  1235.     "xiv",
  1236.     "xvi",
  1237.     "xix",
  1238.     "viii",
  1239.     "xiii",
  1240.     "xvii",
  1241.     "xviii"
  1242.   } ;
  1243.  
  1244.   static const char lroman[] =
  1245.   {
  1246.     1,         /* i            */
  1247.     1,         /* v            */
  1248.     1,         /* x            */
  1249.     2,         /* ii           */
  1250.     2,         /* iv           */
  1251.     2,         /* vi           */
  1252.     2,         /* ix           */
  1253.     2,         /* xi           */
  1254.     2,         /* xv           */
  1255.     2,         /* xx           */
  1256.     3,         /* vii          */
  1257.     3,         /* iii          */
  1258.     3,         /* xii          */
  1259.     3,         /* xiv          */
  1260.     3,         /* xvi          */
  1261.     3,         /* xix          */
  1262.     4,         /* viii         */
  1263.     4,         /* xiii         */
  1264.     4,         /* xvii         */
  1265.     5,         /* xviii        */
  1266.     99         /* end of list  */
  1267.   } ;
  1268.  
  1269.   int i,j ;
  1270.  
  1271.   if ( strspn(p,"ivx") != l || l > 5 )
  1272.   {
  1273.     return FALSE ;
  1274.   }
  1275.  
  1276.   for ( i = 0 ; l > lroman[i] ; i++ ) ;
  1277.   
  1278.   for ( ; l == lroman[i] ; i++ )
  1279.   {
  1280.     j = 0 ;
  1281.     while ( p[j] == roman[i][j] )
  1282.    {
  1283.      j++ ;
  1284.      if ( j == l )
  1285.      {
  1286.        return TRUE ;
  1287.      }
  1288.    }
  1289.   }
  1290.   return FALSE ;
  1291. }
  1292.  
  1293. /*===================================================================*/
  1294. /*                                                                   */
  1295. /*  isterm  -  test for definition list item                         */
  1296. /*  ------                                                           */
  1297. /*                                                                   */
  1298. /*  Assume a line starts with a definition term if:                  */
  1299. /*                                                                   */
  1300. /*  - line is indented less than following line                      */
  1301. /*  - line extends beyond ident of following line                    */
  1302. /*  - indent of following line matches start of a word on this line  */
  1303. /*                                                                   */
  1304. /*===================================================================*/
  1305.  
  1306. static enum boolean isterm(
  1307.   const char *lineptr,                  /* Ptr to line text          */
  1308.   int len,                              /* Length of line            */
  1309.   int indent,                           /* Indent of this line       */
  1310.   int nextindent)                       /* Indent of next line       */
  1311. {
  1312.   if ( indent < nextindent &&
  1313.       len > nextindent &&
  1314.       lineptr[nextindent] != ' ' &&
  1315.       lineptr[nextindent-1] == ' ' )
  1316.   {
  1317.     return TRUE ;
  1318.   }
  1319.   return FALSE ;
  1320. }
  1321.  
  1322. /*===================================================================*/
  1323. /*                                                                   */
  1324. /*  istag  -  test for list of non-break GML tags                    */
  1325. /*  -----                                                            */
  1326. /*                                                                   */
  1327. /*  This function tests the start of the line for the presence of    */
  1328. /*  GML tags which are not required to start on a separate line.     */
  1329. /*                                                                   */
  1330. /*===================================================================*/
  1331.  
  1332. static enum boolean istag(
  1333.   const char *lineptr)                  /* Ptr to line text          */
  1334. {
  1335.   static const char *const tags[] =
  1336.   {
  1337.     "HP",                           /* :hp and :ehp                  */
  1338.     "CIT",                          /* :cit and :ecit                */
  1339.     "Q",                            /* :q and :eq                    */
  1340.     "FNREF",                        /* :fnref and :efnref            */
  1341.     "HDREF"                         /* :hdref and :ehdref            */
  1342.   } ;
  1343.   #define ntags (sizeof(tags)/sizeof(char *))
  1344.  
  1345.   int i,j,c ;
  1346.   const char *tag ;
  1347.  
  1348.   lineptr++ ;
  1349.  
  1350.   if ( toupper(*lineptr) == 'E' )
  1351.   {
  1352.     lineptr++ ;
  1353.   }
  1354.  
  1355.   for ( i = 0 ; i < ntags ; i++ )
  1356.   {
  1357.     tag = tags[i] ;
  1358.     c = tag[0] ;
  1359.     j = 0 ;
  1360.     while ( c == toupper(lineptr[j]) )
  1361.     {
  1362.       j++ ;
  1363.       if ( (c = tag[j]) == '\0' )
  1364.       {
  1365.         return TRUE ;
  1366.       }
  1367.     }
  1368.   }
  1369.   return FALSE ;
  1370. }
  1371.  
  1372. /*===================================================================*/
  1373. /*                                                                   */
  1374. /*  isshort  -  check for short line                                 */
  1375. /*  -------                                                          */
  1376. /*                                                                   */
  1377. /*  This function checks for a short line ending with a punctuation  */
  1378. /*  character (i.e. a 'break' line).                                 */
  1379. /*                                                                   */
  1380. /*===================================================================*/
  1381.  
  1382. static enum boolean isshort(
  1383.   const char *lineptr,                  /* Ptr to line text          */
  1384.   const int len,                        /* Length of line            */
  1385.   const int brklen)                     /* Max length of short line  */
  1386. {
  1387.   if ( len > brklen || strchr(".:!?",lineptr[len-1]) == NULL )
  1388.   {
  1389.     return FALSE ;
  1390.   }
  1391.   return TRUE ;   
  1392. }
  1393.  
  1394. /*===================================================================*/
  1395. /*                                                                   */
  1396. /*  sethangingindent  -  scan forward setting hanging indent flag    */
  1397. /*  ----------------                                                 */
  1398. /*                                                                   */
  1399. /*  This function scans forward from the current line setting the    */
  1400. /*  hanging-indent flag on all lines which have the same or greater  */
  1401. /*  indent than the line following the current line. Null lines      */
  1402. /*  are ignored.                                                     */
  1403. /*                                                                   */
  1404. /*===================================================================*/
  1405.  
  1406. static void sethangingindent(
  1407.   int line,                             /* Current line number       */
  1408.   int nlines,                           /* Number of lines           */
  1409.   int nextindent,                       /* Indent of next line       */
  1410.   char *(*lineptrs)[],                  /* Ptr to line ptr array     */
  1411.   char (*lineflgs)[])                   /* Ptr to line flags array   */
  1412. {
  1413.   char *p ;
  1414.  
  1415.   for ( ++line ;
  1416.         line <= nlines &&
  1417.         (*(p = (*lineptrs)[line]) == '\0' ||
  1418.         (leadblanks(p) >= nextindent)) ;
  1419.         line++ )
  1420.   {
  1421.     if ( *p != '\0' )
  1422.     {
  1423.       (*lineflgs)[line] |= hinflg ;
  1424.     }
  1425.   }
  1426. }
  1427.  
  1428. /*===================================================================*/
  1429. /*                                                                   */
  1430. /*  putdup  -  write 'n' duplicate characters to output file         */
  1431. /*  ------                                                           */
  1432. /*                                                                   */
  1433. /*  This function writes a single character repeatedly to the        */
  1434. /*  1stWord+ document output file.                                   */
  1435. /*                                                                   */
  1436. /*===================================================================*/
  1437.  
  1438. static void putdup(
  1439.   const int c,                          /* Character to be repeated  */
  1440.   const int n,                          /* Duplication count         */
  1441.   FILE *fileptr)                        /* File pointer              */
  1442. {
  1443.   int i ;
  1444.  
  1445.   for ( i = 0 ; i < n ; i++ )
  1446.   {
  1447.     fputc(c,fileptr) ;
  1448.   }
  1449. }
  1450.  
  1451. /*===================================================================*/
  1452.