home *** CD-ROM | disk | FTP | other *** search
/ Education Sampler 1992 [NeXTSTEP] / Education_1992_Sampler.iso / Programming / Source / WAIS / ir / ircfiles.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-02  |  22.7 KB  |  1,023 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.
  4.  
  5.    Brewster@think.com
  6. */
  7.  
  8. /* this file defines a set of helper functions
  9.  * for indexing common types of files.
  10.  * -brewster 7/90
  11.  */
  12.  
  13. /* I encourage adding customizations.
  14.  * (too bad they all have to be hard coded, but
  15.  *  C did not have convenient dynamic linking facilities)
  16.  *
  17.  * Add three functions to this file:
  18.  * boolean foo_separator_function(char *line){}
  19.  * void foo_header_function(char *line){}
  20.  * long foo_date_function(char *line){}
  21.  * void foo_finish_header_function(char *header){}
  22.  *
  23.  * then add the prototypes to ircfiles.h
  24.  * then add the functions to the big case statement in irbuild.c
  25.  *
  26.  *
  27.  * to do:
  28.  *   filter for digests
  29.  *
  30.  */
  31.  
  32.  
  33. /* Change log:
  34.  * 8/90 brewster added the library customizations
  35.  * 6/91 and before - added a bunch of other filters - JG
  36.  */
  37.  
  38. #include <string.h>
  39. #include <ctype.h>
  40. #include "cutil.h"
  41. #include "ircfiles.h"
  42.  
  43. #define MAX_HEADER_LEN 100
  44.  
  45. static char* trim_trailing_newline _AP((char* string));
  46.  
  47. static char*
  48. trim_trailing_newline(string)
  49. char* string;
  50. {
  51.   if(string)
  52.     if(strlen(string) > 0)
  53.       if(string[strlen(string) -1] == '\n')
  54.     string[strlen(string) -1] = '\0';
  55.   return(string);
  56. }
  57.  
  58. /* =================================
  59.  * ===  Groliers Customizations  ===
  60.  * =================================
  61.  */
  62.  
  63. boolean groliers_separator_function(line)
  64. char *line;
  65. {
  66.   if((strlen(line) > strlen("ARTICLE")) &&
  67.      substrcmp(line, "ARTICLE")){
  68.     /* printf("hit %s\n", line); */
  69.     return(true);
  70.   }
  71.   else{
  72.     return(false);
  73.   }
  74. }
  75.  
  76. char groliers_header[MAX_HEADER_LEN + 1];
  77.  
  78. void groliers_header_function(line)
  79. char *line;
  80. {
  81.   if(groliers_separator_function(line)){
  82.     strncpy(groliers_header, line + strlen("ARTICLE") + 2, MAX_HEADER_LEN);
  83.   }
  84. }
  85.  
  86. void groliers_finish_header_function(header)
  87. char *header;
  88. {
  89.   if(strlen(groliers_header) == 0){
  90.     strncpy(header, "Unknown Title", MAX_HEADER_LEN);
  91.   }
  92.   else{
  93.     strncpy(header, groliers_header, MAX_HEADER_LEN);
  94.   }
  95.   groliers_header[0] = '\0';
  96. }
  97.  
  98.  
  99. /* ==============================
  100.  * ===  RMail Customizations  ===
  101.  * ==============================
  102.  */
  103.  
  104. /* this is just a preliminary version. A good version would
  105.  * produce a headline like gnu emacs RMAIL
  106.  */
  107.  
  108.  
  109. boolean mail_separator_function(line)
  110. char *line;
  111. {
  112.   /* this should really look for a "<cr><cr>From " rather than "<cr>From " */
  113.   if((strlen(line) > strlen("From ")) &&
  114.      substrcmp(line, "From ")){
  115.     return(true);
  116.   }
  117.   else{
  118.     return(false);
  119.   }
  120. }
  121.  
  122. boolean rmail_separator_function(line)
  123. char *line;
  124. {
  125.   if(0 == strcmp(line, " \n")){
  126.     return(true);
  127.   }
  128.   else{
  129.     return(false);
  130.   }
  131. }
  132.  
  133. /* This one is portable, but might get the wrong answer.
  134.    I'm open to better code.  - Jonny G
  135. */
  136.  
  137. static char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  138.             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
  139.  
  140. long getdate(line)
  141. char *line;
  142. {
  143.   char date[255], *temp;
  144.   int day, month, year;
  145.   char cmonth[25];
  146.  
  147.   strcpy(date, line);
  148.  
  149.   temp = date;
  150.  
  151.   while(!isdigit(*temp)) temp++;
  152.  
  153.   sscanf(temp, "%d %s %d", &day, cmonth, &year);
  154.  
  155.   for(month = 0; months[month] != NULL; month++)
  156.     if(!strcmp(cmonth, months[month])) break;
  157.  
  158.   if (year > 99) year = year % 100;
  159.  
  160.   if(day > 0 && 
  161.      month < 12 &&
  162.      year > 0) {
  163.     return (10000 * year + 100 * (month+1) + day);
  164.   }
  165.   sscanf(temp, "%d/%d/%d", &month, &day, &year);
  166.  
  167.   if (year > 99) year = year % 100;
  168.  
  169.   if(day > 0 && 
  170.      month < 12 &&
  171.      year > 0) {
  172.     return (10000 * year + 100 * (month+1) + day);
  173.   }
  174.   sscanf(temp, "%d/%d/%d", &year, &month, &day);
  175.  
  176.   if (year > 99) year = year % 100;
  177.  
  178.   if(day > 0 && 
  179.      month < 12 &&
  180.      year > 0) {
  181.     return (10000 * year + 100 * (month+1) + day);
  182.   }
  183.   return 0;
  184. }
  185.  
  186. long mail_date_function(line)
  187. char *line;
  188. {
  189.   if((strlen(line) > strlen("Date: ")) &&
  190.      substrcmp(line, "Date: ")){
  191.     return(getdate(line+6));
  192.   }
  193.   else return -1;
  194. }
  195.  
  196.   
  197.  
  198. char mail_subject[MAX_HEADER_LEN + 1];
  199. char mail_from[MAX_HEADER_LEN + 1];
  200.  
  201. void mail_header_function(line)
  202. char *line;
  203. {
  204.   if((strlen(line) > strlen("Subject: ")) &&
  205.      substrcmp(line, "Subject: ") &&
  206.      (strlen(mail_subject) == 0)){
  207.     strcpy(mail_subject, "Re: ");
  208.     s_strncat(mail_subject, line + strlen("Subject: "), MAX_HEADER_LEN, MAX_HEADER_LEN);
  209.     trim_trailing_newline(mail_subject);
  210.   }
  211.   else if((strlen(line) > strlen("From: ")) &&
  212.      substrcmp(line, "From: ") &&
  213.      (strlen(mail_from) == 0)){
  214.     /* this should find the <foo@bar> field in the from list */
  215.     strncpy(mail_from, line + strlen("From: "), MAX_HEADER_LEN);
  216.     trim_trailing_newline(mail_from);
  217.   }
  218.   
  219. }
  220.  
  221. void mail_finish_header_function(header)
  222. char *header;
  223. {
  224.   if(strlen(mail_subject) != 0 &&
  225.      strlen(mail_from) != 0){
  226.     /* trim the from line if needed */
  227.     if(strlen(mail_from) > 10){
  228.       mail_from[10] = '\0';
  229.     }
  230.     strncpy(header, mail_from, MAX_HEADER_LEN);
  231.     s_strncat(header, " ", MAX_HEADER_LEN, MAX_HEADER_LEN);
  232.     s_strncat(header, mail_subject, MAX_HEADER_LEN, MAX_HEADER_LEN);
  233.     /* printf("%s\n", header); */
  234.   }
  235.   else if(strlen(mail_subject) != 0){
  236.     strncpy(header, mail_subject, MAX_HEADER_LEN);
  237.   }
  238.   else if(strlen(mail_from) != 0){
  239.     strncpy(header, mail_from, MAX_HEADER_LEN);
  240.   }
  241.   else{
  242.     strcpy(header, "Unknown Subject");
  243.   }
  244.   mail_from[0] = '\0';
  245.   mail_subject[0] = '\0';
  246. }
  247.  
  248.  
  249.  
  250.  
  251. boolean mail_or_rmail_separator(line)
  252. char *line;
  253. {
  254.   static boolean blank_line = false;
  255.  
  256.   if((strlen(line) > strlen("From ")) &&
  257.      substrcmp(line, "From ") &&
  258.      blank_line == true){
  259.     blank_line = false;
  260.     return(true);
  261.   }
  262.   
  263.   if(substrcmp(line, "")){
  264.     blank_line = true;
  265.     return(true);
  266.   }    
  267.   
  268.   if(!strcmp(line, "\n")){
  269.       blank_line = true;
  270.     }
  271.     else{
  272.       blank_line = false;
  273.     }
  274.  
  275.   return(false);
  276. }
  277.  
  278.  
  279. /* ========================================
  280.  * ===  Mail Digest Customizations     ====
  281.  * ========================================
  282.  */
  283.  
  284. boolean mail_digest_separator_function(line)
  285. char *line;
  286. {
  287.   if((strlen(line) > strlen("-----------------------------")) &&
  288.      substrcmp(line, "------------------------------")){
  289.     return(true);
  290.   }
  291.   else{
  292.     return(false);
  293.   }
  294. }
  295.  
  296.  
  297. /* ========================================
  298.  * ===  Library Catalog Customizations  ===
  299.  * ========================================
  300.  */
  301.  
  302. /* just use the title */
  303.  
  304. boolean catalog_separator_function(line)
  305. char *line;
  306. {
  307.   if((strlen(line) > strlen("Call:")) &&
  308.      (substrcmp(line, "Call:"))){
  309.     return(true);
  310.   }
  311.   else{
  312.     return(false);
  313.   }
  314. }
  315.  
  316. char catalog_header[MAX_HEADER_LEN + 1];
  317.  
  318. void catalog_header_function(line)
  319. char *line;
  320. {
  321.   if((strlen(line) > strlen("Title:")) && 
  322.      (substrcmp(line, "Title:"))){    
  323.     strncpy(catalog_header, line + strlen("Title:"), MAX_HEADER_LEN);
  324.   }
  325. }
  326.  
  327. void catalog_finish_header_function(header)
  328. char *header;
  329. {
  330.   if(strlen(catalog_header) == 0){
  331.     strcpy(header, "Unknown Title");
  332.   }
  333.   else{
  334.     strncpy(header, catalog_header, MAX_HEADER_LEN);
  335.   }
  336.   catalog_header[0] = '\0';
  337. }
  338.  
  339.  
  340.  
  341. /* ============================
  342.  * ===  Bio Customizations  ===
  343.  * ============================
  344.  */
  345.  
  346. /* customizations for a DB of genetic abstracts */
  347.  
  348. boolean hit_header = false;
  349.  
  350. boolean bio_separator_function(line)
  351. char *line;
  352. {
  353.   if((strlen(line) > strlen(">>>")) &&
  354.      substrcmp(line, ">>>")){
  355.     return(true);
  356.   }
  357.   else{
  358.     return(false);
  359.   }
  360. }
  361.  
  362. char bio_header[MAX_HEADER_LEN + 1];
  363.  
  364. void bio_header_function(line)
  365. char *line;
  366.  
  367. {
  368.   if(hit_header            /* we just hit a seperator previous to this */
  369.      && (!bio_separator_function(line)) /* we are not on the separator now */
  370.      && strlen(bio_header) == 0){ /* and we have not saved the headline yet */
  371.     strcpy(bio_header, line);
  372.     waislog(WLOG_MEDIUM, WLOG_INDEX, "storing line: %s", bio_header);
  373.     hit_header = false;
  374.   }
  375. }
  376.  
  377. void bio_finish_header_function(header)
  378. char *header;
  379.  
  380. {
  381.   hit_header = true; /* turn on the flag */
  382.   if(strlen(bio_header) == 0){
  383.     strcpy(header, "Unknown Title");
  384.   }
  385.   else{
  386.     strcpy(header, bio_header);
  387.   }
  388.   bio_header[0] = '\0';
  389. }
  390.  
  391. /* =================================
  392.  * ===  CMApp   Customizations  ===
  393.  * =================================
  394.  */
  395.  
  396. boolean cmapp_separator_function(line)
  397. char *line;
  398. {
  399.   if((strlen(line) > strlen("@A")) &&
  400.      substrcmp(line, "@A")){
  401.     /* printf("hit %s\n", line); */
  402.     return(true);
  403.   }
  404.   else{
  405.     return(false);
  406.   }
  407. }
  408.  
  409. char cmapp_header[MAX_HEADER_LEN + 1];
  410.  
  411. void cmapp_header_function(line)
  412. char *line;
  413. {
  414.   if((strlen(line) > strlen("APPLICATION:")) &&
  415.      substrcmp(line, "APPLICATION:")){
  416.     /* printf("hit %s\n", line); */
  417.     strncpy(cmapp_header, line + strlen("APPLICATION:"), MAX_HEADER_LEN);
  418.   }
  419. }
  420.  
  421. void cmapp_finish_header_function(header)
  422. char *header;
  423. {
  424.   if(strlen(cmapp_header) == 0){
  425.     strncpy(header, "Unknown Title", MAX_HEADER_LEN);
  426.   }
  427.   else{
  428.     strncpy(header, cmapp_header, MAX_HEADER_LEN);
  429.   }
  430.   cmapp_header[0] = '\0';
  431. }
  432.  
  433. /* =================================
  434.  * ===  Jargon   Customizations  ===
  435.  * =================================
  436.  */
  437.  
  438. boolean jargon_separator_function(line)
  439. char *line;
  440. {
  441.   if((strlen(line) > 0) && line[0] =='<'){
  442.     /* printf("hit %s\n", line); */
  443.     return(true);
  444.   }
  445.   else{
  446.     return(false);
  447.   }
  448. }
  449.  
  450. char jargon_header[MAX_HEADER_LEN + 1];
  451.  
  452. void jargon_header_function(line)
  453. char *line;
  454. {
  455.   if((strlen(line) > 0) && line[0] =='<'){
  456.     char *end_ptr = strchr(line, '>');
  457.     if(NULL != end_ptr){
  458.       strncpy(jargon_header, (1+ line), MIN(MAX_HEADER_LEN, end_ptr - line));
  459.       jargon_header[end_ptr-line-1] = '\0';
  460.     }
  461.   }
  462. }    
  463.  
  464. void jargon_finish_header_function(header)
  465. char *header;
  466. {
  467.   if(strlen(jargon_header) == 0){
  468.     strncpy(header, "Introduction to the Jargon file", MAX_HEADER_LEN);
  469.   }
  470.   else{
  471.     strncpy(header, jargon_header, MAX_HEADER_LEN);
  472.   }
  473.   jargon_header[0] = '\0';
  474. }
  475.  
  476.  
  477.  
  478. /* =================================
  479.  * ===  Internet Resource Guide  ===
  480.  * =================================
  481.  */
  482.  
  483.  
  484. char irg_header[MAX_HEADER_LEN + 1];
  485. boolean irg_header_set = FALSE;
  486.  
  487. boolean irg_separator_function(line)
  488. char *line;
  489. {
  490.   if(line[0] == 12){  /* control L */
  491.     irg_header_set = FALSE;
  492.     return(true);
  493.   }
  494.   else
  495.     return(false);
  496. }
  497.  
  498. void irg_header_function(line)
  499. char *line;
  500. {
  501.   if((irg_header_set == FALSE) &&
  502.      (line[0] == 32 )){ /* space */
  503.     strncpy(irg_header, line + strspn(line, " "), MAX_HEADER_LEN);
  504.     irg_header_set = TRUE;
  505.   }
  506.   
  507. }
  508.  
  509. void irg_finish_header_function(header)
  510. char *header;
  511. {
  512.   if(strlen(irg_header) == 0){
  513.     strncpy(header, "Unknown Title", MAX_HEADER_LEN);
  514.   }
  515.   else{
  516.     strncpy(header, irg_header, MAX_HEADER_LEN);
  517.   }
  518.   irg_header[0] = '\0';
  519.   irg_header_set = FALSE;
  520. }
  521.  
  522. /* ========================
  523.  * ===  Dash Separator  ===
  524.  * ========================
  525.  */
  526.  
  527.  
  528. /*
  529.  * dash-seperate entries
  530.  * used in Introduction to Algorithms bug.list, suggestions, etc.
  531.  * --------------------... at least 20 dashes
  532.  * header
  533.  * item
  534.  *  ..
  535.  * --------------------... at least 20 dashes
  536.  */
  537.  
  538. boolean dash_hit_header = false;
  539.  
  540. boolean dash_separator_function(line)
  541. char *line;
  542. {
  543.   if((strlen(line) > 20) && substrcmp(line,"--------------------")){
  544.     /* printf("hit %s\n", line); */
  545.     return(true);
  546.   }
  547.   else{
  548.     return(false);
  549.   }
  550. }
  551.  
  552. char dash_header[MAX_HEADER_LEN + 1];
  553.  
  554. void dash_header_function(line)
  555. char *line;
  556. {
  557.   if(dash_hit_header
  558.      && (!dash_separator_function(line))
  559.      && strlen(dash_header) == 0) {
  560.     strncpy(dash_header, line, MAX_HEADER_LEN);
  561.     dash_hit_header = false;
  562.   }
  563. }
  564.  
  565. void dash_finish_header_function(header)
  566. char *header;
  567.  
  568. {
  569.   dash_hit_header = true; /* turn on the flag */
  570.   if (strlen(dash_header) == 0) {
  571.     strcpy(header, "No Title");
  572.   }
  573.   else {
  574.     strncpy(header, dash_header, MAX_HEADER_LEN);
  575.   }
  576.   dash_header[0] = '\0';
  577. }
  578.  
  579.  
  580. /* ============================
  581.  * ===  one_line Separator  ===
  582.  * ============================
  583.  */
  584.  
  585. /* this is where each line is a document (good for databases) */
  586.  
  587. boolean one_line_hit_header = false;
  588.  
  589. boolean one_line_separator_function(line)
  590. char *line;
  591. {
  592.     return(true);
  593. }
  594.  
  595. char one_line_header[MAX_HEADER_LEN + 1];
  596.  
  597. void one_line_header_function(line)
  598. char *line;
  599. {
  600.     strncpy(one_line_header, line, MAX_HEADER_LEN);
  601. }
  602.  
  603. void one_line_finish_header_function(header)
  604. char *header;
  605. {
  606.   if (strlen(one_line_header) == 0) {
  607.     strcpy(header, "No Title");
  608.   }
  609.   else {
  610.     strncpy(header, one_line_header, MAX_HEADER_LEN);
  611.   }
  612.   one_line_header[0] = '\0';
  613. }
  614.  
  615. /* =============================
  616.  * ===  Paragraph Separator  ===
  617.  * =============================
  618.  */
  619.  
  620. /* paragraph files - seperated by a blank line.  Next line is the header */
  621.  
  622. char para_header[MAX_HEADER_LEN +1];
  623. static boolean para_start = true;
  624.  
  625. boolean para_separator_function(line)
  626. char *line;
  627. {
  628.   if (para_start == true) {
  629.     para_start = false;
  630.     return true;
  631.   }
  632.   if (strlen(line) < 2)
  633.     para_start = true;
  634.   return false;
  635. }
  636.  
  637. void para_header_function(line)
  638. char *line;
  639. {
  640.   if (para_header[0] == 0)
  641.     strncpy(para_header, line, MAX_HEADER_LEN);
  642. }
  643.  
  644. void para_finish_header_function(header)
  645. char *header;
  646. {
  647.   if (strlen(para_header) == 0) {
  648.     strcpy(header, "No Title");
  649.   }
  650.   else {
  651.     strncpy(header, para_header, MAX_HEADER_LEN);
  652.   }
  653.   para_header[0] = 0;
  654. }  
  655.  
  656. /* ==========================
  657.  * ===  Seeker Separator  ===
  658.  * ==========================
  659.  */
  660.  
  661. boolean seeker_separator_function(line)
  662. char *line;
  663. {
  664.   return(dash_separator_function(line));
  665. }
  666.  
  667. char seeker_header[MAX_HEADER_LEN + 1];
  668. boolean in_headline = FALSE;
  669.  
  670. void seeker_header_function(line)
  671. char *line;
  672. {
  673.   if(strlen(line) > strlen("Headline:") &&
  674.      substrcmp(line, "Headline:")){
  675.     in_headline = TRUE;
  676.     seeker_header[0] = '\0';
  677.     /* printf("hit headline!\n"); */
  678.   }
  679.   else if(in_headline == TRUE &&
  680.       (strlen(seeker_header) < (MAX_HEADER_LEN - 1))){
  681.     s_strncat(seeker_header, line, 
  682.           MAX_HEADER_LEN, MAX_HEADER_LEN);
  683.     trim_trailing_newline(seeker_header);
  684.   }
  685. }
  686.  
  687. void seeker_finish_header_function(header)
  688. char *header;
  689. {
  690.   if (strlen(seeker_header) == 0) {
  691.     strcpy(header, "No Title");
  692.   }
  693.   else {
  694.     strncpy(header, seeker_header, MAX_HEADER_LEN);
  695.   }
  696.   seeker_header[0] = '\0';
  697.   in_headline = TRUE;
  698. }  
  699.  
  700. /* ==========================
  701.  * ===  RLIN Separator  ===
  702.  * ==========================
  703.  */
  704.  
  705. boolean rlin_separator_function(line)
  706. char *line;
  707. {
  708.   return(dash_separator_function(line));
  709. }
  710.  
  711. char rlin_header[MAX_HEADER_LEN + 1];
  712. boolean rlin_in_headline = FALSE;
  713.  
  714. void rlin_header_function(line)
  715. char *line;
  716. {
  717.   if(rlin_separator_function(line)){
  718.     rlin_in_headline = TRUE;
  719.     rlin_header[0] = '\0';
  720.     /* printf("hit headline!\n"); */
  721.   }
  722.   else if(rlin_in_headline == TRUE &&
  723.       (strlen(rlin_header) < (MAX_HEADER_LEN - 1))){
  724.     s_strncat(rlin_header, line, 
  725.           MAX_HEADER_LEN, MAX_HEADER_LEN);
  726.     trim_trailing_newline(rlin_header);
  727.   }
  728. }
  729.  
  730. void rlin_finish_header_function(header)
  731. char *header;
  732. {
  733.   if (strlen(rlin_header) == 0) {
  734.     strcpy(header, "No Title");
  735.   }
  736.   else {
  737.     strncpy(header, rlin_header, MAX_HEADER_LEN);
  738.   }
  739.   rlin_header[0] = '\0';
  740.   in_headline = TRUE;
  741. }  
  742.  
  743. /* ========================================
  744.  * ===  MH_BBoard  Customizations     ====
  745.  * ========================================
  746.  */
  747.  
  748. /* gcardwel@uci.edu
  749. MH bboards use a series of control A's to do a blank line.. yuk!
  750. */
  751.  
  752. boolean mh_bboard_separator_function(line)
  753. char *line;
  754. {
  755.   static boolean blank_line = false;
  756.  
  757.   if((strlen(line) > strlen("BBoard-ID: ")) &&
  758.      substrcmp(line, "BBoard-ID: ") &&
  759.      blank_line == true){
  760.     blank_line = false;
  761.     return(true);
  762.   }
  763.   
  764.   if(!strcmp(line, "\001\001\001\001\n")){
  765.     blank_line = true;
  766.   }
  767.   else{
  768.     blank_line = false;
  769.   }
  770.   return (false);
  771. }
  772.  
  773.  
  774. /* ==========================
  775.  * ===  Objective-C code  ===
  776.  * ==========================
  777.  */
  778.  
  779. /*----------------------- FSA -------------------*/
  780. #define fsa_max_edges 4
  781. #define fsa_error_state (-1)
  782.  
  783. typedef struct
  784. {
  785.     int if_input;
  786.     int then_goto;
  787. }
  788.     fsa_edge;
  789.  
  790. /* action (if non-NULL) is excuted before transfer to next state is made */
  791. /* action takes as arg the int input that will decide the next state */
  792. typedef struct
  793. {
  794.     int default_goto;
  795.     int n_edges;
  796.     fsa_edge edges[fsa_max_edges];
  797.     int (*action)();
  798. }
  799.     fsa_vertex;
  800.  
  801. int fsa_step(input, state_p, table)
  802. int input;
  803. int *state_p;
  804. fsa_vertex *table;
  805. {
  806.     int next_state, e;
  807.     int (*this_action)();
  808.  
  809.     if(*state_p < 0) return(*state_p = fsa_error_state);
  810.     this_action = table[*state_p].action;
  811.     if(this_action) this_action(input);
  812.     for(e=0; e<table[*state_p].n_edges; e++)
  813.         if(input == table[*state_p].edges[e].if_input)
  814.         { next_state = table[*state_p].edges[e].then_goto; break; }
  815.     if(e >= table[*state_p].n_edges) next_state = table[*state_p].default_goto;
  816.     if(next_state < 0) next_state = fsa_error_state;
  817.     return(*state_p = next_state);
  818. }
  819.  
  820. /* sends null char as last input, returns final state */
  821. int fsa_run(s, state_p, table)
  822. char *s;
  823. int *state_p;
  824. fsa_vertex *table;
  825. {
  826.     char *p;
  827.     
  828.     for(p=s; *p; p++)
  829.         fsa_step((int) *p, state_p, table);
  830.     fsa_step(0, state_p, table);
  831.     return(*state_p);
  832. }
  833.  
  834. /*----------------------- end FSA -------------------*/
  835.  
  836. static int wobjc_brace_level = 0;
  837. static int wobjc_paren_level = 0;
  838. static int wobjc_strip_state = 0;
  839. static int wobjc_context = 0;
  840. static boolean wobjc_separator = false;
  841. static char wobjc_class[MAX_HEADER_LEN + 1];
  842. static char *wobjc_class_end = 0;
  843. static char wobjc_header[MAX_HEADER_LEN + 1];
  844. static char *wobjc_header_end = 0;
  845.  
  846. #define WOBJC_BLANK " \t\n\r"
  847. #define WOBJC_WORD "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_0123456789"
  848.  
  849. /* Flag next line as separator, when context fsa says so. */
  850. static int wobjc_separate(input)
  851. int input;
  852. {
  853.     return(wobjc_separator = true);
  854. }
  855.  
  856. /* FSA to parse objective-C constructs. */
  857. static fsa_vertex wobjc_context_fsa[] =
  858. {
  859.     { 0, 1, {{ '@', 1 }}},            /* look for objc constructs */
  860.     { 0, 1, {{ 'i', 20 }}},
  861.     { 3, 1, {{ ' ', 2 }}},                /* look for @imp class */
  862.     { 4, 1, {{ 'A', 3 }}},
  863.     { 4, 3, {{ '+', 6 },{ '-', 8 },{ '@', 10 }}},/* in @imp */
  864.     { 4, 3, {{ '+', 6 },{ '-', 8 },{ '@', 10 }}, wobjc_separate},
  865.     { 6, 1, {{ '{', 7 }}},            /* look for -method: */
  866.     { 5, 1, {{ '{', 7 }}},
  867.     { 8, 1, {{ '{', 9 }}},            /* look for +method: */
  868.     { 5, 1, {{ '{', 9 }}},
  869.     { 4, 1, {{ 'e', 11 }}},            /* look for @end of @imp */
  870.     { 4, 1, {{ 'n', 12 }}},
  871.     { 4, 1, {{ 'd', 0 }}},
  872.     { 14, 1, {{ ' ', 13 }}},               /* look for @intf class */
  873.     { 15, 1, {{ 'A', 14 }}},
  874.     { 15, 1, {{ '@', 16 }}},            /* in @intf */
  875.     { 15, 1, {{ 'e', 17 }}},            /* look for @end of @intf */
  876.     { 15, 1, {{ 'n', 18 }}},
  877.     { 15, 1, {{ 'd', 19 }}},
  878.     { 0, 1, {{ '@', 1 }}, wobjc_separate},
  879.     { 0, 2, {{ 'm', 21 },{ 'n', 33 }}},        /* look for @impl */
  880.     { 0, 1, {{ 'p', 22 }}},
  881.     { 0, 1, {{ 'l', 23 }}},
  882.     { 0, 1, {{ 'e', 24 }}},
  883.     { 0, 1, {{ 'm', 25 }}},
  884.     { 0, 1, {{ 'e', 26 }}},
  885.     { 0, 1, {{ 'n', 27 }}},
  886.     { 0, 1, {{ 't', 28 }}},
  887.     { 0, 1, {{ 'a', 29 }}},
  888.     { 0, 1, {{ 't', 30 }}},
  889.     { 0, 1, {{ 'i', 31 }}},
  890.     { 0, 1, {{ 'o', 32 }}},
  891.     { 0, 1, {{ 'n', 2 }}},
  892.     { 0, 1, {{ 't', 34 }}},            /* look for @intf */
  893.     { 0, 1, {{ 'e', 35 }}},
  894.     { 0, 1, {{ 'r', 36 }}},
  895.     { 0, 1, {{ 'f', 37 }}},
  896.     { 0, 1, {{ 'a', 38 }}},
  897.     { 0, 1, {{ 'c', 39 }}},
  898.     { 0, 1, {{ 'e', 13 }}}
  899. };
  900.  
  901. /* Action to be used by stripping fsa in non-commented, non-quoted state. */
  902. /* This runs context fsa. */
  903. static int wobjc_process_stripped_code(input)
  904. int input;
  905. {
  906.     int context_input;
  907.     
  908.     switch(input)
  909.     {
  910.         /* Increment brace/paren levels as appropriate. */
  911.     case '{': wobjc_brace_level++; break;
  912.     case '}': if(wobjc_brace_level > 0) wobjc_brace_level--; break;
  913.     case '(': wobjc_paren_level++; break;
  914.     case ')': if(wobjc_paren_level > 0) wobjc_paren_level--; break;
  915.     case '\"': break;
  916.     case '\'': break;
  917.     case '/': break;
  918.         
  919.     default:
  920.     /* If in correct context and not in brace/paren/comment/quote, */
  921.     /* then record header info.  */
  922.         if(wobjc_brace_level==0 && wobjc_paren_level==0)
  923.     {
  924.         /* Recording class or instance method.  Ignore multiple blanks. */
  925.         if(wobjc_context==6 || wobjc_context==8)
  926.         {
  927.         if(!wobjc_header_end || wobjc_header_end==wobjc_header)
  928.         {
  929.             strcpy(wobjc_header, (wobjc_context==6 ? "+[" : "-["));
  930.             strcat(wobjc_header, wobjc_class);
  931.             strcat(wobjc_header, " ");
  932.             wobjc_header_end = wobjc_header + strlen(wobjc_header);
  933.         }
  934.         if((wobjc_header_end - wobjc_header)<(MAX_HEADER_LEN-5)
  935.             && !(strchr(WOBJC_BLANK, *(wobjc_header_end-1))
  936.             && strchr(WOBJC_BLANK, input)))
  937.             { *wobjc_header_end++ = input; *wobjc_header_end = 0; }
  938.         }
  939.         
  940.         /* Recording class name for @implementation or @interface. */
  941.         if(strchr(WOBJC_WORD, input)
  942.             && (wobjc_context==2 || wobjc_context==3
  943.             || wobjc_context==13 || wobjc_context==14))
  944.         {
  945.             if(wobjc_context==2 || wobjc_context==13 || !wobjc_class_end)
  946.             wobjc_class_end = wobjc_class;
  947.             if(wobjc_context==13
  948.             || (wobjc_context==14 && !wobjc_header_end))
  949.             wobjc_header_end = wobjc_header;
  950.         if((wobjc_class_end - wobjc_class_end)<(MAX_HEADER_LEN/2))
  951.             { *wobjc_class_end++ = input; *wobjc_class_end = 0; }
  952.         if((wobjc_context==13 || wobjc_context==14)
  953.             && (wobjc_header_end-wobjc_header_end)<(MAX_HEADER_LEN/2))
  954.             { *wobjc_header_end++ = input; *wobjc_header_end = 0; }
  955.         }
  956.     }
  957.     
  958.     /* Since not in comment/quote, run context fsa. */
  959.     /* Input is modified like this: */
  960.     /*    Non-zero brace level => '{'. */
  961.     /*    Else spaces => ' '. */
  962.     /*    Else if in correct contexts, word letters => 'A'. */
  963.     context_input = input;
  964.     if(wobjc_brace_level>0) context_input = '{';
  965.     else if(strchr(WOBJC_BLANK, input)) context_input = ' ';
  966.     else if((wobjc_context==3 || wobjc_context==14)
  967.         && strchr(WOBJC_WORD, input))
  968.             context_input = 'A';
  969.     fsa_step(context_input, &wobjc_context, wobjc_context_fsa);
  970.     break;
  971.     }
  972.     return(true);
  973. }
  974.  
  975. /* FSA to strip out comments and quotes. */
  976. static fsa_vertex wobjc_strip_fsa[] =
  977. {
  978.     { 0, 3, {{ '/', 1 },{ '\"', 5 },{ '\'', 7 }}, wobjc_process_stripped_code},
  979.     { 0, 2, {{ '*', 2 },{ '/', 4 }}},        /* look for comment */
  980.     { 2, 1, {{ '*', 3 }}},            /* in /* comment */
  981.     { 2, 2, {{ '/', 0 },{ '*', 3 }}},
  982.     { 4, 1, {{ '\n', 0 }, { '\0', 0 }}},    /* in // comment */
  983.     { 5, 2, {{ '\\', 6 },{ '\"', 0 }}},        /* in " quote */
  984.     { 5, 0, },
  985.     { 7, 2, {{ '\\', 8 },{ '\'', 0 }}},        /* in ' quote */
  986.     { 7, 0, }
  987. };
  988.  
  989. boolean wobjc_separator_function(line)
  990. char *line;
  991. {
  992.     if(wobjc_separator) { wobjc_separator = false; return true; }
  993.     else return false;
  994. }
  995.  
  996. void wobjc_header_function(line)
  997. char *line;
  998. {
  999.     /* Run stripping fsa, which will run context fsa. */
  1000.     fsa_run(line, &wobjc_strip_state, wobjc_strip_fsa);
  1001.     return;
  1002. }
  1003.  
  1004. void wobjc_finish_header_function(header)
  1005. char *header;
  1006. {
  1007.     char *p;
  1008.     
  1009.     /* Flush terminal blanks and balance opening '[' if any. */
  1010.     for(p=wobjc_header+strlen(wobjc_header);
  1011.         p>wobjc_header && strchr(WOBJC_BLANK, *(p-1)); p--);
  1012.     if(wobjc_header[0]=='+' || wobjc_header[0]=='-') *p++ = ']';
  1013.     *p = 0;
  1014.     
  1015.     /* Copy out final header. */
  1016.     strcpy(header, wobjc_header);
  1017.     wobjc_header[0] = 0;
  1018.     wobjc_header_end = wobjc_header;
  1019.     return;
  1020. }
  1021.  
  1022.  
  1023.