home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 167_01 / mtp.c < prev    next >
Text File  |  1985-11-14  |  20KB  |  792 lines

  1. /* 
  2. HEADER:     CUG
  3. TITLE:        MTP.C - Macro Text Processor;
  4. VERSION:    0.05;
  5. DATE:        09/13/86;
  6. DESCRIPTION:    "MTP is a Macro Text Processor that performs
  7.         macro expansions of specified text patterns in
  8.         a file. Based on the deterministic finite state
  9.         automaton of UNIX's fgrep utility, it has the
  10.         distinction of not requiring a symbol table for
  11.         the list of macro expansions. The text patterns
  12.         are identified without backtracking, and need not
  13.         consist of one alphanumeric 'word' as with most
  14.         other macro processors.";
  15. KEYWORDS:    macro,text,fgrep,filter;
  16. SYSTEM:        ANY;
  17. FILENAME:    MTP.C;
  18. WARNINGS:    "As you can see from the version number, MTP is
  19.         very much in the developmental stage. This is not
  20.         to say it is bug-ridden. What is presented here
  21.         should be free of bugs. It does however have very
  22.         few options at present. These will be added in
  23.         future versions, based on user (yes - YOU!)
  24.         feedback. It is being released as an example of
  25.         what you can do with the Aho-Corasick algorithm
  26.         other than FGREP (see FGREP.DOC)";
  27. CRC:        xxxx
  28. SEE-ALSO:    FGREP.DOC,FGREP.C;
  29. AUTHORS:    Ian Ashdown - byHeart Software;
  30. COMPILERS:    ANY;
  31. REFERENCES:    ;
  32. ENDREF
  33. */
  34.  
  35. /*-------------------------------------------------------------*/
  36.  
  37. /* MTP.C - Macro Text Processor
  38.  *
  39.  * Version 0.05          September 13th, 1986
  40.  *
  41.  * Copyright 1986:    Ian Ashdown
  42.  *              byHeart Software
  43.  *              620 Ballantree Road
  44.  *              West Vancouver, B.C.
  45.  *              Canada V7S 1W3
  46.  *              (604) 922-6148
  47.  *
  48.  * This program may be copied for personal, non-commercial use
  49.  * only, provided that the above copyright notice is included in
  50.  * all copies of the source code. Copying for any other use
  51.  * without previously obtaining the written permission of the
  52.  * author is prohibited.
  53.  *
  54.  * Notes:
  55.  *
  56.  * The algorithm used in this program constructs a deterministic
  57.  * finite state automaton (FSA) for pattern matching from the sub-
  58.  * strings, then uses the FSA to process the text string in one
  59.  * pass. The time taken to construct the FSA is proportional to
  60.  * the sum of the lengths of the the substrings. The number of
  61.  * state transitions made by the FSA in processing the text
  62.  * string is independent of the number of substrings.
  63.  *
  64.  * Algorithm Source:
  65.  *
  66.  * "Efficient String Matching: An Aid to Bibliographic Search"
  67.  * Alfred V. Aho & Margaret J. Corasick
  68.  * Communications of the ACM
  69.  * pp. 333 - 340, Vol. 18 No. 6 (June '75)
  70.  *
  71.  * USAGE: mtp [-n] string_file <files>
  72.  *
  73.  * where:
  74.  *
  75.  *    -n    Substitutions are not nested. That is, once a
  76.  *        translation string has been substituted for a
  77.  *        source string, the substitution itself is not
  78.  *        scanned for another string to substitute.
  79.  *
  80.  *    string_file
  81.  *
  82.  *        is a text file of newline-separated
  83.  *        source-translation strings. Each line of the file
  84.  *        comprises one string, and consists of a source
  85.  *        string, followed by one or more '\t' (tab)
  86.  *        characters, and a translation string. Source
  87.  *        strings may not contain '\t' or '\0' (NULL)
  88.  *        characters. Translation strings may not contain
  89.  *        '\0' characters. Empty lines consisting of only a
  90.  *        newline ('\n') are permitted. Example:
  91.  *
  92.  *        Replace me\twith me!
  93.  *
  94.  *    file    is any text file consisting of newline-separated
  95.  *        strings.
  96.  *
  97.  * DIAGNOSTICS:
  98.  *
  99.  * Exit status is 0 unless error was encountered, in which case
  100.  * an exit status of 2 is returned.
  101.  *
  102.  * BUGS:  Lines are limited to 256 characters.
  103.  */
  104.  
  105. /*** Include Files ***/
  106.  
  107. #include <stdio.h>
  108. #include <ctype.h>
  109.  
  110. /*** Definitions ***/
  111.  
  112. #define TRUE           1
  113. #define FALSE           0
  114. #define MAX_LINE     257  /* Maximum number of characters */
  115.                   /* per line plus NULL delimiter */
  116.  
  117. /* The following definition applies to the Lattice C compiler
  118.  * (Version 2.15) for MS-DOS. The Lattice library does not have
  119.  * the Unix function "index()". However, it does have an
  120.  * equivalent function called "stpchr()".
  121.  */
  122.  
  123. /* #define index stpchr */    /* Remove comment delimiters for */
  124.                   /* Lattice C version 2.15 */
  125.  
  126. #define CMD_ERR       0    /* Error codes */
  127. #define OPT_ERR       1
  128. #define INP_ERR       2
  129. #define STR_ERR       3
  130. #define MEM_ERR       4
  131.  
  132. /*** Typedefs ***/
  133.  
  134. typedef int BOOL;    /* Boolean flag */
  135.  
  136. /* Some C compilers, including Lattice C, do not support the
  137.  * "void" data type. Remove the following comment delimiters for
  138.  * such compilers.
  139.  */
  140.  
  141. /* typedef int void; */
  142.  
  143. /*** Data Structures ***/
  144.  
  145. /* Queue element */
  146.  
  147. typedef struct queue
  148. {
  149.   struct state_el *st_ptr;
  150.   struct queue *next_el;
  151. } QUEUE;
  152.  
  153. /* Transition element */
  154.  
  155. typedef struct transition
  156. {
  157.   char lchar;            /* Transition character */
  158.   struct state_el *nextst_ptr;    /* Transition state pointer */
  159.   struct transition *next_el;
  160. } TRANSITION;
  161.  
  162. /* FSA state element */
  163.  
  164. typedef struct state_el
  165. {
  166.   TRANSITION *go_ls;    /* Pointer to head of "go" list */
  167.   TRANSITION *mv_ls;    /* Pointer to head of "move" list */
  168.   struct state_el *fail_state;    /* "failure" transition state */
  169.   char *out_str;    /* Terminal state message (if any) */
  170.   int src_length;    /* Source substring length */
  171.   int trn_length;    /* Translation substring length */
  172. } FSA;
  173.  
  174. /*** Global Variables and Structures ***/
  175.  
  176. /* Dummy "failure" state */
  177.  
  178. FSA FAIL_STATE;
  179.  
  180. /* Define a separate data structure for State 0 of the FSA to
  181.  * speed processing of the input while the FSA is in that state.
  182.  * Since the Aho-Corasick algorithm only defines "go" transitions
  183.  * for this state (one for each valid input character) and no
  184.  * "failure" transitions or output messages, only an array of
  185.  * "go" transition state numbers is needed. The array is accessed
  186.  * directly, using the input character as the index.
  187.  */
  188.  
  189. FSA *MZ[128];    /* State 0 of FSA */
  190.  
  191. /* Command-line option flags */
  192.  
  193. BOOL nflag = FALSE;  /* Substitution nesting disabled if TRUE */
  194.  
  195. /*** Main Body of Program ***/
  196.  
  197. int main(argc,argv)
  198. int argc;
  199. char **argv;
  200. {
  201.   char *temp;
  202.   void proc_file(),
  203.        bd_go(),
  204.        bd_move(),
  205.        error();
  206.  
  207.   /* Check for minimum number of command line arguments */
  208.  
  209.   if(argc < 2)
  210.     error(CMD_ERR,NULL);
  211.  
  212.   /* Parse the command line for user-selected options */
  213.  
  214.   while(--argc && (*++argv)[0] == '-')
  215.     for(temp = argv[0]+1; *temp != '\0'; temp++)    
  216.       switch(toupper(*temp))
  217.       {
  218.     case 'N':
  219.       nflag = TRUE;
  220.       break;
  221.     default:
  222.       error(OPT_ERR,NULL);
  223.       }
  224.  
  225.   /* Check for string file argument */
  226.  
  227.   if(!argc)
  228.     error(CMD_ERR,NULL);
  229.  
  230.   /* Build the "go" transitions. */
  231.  
  232.   bd_go(*argv++);
  233.   argc--;
  234.  
  235.   /* Build the "failure" and "move" transitions */
  236.  
  237.   bd_move();
  238.  
  239.   /* Process each of the input files if not "stdin". */
  240.  
  241.   if(!argc)
  242.     proc_file(NULL);
  243.   else
  244.     while(argc--)
  245.       proc_file(*argv++);
  246.  
  247.   /* Return 0 to the parent process to indicate no errors. */
  248.  
  249.   exit(0);
  250. }
  251.  
  252. /*** Functions and Procedures ***/
  253.  
  254. /* PROC_FILE() - Run the FSA on the input file "in_file" */
  255.  
  256. void proc_file(in_file)
  257. char *in_file;
  258. {
  259.   char buffer[2*MAX_LINE],    /* Input string buffer */
  260.        *nl,
  261.        *index(),
  262.        *fgets();
  263.   FILE *in_fd,
  264.        *fopen();
  265.   void run_fsa(),
  266.        error();
  267.  
  268.   if(in_file != NULL)    /* A file was specified as the input */
  269.   {
  270.     if(!(in_fd = fopen(in_file,"r")))
  271.       error(INP_ERR,in_file);
  272.   }
  273.   else
  274.     in_fd = stdin;
  275.  
  276.   /* Read in a line at a time for processing */
  277.  
  278.   while(fgets(buffer,MAX_LINE,in_fd))
  279.   {
  280.     if(nl = index(buffer,'\n'))  /* Remove newline */
  281.       *nl = '\0';
  282.     run_fsa(buffer);    /* Run the FSA on string "buffer" */
  283.     puts(buffer);    /* Output the translated string */
  284.   }
  285.   if(in_file != NULL)
  286.     fclose(in_fd);
  287. }
  288.  
  289. /* RUN_FSA() - Run the finite state automaton with string
  290.  *           "target" as input.
  291.  */
  292.  
  293. void run_fsa(target)
  294. register char *target;
  295. {
  296.   register FSA *st_ptr;
  297.   char tail[MAX_LINE];
  298.   FSA *go(),
  299.       *move();
  300.  
  301.   st_ptr = NULL;    /* Initialize FSA */
  302.  
  303.   /* Process the next input character in the target string */
  304.  
  305.   while(*target)
  306.   {
  307.     st_ptr = move(st_ptr,*target);
  308.  
  309.     /* Substitute translation string for source string */
  310.  
  311.     if(st_ptr && st_ptr->out_str)  /* Terminal state? */
  312.     {
  313.       /* Longest possible substring match? */
  314.  
  315.       if(!move(st_ptr,*(target+1)))
  316.       {
  317.     /* Substitute translation for source in target string */
  318.  
  319.     strcpy(tail,st_ptr->out_str);
  320.     strcat(tail,target+1);
  321.     target -= (st_ptr->src_length - 1);
  322.     strcpy(target,tail);
  323.     if(nflag == TRUE)  /* Nesting disabled? */
  324.       target += st_ptr->trn_length;
  325.       }
  326.     }
  327.     target++;
  328.   }
  329. }
  330.  
  331. /* GO() - Process "litchar" and return a pointer to the FSA's
  332.  *      corresponding "go" transition state. If the character
  333.  *      is not in the FSA state's "go" transition list, then
  334.  *      return a pointer to FAIL_STATE.
  335.  */
  336.  
  337. FSA *go(st_ptr,litchar)
  338. FSA *st_ptr;
  339. register char litchar;
  340. {
  341.   register TRANSITION *current;
  342.  
  343.   /* If State 0, then access separate State 0 data structure of
  344.    * the FSA. Note that there are no failure states defined for
  345.    * any input to FSA State 0.
  346.    */
  347.  
  348.   if(!st_ptr)
  349.     return MZ[litchar];
  350.   else
  351.   {
  352.     /* Point to the head of the linked list of "go" transitions
  353.      * associated with the state.
  354.      */
  355.  
  356.     current = st_ptr->go_ls;
  357.  
  358.     /* Transverse the list looking for a match to the input
  359.      * character.
  360.      */
  361.  
  362.     while(current)
  363.     {
  364.       if(current->lchar == litchar)
  365.     break;
  366.       current = current->next_el;
  367.     }
  368.  
  369.     /* Null value for "current" indicates end of list was reached
  370.      * without having found match to input character.
  371.      */
  372.  
  373.     return current ? current->nextst_ptr : &FAIL_STATE;
  374.   }
  375. }
  376.  
  377. /* MOVE() - Process "litchar" and return a pointer to the FSA's
  378.  *        corresponding "move" transition state.
  379.  */
  380.  
  381. FSA *move(st_ptr,litchar)
  382. FSA *st_ptr;
  383. register char litchar;
  384. {
  385.   register TRANSITION *current;
  386.  
  387.   /* If State 0, then access separate State 0 data structure of
  388.    * the FSA.
  389.    */
  390.  
  391.   if(!st_ptr)
  392.     return MZ[litchar];
  393.   else
  394.   {
  395.     /* Point to the head of the linked list of "move" transitions
  396.      * associated with the state.
  397.      */
  398.  
  399.     current = st_ptr->mv_ls;
  400.  
  401.     /* Transverse the list looking for a match to the input
  402.      * character.
  403.      */
  404.  
  405.     while(current)
  406.     {
  407.       if(current->lchar == litchar)
  408.     break;
  409.       current = current->next_el;
  410.     }
  411.  
  412.     /* Null value for "current" indicates end of list was reached
  413.      * without having found match to input character. The
  414.      * returned pointer is then to State 0.
  415.      */
  416.  
  417.     return current ? current->nextst_ptr : NULL;
  418.   }
  419. }
  420.  
  421. /* BD_GO() - Build the "go" transitions for each state from the
  422.  *         string "str".
  423.  */ 
  424.  
  425. void bd_go(str)
  426. char *str;
  427. {
  428.   register char litchar;
  429.   char *nl,
  430.        inp_buffer[MAX_LINE],    /* Input string buffer */
  431.        src_buffer[MAX_LINE],    /* Source string buffer */
  432.        trn_buffer[MAX_LINE],    /* Translation string buffer */
  433.        *inp_ptr,        /* Input buffer pointer */
  434.        *src_ptr,        /* Source buffer pointer */
  435.        *trn_ptr,        /* Translation buffer pointer */
  436.        *fgets();
  437.   FILE *str_fd,
  438.        *fopen();
  439.   void error(),
  440.        enter();
  441.  
  442.   /* Initialize FSA State 0 "go" transition array so that every
  443.    * invocation of "go()" with "state" = 0 initially returns a
  444.    * pointer to FAIL_STATE.
  445.    */
  446.  
  447.   for(litchar = 1; litchar & 127; litchar++)
  448.     MZ[litchar] = &FAIL_STATE;
  449.  
  450.   /* Get the newline-terminated strings from the file "str" one
  451.    * at a time and enter them into the FSA.
  452.    */
  453.  
  454.   if(!(str_fd = fopen(str,"r")))
  455.     error(STR_ERR,str);
  456.  
  457.   while(fgets(inp_buffer,MAX_LINE,str_fd))
  458.   {
  459.     /* Skip blank lines */
  460.  
  461.     if(*inp_buffer == '\n')
  462.       continue;
  463.  
  464.     /* Initialize buffer pointers */
  465.  
  466.     inp_ptr = inp_buffer;
  467.     src_ptr = src_buffer;
  468.     trn_ptr = trn_buffer;
  469.  
  470.     /* Copy source string to source buffer */
  471.  
  472.     while(*inp_ptr != '\t')
  473.       *src_ptr++ = *inp_ptr++;
  474.     *src_ptr = '\0';    /* Terminate source string */
  475.  
  476.     /* Skip tabs */
  477.  
  478.     while(*inp_ptr == '\t')
  479.       inp_ptr++;
  480.  
  481.     /* Copy translation string to translation buffer */
  482.  
  483.     while(*inp_ptr != '\n')
  484.       *trn_ptr++ = *inp_ptr++;
  485.     *trn_ptr = '\0';    /* Terminate translation string */
  486.  
  487.     enter(src_buffer,trn_buffer);
  488.   }
  489.   fclose(str_fd);
  490.  
  491.   /* For every input character that does not lead to a defined
  492.    * "go" transition from FSA State 0, set the corresponding
  493.    * element in the State 0 "go" transition array to indicate
  494.    * a "go" transition to State 0.
  495.    */
  496.  
  497.   for(litchar = 1; litchar & 127; litchar++)
  498.     if(MZ[litchar] == &FAIL_STATE)
  499.       MZ[litchar] = NULL;
  500. }
  501.  
  502. /* ENTER() - Enter a string into the FSA by running each
  503.  *         character in turn through the current partially-
  504.  *         built FSA. When a failure occurs, add the remainder
  505.  *         of the string to the FSA as one new state per
  506.  *         character. (Note that '\0' can never be a valid
  507.  *         character - C uses it to terminate a string.)
  508.  */
  509.  
  510. void enter(src_str,trn_str)
  511. char *src_str,
  512.      *trn_str;
  513. {
  514.   FSA *s,
  515.       *go(),
  516.       *create();
  517.   TRANSITION *current,
  518.          *insert();
  519.   char *strsave();
  520.   register char *temp;
  521.   register FSA *st_ptr = NULL;    /* Start in FSA State 0 */
  522.   register FSA *nextst_ptr;
  523.  
  524.   /* Run each character in turn through partially-built FSA until
  525.    * a failure occurs.
  526.    */  
  527.  
  528.   temp = src_str;
  529.   while((s = go(st_ptr,*temp)) != &FAIL_STATE)
  530.   {
  531.     temp++;
  532.     st_ptr = s;
  533.   }
  534.  
  535.   /* Process the remainder of the string */
  536.  
  537.   while(*temp)
  538.   {
  539.     /* If a new state, then create a new state and insert
  540.      * transition character and "go" transition in current
  541.      * state. (Note special case of FSA State 0.)
  542.      */
  543.  
  544.     if(!st_ptr)
  545.       nextst_ptr = MZ[*temp++] = create();
  546.     else if(!(current = st_ptr->go_ls))
  547.     {
  548.       nextst_ptr = create();
  549.       st_ptr->go_ls = insert(nextst_ptr,*temp++);
  550.     }
  551.     else
  552.     {
  553.       /* ... or it was the character that the FSA returned a
  554.        * "failure" for. Find the tail of the current state's list
  555.        * of "go" transitions, create a new state and append it
  556.        * to the current state's "go" list.
  557.        */
  558.  
  559.       while(current->next_el)
  560.     current = current->next_el;
  561.       nextst_ptr = create();
  562.       current->next_el = insert(nextst_ptr,*temp++);
  563.     }
  564.     st_ptr = nextst_ptr;
  565.   }
  566.  
  567.   /* Store lengths of source and translation strings */
  568.  
  569.   st_ptr->src_length = strlen(src_str);
  570.   st_ptr->trn_length = strlen(trn_str);
  571.  
  572.   /* Make translation string terminal state's output message */
  573.  
  574.   st_ptr->out_str = strsave(trn_str);
  575. }
  576.  
  577. /* INSERT() - Create a new "go" transition and return a pointer
  578.  *          to it.
  579.  */
  580.  
  581. TRANSITION *insert(st_ptr,litchar)
  582. FSA *st_ptr;
  583. char litchar;
  584. {
  585.   TRANSITION *current;
  586.   char *malloc();
  587.   void error();
  588.     
  589.   if(!(current = (TRANSITION *)malloc(sizeof(TRANSITION))))
  590.     error(MEM_ERR,NULL);
  591.   current->lchar = litchar;
  592.   current->nextst_ptr = st_ptr;
  593.   current->next_el = NULL;
  594.   return current;
  595. }
  596.  
  597. /* CREATE() - Create an FSA state and return a pointer to it. */
  598.  
  599. FSA *create()
  600. {
  601.   FSA *st_ptr;
  602.   char *malloc();
  603.   void error();
  604.  
  605.   if(!(st_ptr = (FSA *)malloc(sizeof(FSA))))
  606.     error(MEM_ERR,NULL);
  607.   st_ptr->go_ls = st_ptr->mv_ls = NULL;
  608.   st_ptr->fail_state = NULL;
  609.   st_ptr->out_str = NULL;
  610.   return st_ptr;
  611. }
  612.  
  613. /* BD_MOVE() - Build the "failure" and "move" transitions for
  614.  *           each state from the "go" transitions.
  615.  */
  616.  
  617. void bd_move()
  618. {
  619.   register char litchar;
  620.   register FSA *r,    /* Temporary FSA state pointers */
  621.            *s,
  622.            *t;
  623.   FSA *go(),
  624.       *move();
  625.   TRANSITION *current,
  626.          *insert();
  627.   QUEUE *first,        /* Pointer to head of queue */
  628.     *last;        /* Pointer to tail of queue */
  629.   void add_queue(),
  630.        delete_queue();
  631.  
  632.   last = first = NULL;    /* Initialize the queue of FSA states */
  633.  
  634.   /* For each input character with a "go" transition out of FSA
  635.    * State 0, add a pointer to the "go" state to the queue. Note
  636.    * that this will also serve as the "move" transition list for
  637.    * State 0.
  638.    */
  639.  
  640.   for(litchar = 1; litchar & 127; litchar++)
  641.     if(s = go(NULL,litchar))
  642.       add_queue(&first,&last,s);
  643.  
  644.   /* While there are still state pointers in the queue, do ... */
  645.  
  646.   while(first)
  647.   {
  648.     /* Remove State "r" pointer from the head of the queue. */
  649.  
  650.     r = first->st_ptr;
  651.     delete_queue(&first);
  652.  
  653.     /* For every input to State "r" that has a "go" transition to
  654.      * State "s", do ...
  655.      */
  656.  
  657.     for(litchar = 1; litchar & 127; litchar++)
  658.     {
  659.       if((s = go(r,litchar)) != &FAIL_STATE)
  660.       {
  661.     /* If a defined "go" transition exists for State "r" on
  662.      * input "litchar", add a pointer to State "s" to the end
  663.      * of the queue.
  664.      */
  665.  
  666.     add_queue(&first,&last,s);
  667.  
  668.     /* Calculate the "failure" transition of State "s" using
  669.      * the following algorithm.
  670.      */
  671.  
  672.     t = r->fail_state;
  673.     while(go(t,litchar) == &FAIL_STATE)
  674.       t = t->fail_state;
  675.     s->fail_state = go(t,litchar);
  676.       }
  677.       else
  678.       {
  679.     /* ... otherwise set the pointer to State "s" to a
  680.      * pointer to the precalculated "move" transition of
  681.      * State "r"'s failure state on input "litchar".
  682.      */
  683.  
  684.     s = move(r->fail_state,litchar);
  685.       }
  686.  
  687.       /* Add State "s" as the "move" transition for State "r" on
  688.        * input "litchar" only if it is not State 0.
  689.        */
  690.  
  691.       if(s)
  692.     if(!r->mv_ls)    /* First instance of the list? */
  693.       current = r->mv_ls = insert(s,litchar);
  694.     else        /* No, just another one ... */
  695.       current = current->next_el = insert(s,litchar);
  696.     }
  697.   }
  698. }
  699.  
  700. /* ADD_QUEUE() - Add an instance to the tail of a queue */
  701.  
  702. void add_queue(head_ptr,tail_ptr,st_ptr)
  703. QUEUE **head_ptr,
  704.       **tail_ptr;
  705. FSA *st_ptr;
  706. {
  707.   QUEUE *pq;
  708.   char *malloc();
  709.   void error();
  710.  
  711.   /* Allocate the necessary memory and set the variables. */
  712.  
  713.   if(!(pq = (QUEUE *)malloc(sizeof(QUEUE))))
  714.     error(MEM_ERR,NULL);
  715.  
  716.   pq->st_ptr = st_ptr;
  717.   pq->next_el = NULL;
  718.  
  719.   if(!*head_ptr)    /* First instance of the queue? */
  720.     *tail_ptr = *head_ptr = pq;
  721.   else            /* No, just another one ... */
  722.     *tail_ptr = (*tail_ptr)->next_el = pq;
  723. }
  724.  
  725. /* DELETE_QUEUE() - Delete an instance from the head of queue */
  726.  
  727. void delete_queue(head_ptr)
  728. QUEUE **head_ptr;
  729. {
  730.   *head_ptr = (*head_ptr)->next_el;
  731. }
  732.  
  733. /* STRSAVE() - Save a string somewhere in memory */
  734.  
  735. char *strsave(str)
  736. char *str;
  737. {
  738.   int strlen();
  739.   char *p,
  740.        *malloc();
  741.   void error();
  742.  
  743.   if(p = malloc(strlen(str) + 1))
  744.     strcpy(p,str);
  745.   else
  746.     error(MEM_ERR,NULL);
  747.   return p;
  748. }
  749.  
  750. /* ERROR() - Error reporting. Returns an exit status of 2 to the
  751.  *         parent process.
  752.  */
  753.  
  754. void error(n,str)
  755. int n;
  756. char *str;
  757. {
  758.   fprintf(stderr,"\007\n*** ERROR - ");
  759.   switch(n)
  760.   {
  761.     case CMD_ERR:
  762.       fprintf(stderr,"Illegal command line");
  763.       break;
  764.     case OPT_ERR:
  765.       fprintf(stderr,"Illegal command line option");
  766.       break;
  767.     case INP_ERR:
  768.       fprintf(stderr,"Can't open input file %s",str);
  769.       break;
  770.     case STR_ERR:
  771.       fprintf(stderr,"Can't open string file %s",str);
  772.       break;
  773.     case MEM_ERR:
  774.       fprintf(stderr,"Out of memory");
  775.       break;
  776.     default:
  777.       break;
  778.   }
  779.   fprintf(stderr," ***\n\nUsage: mtp [-n]");
  780.   fprintf(stderr," string_file <files>\n");
  781.   exit(2);
  782. }
  783.  
  784. /*** End of MTP.C ***/
  785. string somewhere in memory */
  786.  
  787. char *strsave(str)
  788. char *str;
  789. {
  790.   int strlen();
  791.   char *p,
  792.        *malloc();