Changeset 1174 for branches/z3950-branch


Ignore:
Timestamp:
2000-05-17T14:57:06+12:00 (24 years ago)
Author:
johnmcp
Message:

checkpoint - can now connect and get status information from a z39.50 server.
Still need to get queries and data retrieval working.

Location:
branches/z3950-branch/gsdl/src/recpt
Files:
2 added
7 edited

Legend:

Unmodified
Added
Removed
  • branches/z3950-branch/gsdl/src/recpt/Makefile.in

    r1168 r1174  
    7777RECEPTHEADERS =
    7878
    79 LIBRARYHEADERS = nullproto.h z3950proto.h
     79LIBRARYHEADERS = nullproto.h z3950proto.h z3950server.h
    8080
    8181HEADERS = $(COMMONHEADERS) $(RECPTHEADERS) $(LIBRARYHEADERS)
     
    9595RECPTSOURCES = recptmain.cpp
    9696
    97 LIBRARYSOURCES = nullproto.cpp z3950proto.cpp librarymain.cpp
     97LIBRARYSOURCES = nullproto.cpp z3950proto.cpp z3950server.cpp librarymain.cpp
    9898
    9999SOURCES = $(COMMONSOURCES) $(RECPTSOURCES) $(LIBRARYSOURCES)
     
    113113RECPTOBJECTS = recptmain.o
    114114
    115 LIBRARYOBJECTS = nullproto.o z3950proto.o librarymain.o zparse.tab.o
     115LIBRARYOBJECTS = nullproto.o z3950proto.o z3950server.o \
     116         librarymain.o zparse.tab.o parse.yy.o
    116117
    117118OBJECTS = $(COMMONOBJECTS) $(RECPTOBJECTS) $(LIBRARYOBJECTS)
  • branches/z3950-branch/gsdl/src/recpt/TODO

    r1169 r1174  
    2121Needs updating for all added files (z*, parse.fl)
    2222
     23yaz
     24---
     25Maybe put libyaz.a in $GSDLHOME/lib ?
     26
     27
     28DESIGN ISSUES
     29for z39.50 (and eventually nullproto when corba allows recept. and
     30coll server to be physically separate), if the server is unavailable,
     31should this be determined at:
     32  * "home page" creation time, in which case the recept. must ping and
     33test each collection it knows about, which may cause unacceptable delay, or
     34  * "about page" creation time (ie individual collection), in which case
     35the collection can not customise the page layout by modifying macros.
     36
     37Eg currently, the z39.50 collection will not create a TCP connection to the
     38"target" (ie z39.50 server somewhere around the world) until it tries to
     39get the "About this collection" information for that collection.
     40If the connection fails, the protocol can not change _queryform_ part of
     41the page.
     42
     43
     44MULTI-LINGUAL SUPPORT
     45Multi-lingual - eg macros - want to set _collectionextra_ using
     46metadata["collectionextra"] based on the chosen interface language.
     47Should be done by OIDtools.cpp:get_info() and any action that calls
     48proto->filter().
     49queryaction.cpp (c. line 1097):
     50  // add the requested language as an option to the query
     51  if (!args["l"].empty()) {
     52    OptionValue_t option;
     53    option.name="Language";
     54    option.value=args["l"];
     55    request.filterOptions.push_back(option);
     56  }
     57
  • branches/z3950-branch/gsdl/src/recpt/parse.fl

    r1168 r1174  
    2626%%
    2727    char *string;
    28     int string_size;
     28    int string_len;
    2929
    3030{COMMENT}       lineno++;yylval.string=yytext;
     
    4040Languages       return(LANGUAGES);
    4141About           return(ABOUT);
    42 en          return(EN);
    43 fr          return(FR);
    44 mi          return(MI);
    45 zh          return(ZH);
    46 de          return(DE);
    4742Browse          return(BROWSE);
    4843Date[lL]ist     return(DATELIST);
     
    6762^document       return (DOCUMENT);
    6863^section        return (SECTION);
    69 \"[^\" ]*\"     { /* for single word strings */
    70               if ((string=strdup(yytext+1))==NULL)
    71                {
    72                  fprintf(stderr,"malloc failed\n");
    73                  exit(1);
    74                }
    75               string[strlen(string)-1]='\0'; /* chomp " */
    76               /* yylval=yytext; */
    77               yylval.string=string;
    78               return(STRING);
    79             }
    80 \"[^\"]*        {
    81              BEGIN(STRINGCOND);
    82              /* the +1 is to skip the leading " */
    83              if ((string=strdup(yytext+1))==NULL)
    84                {
    85                   fprintf(stderr,"malloc failed\n");
    86                   exit(1);
    87                }
    88              else string_size=strlen(yytext)-1;
    89             }
    9064[iI]con         return (ICON);
    9165[sS]mall[iI]con     return (SMALLICON);
     66\"          {string=NULL;BEGIN(STRINGCOND);}
    9267<STRINGCOND>{
    93     ([^\"\\])*  {
    94              if (strlen(string)+strlen(yytext)>string_size)
    95               { /* what if strlen(yytext) > 40 !!!!!! */
    96                if ((string=realloc(string,string_size+40))==NULL)
    97                  {
    98                    fprintf(stderr,"realloc failed\n");
    99                    exit(1);
    100                  }
    101                else string_size+=40;
    102               }
    103              strcat(string,yytext);
     68  ([^\"\\])*        {/* append this to our current string */
     69            if (string==NULL) string_len=0; else
     70            string_len=strlen(string);
     71            string=realloc(string,string_len+strlen(yytext)+1);
     72            /* +1 is for trailing \0 */
     73            strcpy(string+string_len,yytext);
    10474            }
    105     "\\"\"      |
    106     \"\"        { /* replace with a single " */
    107             /* yes, this is dodgy for now... */
    108             strcat(string,"\"");
     75  "\\"\"        |
     76  \"\"      {       /*replace quoted quote with one quote*/
     77            string_len=strlen(string);
     78            string=realloc(string,string_len+2);
     79            string[string_len]='\"';
     80            string[string_len+1]='\0';
    10981            }
    110     \"      {
    111               BEGIN(INITIAL);
    112               yylval.string=string;
    113               return(STRING);
     82  \"        {
     83            BEGIN(INITIAL);
     84            yylval.string=string;
     85            return(STRING);
    11486              /* note that the string may have long bits of
    115                  whitespace in it that could be removed. */
     87                 whitespace in it that we could remove. */
    11688            }
    11789}
     
    12092(\n|\r)         lineno++;
    12193%%
     94/******** deleted rules: *******
     95en          return(EN);
     96fr          return(FR);
     97mi          return(MI);
     98zh          return(ZH);
     99de          return(DE);
     100***************/
    122101
    123102
    124103
    125104
    126 
  • branches/z3950-branch/gsdl/src/recpt/queryaction.cpp

    r962 r1174  
    2828/*
    2929   $Log$
     30   Revision 1.36.6.1  2000/05/17 02:57:00  johnmcp
     31   checkpoint - can now connect and get status information from a z39.50 server.
     32   Still need to get queries and data retrieval working.
     33
    3034   Revision 1.36  2000/02/21 21:57:48  sjboddie
    3135   actions are now configured with gsdlhome
     
    10951099    args["q"]=formattedstring;
    10961100  }
     1101
    10971102  format_querystring (formattedstring, args.getintarg("b"));
    10981103  set_queryfilter_options (request, formattedstring, args);
  • branches/z3950-branch/gsdl/src/recpt/z3950proto.cpp

    r1168 r1174  
    66#include "comtypes.h"
    77#include <stdio.h>
     8// z39.50 yaz stuff
     9
    810
    911// config file parsing stuff
     
    1113// defines "struct z3950cfg *zserver_list" as the head of the list.
    1214extern FILE *yyin;
    13 // for some reason, adding the following line screws up the linking...
    14 //extern int zconfigparse();
    15 
    16 /***
    17     We will use the "collection" argument as the name of the database
    18     to query on the z3950 server.
    19 ***/
    20 
    21 
    22 z3950_server::z3950_server() {
    23   count=0;
    24   info=NULL;
    25   meta["iconcollection"]="/~johnmcp/gsdl/images/zserver.png";
    26   // meta["collectionextra"]="Extra collection information";
    27   // meta["collectionname"]="overwritten";
    28 }
    29 void z3950_server::setMeta(const text_t &key, const text_t &value) {
    30   meta[key]=value;
    31 }
    32 void z3950_server::setName(const text_t &newname) {
    33   title=newname;
    34   meta["collectionname"]=newname;
    35   // cout << "Have set server name\n";
    36 }
    37 
    38 
    39 
    40 
    41 // z3950 Protocol functions
     15extern "C" {
     16  extern int zconfigparse();
     17}
     18
    4219z3950proto::z3950proto() {
    4320  zserver_count=0;
     21}
     22
     23z3950proto::~z3950proto() {
    4424}
    4525
     
    5333void z3950proto::read_config_file(const text_t &filename) {
    5434  struct z3950cfg *here;
     35  struct z3950cfg *oldhere;
    5536  z3950_server *zserver;
    5637  ShortColInfo_t *tempinfo;
    5738
    58   cerr << "attempting to read config file: " << filename.getcstr() << "\n";
    5939  // zconfigparse() is defined in zparse.tab.c,
    6040  // which is the bison output of zparse.y
     
    9474    }
    9575
    96     // and about list .... later.
     76    // About list
     77    if (here->about!=NULL) {
     78      struct z3950aboutlist *about_here=here->about;
     79      struct z3950aboutlist *oldabout;
     80
     81      while (about_here!=NULL) {
     82    // problem with default lang (null):
     83    if (about_here->lang==NULL)
     84      zserver->addAbout("en",about_here->text);
     85    else
     86      zserver->addAbout(about_here->lang, about_here->text);
     87    oldabout=about_here;
     88    about_here=about_here->next;
     89    free(oldabout->lang);
     90    free(oldabout->text);
     91    free(oldabout);
     92      }
     93    }
     94
     95    oldhere=here;
     96    here=here->next;
     97    free(oldhere->shortname);
     98    free(oldhere->hostname);
     99    free(oldhere->dbname);
     100    free(oldhere->longname);
     101    free(oldhere->icon);
     102    free(oldhere->smallicon);
     103    free(oldhere);
     104
    97105
    98106    add_server(*zserver);
    99     here=here->next;
    100   }
    101 
    102   // finally, delete all unneeded allocated memory in server_list
     107    /* assume that the push_back operator creates a copy.
     108       BUT: doesn't create copies of structures pointed to... only
     109       creates a copy of the pointer (to the same location!) */
     110  } // end of while loop.
    103111
    104112}
     
    200208  }
    201209
    202   /*  collectinfo.shortInfo.name="sdfg";
    203       collectinfo.shortInfo.host="localhost";
    204       collectinfo.shortInfo.port=0; */
    205210  const ShortColInfo_t *colinfo=here->getInfo();
    206211  collectinfo.shortInfo.name=colinfo->name;
     
    214219  // leave ccsCols empty (no cross-coll. searching - for now)
    215220  /*collectinfo.ccsCols=(text_tarray);*/ //not like this!!!
     221  // This info is available from the config file -- johnmcp */
    216222  collectinfo.languages.push_back("en");
    217223  collectinfo.languages.push_back("fr");
     
    221227  // copy the text maps over.
    222228  //  collectinfo.collectionmeta; // text_tmap
    223   //  delete collectinfo.collectionmeta;
    224229  collectinfo.collectionmeta=*(here->getMeta());
    225230  /* collectinfo.format; //text_tmap
     
    243248             FilterResponse_t &response,
    244249             comerror_t &err, ostream &logout) {
    245 
    246250  // this function is called when:
    247251  //  *  creating the title page,(looking for iconcoll* & collectname metadata)
    248252  //  *  creating the about page (looking for "Title" metadata)
     253  //  * doing the query - (note that a request for metadata comes first, then
     254  // filterOptions = FRmetadata | FROID | FRtermFreq (64+4+1)
     255
     256  // metadata-only requests have filterName="NullFilter", else "QueryFilter".
    249257  // For the title page, we should not create a connection to the target
    250258  // (target means the actual z39.50 server, origin means us), but
     
    255263  // cerr now goes to errout.txt in etc directory
    256264
    257   response.numDocs=0;
    258   response.isApprox=Approximate; // Exact | Approximate | MoreThan
     265  // get relevant "collection"
     266  z3950_server_array::iterator zserver = zservers.begin();
     267  z3950_server_array::iterator zend = zservers.end();
     268  while (zserver != zend) {
     269    if(zserver->getName()==collection) {
     270      break;
     271    }
     272    zserver++;
     273  }
     274  // now have collection in zserver.
     275 
     276  //  ColInfoResponse_t *info = new ColInfoResponse_t;
     277  ColInfoResponse_t info;
     278  ResultDocInfo_t *docInfo=new ResultDocInfo_t;
     279 
     280
    259281  // leave response.termInfo empty
    260282  // leave response.docInfo empty
     
    269291
    270292
     293  // See if this is for a query action
     294  /*** johnmcp **********
     295       this is not enough, as FROID is set for the about action,
     296       and furthermore, the number of docInfo's (+1?) seems to imply the number
     297       of search methods.
     298       ** ABOUT ACTION
     299       * OIDtools.get_children()
     300       .   FRmetadata, FilterOptions ParentNode=''
     301       * OIDtools.get_info()
     302       .   FRmetadata
     303       ** QUERY ACTION
     304       * OIDtools.get_info() <- receptionist.define_general_macros()
     305       .    FRmetadata, no OptionValues
     306       * queryaction.search_single_collection
     307       .  FRmetadata | FROID | FRtermFreq, fltrOps Term/QueryType/Casefold/..
     308  */
     309  if (request.filterResultOptions & /*FROID*/FRtermFreq) {
     310    // Pretty bad hack: this is assuming
     311    // FRtermFreq is only set for query action, not about action.
     312
     313    //  what about:
     314    // OptionValue_tarray filterOptions
     315    // docSet
     316    // requestParams
     317    // refParams
     318    // fields -- requested metadata fields.
     319  /*
     320    if (!request.requestParams.empty())
     321    cout << "RequestParams: " << request.requestParams.getcstr() << "\n";
     322    if (!request.refParams.empty())
     323    cout << "RefParams: " << request.refParams.getcstr() << "\n";
     324  */    OptionValue_tarray::iterator ov_here=request.filterOptions.begin();
     325  OptionValue_tarray::iterator ov_end=request.filterOptions.end();
     326  while (ov_here != ov_end) {
     327    cout << "OV pair: `" << ov_here->name.getcstr() << "'=`"
     328     << ov_here->value.getcstr() << "'\n";
     329    ov_here++;
     330  }/*
     331    cout << "end\n\n";
     332  */
     333
     334    /* Sample OptionValue pairs
     335      `StartResults'=`1'
     336      `EndResults'=`20'
     337      `Term'=`firstword secondword' (term is just whatever the user typed in)
     338      `QueryType'=`ranked' => 'OR' (cgiarg t=1)
     339      `QueryType' = `boolean' => 'AND' (cgiarg t=0)
     340      `Casefold'=`true'
     341      `Stem'=`false'
     342      `Maxdocs'=`50'
     343     */
     344    docInfo->metadata["Title"].values.push_back("dummy title");
     345    docInfo->result_num=1;
     346
     347    if (request.filterResultOptions & FRtermFreq) {
     348      response.numDocs=2;
     349      response.isApprox=Exact; // Exact | Approximate | MoreThan
     350    }
     351
     352  response.docInfo.push_back(*docInfo);
     353  // create a 2nd dummy record
     354  docInfo=new ResultDocInfo_t;
     355  docInfo->metadata["Title"].values.push_back("another title");
     356  docInfo->result_num=2;
     357  response.docInfo.push_back(*docInfo);
     358 
     359  } // end of if (... & FROID)
     360  else {
     361    // this wasn't a query action
     362
     363    if (request.filterOptions.size()>0 &&
     364    request.filterOptions[0].name=="ParentNode") {
     365      // don't want to return anything
     366      //delete (docInfo);
     367    } else {
     368      // in case we need to return only metadata
     369      response.docInfo.push_back(*docInfo);
     370    }
     371  }
     372
     373  // Fill in metadata for each response.docInfo (if wanted)
    271374  if (request.filterResultOptions & FRmetadata) {
    272 
    273     ColInfoResponse_t *info = new ColInfoResponse_t;
    274     get_collectinfo (collection, *info, err, logout);
     375    get_collectinfo (collection, info, err, logout);
    275376    // should check err returned here....
    276 
    277     ////////// cerr << "Filter.\n";
    278 
    279     ResultDocInfo_t *docInfo=new ResultDocInfo_t;
    280 
    281377    /* In the absence of any other information, (eg commented code),
    282378       assuming that if the request.fields is empty, then we should return
    283379       all metadata, otherwise return only the requested fields */
     380    /**** comtypes.h has: "text_tset fields;   // empty if not used" ****/
    284381
    285382    if (!request.fields.empty()) {
    286       // loop on all the metadata fields in request.fields (type text_tset)
    287       text_tset::iterator fields_here=request.fields.begin();
    288       text_tset::iterator fields_end=request.fields.end();
    289       text_tmap::iterator it;
    290       while (fields_here!=fields_end) {
    291     it=info->collectionmeta.find(*fields_here);
    292     ////////// cerr << "filter: getting " << (*fields_here).getcstr();
    293     if (it!=info->collectionmeta.end())
    294       docInfo->metadata[*fields_here].values.push_back((*it).second);
    295     else {
    296       docInfo->metadata[*fields_here].values.push_back("");
    297       /////// cerr << " (not found)";
    298     }
    299     ////////cerr << "\n";
    300     fields_here++;
    301       } // end of while loop
     383      // loop on each document being returned
     384      ResultDocInfo_tarray::iterator docs_here=response.docInfo.begin();
     385      ResultDocInfo_tarray::iterator docs_end=response.docInfo.end();
     386      while (docs_here!=docs_end) {
     387   
     388    // loop on all the metadata fields in request.fields (type text_tset)
     389    text_tset::iterator fields_here=request.fields.begin();
     390    text_tset::iterator fields_end=request.fields.end();
     391    text_tmap::iterator it;
     392    while (fields_here!=fields_end) {
     393      it=info.collectionmeta.find(*fields_here);
     394      ////////// cerr << "filter: getting " << (*fields_here).getcstr();
     395      if (it!=info.collectionmeta.end())
     396        docs_here->metadata[*fields_here].values.push_back((*it).second);
     397      else {
     398        docs_here->metadata[*fields_here].values.push_back("");
     399        /////// cerr << " (not found)";
     400      }
     401      ////////cerr << "\n";
     402      fields_here++;
     403    } // end of inner while loop
     404    docs_here++;
     405      } // end of outer while loop
    302406    } // end of if (!request.fields.empty())
    303     else {
    304       // return all metadata for about page or query
    305       text_tmap::iterator colmeta_here=info->collectionmeta.begin();
    306       text_tmap::iterator colmeta_end=info->collectionmeta.end();
     407
     408    else { // request.fields empty: return all metadata for about page or query
     409      // we'll only put it in the first docInfo.
     410      text_tmap::iterator colmeta_here=info.collectionmeta.begin();
     411      text_tmap::iterator colmeta_end=info.collectionmeta.end();
    307412      while (colmeta_here!=colmeta_end) {
    308     docInfo->metadata[(*colmeta_here).first].
     413    response.docInfo[0].metadata[(*colmeta_here).first].
    309414      values.push_back((*colmeta_here).second);
    310415    /////cerr << "\t" << (*colmeta_here).first.getcstr() << "\n";
     
    312417      }
    313418      // get data from target.
    314       z3950_server_array::iterator zserver = zservers.begin();
    315       z3950_server_array::iterator zend = zservers.end();
    316       while (zserver != zend) {
    317     if(zserver->getName()==collection) {
    318       break;
    319     }
    320     zserver++;
    321       }
    322       // now have collection in zserver.
    323419      // check if "collectionextra" metadata is set. If it isn't, we should
    324420      // create connection to target to get it.
    325       if (info->collectionmeta.find("collectionextra")==colmeta_end) {
     421      if (info.collectionmeta.find("collectionextra")==colmeta_end) {
    326422    // it doesn't exist.
    327     zserver->setMeta("collectionextra","This is some extra info.");
    328     docInfo->metadata["collectionextra"].values.push_back("This is extra");
     423    text_t *abouttext;
     424    // this gets the server's name and id, plus MOTD, etc.
     425    abouttext=zserver->connect_getAbout();
     426    // add in the text we read in from config file.
     427    // how do we incorporate multi-lingual metadata?
     428    (*abouttext)+="<P>\n";
     429    (*abouttext)+=zserver->getAbout("en");
     430    zserver->setMeta("collectionextra",*abouttext);
     431    response.docInfo[0].metadata["collectionextra"].values.push_back(*abouttext);
     432    delete (abouttext);
    329433      }
    330      
    331434    } // end of else
    332 
    333     response.docInfo.push_back(*docInfo);
     435   
    334436  } //end of  if (... & FRmetadata) ...
     437
     438
     439
    335440  err=noError;
    336441}
  • branches/z3950-branch/gsdl/src/recpt/z3950proto.h

    r1168 r1174  
    44
    55#include "recptproto.h"
    6 
    7 // each "server" (ie collection) consists of a {z39.50 server + database} pair.
    8 class z3950_server {
    9  protected:
    10   text_t title;    // Descriptive name
    11   text_t hostname; // network name (IP or host+domain name)
    12   text_tmap meta;  // metadata for this collection (ie server/database pair)
    13   // this is currently only iconcollection (and iconcollectionsmall)
    14   int count;
    15   //  z3950_DB_array* db;
    16   ShortColInfo_t *info; // has  (database) name, host and port
    17 
    18  public:
    19   z3950_server();
    20   const text_t getName() {return title;}
    21   void setMeta(const text_t &key, const text_t &value);
    22   void setName(const text_t &newname);
    23   void setInfo(ShortColInfo_t *newinfo) {info=newinfo;}
    24   const ShortColInfo_t *getInfo() {return info;}
    25   const text_tmap *getMeta() {return &meta;}
    26 };
    27 
    28 typedef  vector<z3950_server>  z3950_server_array;
    29 
    30 
    31 
     6#include "z3950server.h"
    327
    338class z3950proto : public recptproto {
     
    3712public:
    3813  z3950proto();
    39   virtual ~z3950proto() {}
     14  virtual ~z3950proto();
    4015
    4116  int getServerCount() { return zserver_count;}
     
    8863};
    8964
    90 
    9165#endif
  • branches/z3950-branch/gsdl/src/recpt/zparse.y

    r1168 r1174  
    7272%type <cfg>      zserver zserverlist
    7373%type <number>  port
    74 %type <string>   icon smallicon
    75 %type <about>    about
     74%type <string>   icon smallicon lang
     75%type <about>    about aboutLang aboutList
    7676%%
    7777/* grammar */
     
    8282              | {fprintf(stderr,"No version - not GSDL config file?\n");exit(1);}
    8383
    84 zserverlist   : zserverlist zserver {$2->next=$1;$$=$2;}
     84zserverlist   : zserverlist zserver {if ($2!=NULL) {$2->next=$1;$$=$2;}}
    8585              | zserver ;
    8686
     
    9797             $$->about=$8;
    9898           }
     99              | error {$$=NULL;}
    99100              ;
    100101
     
    107108                  errormsg("Icon must be enclosed in quotes");
    108109          yylex();yylex();}
    109               | {;}
     110              | {$$=NULL;}
    110111smallicon     : SMALLICON STRING {$$=$2;}
    111112              | SMALLICON DATA {$$=$2;
     
    114115          yylex();yylex();
    115116          }
    116               | {;}
     117              | {$$=NULL;}
    117118
    118119about         : aboutList {;}
    119               | {;}
     120              | {$$=NULL;}
    120121
    121122aboutList     : aboutLang {;}
    122               | aboutList aboutLang {;}
    123 
    124 aboutLang     : ABOUT lang STRING {/*printf("string is <%s>\n",$3)*/;}
    125               | ABOUT STRING {
    126                                if (defLanguage)
     123                      /* note this action reverses the order of the list */
     124              | aboutList aboutLang {$$=$2;$2->next=$1;}
     125
     126
     127aboutLang     : ABOUT lang STRING
     128                             {
     129                   if(($$=malloc(sizeof(struct z3950aboutlist)))
     130                  ==NULL) {
     131                 fprintf(stderr,"Malloc failed\n");
     132                 exit(1);
     133                   }
     134                   $$->lang=$2;
     135                   $$->text=$3;
     136                   $$->next=NULL;
     137                 }
     138              | ABOUT STRING
     139                             {
     140                   if (defLanguage)
    127141                         {
    128142                   fprintf(stderr,"warning: (line %d): already have a default language in config file\n",lineno);
    129143                 }
    130144                   else defLanguage=1;
    131                          }
    132  /* this can only happen once (default lang) */
     145                   
     146                   if(($$=malloc(sizeof(struct z3950aboutlist)))
     147                  ==NULL) {
     148                 fprintf(stderr,"Malloc failed\n");
     149                 exit(1);
     150                   }
     151                   $$->lang=NULL; /* default lang... */
     152                   $$->text=$2;
     153                   $$->next=NULL;
     154                 }
     155/* this can only happen once (default lang) */
     156
    133157
    134158/* languages should be done using a lookup, rather than hard-coding
    135    them as tokens like this. Eg '_' token for en_BR */
    136 lang          : lang '_' DATA {;}
    137               | EN {;}
    138               | FR {;}
    139               | MI {;}
    140               | ZH {;}
    141               | DE {;}
    142               | DATA {errormsg("unknown language");}
     159   them as tokens. Also '_' token for en_BR */
     160lang          : DATA '_' DATA
     161                             {
     162                   $$=malloc(strlen($1)+strlen($3)+1);
     163                   strncpy($$,$1,strlen($1));
     164                   $$[strlen($1)]='_';
     165                   strncpy($$+strlen($1)+1,$3,strlen($3));
     166                   free($1);free($3);
     167                 }
     168              | DATA {;}
     169/*lang '_' DATA {;} */
     170/*              | EN {;}
     171        | FR {;}
     172        | MI {;}
     173        | ZH {;}
     174        | DE {;}
     175        | DATA {errormsg("unknown language");}*/
    143176              | error {errormsg("missing language");}
    144177
Note: See TracChangeset for help on using the changeset viewer.