home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / netsrcs / markov < prev    next >
Encoding:
Internet Message Format  |  1987-03-06  |  19.4 KB

  1. From jbuck@epimass.UUCP Fri Mar  6 21:00:36 1987
  2. Path: beno!seismo!epiwrl!epimass!jbuck
  3. From: jbuck@epimass.UUCP (Joe Buck)
  4. Newsgroups: net.sources
  5. Subject: Revised and improved markov3, a Usenet article generator
  6. Message-ID: <954@epimass.UUCP>
  7. Date: 7 Mar 87 02:00:36 GMT
  8. Organization: Entropic Processing, Inc., Cupertino, CA
  9. Lines: 758
  10.  
  11. #! /bin/sh
  12. # This is a shell archive, meaning:
  13. # 1. Remove everything above the #! /bin/sh line.
  14. # 2. Save the resulting text in a file.
  15. # 3. Execute the file with /bin/sh (not csh) to create the files:
  16. #    README
  17. #    markov3.l
  18. #    markov3.6
  19. #    Makefile
  20. #    getopt.c
  21. #    PATCHLEVEL
  22. # This archive created: Fri Mar  6 17:57:28 1987
  23. export PATH; PATH=/bin:$PATH
  24. if test -f 'README'
  25. then
  26.     echo shar: will not over-write existing file "'README'"
  27. else
  28. cat << \SHAR_EOF > 'README'
  29. This is a cleaned-up reposting of the markov3 program.  The following
  30. changes have been made:
  31.  
  32. The null pointer dereferencing bugs have been fixed (I hope).
  33.  
  34. The code that uses "rand" should now be portable (the patches posted
  35. to the net to fix this problem were wrong, they break the code on
  36. some machines in order to fix it on others.  I stole some code from
  37. "hack" to do things right.  If hack works for you, this should).
  38.  
  39. markov3 now understands "notes" cruft (thanks to Rich Salz).
  40.  
  41. Because of the 50% rule in news 2.11, people often use some other
  42. character than ">" for inclusions.  markov3 assumes that lines
  43. beginning with any of 
  44.  
  45.     > < ) | # } ]
  46.  
  47. are inclusions (without this rule, funny-looking output results if
  48. anyone uses non-standard "quoting").
  49.  
  50. The random number generator is initialized using the time, if neither
  51. the -s flag nor the new -x flag is given.
  52.  
  53. This will be the last complete posting; a "patchlevel" file is included
  54. and I will send out patches if there are further bugs or improvements.
  55.  
  56. Here's the original README.
  57. ---------------------------------------------------------------------------
  58. I created a bit of a stir with this program in December 1986 when I
  59. used an earlier version of it to simulate a certain well-known net
  60. personality (Hi Laura!).  It digests Usenet articles and spits out
  61. other articles with similar characteristics.  You need lex to run it,
  62. but otherwise it should run on any Unix I know of.  
  63.  
  64. I had several requests for the program but didn't consider it
  65. "ready".  It's as ready as it will ever be now.
  66.  
  67. The program uses getopt(3).  There are several public-domain versions
  68. available for Berkeley systems from the mod.sources archives.  Since
  69. it's small, I've included Henry Spencer's version, but you'll have
  70. to change the Makefile to use it.
  71.  
  72. For best results, feed it at least ten articles by the same person
  73. or on the same subject.  If there are fewer articles the output
  74. resembles the original too much; if there is too much variety in
  75. the articles the output is more incoherent than it otherwise is.
  76.  
  77. The program requires lots of memory if it is given lots of input;
  78. the small-model people will have problems.
  79.  
  80. Please don't post the output to the net (though I'd be happy to
  81. see some of the more interesting results).
  82.  
  83. Send comments, suggestions for improvement, fan mail, and flames
  84. to me: {sun,hplabs,ames,ihnp4}!oliveb!epimass!jbuck.
  85. SHAR_EOF
  86. fi # end of overwriting check
  87. if test -f 'markov3.l'
  88. then
  89.     echo shar: will not over-write existing file "'markov3.l'"
  90. else
  91. cat << \SHAR_EOF > 'markov3.l'
  92. %{
  93. /*
  94.  * Copyright (c) 1986, 1987 by Joe Buck
  95.  *
  96.  * Permission is granted to use, redistribute, or modify this program,
  97.  * as long as you don't pretend you wrote it.  Send improvements or
  98.  * bug reports to {ihnp4,hplabs,ames,sun}!oliveb!epimass!jbuck.
  99.  *
  100.  * The program generates simulated Usenet articles, given Usenet articles
  101.  * as input.
  102.  *
  103.  * This program constructs a table of frequencies for a token appearing,
  104.  * given the two preceding tokens.  A "token" is a sequence of non-blank
  105.  * characters.  An entirely blank line is also treated as a token, as is
  106.  * the beginning and end of an article.
  107.  *
  108.  * The program is designed to process news articles, rejecting text from
  109.  * the header, signature, and included text, together with cruft inserted
  110.  * by rn and notes.  A paragraph of included text is treated like a token.
  111.  *
  112.  * After the table is built (and it can be big), articles are generated
  113.  * on the standard output.
  114.  */
  115. #ifndef lint
  116. static char *sccs_id = "@(#)markov3.l    1.1 3/6/87 epimass!jbuck";
  117. #endif
  118. #include <sys/types.h>        /* for time_t */
  119. int in_included_text = 0;
  120. %}
  121. %Start HDR BODY SIG
  122. %%
  123. <HDR>^[^ \t]+:.*\n    ;    /* Header line, e.g. "From: foo@bar.UUCP" */
  124. <HDR>^[ \t]+[^ \t].*\n    ;    /* Continuation of header line */
  125. <HDR>^[ \t]*$        BEGIN BODY;
  126. <BODY>^"-- "$        BEGIN SIG;
  127. <BODY>^[><)|#}].*\n    { /* 50% rule gets people to change ">"
  128.                  to other characters; this gets most of them */
  129.               if (!in_included_text) {
  130.                       in_included_text = 1;
  131.                   process_token ("\n> ...\n\n");
  132.               }
  133.             }
  134. <BODY>"]".*\n        { /* should have been included in the above.  My
  135.                  lex generates bad C code if I say [[><...]
  136.                  even though ed(1) says that's a valid regular
  137.                  expression. */
  138.               if (!in_included_text) {
  139.                   in_included_text = 1;
  140.                   process_token ("\n> ...\n\n");
  141.               }
  142.             }
  143. <BODY>^"In article".*\n    ;    /* Reject rn crud */
  144. <BODY>^"/* Written".*"*/"\n    ;        /* Also NOTES crud */
  145. <BODY>^"/* End of text from".*"*/"\n    ;        /* NOTES */
  146. <BODY>^"/* ----------".*"----------*/"\n    ;        /* NOTES */
  147. <BODY>[ \t]+        ;    /* Skip white space */
  148. <BODY>\n[ \t\n]*\n    { process_token ("\n"); /* Paragraph break */}
  149. <BODY>[^ \t\n]+        { in_included_text = 0; process_token (yytext); }
  150. <HDR>.            ;    /* Eat anything that escaped */
  151. <HDR>\n            ;
  152. <BODY>\n        ;
  153. <SIG>.            ;
  154. <SIG>\n            ;
  155. %%
  156. void perror(), exit();
  157. char *strcpy(), *malloc();
  158. extern int optind;
  159. extern char *optarg;
  160.  
  161. /*
  162.  * hashtab is a hash table storing all the tokens we encounter.
  163.  */
  164. struct htentry {
  165.     char *htext;
  166.     struct htentry *hlink;
  167. };
  168.  
  169. #define HSIZE 3557        /* Should be prime */
  170. #define Fprintf (void)fprintf
  171. #define Printf (void)printf
  172.  
  173. struct htentry hashtab[HSIZE];
  174.  
  175. /*
  176.  * node and succnode are portions of the big structure we're going to build.
  177.  * node represents something like ("was", "a") in a binary tree.
  178.  * a linked list of succnodes contain tokens that may follow ("was", "a")
  179.  */
  180. struct node {
  181.     char *text;
  182.     char *text2;
  183.     int ocount;
  184.     struct node *lc, *rc;
  185.     struct succnode *succ;
  186. };
  187.  
  188. struct succnode {
  189.     struct node *scnod;
  190.     int    count;
  191.     struct succnode *link;
  192. };
  193.  
  194.  
  195. struct node *prev_code = NULL;
  196. char *prev_token = NULL, **Argv;
  197. int init_state = HDR;
  198. int verbose = 0;
  199. struct node *root = NULL, *tknptr;
  200. struct succnode *start = NULL;
  201. int n_pairs = 0, n_tokens = 0, n_files = 0, n_total = 0;
  202.  
  203. struct node *insert_token();
  204. char *savetoken();
  205.  
  206. process_token (txt)
  207. char *txt;
  208. {
  209.      struct node *code;
  210.      char *token = savetoken (txt);
  211. /* We have a new token.  Say the previous two tokens were "one" "way"
  212.  * and the current token is "to".  Then prev_code points to a node
  213.  * for ("one", "way") and token is "to".  This function adds ("way", "to") as a
  214.  * successor to ("one","way") and makes prev_code point to ("way","to").
  215.  */
  216.      code = insert_token (prev_token, token);
  217.      insert_pair (prev_code, code);
  218.      prev_code = code;
  219.      prev_token = token;
  220.      return;
  221. }
  222.  
  223. /*
  224.  * here it is, the main function.
  225.  */
  226. main (argc, argv)
  227. int argc;
  228. char  **argv;
  229. {
  230.     int     i, c, n_articles = 10, sflag = 0;
  231.     char *dumpfile = NULL;
  232.     extern int  optind;
  233.     extern char *optarg;
  234.  
  235.     while ((c = getopt (argc, argv, "pxvn:d:s:")) != EOF) {
  236.     switch (c) {
  237.         case 'v':
  238.         verbose = 1;
  239.         break;
  240.         case 'p':        /* Input is plain text, not Usenet stuff */
  241.         init_state = BODY;
  242.         break;
  243.         case 'n':         /* # articles to generate */
  244.         n_articles = atoi (optarg);
  245.         break;
  246.         case 'd':        /* where to dump the data structure */
  247.         dumpfile = optarg;
  248.         break;
  249.         case 's':        /* Set the seed for rand; fall through */
  250.         srand (atoi (optarg));
  251.         case 'x':        /* set flag to prevent srand */
  252.         sflag++;
  253.         break;
  254.         default:
  255.         Fprintf (stderr,
  256.          "Usage: markov3 [-pvx] [-s seed] [-n n_art] [-d dump] files\n");
  257.         exit (1);
  258.     }
  259.     }
  260.     BEGIN init_state;        /* initial state of lexical analyzer */
  261.     if (!sflag)            /* set random number generator */
  262.     srand ((int)time ((time_t *)0));
  263. /* Note: if optind == argc, there are no file arguments.  yyin is left
  264.  * initialized to stdin.
  265.  */
  266.     if (optind < argc) {
  267. /* yyin is lex input stream.  Point to first file. */
  268.     if ((yyin = fopen (argv[optind], "r")) == NULL) {
  269.         perror (argv[optind]);
  270.         exit (1);
  271.     }
  272.     optind++;        /* skip to next file */
  273.     }
  274.     Argv = argv;        /* make it global so yywrap can access it */
  275.     n_files = 1;
  276. /* yylex puts all the input files through the lexical analyzer and builds
  277.  * the database.
  278.  */
  279.     (void) yylex ();
  280.     if (dumpfile)
  281.     dump_database (dumpfile);
  282.     if (verbose)
  283.     Fprintf (stderr,
  284.      "Total of %d tokens (%d different), %d different pairs, %d files\n",
  285.         n_total, n_tokens, n_pairs, n_files);
  286. /* Generate the articles, separated by form feeds */
  287.     for (i = 0; i < n_articles; i++) {
  288.     if (i > 0) output_word ("\n\f\n");
  289.     generate_article ();
  290.     }
  291.     return 0;
  292. }
  293.  
  294. /*
  295.  * Lex calls this when EOF is reached.  It opens the next file if there
  296.  * is one.  Lex interprets a return value of 1 to mean "all done" and 0
  297.  * to mean "keep going".
  298.  */
  299. yywrap () {
  300.     (void) fclose (yyin);
  301.     insert_pair (prev_code, (struct node *)0);
  302.     prev_code = NULL;
  303.     if (Argv[optind] == NULL) return 1;
  304.     else if ((yyin = fopen (Argv[optind], "r")) == NULL) {
  305.     perror (Argv[optind]);
  306.     exit (1);
  307.     }
  308.     optind++;
  309.     in_included_text = 0;
  310.     if (verbose && n_files % 10 == 0)
  311.     Fprintf (stderr, "%d files\n", n_files);
  312.     n_files++;
  313.     BEGIN init_state;
  314.     return 0;
  315. }
  316.  
  317. /*
  318.  * This function saves a token in the hash table (if it isn't there
  319.  * already) and returns a pointer to the stored copy.
  320.  */
  321. char *
  322. savetoken (txt)
  323. char *txt;
  324. {
  325.     int h;
  326.     char *p;
  327.     struct htentry *hp;
  328.  
  329.     n_total++;
  330.     for (p = txt, h = 0; *p; h += *p++);
  331.     hp = hashtab + (h % HSIZE);
  332.     while (hp->hlink) {
  333.     if (strcmp (hp->htext, txt) == 0) {
  334.         return hp->htext;
  335.     }
  336.     hp = hp->hlink;
  337.     }
  338. /* OK, it's a new token.  Make hp->hlink point to a new,
  339.  * null block and make hp->htext point to the text.
  340.  */
  341.     hp->hlink = (struct htentry *) malloc (sizeof *hp);
  342.     hp->htext = malloc ((unsigned)(strlen (txt) + 1));
  343.     (void) strcpy (hp->htext, txt);
  344.     hp->hlink->hlink = NULL;
  345.     hp->hlink->htext = NULL;
  346.     n_tokens++;
  347.     return hp->htext;
  348. }
  349.  
  350. /*
  351.  * This recursive function inserts a token pair into the tree.
  352.  */
  353. struct node *
  354. insert_in_tree (p, txt, txt2)
  355. struct node *p;
  356. char *txt, *txt2;
  357. {
  358.     int cmp;
  359.     if (p == NULL) {
  360. /* Create a new node. */
  361.     p = (struct node *) malloc (sizeof *p);
  362.     p->text = txt;
  363.     p->text2 = txt2;
  364.     p->lc = p->rc = NULL;
  365.     p->succ = NULL;
  366.     p->ocount = 1;
  367.     tknptr = p;
  368.     n_pairs++;
  369.     if (verbose && n_pairs % 1000 == 0)
  370.         Fprintf (stderr, "%d pairs\n", n_pairs);
  371.     return p;
  372.     }
  373.     cmp = my_strcmp (p->text, txt);
  374.     if (cmp == 0) cmp = my_strcmp (p->text2, txt2);
  375.     if (cmp == 0) {
  376. /* It's a match.  Increment the count. */
  377.         tknptr = p;
  378.     p->ocount += 1;
  379.     }
  380. /* Look in the subtrees. */
  381.     else if (cmp < 0) p->lc = insert_in_tree (p->lc, txt, txt2);
  382.     else p->rc = insert_in_tree (p->rc, txt, txt2);
  383.     return p;
  384. }
  385.  
  386. /*
  387.  * This just calls insert_in_tree starting at the root
  388.  */
  389. struct node *
  390. insert_token (txt, txt2)
  391. char *txt,*txt2;
  392. {
  393.     root = insert_in_tree (root, txt, txt2);
  394.     return tknptr;
  395. }
  396.  
  397. /*
  398.  * This function adds a successor.
  399.  */
  400. struct succnode *
  401. insert_in_succ_chain (sp, np)
  402. struct succnode *sp;
  403. struct node *np;
  404. {
  405.     if (sp == NULL) {
  406.     sp = (struct succnode *) malloc (sizeof *sp);
  407.     sp->scnod = np;
  408.     sp->count = 1;
  409.     sp->link = NULL;
  410.     }
  411.     else if (sp->scnod == np)
  412.     sp->count += 1;
  413.     else sp->link = insert_in_succ_chain (sp->link, np);
  414.     return sp;
  415. }
  416.  
  417. /*
  418.  * This calls insert_in_succ_chain starting at the right place.
  419.  */
  420. insert_pair (p1, p2)
  421. struct node *p1, *p2;
  422. {
  423.     if (p1) p1->succ = insert_in_succ_chain (p1->succ, p2);
  424.     else start = insert_in_succ_chain (start, p2);
  425. }
  426.  
  427. /*
  428.  * This function dumps the stored data structure onto a file.
  429.  * Now if only I had a function to read it back in.
  430.  */
  431. char *
  432. pr_token (txt)
  433. char *txt;
  434. {
  435.     if (txt[0] != '\n')
  436.     return txt;
  437.     return txt[1] ? "<INCL>" : "<LF>";
  438. }
  439.  
  440. treedump (tree, fp)
  441. struct node *tree;
  442. FILE *fp;
  443. {
  444.     if (tree) {
  445.     treedump (tree->rc, fp);
  446.     Fprintf (fp, "( %s %s ) %d", pr_token (tree->text),
  447.             pr_token (tree->text2), tree->ocount);
  448.     chaindump (tree->succ, fp);
  449.     treedump (tree->lc, fp);
  450.     }
  451. }
  452.  
  453. /*
  454.  * Subroutine of treedump; it does one row.
  455.  */
  456. chaindump (p, fp)
  457. struct succnode *p;
  458. FILE *fp;
  459. {
  460.     char   *text;
  461.     while (p) {
  462.     if (p->scnod == NULL)
  463.         text = "<EOF>";
  464.     else text = pr_token (p->scnod->text2);
  465.     Fprintf (fp, " %s %d", text, p->count);
  466.     p = p->link;
  467.     }
  468.     putc ('\n', fp);
  469. }
  470.  
  471. /*
  472.  * This routine generates the dump file (-d option)
  473.  */
  474. dump_database (file)
  475. char *file;
  476. {
  477.     FILE *fp = fopen (file, "w");
  478.     if (fp == NULL) {
  479.     Fprintf (stderr, "markov: can't open ");
  480.     perror (file);
  481.     exit (1);
  482.     }
  483.     Fprintf (fp, "START:");
  484.     chaindump (start, fp);
  485.     treedump (root, fp);
  486. }
  487.  
  488. /* roll (n) generates a uniformly distributed rv between 0 and n-1.
  489.  * This code is stolen from "hack" and should be portable.  If you
  490.  * change this, remember that different systems have rand functions
  491.  * with different ranges, and the bottom bits are often no good.
  492.  */
  493. #define roll(n) ((rand() >> 3) % n)
  494.  
  495. /*
  496.  * This function generates an article by traversing the
  497.  * structure we've built.
  498.  */
  499. generate_article () {
  500.     struct succnode *p = start;
  501.     int ncounts = n_files;
  502.     int n, accum;
  503.     char *tp;
  504.  
  505.     while (1) {
  506. /* Roll the dice to find out the next token.  The code below selects the
  507.  * next token, and the new state, with a probability corresponding to the
  508.  * frequency in the input.
  509.  */
  510.     n = roll (ncounts);
  511.     accum = p->count;
  512.     while (accum <= n && p->link) {
  513.         p = p->link;
  514.         accum += p->count;
  515.     }
  516.     if (p->scnod == NULL)
  517.         break;
  518.     tp = p->scnod->text2;
  519. /* Check for "end of story" */
  520.     if (tp == NULL)
  521.         break;
  522.     output_word (tp);
  523.     ncounts = p->scnod->ocount;
  524.     p = p->scnod->succ;
  525.     }
  526.     output_word ("\n");    /* This will flush the buffer as well. */
  527.     return;
  528. }
  529.  
  530. /*
  531.  * This version handles null strings *
  532.  */
  533. my_strcmp (a, b)
  534. register char *a, *b;
  535. {
  536.     if (a == NULL) return b ? -1 : 0;
  537.     if (b == NULL) return 1;
  538.     return strcmp (a, b);
  539. }
  540.  
  541. #define LEN 75
  542. output_word (word)
  543. char *word;
  544. {
  545.     static char line[LEN+1];
  546.     static int room = LEN;
  547.     int l;
  548.  
  549.     if (word == NULL) return;
  550.     l = strlen (word);
  551. /* If word won't fit, or starts with \n, dump the current line */
  552.     if ((l >= room || word[0] == '\n') && line[0]) {
  553.     Printf ("%s\n", line);
  554.     line[0] = 0;
  555.     room = LEN;
  556.     }
  557. /* If word won't fit in the buffer or starts with \n, print it now */
  558.     if (l >= LEN)
  559.     Printf ("%s\n", word);
  560.     else if (word[0] == '\n')
  561.     Printf ("%s", word);
  562. /* Otherwise fill it in */
  563.     else {
  564.     (void)strcat (line, word);
  565.     (void)strcat (line, " ");
  566.     room -= (l + 1);
  567.     }
  568.     return;
  569. }
  570. SHAR_EOF
  571. fi # end of overwriting check
  572. if test -f 'markov3.6'
  573. then
  574.     echo shar: will not over-write existing file "'markov3.6'"
  575. else
  576. cat << \SHAR_EOF > 'markov3.6'
  577. .\" markov3
  578. .\" @(#)markov3.6    1.1 3/6/87 epimass!jbuck
  579. .TH MARKOV3 6 "3/6/87"
  580. .UC 4
  581. .SH NAME
  582. markov3 \- Digest and spit out quasi-random Usenet articles
  583. .SH SYNOPSIS
  584. .B markov3
  585. [
  586. .B \-pv
  587. ] [
  588. .B \-n
  589. .I n_articles
  590. ] [
  591. .B \-d
  592. .I dumpfile
  593. ] [
  594. .B \-s
  595. .I seed
  596. ] [
  597. .B \-x
  598. ]
  599. files
  600. .SH DESCRIPTION
  601. .PP
  602. .I Markov3
  603. digests Usenet articles and builds an internal data structure that
  604. models the articles as if they came from a random process, where
  605. each word is determined by the previous two.  It then emits a series
  606. of articles on the standard output that have the same distribution
  607. of words, word pairs, and word triplets as do the input files.
  608. The name
  609. .I markov3
  610. comes from the fact that this structure is called a Markov chain,
  611. and that the statistics for word triplets are modeled.
  612. Here, a "word" is a sequence of printable characters surrounded by
  613. whitespace.  Paragraph breaks (blank lines) are also treated as a
  614. "word".  Paragraphs of included text are treated as single "words"
  615. and printed as "> ...".
  616. .PP
  617. By default, the program expects to be fed Usenet articles; it strips
  618. off headers, included text, and signatures (or at least it tries).
  619. The
  620. .B \-p
  621. (plain) option disables the header-stripping feature (otherwise
  622. everything is skipped until a blank line is encountered).
  623. .PP
  624. By default, 10 articles, separated by form feeds, are written on the
  625. standard output.  The
  626. .B \-n
  627. option lets you specify a different number.
  628. .PP
  629. The
  630. .B \-x
  631. option does not seed the random number generator; this is useful
  632. for simulating people who repeat themselves.
  633. .PP
  634. The
  635. .B \-d
  636. (dump) option dumps a representation of the internal data structure
  637. built by
  638. .I markov3
  639. on the named file.
  640. .PP
  641. Finally, the
  642. .B \-v
  643. (verbose)
  644. option prints some statistics on the standard error.
  645. .SH "CAVEATS"
  646. This program allocates lots of memory if given large amounts of input.
  647. On virtual memory systems, the paging behavior is atrocious because
  648. pointers tend to point every which way, and many pointers are dereferenced
  649. for every word processed.  This could be improved, I'm sure.
  650. .PP
  651. Posting articles generated by
  652. .I markov3
  653. to the net may be hazardous to your health.
  654. .PP
  655. Not as smart as Mark V. Shaney.
  656. .SH "PORTABILITY"
  657. An effort has been made to make this program as portable as possible;
  658. an earlier version was much less portable because of problems with
  659. null pointers and rand(3).  Please let me know if you have further problems.
  660. .PP
  661. If you don't have lex, you'll need to rewrite the lexical analyzer
  662. but most of the program is in C.
  663. SHAR_EOF
  664. fi # end of overwriting check
  665. if test -f 'Makefile'
  666. then
  667.     echo shar: will not over-write existing file "'Makefile'"
  668. else
  669. cat << \SHAR_EOF > 'Makefile'
  670. CFLAGS=-O
  671.  
  672. GOPT=
  673. # BSD people remove the following comment
  674. # GOPT=getopt.o
  675. markov3: markov3.o $(GOPT)
  676.     cc $(CFLAGS) markov3.o $(GOPT) -o markov3
  677.  
  678. markov3.c:    markov3.l
  679.         lex markov3.l
  680.         mv lex.yy.c markov3.c
  681.  
  682. shar:
  683.         shar README markov3.l markov3.6 Makefile getopt.c PATCHLEVEL > shar
  684. SHAR_EOF
  685. fi # end of overwriting check
  686. if test -f 'getopt.c'
  687. then
  688.     echo shar: will not over-write existing file "'getopt.c'"
  689. else
  690. cat << \SHAR_EOF > 'getopt.c'
  691. /*
  692.  * getopt - get option letter from argv
  693.  * by Henry Spencer
  694.  * posted to Usenet net.sources list
  695.  */
  696.  
  697. #include <stdio.h>
  698.  
  699. char    *optarg;    /* Global argument pointer. */
  700. int    optind = 0;    /* Global argv index. */
  701.  
  702. static char    *scan = NULL;    /* Private scan pointer. */
  703.  
  704. extern char    *index();
  705.  
  706. int
  707. getopt(argc, argv, optstring)
  708. int argc;
  709. char *argv[];
  710. char *optstring;
  711. {
  712.     register char c;
  713.     register char *place;
  714.  
  715.     optarg = NULL;
  716.  
  717.     if (scan == NULL || *scan == '\0') {
  718.         if (optind == 0)
  719.             optind++;
  720.     
  721.         if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
  722.             return(EOF);
  723.         if (strcmp(argv[optind], "--")==0) {
  724.             optind++;
  725.             return(EOF);
  726.         }
  727.     
  728.         scan = argv[optind]+1;
  729.         optind++;
  730.     }
  731.  
  732.     c = *scan++;
  733.     place = index(optstring, c);
  734.  
  735.     if (place == NULL || c == ':') {
  736.         fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
  737.         return('?');
  738.     }
  739.  
  740.     place++;
  741.     if (*place == ':') {
  742.         if (*scan != '\0') {
  743.             optarg = scan;
  744.             scan = NULL;
  745.         } else {
  746.             optarg = argv[optind];
  747.             optind++;
  748.         }
  749.     }
  750.  
  751.     return(c);
  752. }
  753. SHAR_EOF
  754. fi # end of overwriting check
  755. if test -f 'PATCHLEVEL'
  756. then
  757.     echo shar: will not over-write existing file "'PATCHLEVEL'"
  758. else
  759. cat << \SHAR_EOF > 'PATCHLEVEL'
  760. 1
  761. SHAR_EOF
  762. fi # end of overwriting check
  763. #    End of shell archive
  764. exit 0
  765. -- 
  766. - Joe Buck     {hplabs,ihnp4,sun,ames}!oliveb!epimass!jbuck
  767.         seismo!epiwrl!epimass!jbuck  {pesnta,tymix,apple}!epimass!jbuck
  768.   Entropic Processing, Inc., Cupertino, California
  769.  
  770.  
  771.