123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681 |
- /*
- * Wine Message Compiler parser
- *
- * Copyright 2000 Bertho A. Stultiens (BS)
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * NOTES:
- *
- * The basic grammar of the file is yet another example of, humpf,
- * design. There is a mix of context-insensitive and -sensitive
- * stuff, which makes it rather complicated.
- * The header definitions are all context-insensitive because they have
- * delimited arguments, whereas the message headers are (semi-) context-
- * sensitive and the messages themselves are, well, RFC82[12] delimited.
- * This mixture seems to originate from the time that ms and ibm were
- * good friends and developing os/2 according to the "compatibility"
- * switch and reading some comments here and there.
- *
- * I'll ignore most of the complications and concentrate on the concept
- * which allows me to use yacc. Basically, everything is context-
- * insensitive now, with the exception of the message-text itself and
- * the preceding language declaration.
- *
- */
- %{
- #include "config.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <assert.h>
- #include "wmc.h"
- #include "utils.h"
- #include "lang.h"
- static const char err_syntax[] = "Syntax error\n";
- static const char err_number[] = "Number expected\n";
- static const char err_ident[] = "Identifier expected\n";
- static const char err_assign[] = "'=' expected\n";
- static const char err_popen[] = "'(' expected\n";
- static const char err_pclose[] = "')' expected\n";
- static const char err_colon[] = "':' expected\n";
- static const char err_msg[] = "Message expected\n";
- /* Scanner switches */
- int want_nl = 0; /* Request next newlinw */
- int want_line = 0; /* Request next complete line */
- int want_file = 0; /* Request next ident as filename */
- struct node *nodehead = NULL; /* The list of all parsed elements */
- static struct node *nodetail;
- struct lan_blk *lanblockhead; /* List of parsed elements transposed */
- static int base = 16; /* Current printout base to use (8, 10 or 16) */
- static WCHAR *cast = NULL; /* Current typecast to use */
- static int last_id = 0; /* The last message ID parsed */
- static int last_sev = 0; /* Last severity code parsed */
- static int last_fac = 0; /* Last facility code parsed */
- static WCHAR *last_sym = NULL;/* Last alias symbol parsed */
- static int have_sev; /* Set if severity parsed for current message */
- static int have_fac; /* Set if facility parsed for current message */
- static int have_sym; /* Set is symbol parsed for current message */
- static struct cp_xlat *cpxlattab = NULL; /* Codepage translation table */
- static int ncpxlattab = 0;
- /* Prototypes */
- static WCHAR *merge(WCHAR *s1, WCHAR *s2);
- static struct lanmsg *new_lanmsg(struct lan_cp *lcp, WCHAR *msg);
- static struct msg *add_lanmsg(struct msg *msg, struct lanmsg *lanmsg);
- static struct msg *complete_msg(struct msg *msg, int id);
- static void add_node(enum node_type type, void *p);
- static void do_add_token(enum tok_enum type, struct token *tok, const char *code);
- static void test_id(int id);
- static int check_languages(struct node *head);
- static struct lan_blk *block_messages(struct node *head);
- static void add_cpxlat(int lan, int cpin, int cpout);
- static struct cp_xlat *find_cpxlat(int lan);
- %}
- %union {
- WCHAR *str;
- unsigned num;
- struct token *tok;
- struct lanmsg *lmp;
- struct msg *msg;
- struct lan_cp lcp;
- }
- %token tSEVNAMES tFACNAMES tLANNAMES tBASE tCODEPAGE
- %token tTYPEDEF tNL tSYMNAME tMSGEND
- %token tSEVERITY tFACILITY tLANGUAGE tMSGID
- %token <str> tIDENT tLINE tFILE tCOMMENT
- %token <num> tNUMBER
- %token <tok> tTOKEN
- %type <str> alias lines
- %type <num> optcp id msgid clan
- %type <tok> token
- %type <lmp> body
- %type <msg> bodies msg
- %type <lcp> lang
- %%
- file : items {
- if(!check_languages(nodehead))
- xyyerror("No messages defined\n");
- lanblockhead = block_messages(nodehead);
- }
- ;
- items : decl
- | items decl
- ;
- decl : global
- | msg { add_node(nd_msg, $1); }
- | tCOMMENT { add_node(nd_comment, $1); }
- | error { xyyerror(err_syntax); /* `Catch all' error */ }
- ;
- global : tSEVNAMES '=' '(' smaps ')'
- | tSEVNAMES '=' '(' smaps error { xyyerror(err_pclose); }
- | tSEVNAMES '=' error { xyyerror(err_popen); }
- | tSEVNAMES error { xyyerror(err_assign); }
- | tFACNAMES '=' '(' fmaps ')'
- | tFACNAMES '=' '(' fmaps error { xyyerror(err_pclose); }
- | tFACNAMES '=' error { xyyerror(err_popen); }
- | tFACNAMES error { xyyerror(err_assign); }
- | tLANNAMES '=' '(' lmaps ')'
- | tLANNAMES '=' '(' lmaps error { xyyerror(err_pclose); }
- | tLANNAMES '=' error { xyyerror(err_popen); }
- | tLANNAMES error { xyyerror(err_assign); }
- | tCODEPAGE '=' '(' cmaps ')'
- | tCODEPAGE '=' '(' cmaps error { xyyerror(err_pclose); }
- | tCODEPAGE '=' error { xyyerror(err_popen); }
- | tCODEPAGE error { xyyerror(err_assign); }
- | tTYPEDEF '=' tIDENT { cast = $3; }
- | tTYPEDEF '=' error { xyyerror(err_number); }
- | tTYPEDEF error { xyyerror(err_assign); }
- | tBASE '=' tNUMBER {
- switch(base)
- {
- case 8:
- case 10:
- case 16:
- base = $3;
- break;
- default:
- xyyerror("Numberbase must be 8, 10 or 16\n");
- }
- }
- | tBASE '=' error { xyyerror(err_number); }
- | tBASE error { xyyerror(err_assign); }
- ;
- /*----------------------------------------------------------------------
- * SeverityNames mapping
- */
- smaps : smap
- | smaps smap
- | error { xyyerror(err_ident); }
- ;
- smap : token '=' tNUMBER alias {
- $1->token = $3;
- $1->alias = $4;
- if($3 & (~0x3))
- xyyerror("Severity value out of range (0x%08x > 0x3)\n", $3);
- do_add_token(tok_severity, $1, "severity");
- }
- | token '=' error { xyyerror(err_number); }
- | token error { xyyerror(err_assign); }
- ;
- /*----------------------------------------------------------------------
- * FacilityNames mapping
- */
- fmaps : fmap
- | fmaps fmap
- | error { xyyerror(err_ident); }
- ;
- fmap : token '=' tNUMBER alias {
- $1->token = $3;
- $1->alias = $4;
- if($3 & (~0xfff))
- xyyerror("Facility value out of range (0x%08x > 0xfff)\n", $3);
- do_add_token(tok_facility, $1, "facility");
- }
- | token '=' error { xyyerror(err_number); }
- | token error { xyyerror(err_assign); }
- ;
- alias : /* Empty */ { $$ = NULL; }
- | ':' tIDENT { $$ = $2; }
- | ':' error { xyyerror(err_ident); }
- ;
- /*----------------------------------------------------------------------
- * LanguageNames mapping
- */
- lmaps : lmap
- | lmaps lmap
- | error { xyyerror(err_ident); }
- ;
- lmap : token '=' tNUMBER setfile ':' tFILE optcp {
- $1->token = $3;
- $1->alias = $6;
- $1->codepage = $7;
- do_add_token(tok_language, $1, "language");
- if(!find_language($3) && !find_cpxlat($3))
- mcy_warning("Language 0x%x not built-in, using codepage %d; use explicit codepage to override\n", $3, WMC_DEFAULT_CODEPAGE);
- }
- | token '=' tNUMBER setfile ':' error { xyyerror("Filename expected\n"); }
- | token '=' tNUMBER error { xyyerror(err_colon); }
- | token '=' error { xyyerror(err_number); }
- | token error { xyyerror(err_assign); }
- ;
- optcp : /* Empty */ { $$ = 0; }
- | ':' tNUMBER { $$ = $2; }
- | ':' error { xyyerror("Codepage-number expected\n"); }
- ;
- /*----------------------------------------------------------------------
- * Codepages mapping
- */
- cmaps : cmap
- | cmaps cmap
- | error { xyyerror(err_ident); }
- ;
- cmap : clan '=' tNUMBER ':' tNUMBER {
- static const char err_nocp[] = "Codepage %d not builtin; cannot convert\n";
- if(find_cpxlat($1))
- xyyerror("Codepage translation already defined for language 0x%x\n", $1);
- if($3 && !is_valid_codepage($3))
- xyyerror(err_nocp, $3);
- if($5 && !is_valid_codepage($5))
- xyyerror(err_nocp, $5);
- add_cpxlat($1, $3, $5);
- }
- | clan '=' tNUMBER ':' error { xyyerror(err_number); }
- | clan '=' tNUMBER error { xyyerror(err_colon); }
- | clan '=' error { xyyerror(err_number); }
- | clan error { xyyerror(err_assign); }
- ;
- clan : tNUMBER { $$ = $1; }
- | tTOKEN {
- if($1->type != tok_language)
- xyyerror("Language name or code expected\n");
- $$ = $1->token;
- }
- ;
- /*----------------------------------------------------------------------
- * Message-definition parsing
- */
- msg : msgid sevfacsym { test_id($1); } bodies { $$ = complete_msg($4, $1); }
- ;
- msgid : tMSGID '=' id {
- if($3 & (~0xffff))
- xyyerror("Message ID value out of range (0x%08x > 0xffff)\n", $3);
- $$ = $3;
- }
- | tMSGID error { xyyerror(err_assign); }
- ;
- id : /* Empty */ { $$ = ++last_id; }
- | tNUMBER { $$ = last_id = $1; }
- | '+' tNUMBER { $$ = last_id += $2; }
- | '+' error { xyyerror(err_number); }
- ;
- sevfacsym: /* Empty */ { have_sev = have_fac = have_sym = 0; }
- | sevfacsym sev { if(have_sev) xyyerror("Severity already defined\n"); have_sev = 1; }
- | sevfacsym fac { if(have_fac) xyyerror("Facility already defined\n"); have_fac = 1; }
- | sevfacsym sym { if(have_sym) xyyerror("Symbolname already defined\n"); have_sym = 1; }
- ;
- sym : tSYMNAME '=' tIDENT { last_sym = $3; }
- | tSYMNAME '=' error { xyyerror(err_ident); }
- | tSYMNAME error { xyyerror(err_assign); }
- ;
- sev : tSEVERITY '=' token {
- struct token *tok = lookup_token($3->name);
- if(!tok)
- xyyerror("Undefined severityname\n");
- if(tok->type != tok_severity)
- xyyerror("Identifier is not of class 'severity'\n");
- last_sev = tok->token;
- }
- | tSEVERITY '=' error { xyyerror(err_ident); }
- | tSEVERITY error { xyyerror(err_assign); }
- ;
- fac : tFACILITY '=' token {
- struct token *tok = lookup_token($3->name);
- if(!tok)
- xyyerror("Undefined facilityname\n");
- if(tok->type != tok_facility)
- xyyerror("Identifier is not of class 'facility'\n");
- last_fac = tok->token;
- }
- | tFACILITY '=' error { xyyerror(err_ident); }
- | tFACILITY error { xyyerror(err_assign); }
- ;
- /*----------------------------------------------------------------------
- * Message-text parsing
- */
- bodies : body { $$ = add_lanmsg(NULL, $1); }
- | bodies body { $$ = add_lanmsg($1, $2); }
- | error { xyyerror("'Language=...' (start of message text-definition) expected\n"); }
- ;
- body : lang setline lines tMSGEND { $$ = new_lanmsg(&$1, $3); }
- ;
- /*
- * The newline is to be able to set the codepage
- * to the language based codepage for the next
- * message to be parsed.
- */
- lang : tLANGUAGE setnl '=' token tNL {
- struct token *tok = lookup_token($4->name);
- struct cp_xlat *cpx;
- if(!tok)
- xyyerror("Undefined language\n");
- if(tok->type != tok_language)
- xyyerror("Identifier is not of class 'language'\n");
- if((cpx = find_cpxlat(tok->token)))
- {
- set_codepage($$.codepage = cpx->cpin);
- }
- else if(!tok->codepage)
- {
- const struct language *lan = find_language(tok->token);
- if(!lan)
- {
- /* Just set default; warning was given while parsing languagenames */
- set_codepage($$.codepage = WMC_DEFAULT_CODEPAGE);
- }
- else
- {
- /* The default seems to be to use the DOS codepage... */
- set_codepage($$.codepage = lan->doscp);
- }
- }
- else
- set_codepage($$.codepage = tok->codepage);
- $$.language = tok->token;
- }
- | tLANGUAGE setnl '=' token error { xyyerror("Missing newline\n"); }
- | tLANGUAGE setnl '=' error { xyyerror(err_ident); }
- | tLANGUAGE error { xyyerror(err_assign); }
- ;
- lines : tLINE { $$ = $1; }
- | lines tLINE { $$ = merge($1, $2); }
- | error { xyyerror(err_msg); }
- | lines error { xyyerror(err_msg); }
- ;
- /*----------------------------------------------------------------------
- * Helper rules
- */
- token : tIDENT { $$ = xmalloc(sizeof(struct token)); memset($$,0,sizeof(*$$)); $$->name = $1; }
- | tTOKEN { $$ = $1; }
- ;
- setnl : /* Empty */ { want_nl = 1; }
- ;
- setline : /* Empty */ { want_line = 1; }
- ;
- setfile : /* Empty */ { want_file = 1; }
- ;
- %%
- static WCHAR *merge(WCHAR *s1, WCHAR *s2)
- {
- int l1 = unistrlen(s1);
- int l2 = unistrlen(s2);
- s1 = xrealloc(s1, (l1 + l2 + 1) * sizeof(*s1));
- unistrcpy(s1+l1, s2);
- free(s2);
- return s1;
- }
- static void do_add_token(enum tok_enum type, struct token *tok, const char *code)
- {
- struct token *tp = lookup_token(tok->name);
- if(tp)
- {
- if(tok->type != type)
- mcy_warning("Type change in token\n");
- if(tp != tok)
- xyyerror("Overlapping token not the same\n");
- /* else its already defined and changed */
- if(tok->fixed)
- xyyerror("Redefinition of %s\n", code);
- tok->fixed = 1;
- }
- else
- {
- add_token(type, tok->name, tok->token, tok->codepage, tok->alias, 1);
- free(tok);
- }
- }
- static struct lanmsg *new_lanmsg(struct lan_cp *lcp, WCHAR *msg)
- {
- struct lanmsg *lmp = xmalloc(sizeof(*lmp));
- lmp->lan = lcp->language;
- lmp->cp = lcp->codepage;
- lmp->msg = msg;
- lmp->len = unistrlen(msg) + 1; /* Include termination */
- lmp->file = input_name;
- lmp->line = line_number;
- if(lmp->len > 4096)
- mcy_warning("Message exceptionally long; might be a missing termination\n");
- return lmp;
- }
- static struct msg *add_lanmsg(struct msg *msg, struct lanmsg *lanmsg)
- {
- int i;
- if(!msg)
- {
- msg = xmalloc(sizeof(*msg));
- memset( msg, 0, sizeof(*msg) );
- }
- msg->msgs = xrealloc(msg->msgs, (msg->nmsgs+1) * sizeof(*(msg->msgs)));
- msg->msgs[msg->nmsgs] = lanmsg;
- msg->nmsgs++;
- for(i = 0; i < msg->nmsgs-1; i++)
- {
- if(msg->msgs[i]->lan == lanmsg->lan)
- xyyerror("Message for language 0x%x already defined\n", lanmsg->lan);
- }
- return msg;
- }
- static int sort_lanmsg(const void *p1, const void *p2)
- {
- return (*(const struct lanmsg * const *)p1)->lan - (*(const struct lanmsg * const*)p2)->lan;
- }
- static struct msg *complete_msg(struct msg *mp, int id)
- {
- assert(mp != NULL);
- mp->id = id;
- if(have_sym)
- mp->sym = last_sym;
- else
- xyyerror("No symbolic name defined for message id %d\n", id);
- mp->sev = last_sev;
- mp->fac = last_fac;
- qsort(mp->msgs, mp->nmsgs, sizeof(*(mp->msgs)), sort_lanmsg);
- mp->realid = id | (last_sev << 30) | (last_fac << 16);
- if(custombit)
- mp->realid |= 1 << 29;
- mp->base = base;
- mp->cast = cast;
- return mp;
- }
- static void add_node(enum node_type type, void *p)
- {
- struct node *ndp = xmalloc(sizeof(*ndp));
- memset( ndp, 0, sizeof(*ndp) );
- ndp->type = type;
- ndp->u.all = p;
- if(nodetail)
- {
- ndp->prev = nodetail;
- nodetail->next = ndp;
- nodetail = ndp;
- }
- else
- {
- nodehead = nodetail = ndp;
- }
- }
- static void test_id(int id)
- {
- struct node *ndp;
- for(ndp = nodehead; ndp; ndp = ndp->next)
- {
- if(ndp->type != nd_msg)
- continue;
- if(ndp->u.msg->id == id && ndp->u.msg->sev == last_sev && ndp->u.msg->fac == last_fac)
- xyyerror("MessageId %d with facility 0x%x and severity 0x%x already defined\n", id, last_fac, last_sev);
- }
- }
- static int check_languages(struct node *head)
- {
- static const char err_missing[] = "Missing definition for language 0x%x; MessageID %d, facility 0x%x, severity 0x%x\n";
- struct node *ndp;
- int nm = 0;
- struct msg *msg = NULL;
- for(ndp = head; ndp; ndp = ndp->next)
- {
- if(ndp->type != nd_msg)
- continue;
- if(!nm)
- {
- msg = ndp->u.msg;
- }
- else
- {
- int i;
- struct msg *m1;
- struct msg *m2;
- if(ndp->u.msg->nmsgs > msg->nmsgs)
- {
- m1 = ndp->u.msg;
- m2 = msg;
- }
- else
- {
- m1 = msg;
- m2 = ndp->u.msg;
- }
- for(i = 0; i < m1->nmsgs; i++)
- {
- if(i > m2->nmsgs)
- error(err_missing, m1->msgs[i]->lan, m2->id, m2->fac, m2->sev);
- else if(m1->msgs[i]->lan < m2->msgs[i]->lan)
- error(err_missing, m1->msgs[i]->lan, m2->id, m2->fac, m2->sev);
- else if(m1->msgs[i]->lan > m2->msgs[i]->lan)
- error(err_missing, m2->msgs[i]->lan, m1->id, m1->fac, m1->sev);
- }
- }
- nm++;
- }
- return nm;
- }
- #define MSGRID(x) ((*(const struct msg * const*)(x))->realid)
- static int sort_msg(const void *p1, const void *p2)
- {
- return MSGRID(p1) > MSGRID(p2) ? 1 : (MSGRID(p1) == MSGRID(p2) ? 0 : -1);
- }
- /*
- * block_messages() basically transposes the messages
- * from ID/language based list to a language/ID
- * based list.
- */
- static struct lan_blk *block_messages(struct node *head)
- {
- struct lan_blk *lbp;
- struct lan_blk *lblktail = NULL;
- struct lan_blk *lblkhead = NULL;
- struct msg **msgtab = NULL;
- struct node *ndp;
- int nmsg = 0;
- int i;
- int nl;
- int factor = 2;
- for(ndp = head; ndp; ndp = ndp->next)
- {
- if(ndp->type != nd_msg)
- continue;
- msgtab = xrealloc(msgtab, (nmsg+1) * sizeof(*msgtab));
- msgtab[nmsg++] = ndp->u.msg;
- }
- assert(nmsg != 0);
- qsort(msgtab, nmsg, sizeof(*msgtab), sort_msg);
- for(nl = 0; nl < msgtab[0]->nmsgs; nl++) /* This should be equal for all after check_languages() */
- {
- lbp = xmalloc(sizeof(*lbp));
- memset( lbp, 0, sizeof(*lbp) );
- if(!lblktail)
- {
- lblkhead = lblktail = lbp;
- }
- else
- {
- lblktail->next = lbp;
- lbp->prev = lblktail;
- lblktail = lbp;
- }
- lbp->nblk = 1;
- lbp->blks = xmalloc(sizeof(*lbp->blks));
- lbp->blks[0].idlo = msgtab[0]->realid;
- lbp->blks[0].idhi = msgtab[0]->realid;
- /* The plus 4 is the entry header; (+3)&~3 is DWORD alignment */
- lbp->blks[0].size = ((factor * msgtab[0]->msgs[nl]->len + 3) & ~3) + 4;
- lbp->blks[0].msgs = xmalloc(sizeof(*lbp->blks[0].msgs));
- lbp->blks[0].nmsg = 1;
- lbp->blks[0].msgs[0] = msgtab[0]->msgs[nl];
- lbp->lan = msgtab[0]->msgs[nl]->lan;
- for(i = 1; i < nmsg; i++)
- {
- struct block *blk = &(lbp->blks[lbp->nblk-1]);
- if(msgtab[i]->realid == blk->idhi+1)
- {
- blk->size += ((factor * msgtab[i]->msgs[nl]->len + 3) & ~3) + 4;
- blk->idhi++;
- blk->msgs = xrealloc(blk->msgs, (blk->nmsg+1) * sizeof(*blk->msgs));
- blk->msgs[blk->nmsg++] = msgtab[i]->msgs[nl];
- }
- else
- {
- lbp->nblk++;
- lbp->blks = xrealloc(lbp->blks, lbp->nblk * sizeof(*lbp->blks));
- blk = &(lbp->blks[lbp->nblk-1]);
- blk->idlo = msgtab[i]->realid;
- blk->idhi = msgtab[i]->realid;
- blk->size = ((factor * msgtab[i]->msgs[nl]->len + 3) & ~3) + 4;
- blk->msgs = xmalloc(sizeof(*blk->msgs));
- blk->nmsg = 1;
- blk->msgs[0] = msgtab[i]->msgs[nl];
- }
- }
- }
- free(msgtab);
- return lblkhead;
- }
- static int sc_xlat(const void *p1, const void *p2)
- {
- return ((const struct cp_xlat *)p1)->lan - ((const struct cp_xlat *)p2)->lan;
- }
- static void add_cpxlat(int lan, int cpin, int cpout)
- {
- cpxlattab = xrealloc(cpxlattab, (ncpxlattab+1) * sizeof(*cpxlattab));
- cpxlattab[ncpxlattab].lan = lan;
- cpxlattab[ncpxlattab].cpin = cpin;
- cpxlattab[ncpxlattab].cpout = cpout;
- ncpxlattab++;
- qsort(cpxlattab, ncpxlattab, sizeof(*cpxlattab), sc_xlat);
- }
- static struct cp_xlat *find_cpxlat(int lan)
- {
- struct cp_xlat t;
- if(!cpxlattab) return NULL;
- t.lan = lan;
- return (struct cp_xlat *)bsearch(&t, cpxlattab, ncpxlattab, sizeof(*cpxlattab), sc_xlat);
- }
|