home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / mm / mm-ccmd-0.91.tar.Z / mm-ccmd-0.91.tar / work / mm / address.c < prev    next >
C/C++ Source or Header  |  2002-02-22  |  35KB  |  1,438 lines

  1. /*
  2.  * Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  * the City of New York.  Permission is granted to any individual or
  4.  * institution to use, copy, or redistribute this software so long as it
  5.  * is not sold for profit, provided this copyright notice is retained.
  6.  */
  7.  
  8. #ifndef lint
  9. static char *rcsid = "$Header: /f/src2/encore.bin/cucca/mm/tarring-it-up/RCS/address.c,v 2.1 90/10/04 18:23:18 melissa Exp $";
  10. #endif
  11.  
  12. #include "mm.h"
  13. #include "ccmd.h"
  14.  
  15. char *malloc(), *safe_strcat(), *realloc(), *safe_free(), *safe_strcpy();
  16. static int parselen;
  17. addresslist *lookup_alias();
  18.  
  19. /* 
  20.  * break masks for various fdbs
  21.  */
  22.  
  23. /*
  24.  * break mask for comment text.   Cannot have "(" or ")" in it.
  25.  */
  26. static brktab commentbrk = {
  27.   {                    /* print chars except (, ), ? */
  28.     0xff, 0xff, 0xff, 0xff, 0x80, 0xc0, 0x00, 0x01,
  29.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
  30.   },
  31.   {                    /* all print characters */
  32.     0xff, 0xbf, 0xff, 0xff, 0x00, 0xc0, 0x00, 0x01,
  33.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
  34.   }
  35. };
  36.  
  37. /*
  38.  * break mask for remote user.  A slightly modified field break mask.
  39.  * (also used in seq.c)
  40.  */
  41. brktab rembrk = {            /* break table for remote user name */
  42.     {                    /* letters only in first position */
  43.       0xff, 0xff, 0xff, 0xff, 0xf3, 0xfb, 0x00, 0x3b,
  44.       0x80, 0x00, 0x00, 0x16, 0x80, 0x00, 0x00, 0x1f
  45.     },
  46.     {                    /* letters, digits and hyphens here */
  47.       0xff, 0xff, 0xff, 0xff, 0xb2, 0xe8, 0x00, 0x3b,
  48.       0x80, 0x00, 0x00, 0x16, 0x80, 0x00, 0x00, 0x1f
  49.     }                    /* (also +%_.!/) */
  50. };
  51.  
  52. /*
  53.  * break mask for a username.
  54.  */
  55. static brktab aliasbrk = {        /* all valid chars for users */
  56.   {                    /* alphanums, "~#/_-\[]," */
  57.     0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0x00, 0x3f,
  58.     0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0b,
  59.   },
  60.   {
  61.     0xff, 0xff, 0xff, 0xff, 0xbb, 0xd9, 0x00, 0x1f,
  62.     0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0b,
  63.   }
  64. };
  65.  
  66. /*
  67.  * break mask for a username.
  68.  */
  69. static brktab usrbrk = {        /* all valid chars for users */
  70.   {                    /* alphanums, "~#/_-\[]," */
  71.     0xff, 0xff, 0xff, 0xff, 0xff, 0xd1, 0x00, 0x3f,
  72.     0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0b,
  73.   },
  74.   {
  75.     0xff, 0xff, 0xff, 0xff, 0xfb, 0xd1, 0x00, 0x3f,
  76.     0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0b,
  77.   }
  78. };
  79.  
  80. /*
  81.  * break mask for a hostname.
  82.  */
  83. static brktab hostbrk = {
  84. {                    /* letters only in first position */
  85.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f,
  86.     0x00, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x1f
  87.   },
  88.   {                    /* alphanums, "-." */
  89.     0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x00, 0x3f,
  90.     0x00, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x1f
  91.   }
  92. };
  93.  
  94. /*
  95.  * break mask for at sign token parse
  96.  */
  97. static brktab atbrk = {
  98.     {
  99.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  100.     0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
  101.     },
  102.     {
  103.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  104.     0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
  105.     }
  106. };
  107.  
  108. /*
  109.  * fdb's for all of the address parses.
  110.  */
  111.  
  112. /*
  113.  * start of comment
  114.  */
  115. static fdb comment = { _CMTOK, TOK_WAK|CM_SDH, nil, (pdat) "(",
  116.     nil, nil, nil, nil };
  117.  
  118. /* 
  119.  * text of a comment
  120.  */
  121. static fdb commenttxt = { _CMFLD, FLD_EMPTY|CM_SDH, nil, nil, "a comment",
  122.     nil, &commentbrk, nil};
  123.  
  124. /*
  125.  * end of a comment
  126.  */
  127. static fdb commentend = { _CMTOK, TOK_WAK|CM_SDH, nil, (pdat) ")",
  128.     "\")\" to end the comment", nil, nil, nil };
  129.  
  130. /*
  131.  * start of a mailbox
  132.  */
  133. static fdb mbox = { _CMTOK, TOK_WAK|CM_SDH, nil, (pdat) "<", "<mailbox>", 
  134.     nil, nil, "invalid mail recipient" };
  135.  
  136. /*
  137.  * end of a mailbox
  138.  */
  139. static fdb mboxend = { _CMTOK, TOK_WAK|CM_SDH, nil, (pdat) ">" , ">", nil,
  140.     nil, "expected a \">\"" };
  141.  
  142. /*
  143.  * start of an internet address
  144.  */
  145. static fdb ipaddr = { _CMTOK, TOK_WAK|CM_SDH, nil, (pdat) "[", nil, nil, nil,
  146.     "invalid hostname or host address" };
  147.  
  148. /*
  149.  * a number in an IP address
  150.  */
  151. static fdb ipaddrnum = { _CMNUM, CM_SDH, nil, (pdat) 10, "host IP address",
  152.     nil, nil, "expected an IP address octet" };
  153.  
  154. /*
  155.  * a "." in an IP address
  156.  */
  157. static fdb ipaddrdot = { _CMTOK, TOK_WAK|CM_SDH, nil, ".", ".", nil, nil,
  158.     "expected a \".\"" };
  159.  
  160. /*
  161.  * end of an ip address
  162.  */
  163. static fdb ipaddrend = { _CMTOK, TOK_WAK|CM_SDH, nil, (pdat) "]", nil, nil, 
  164.     nil, nil };
  165.  
  166. /* 
  167.  * a "::" to make a string into an ugly mail11 hostname (for melissa)
  168.  */
  169. static fdb mail11 = { _CMTOK, CM_SDH, nil, (pdat) "::", nil, nil, nil, nil };
  170.  
  171. /* 
  172.  * a ":" to make a string into a group name
  173.  */
  174. static fdb group = { _CMTOK, CM_SDH, nil, (pdat) ":", 
  175.     "\":\" to make this a group name", nil, nil, nil };
  176.  
  177. /*
  178.  * ";" to end of a group list
  179.  */
  180. static fdb groupend = { _CMTOK, CM_SDH, nil, (pdat) ";", nil, nil, nil, nil };
  181.  
  182. /*
  183.  * a comma for between addresses
  184.  */
  185. static fdb comma = { _CMTOK, TOK_WAK|CM_SDH, nil, (pdat) ",",
  186.     "\",\" for another address", nil, nil,
  187.     "comma required between addresses" };
  188.  
  189. /*
  190.  * a comma for route info
  191.  */
  192. static fdb comma2 = { _CMTOK, TOK_WAK|CM_SDH, nil, (pdat) ",",
  193.     "\",\" for an additional route", nil, nil, nil };
  194.  
  195. /*
  196.  * a colon
  197.  */
  198. static fdb colon = { _CMTOK, TOK_WAK|CM_SDH, nil, (pdat) ":",
  199.     "\":\" to end route information", nil, nil, "expected a \":\"" };
  200.  
  201.  
  202. /*
  203.  * remote user name.   really a field.
  204.  */
  205. static fdb remusr = { _CMFLD, FLD_EMPTY|CM_SDH, nil, nil, "network address",
  206.     nil, &rembrk, "invalid mail recipient" };
  207.  
  208. /*
  209.  * a field.  used for parts of phrases.
  210.  */
  211. static fdb field = { _CMFLD, FLD_EMPTY|CM_SDH, nil, nil, nil, nil,
  212.     &rembrk, nil };
  213.  
  214. /*
  215.  * a quoted string
  216.  */
  217. static fdb qstr = { _CMQST, CM_SDH, nil, nil, "quoted string", nil, 
  218.     nil, nil };
  219.  
  220. /*
  221.  * a username
  222.  */
  223. static fdb username = { _CMUSR, CM_SDH, nil, nil, "user name" , nil,
  224.     &usrbrk, nil };
  225.  
  226. /*
  227.  * "." to signify sending to self
  228.  */
  229. static fdb dot = { _CMTOK, CM_SDH, nil, (pdat) ".", 
  230.     "\".\" to send to yourself", nil, nil, nil };
  231.  
  232. /*
  233.  * an asterix to send to a file
  234.  */
  235. static fdb asterix = { _CMTOK, CM_SDH|TOK_WAK, nil, (pdat) "*",
  236.     "\"*\" to send to a file", nil, nil, nil };
  237.  
  238. /*
  239.  * an @ sign for an indirect address through a file.
  240.  */
  241. static fdb indirect = { _CMTOK, CM_SDH|TOK_WAK, nil, (pdat) "@", 
  242.     "\"@\" to obtain addresses from a file", nil, nil, nil };
  243.  
  244. /*
  245.  * a "@@" for mailing lists (put in aliases to postpone reading the file until
  246.  * the message is being sent.  Only works in the define command.
  247.  */
  248.  
  249. static fdb mlist = { _CMTOK, CM_SDH|TOK_WAK, nil, (pdat) "@@", 
  250.     "\"@@\" to obtain addresses from a file when alias is invoked",
  251.     nil, nil, nil };
  252.  
  253. /*
  254.  * confirmation
  255.  */
  256. static fdb confirm = { _CMCFM, 0, nil, nil, nil, nil, nil, nil };
  257.  
  258. /*
  259.  * a host name
  260.  */
  261. static fdb hostname = { _CMFLD, CM_SDH, nil, nil, "host name", nil,
  262.     &hostbrk, nil};
  263.  
  264. /*
  265.  * at sign for user@host
  266.  */
  267. static fdb at = { _CMTOK, CM_SDH|TOK_WAK, nil, (pdat) "@", 
  268.     "\"@\" for network host name", nil, &atbrk, nil };
  269.  
  270. /*
  271.  * an output file for "*filename"
  272.  */
  273. static fdb outfile = { _CMFIL, FIL_NODIR|FIL_PO|CM_SDH, nil, nil, 
  274.     "filename to output message to", nil, nil, "invalid output filename" };
  275.  
  276. /*
  277.  * an input filename for "@filename"
  278.  */
  279. static fdb indfile = { _CMFIL, CM_SDH|FIL_NODIR, nil, nil, 
  280.     "filename of indirect file",nil, nil, "invalid indirect filename" };
  281.  
  282. /* 
  283.  * an input file @@filename
  284.  */
  285. static fdb listfile = { _CMFIL, CM_SDH|FIL_PO|FIL_NODIR, nil, nil, 
  286.     "filename of deferred indirect file", nil, nil,
  287.     "invalid indirect filename" };
  288.  
  289.  
  290. /* 
  291.  * keyword table for user defined aliases
  292.  */
  293. static fdb aliasfdb = {
  294.     _CMKEY, CM_SDH|KEY_EMO, nil, nil, "mail alias", nil,  &aliasbrk, nil
  295. };
  296.  
  297.  
  298.  
  299. static int active_label = FALSE;
  300.  
  301. #ifdef TEST
  302. char atmbuf[100];
  303. static int cmdbuf[10000];        /* ccmd work buffers */
  304. static char wrkbuf[100];
  305.  
  306. main() 
  307. {
  308.     char *cmini();
  309.     static addresslist a= { nil, nil };
  310.  
  311.     cmcsb._cmntb = "#";            /* comment to eol starts with '#' */
  312.     cm_set_ind(FALSE);            /* no indirections allowed */
  313.     cmseti(stdin, stdout, stderr);
  314.     cmbufs (cmdbuf, sizeof cmdbuf, atmbuf, sizeof atmbuf,
  315.         wrkbuf, sizeof wrkbuf);    /* set up buffers for ccmd */
  316.     while(1) {
  317.     cmseter();
  318.     prompt("addr>");
  319.     cmsetrp();
  320.     parse_addresses(&a);
  321.     disp_addresses(stdout,"address",&a,TRUE,TRUE,FALSE,TRUE);
  322.     }
  323. }
  324. #else 
  325. #define atmbuf cmcsb._cmabp
  326. #endif /* TEST */
  327.  
  328. /* 
  329.  * parse a list of addresses.
  330.  * returns an addresslist in a.
  331.  * since it will be called inside of reparses, it free's
  332.  * the data pointed to by a.
  333.  */
  334.  
  335. parse_addresses(a) 
  336. addresslist *a;
  337. {
  338.     static char *bp=nil;
  339.     cm_set_ind(FALSE);
  340.     free_addresslist(a);
  341.     active_label = FALSE;
  342.     while(TRUE) {
  343.     if (bp) {
  344.         free(bp);
  345.         bp = nil;
  346.     }
  347.     if (!parse_address_1(a,&bp,FALSE,FALSE))
  348.         break;
  349.     }
  350.     if (active_label) {            /* if still an active_label */
  351.     bp = safe_strcat(bp,";",FALSE);
  352.     add_addresslist(a,bp,ADR_GROUPEND);
  353.     bp = safe_free(bp);
  354.     }
  355.     active_label = FALSE;
  356. }
  357.  
  358. parse_address(a) 
  359. addresslist *a;
  360. {
  361.     static char *bp=nil;
  362.     cm_set_ind(FALSE);
  363.     free_addresslist(a);
  364.     active_label = FALSE;
  365.     while(TRUE) {
  366.     if (bp) {
  367.         free(bp);
  368.         bp = nil;
  369.     }
  370.     if (!parse_address_1(a,&bp,TRUE,FALSE))
  371.         break;
  372.     }
  373.     if (active_label) {            /* if still an active_label */
  374.     bp = safe_strcat(bp,";",FALSE);
  375.     add_addresslist(a,bp,ADR_GROUPEND);
  376.     bp = safe_free(bp);
  377.     }
  378.     active_label = FALSE;
  379. }
  380.  
  381. parse_define(a) 
  382. addresslist *a;
  383. {
  384.     static char *bp=nil;
  385.     cm_set_ind(FALSE);
  386.     free_addresslist(a);
  387.     active_label = FALSE;
  388.     while(TRUE) {
  389.     if (bp) {
  390.         free(bp);
  391.         bp = nil;
  392.     }
  393.     if (!parse_address_1(a,&bp,FALSE,TRUE))
  394.         break;
  395.     }
  396.     if (active_label) {            /* if still an active_label */
  397.     bp = safe_strcat(bp,";",FALSE);
  398.     add_addresslist(a,bp,ADR_GROUPEND);
  399.     bp = safe_free(bp);
  400.     }
  401.     active_label = FALSE;
  402. }
  403. /*
  404.  * parse a single address, terminated by a comma, or a confirm.
  405.  * also allow group names, or endings.
  406.  * returns 1 if parsing should continue.  0 if it should end.
  407.  * returns the parsed address in a.
  408.  */
  409. parse_address_1(a,this,one,alias) 
  410. addresslist *a;                /* list of addresses */
  411. char **this;                /* address currently being parsed */
  412. int one;                /* only accept one address? */
  413. {
  414.     pval pv;
  415.     fdb *used;
  416.  
  417.     aliasfdb._cmdat = (pdat) mk_alias_keys();
  418.     if (!alias)
  419.     parse(fdbchn(&confirm,        /* confirm to send list */
  420.              &asterix,        /* *filename */
  421.              &indirect,        /* @filename */
  422.              &aliasfdb,        /* mail alias */
  423. #ifdef undef
  424.              &username,        /* username */
  425. #endif
  426.              &dot,        /* "." */
  427.              &qstr,        /* quotedstring */
  428.              &comment,        /* (comment) */
  429.              &mbox,        /* <mbox> */
  430.              &groupend,        /* ";" */
  431.              &remusr,        /* remote user */
  432.              nil), &pv, &used);
  433.     else
  434.     parse(fdbchn(&confirm,        /* confirm to send list */
  435.              &asterix,        /* *filename */
  436.              &mlist,        /* @@file */
  437.              &indirect,        /* @filename */
  438.              &aliasfdb,        /* mail alias */
  439. #ifdef undef
  440.              &username,        /* username */
  441. #endif
  442.              &dot,        /* "." */
  443.              &qstr,        /* quotedstring */
  444.              &comment,        /* (comment) */
  445.              &mbox,        /* <mbox> */
  446.              &groupend,        /* ";" */
  447.              &remusr,        /* remote user */
  448.              nil), &pv, &used);
  449.     if (used == &confirm) {        /* confirm: done with this parse */
  450.     if (*this != nil) {        /* if anything had been typed */
  451.         add_addresslist(a,*this,ADR_ADDRESS); /* add it to the list */
  452.         *this = safe_free(*this);
  453.     }
  454.     return(0);
  455.     
  456.     }
  457.     else if (used == &asterix) {    /* "*"? */
  458.     if (directory_folders)
  459.         outfile._cmffl &= ~FIL_NODIR;
  460.     else
  461.         outfile._cmffl |= FIL_NODIR;
  462.     parse(fdbchn(&outfile,nil),&pv,&used);    /* get a filename */
  463. #ifdef undef
  464.     *this = safe_strcat(*this,"*",TRUE); /* copy it somewhere */
  465. #endif
  466.     *this = safe_strcat(*this,pv._pvfil[0],FALSE);
  467.     add_addresslist(a,*this,ADR_FILE);
  468.     return(parse_sep2(a,this,one));    /* get a comma or confirm */
  469.     }
  470.     else if (used == &indirect) {    /* @filename */
  471.     FILE *newinf;            /* new input file */
  472.     char name[100];
  473.     parse(fdbchn(&indfile,nil),&pv,&used);
  474.     strcpy(name,pv._pvfil[0]);
  475.     parse(fdbchn(&confirm,nil),&pv,&used);
  476.     newinf = fopen(name, "r");
  477.     if (!newinf) {
  478.         cmxeprintf("?Could not open %s\n",name);
  479.         return(0);
  480.     }
  481.     ind_oldfds();            /* tell ccmd we are in indirect mode */
  482.     cmseti(newinf, nil, cmcsb._cmej); /* install new input file */
  483.     cmcsb._cmflg2 |= CM_IND;    /* set indirection bit.  EOF handled */
  484.                     /* through ccmd */
  485.     return(1);            /* keep parsing from new file. */
  486.     }
  487.     else if (used == &dot) {        /* address = "." */
  488.     *this = safe_strcat(*this,user_name,TRUE);
  489.     return(parse_addr1(a,this,one)); /* keep parsing. */
  490.     }
  491.     else if (used == &qstr) {        /* "address" */
  492.     *this = safe_strcat(*this,"\"",TRUE);
  493.     *this = safe_strcat(*this,atmbuf,FALSE);
  494.     *this = safe_strcat(*this,"\"",FALSE);
  495.     return(parse_addr1(a,this,one));
  496.     }
  497.     else if (used == &comment) {
  498.     parse_comment(a,this);        /* (comment) */
  499.     return(parse_address(a,this,one)); /* comma or confirm */
  500.     }    
  501.     else if (used == &remusr) {
  502.     *this = safe_strcat(*this, atmbuf,TRUE);
  503.     return(parse_addr1(a,this,one)); /* possibly more address */
  504.     }
  505.     else if (used == &aliasfdb) {
  506. #ifdef notdef
  507.     *this = safe_strcat(*this,
  508.              ((keytab *)aliasfdb._cmdat)->_ktwds[pv._pvkey]._kwkwd,
  509.                 TRUE);
  510. #endif
  511.     *this = safe_strcat (*this, atmbuf, TRUE);
  512.     if (index(*this,'@'))
  513.         return(parse_addr4(a,this,one));
  514.     else
  515.         return(parse_addr3(a,this,one)); /* maybe just a phrase */
  516.     }
  517.     else if (used == &username) {
  518.     *this = safe_strcat(*this, pv._pvusr[0]->pw_name,TRUE);
  519.     return(parse_addr1(a,this,one)); /* possibly more address */
  520.     }
  521.     else if (used == &mbox) {        /* <mbox> */
  522.     parse_mbox(a,this);
  523.     return(parse_sep1(a,this,one));    /* comma or confirm */
  524.     }
  525.     else if (used == &groupend) {
  526.     if (active_label) {
  527.         add_addresslist(a,";",ADR_GROUPEND);
  528.     }
  529.     parse_sep2(a,this,one);
  530.     }
  531.     else if (used == &mlist) {
  532.     parse(&listfile,&pv,&used);
  533.     add_addresslist(a,pv._pvfil[0], ADR_LISTFILE);
  534.     return(parse_sep1(a,this,one));
  535.     }
  536. }
  537.  
  538. /*
  539.  * called when the first token of an address has been parsed, 
  540.  * and we need to parse more and see where it leads us.
  541.  */
  542.  
  543. parse_addr1(a,this,one)            /* parse the rest of an address */
  544. addresslist *a;
  545. char **this;
  546. {
  547.     pval pv;
  548.     fdb *used;
  549.  
  550.     parse(fdbchn(&confirm,
  551.          &at,            /* @ host */
  552.          &mail11,        /* host:: */
  553.          &group,        /* ":" */
  554.          &comment,
  555.          &mbox,            /* phrase <mbox> */
  556.          &groupend,        /* ";" to end a group */
  557.          &qstr,
  558.          &comma,
  559.          &field,        /* continued phrase */
  560.          nil), &pv, &used);
  561.     if (used == &mbox) {
  562.     parse_mbox(a,this);        /* finish the mbox */
  563.     return(parse_sep1(a,this,one));    /* comma or confirm */
  564.     }
  565.     else if (used == &field) {
  566.     *this = safe_strcat(*this, atmbuf,TRUE);
  567.     return(parse_addr2(a,this,one));
  568.     }
  569.     else if (used == &group) {        /* a field name */
  570.     if (active_label) {        /* alread in one?   close it off */
  571.         add_addresslist(a,";",ADR_GROUPEND);
  572.     }
  573.     add_addresslist(a,*this,ADR_GROUP);
  574.     *this = safe_free(*this);
  575.     return(1);            /* continue the parse */
  576.     }
  577.     else if (used == &mail11) {        /* MAY ACCEPT BOGUS ADDRESSES */
  578.     *this = safe_strcat(*this, "::", FALSE);
  579.     return(parse_address_1(a,this,one));
  580.     }
  581.     else if (used == &at) {        /* @ host */
  582.      parse_host(a,this);        /* get the host */
  583.     return(parse_sep1(a,this,one));    /* comma or confirm */
  584.     }
  585.     else if (used == &comma) {        /* comma */
  586.     add_addresslist(a,*this,ADR_ADDRESS); /* record this address */
  587.     *this = safe_free(*this);
  588.     return(1);            /* parse for more addresses*/
  589.     }
  590.     else if (used == &confirm) {    /* confirm, all done; */
  591.     add_addresslist(a,*this,ADR_ADDRESS); /* record this address */
  592.     *this = safe_free(*this);
  593.     return(0);            /* pop all the way up */
  594.     }
  595.     else if (used == &comment) {    /* comment, */
  596.     parse_comment(a,this);        /* get the comment */
  597.     parse_addr1(a,this,one);        /* keep trying */
  598.     }
  599.     else if (used == &qstr) {        /* "address" */
  600.     *this = safe_strcat(*this,"\"",TRUE);
  601.     *this = safe_strcat(*this,atmbuf,FALSE);
  602.     *this = safe_strcat(*this,"\"",FALSE);
  603.     return(parse_addr2(a,this,one));
  604.     }
  605.     else if (used == &groupend) {
  606.     add_addresslist(a,*this, ADR_ADDRESS); /* save the address */
  607.     *this = safe_free(*this);
  608.     add_addresslist(a,";", ADR_GROUPEND); /* post the group end */
  609.     return(parse_sep2(a,this,one));    /* try for a comman or confirm */
  610.     }
  611. }
  612.  
  613. /*
  614.  * parse a phrase so far (two words or more).
  615.  * allow it to be made into a group name, or allow a <mbox>.  
  616.  * a comment can be inserted, or the phrase can continue.
  617.  */
  618. parse_addr2(a,this,one)            /* parse a phrase and mailbox*/
  619. addresslist *a;
  620. char **this;
  621. {
  622.     pval pv;
  623.     fdb *used;
  624.  
  625.     parse(fdbchn(&group,        /* ":" */
  626.          &mbox,            /* phrase <mbox> */
  627.          &comment,
  628.          &qstr,
  629.          &field,        /* continued phrase */
  630.          nil), &pv, &used);
  631.     if (used == &mbox) {
  632.     parse_mbox(a,this);        /* finish the mbox */
  633.     return(parse_sep1(a,this,one));    /* comma or confirm */
  634.     }
  635.     else if (used == &group) {        /* a field name */
  636.     add_addresslist(a,*this,ADR_GROUP);
  637.     *this = safe_free(*this);
  638.     return(1);            /* continue the parse */
  639.     }
  640.     else if (used == &comment) {    /* comment, */
  641.     parse_comment(a,this);        /* get the comment */
  642.     parse_addr2(a,this,one);    /* keep trying */
  643.     }
  644.     else if (used == &qstr) {        /* "address" */
  645.     *this = safe_strcat(*this,"\"",TRUE);
  646.     *this = safe_strcat(*this,atmbuf,FALSE);
  647.     *this = safe_strcat(*this,"\"",FALSE);
  648.     return(parse_addr2(a,this,one));
  649.     }
  650.     else if (used == &field) {
  651.     *this = safe_strcat(*this, atmbuf,TRUE);
  652.     return(parse_addr2(a,this,one));
  653.     }
  654. }
  655.  
  656. /*
  657.  * called when the first token of an address has been parsed, and is an alias
  658.  * and we need to parse more and see where it leads us.
  659.  */
  660.  
  661. parse_addr3(a,this,one)            /* parse the rest of an address */
  662. addresslist *a;
  663. char **this;
  664. {
  665.     pval pv;
  666.     fdb *used;
  667.  
  668.     parse(fdbchn(&confirm,
  669.          &comma,
  670.          &at,            /* @ host */
  671.          &group,        /* ":" */
  672.          &comment,
  673.          &mbox,            /* phrase <mbox> */
  674.          &groupend,        /* ";" to end a group */
  675.          &field,        /* continued phrase */
  676.          nil), &pv, &used);
  677.     if (used == &mbox) {
  678.     parse_mbox(a,this);        /* finish the mbox */
  679.     return(parse_sep1(a,this,one));    /* comma or confirm */
  680.     }
  681.     else if (used == &field) {
  682.     *this = safe_strcat(*this, atmbuf,TRUE);
  683.     return(parse_addr2(a,this,one));
  684.     }
  685.     else if (used == &group) {        /* a group name */
  686.     if (active_label) {        /* alread in one?   close it off */
  687.         add_addresslist(a,";",ADR_GROUPEND);
  688.     }
  689.     add_addresslist(a,*this,ADR_GROUP);
  690.     *this = safe_free(*this);
  691.     return(1);            /* continue the parse */
  692.     }
  693.     else if (used == &at) {        /* @ host */
  694.      parse_host(a,this);        /* get the host */
  695.     return(parse_sep1(a,this,one));    /* comma or confirm */
  696.     }
  697.     else if (used == &comma) {        /* comma */
  698.     add_addresslist(a,*this,ADR_ALIAS); /* record this address */
  699.     *this = safe_free(*this);
  700.     return(1);            /* parse for more addresses*/
  701.     }
  702.     else if (used == &confirm) {    /* confirm, all done; */
  703.     add_addresslist(a,*this,ADR_ALIAS); /* record this address */
  704.     *this = safe_free(*this);
  705.     return(0);            /* pop all the way up */
  706.     }
  707.     else if (used == &comment) {    /* comment, */
  708.     parse_comment(a,this);        /* get the comment */
  709.     parse_addr3(a,this,one);        /* keep trying */
  710.     }
  711.     else if (used == &groupend) {
  712.     add_addresslist(a,*this, ADR_ALIAS); /* save the address */
  713.     *this = safe_free(*this);
  714.     add_addresslist(a,";", ADR_GROUPEND); /* post the group end */
  715.     return(parse_sep2(a,this,one));    /* try for a comman or confirm */
  716.     }
  717. }
  718.  
  719.  
  720. /* 
  721.  * after an alias with an @ in it.
  722.  */
  723. parse_addr4(a,this,one)            /* parse the rest of an address */
  724. addresslist *a;
  725. char **this;
  726. {
  727.     pval pv;
  728.     fdb *used;
  729.  
  730.     parse(fdbchn(&confirm,
  731.          &comma,
  732.          &comment,
  733.          &groupend,        /* ";" to end a group */
  734.          nil), &pv, &used);
  735.  
  736.     if (used == &comma) {        /* comma */
  737.     add_addresslist(a,*this,ADR_ALIAS); /* record this address */
  738.     *this = safe_free(*this);
  739.     return(1);            /* parse for more addresses*/
  740.     }
  741.     else if (used == &confirm) {    /* confirm, all done; */
  742.     add_addresslist(a,*this,ADR_ALIAS); /* record this address */
  743.     *this = safe_free(*this);
  744.     return(0);            /* pop all the way up */
  745.     }
  746.     else if (used == &comment) {    /* comment, */
  747.     parse_comment(a,this);        /* get the comment */
  748.     parse_addr4(a,this,one);    /* keep trying */
  749.     }
  750.     else if (used == &groupend) {
  751.     add_addresslist(a,*this, ADR_ALIAS); /* save the address */
  752.     *this = safe_free(*this);
  753.     add_addresslist(a,";", ADR_GROUPEND); /* post the group end */
  754.     return(parse_sep2(a,this,one));    /* try for a comman or confirm */
  755.     }
  756. }
  757.  
  758.  
  759. /*
  760.  * between addresses.   
  761.  * a comma means get more addresses.  confirm means all done.
  762.  * comments, as always are applicable
  763.  */
  764.  
  765. parse_sep1(a,this,one) 
  766. addresslist *a;
  767. char **this;
  768. {
  769.     pval pv;
  770.     fdb *used;
  771.  
  772.     do {
  773.     if (!one) 
  774.         parse(fdbchn(&confirm,
  775.              &comma, 
  776.              &comment,
  777.              &groupend,
  778.              nil), &pv, &used);
  779.     else
  780.         parse(fdbchn(&confirm,
  781.              &comment,
  782.              &groupend,
  783.              nil), &pv, &used);
  784.     if (used == &comment) 
  785.         parse_comment(a,this);
  786.     else if (used == &groupend) {
  787.         if (*this) {
  788.         add_addresslist(a,*this,ADR_ADDRESS);
  789.         *this = safe_free(*this);
  790.         }
  791.         add_addresslist(a,";",ADR_GROUPEND);
  792.     }
  793.     else {
  794.         if (*this) {
  795.         add_addresslist(a,*this,ADR_ADDRESS);
  796.         *this = safe_free(*this);
  797.         }
  798.     }
  799.     } while (used != &comma && used != &confirm);
  800.     return(used == &comma);
  801. }
  802.  
  803. /*
  804.  * sep2: parse a separator (no group ends), and don't add anything to the
  805.  * addresslist.
  806.  */
  807. parse_sep2(a,this,one) 
  808. addresslist *a;
  809. char **this;
  810. {
  811.     pval pv;
  812.     fdb *used;
  813.  
  814.     do {
  815.     if (!one) 
  816.         parse(fdbchn(&confirm,
  817.              &comma, 
  818.              &comment,
  819.              nil), &pv, &used);
  820.     else
  821.         parse(fdbchn(&confirm,
  822.              &comment,
  823.              nil), &pv, &used);
  824.  
  825.     if (used == &comment) {
  826.         if (*this) {
  827.         free(*this);
  828.         *this = nil;
  829.         }
  830.         parse_comment(a,this);
  831.     }
  832.     } while (used == &comment);
  833.     return(used == &comma);
  834. }
  835.  
  836. parse_comment(a,this) 
  837. addresslist *a;
  838. char **this;
  839. {
  840.     pval pv;
  841.     fdb *used;
  842.  
  843.     *this = safe_strcat(*this,"(",TRUE);
  844.     do {
  845.     parse(fdbchn(&commentend,
  846.              &comment,
  847.              &commenttxt,
  848.              nil), &pv, &used);
  849.     if (used == &comment) {
  850.          parse_comment(a,this);
  851.     }
  852.     else if (used == &commentend) {
  853.         *this = safe_strcat(*this,")",FALSE);
  854.     }
  855.     else if (used == &commenttxt) {
  856.         if (*this && strlen(*this) > 0 && (*this)[strlen(*this)-1] !='(')
  857.         *this = safe_strcat(*this,atmbuf,TRUE);
  858.         else
  859.         *this = safe_strcat(*this,atmbuf,FALSE);
  860.     }
  861.     } while(used != &commentend);
  862. }  
  863.  
  864. parse_host(a, this) 
  865. addresslist *a;
  866. char **this;
  867. {
  868.     pval pv;
  869.     fdb *used;
  870.     
  871.     parse(fdbchn(&ipaddr,
  872.          &hostname,
  873.          nil), &pv, &used);
  874.     if (used == &ipaddr)
  875.     parse_ipaddr(a, this);
  876.     else {
  877.     *this = safe_strcat(*this,"@",FALSE);
  878.     *this = safe_strcat(*this,atmbuf,FALSE);
  879.     }
  880. }    
  881.  
  882. parse_ipaddr(a, this)
  883. addresslist *a;
  884. char **this;
  885. {
  886.     pval pv;
  887.     fdb *used;
  888.     int i;
  889.  
  890.     *this = safe_strcat(*this,"@[",FALSE);
  891.     for(i = 0; i < 4; i++) {
  892.     parse(fdbchn(&ipaddrnum,nil), &pv, &used);
  893.     *this = safe_strcat(*this, atmbuf,FALSE);
  894.     if (pv._pvint > 255) {
  895.         cmxbol();
  896.         cmxeprintf("?Address value > 255\n");
  897.         ccmd_errnp(CMxOK);
  898.     }
  899.     if (i == 3) break;
  900.     parse(fdbchn(&ipaddrdot, nil), &pv, &used);
  901.     *this = safe_strcat(*this,".",FALSE);
  902.     }
  903.     parse(fdbchn(&ipaddrend, nil), &pv, &used);
  904.     *this = safe_strcat(*this,"]",FALSE);
  905. }
  906.  
  907. parse_mbox(a, this)
  908. addresslist *a;
  909. char **this;
  910. {
  911.     pval pv;
  912.     fdb *used;
  913.  
  914.     *this = safe_strcat(*this, "<",TRUE);
  915.     parse(fdbchn(
  916. #ifdef undef
  917.          &username,
  918. #endif
  919.          &at,
  920.          &remusr,
  921.          nil), &pv, &used);
  922.     if (used == &at) {
  923.     parse_mbox2(a,this);
  924.     parse(fdbchn(
  925. #ifdef undef
  926.              &username,
  927. #endif
  928.              &remusr,
  929.              nil), &pv, &used);
  930.     }
  931.     if (used == &username)
  932.     *this = safe_strcat(*this, pv._pvusr[0]->pw_name,FALSE);
  933.     else
  934.     *this = safe_strcat(*this, atmbuf, false);
  935.     parse(fdbchn(&mboxend,
  936.          &at,
  937.          nil), &pv, &used);
  938.     if (used == &at) {
  939.     parse_host(a,this);
  940.     parse(fdbchn(&mboxend, nil), &pv, &used);
  941.     }
  942.     *this = safe_strcat(*this, ">", false);
  943. }
  944.  
  945.  
  946. /*
  947.  * saw a [route] in a mailbox
  948.  */
  949. parse_mbox2(a,this) 
  950. addresslist *a;
  951. char **this;
  952. {
  953.     pval pv;
  954.     fdb *used;
  955.  
  956.     parse_host(a,this);
  957.     parse(fdbchn(&comma2,
  958.          &colon,
  959.          nil), &pv, &used);
  960.     if (used == &comma2) {
  961.     *this = safe_strcat(*this, ",", FALSE);
  962.     parse(fdbchn(&at,nil),&pv,&used);
  963.     parse_mbox2(a,this);
  964.     }
  965.     else {    
  966.     *this = safe_strcat(*this,atmbuf, false);
  967.     }
  968. }
  969.  
  970.  
  971. free_addresslist(a) 
  972. addresslist *a;
  973. {
  974.     addr_unit *a1, *a2;
  975.     for (a1 = a->first; a1 != a->last; ) {
  976.     a2 = a1->next;
  977.     free_address(a1);
  978.     a1 = a2;
  979.     }
  980.     a->first = a->last = nil;
  981. }
  982.  
  983. free_address(a) 
  984. addr_unit *a;
  985. {
  986.     if (a) {
  987.     if (a->data) {
  988.         free(a->data);
  989.     }
  990.     free(a);
  991.     }
  992. }
  993.  
  994. add_addresslist(a,str,type)
  995. addresslist *a;
  996. char *str;
  997. int type;
  998. {
  999.     addr_unit *au;
  1000.     addresslist *aliasaddr;
  1001.     if (type == ADR_GROUPEND && !active_label)
  1002.     return;
  1003.     if (((!aliases_use_groups) || active_label) && (type == ADR_ALIAS)) {
  1004.     aliasaddr = (addresslist *) lookup_alias (str);
  1005.     if (aliasaddr)
  1006.         for (au = aliasaddr->first; au; au = au->next)
  1007.         if (au->type != ADR_GROUP && au->type != ADR_GROUPEND &&
  1008.             au->type != ADR_AL_EXPAND)
  1009.             add_addresslist (a,au->data, au->type);
  1010.     return;
  1011.     }
  1012.     if (type != ADR_MLIST) {
  1013.     if (a->last == nil) {
  1014.         a->last = a->first = (addr_unit *) malloc(sizeof(addr_unit));
  1015.         a->first->next = a->first->prev = nil;
  1016.     }
  1017.     else {
  1018.         a->last->next = (addr_unit *) malloc(sizeof(addr_unit));
  1019.         a->last->next->prev = a->last;
  1020.         a->last = a->last->next;
  1021.         a->last->next = nil;
  1022.     }
  1023.     a->last->type = type;
  1024.     }
  1025.  
  1026.     switch (type) {
  1027.     case ADR_ADDRESS:
  1028.     case ADR_FILE:
  1029.     a->last->data = malloc(strlen(str)+1);
  1030.     strcpy(a->last->data, str);
  1031.     break;
  1032.     case ADR_GROUP:
  1033.     case ADR_AL_EXPAND:
  1034.     a->last->data = malloc(strlen(str)+1);
  1035.     strcpy(a->last->data, str);
  1036.     active_label = TRUE;
  1037.     break;
  1038.     case ADR_GROUPEND:
  1039.     if (active_label) {
  1040.         a->last->data = malloc(strlen(str)+1);
  1041.         strcpy(a->last->data, str);
  1042.         active_label = FALSE;
  1043.     }
  1044.     break;
  1045.     case ADR_ALIAS: 
  1046.     adr_alias(a,str);
  1047.     break;
  1048.     case ADR_LISTFILE:
  1049.     a->last->data = malloc(MAXPATHLEN+2);
  1050.     if (str[0] == '/')        /* abs path already? */
  1051.         strcpy(a->last->data, str);
  1052.     else {
  1053.         getwd(a->last->data);
  1054.         strcat(a->last->data, "/");
  1055.         strcat(a->last->data, str);
  1056.     }
  1057.     a->last->type = ADR_MLIST;    /* make it get expanded next time. */
  1058.     break;
  1059.     case ADR_MLIST:
  1060.     adr_mlist(a, str);
  1061.     break;
  1062.     }
  1063. }
  1064.  
  1065. adr_mlist(a, str)
  1066. addresslist *a;
  1067. char *str;
  1068. {
  1069.     FILE *newinf;        /* new input file */
  1070.     static addresslist a1 = { nil, nil };
  1071.     csb savecsb;
  1072.     char abuf[BUFSIZ], wbuf[BUFSIZ];
  1073.     int cbuf[BUFSIZ*5];
  1074.  
  1075.     newinf = fopen(str, "r");
  1076.     if (!newinf) {
  1077.     cmxeprintf("Could not open %s\n",str);
  1078.     }
  1079.     else {
  1080.     savecsb = cmcsb;
  1081.     cmbufs(cbuf,10*BUFSIZ, abuf,BUFSIZ, wbuf,BUFSIZ);
  1082.     ind_oldfds();            /* set up indirect mode */
  1083.                     /* install new input file */
  1084.     cmseti(newinf, nil, cmcsb._cmej);
  1085.                     /* set indirection bit.  EOF handled */
  1086.     cmcsb._cmflg2 |= CM_IND;    /* through ccmd */
  1087.  
  1088.     parse_addresses(&a1);        /* parse from list file. */
  1089.     cmcsb = savecsb;
  1090.     fclose(newinf);
  1091.     merge_addresses(a,&a1);
  1092.     free_addresslist(&a1);
  1093.     }
  1094. }
  1095.  
  1096. adr_alias(a,str)
  1097. addresslist *a;
  1098. char *str;
  1099. {
  1100.     addresslist *aliasaddr;
  1101.     addr_unit *au;
  1102.  
  1103.     aliasaddr = lookup_alias(str);
  1104.     if (aliasaddr) {
  1105.     if (active_label) {
  1106.         a->last->data = safe_strcpy(";");
  1107.         a->last->type = ADR_GROUPEND;
  1108.         active_label = false;
  1109.         add_addresslist(a,str,ADR_AL_EXPAND);
  1110.     }
  1111.     else {
  1112.         a->last->data = safe_strcpy(str);
  1113.         a->last->type = ADR_AL_EXPAND;
  1114.         active_label = true;
  1115.     }
  1116.     au = aliasaddr->first;
  1117.     for( ; au != nil; au = au->next) {
  1118.         if (au->type != ADR_GROUP && au->type != ADR_GROUPEND
  1119.         && au->type != ADR_AL_EXPAND)
  1120.         add_addresslist(a,au->data, au->type);
  1121.     }
  1122.     }
  1123.     if (active_label)
  1124.     add_addresslist(a,";", ADR_GROUPEND);
  1125. }
  1126.  
  1127.  
  1128. disp_addresses(fp,prefix,a,expand,newline,smail,file_asterix) 
  1129. char *prefix;
  1130. addresslist *a;
  1131. int expand;
  1132. FILE *fp;
  1133. int newline;
  1134. int smail;
  1135. {
  1136.     int linebeg;            /* any addresses on this line yet? */
  1137.  
  1138.     int endcol = cmcsb._cmcmx-1;    /* leave space for "," */
  1139.     addr_unit *au = a->first;
  1140.     int len = 0;
  1141.  
  1142.     if (a->first && prefix) {
  1143.     fprintf(fp,"%s: ",prefix);
  1144.     len = strlen(prefix)+2;
  1145.     }
  1146.     linebeg = TRUE;            /* no addresses printed yet */
  1147.     while(au) {
  1148.     if (len + disp_address(fp,au,FALSE,smail,file_asterix) > endcol)
  1149.         /* won't fit on this line */
  1150.         if (!linebeg) {        /* anything on this line yet? */
  1151.         len = 3;        /* 3 spaces, another one later */
  1152.         if (newline) fprintf(fp,"\n   "); /* three spaces */
  1153.         linebeg = TRUE;        /* at line beginning again */
  1154.         }                /* else, won't fit anyway */
  1155.     if (au != a->first) {
  1156.         len += 1;
  1157.         fputc (' ', fp);
  1158.     }
  1159.     len += disp_address(fp,au,TRUE,smail,file_asterix);
  1160.     linebeg = FALSE;        /* we wrote something on this line */
  1161.     if ((au->type == ADR_GROUP || au->type == ADR_AL_EXPAND) && !expand) {
  1162.         while(au->next->type != ADR_GROUPEND)
  1163.         au = au->next;
  1164.     }
  1165.     else if (!(au->type == ADR_GROUP || au->type == ADR_AL_EXPAND) &&
  1166.          au->next && au->next->type != ADR_GROUPEND) {
  1167.         len += 1;
  1168.         fputc (',', fp);
  1169.     }
  1170.     if (au) au = au->next;
  1171.     }
  1172.     if (len > 0 && newline) fputc('\n', fp);
  1173. }
  1174.  
  1175. int
  1176. disp_address(fp,a,prflag,smail,file_asterix)
  1177. FILE *fp;
  1178. addr_unit *a; 
  1179. int prflag;
  1180. int smail;
  1181. {
  1182.     char *str;
  1183.     int len;
  1184.     switch(a->type) {
  1185.     case ADR_ADDRESS:
  1186.     case ADR_ALIAS:
  1187.     str = malloc(strlen(a->data)+1);
  1188.     if (smail && a->data[0] == '\\')
  1189.       strcpy(str,a->data+1);
  1190.     else
  1191.       strcpy(str,a->data);
  1192.     break;
  1193.     case ADR_FILE:
  1194.     str = malloc(strlen(a->data)+2);
  1195.     sprintf(str,"%s%s",file_asterix ? "*" : "", a->data);
  1196.     break;
  1197.     case ADR_GROUP:
  1198.     case ADR_AL_EXPAND:
  1199.     str = malloc(strlen(a->data)+2);
  1200.     strcpy(str,a->data);
  1201.     strcat(str,":");
  1202.     break;
  1203.     case ADR_GROUPEND:
  1204.     str = malloc(strlen(a->data)+1);
  1205.     strcpy(str,a->data);
  1206.     break;
  1207.     case ADR_LISTFILE:
  1208.     case ADR_MLIST:
  1209.     str = malloc(strlen(a->data)+3);
  1210.     sprintf(str,"@@%s",a->data);
  1211.     break;
  1212.     default:
  1213.     printf("Invalid flag in message\n");
  1214.     fflush(stdout);
  1215.     abort();
  1216.     }
  1217.     if (prflag)
  1218.     fprintf(fp,"%s",str);
  1219.     len = strlen(str);
  1220.     free(str);
  1221.     return(len);
  1222. }
  1223.  
  1224. /*
  1225.  * merge list 2 into list 1
  1226.  */
  1227.  
  1228. merge_addresses(a1,a2) 
  1229. addresslist *a1, *a2;
  1230. {
  1231.     addr_unit *au1 = a1->first, *au2 = a2->first;
  1232.  
  1233.     while(au2) {
  1234.     au1 = a1->first;
  1235.     switch(au2->type) {
  1236.     case ADR_GROUP:            /* adding a group... */
  1237.     case ADR_AL_EXPAND:
  1238.         while(au1 && !(au1->type == ADR_GROUP ||
  1239.                au1->type == ADR_AL_EXPAND ||
  1240.                !ustrcmp(au1->data, au2->data)))
  1241.         au1 = au1->next;
  1242.         if (au1 == nil) {    /* add group to end of list */
  1243.         add_addresslist(a1,au2->data,ADR_GROUP);
  1244.         au2 = au2->next;
  1245.         while(au2 && au2->type != ADR_GROUPEND) {
  1246.             add_addresslist(a1,au2->data, ADR_ADDRESS);
  1247.             au2 = au2->next;
  1248.         }
  1249.         add_addresslist(a1,";",ADR_GROUPEND);
  1250.         }
  1251.         else {            /* add to an existing group */
  1252.         addr_unit *temp, *temp2;
  1253.         while(au1->next->type != ADR_GROUPEND)
  1254.             au1 = au1->next;    /* find end of group */
  1255.         temp = au1->next;    /* save old values */
  1256.         temp2 = a1->last;
  1257.         a1->last = au1;
  1258.         au1->next = nil;
  1259.         au2 = au2->next;    /* skip group name */
  1260.         while(au2->type != ADR_GROUPEND) { /* add new members */
  1261.             add_addresslist(a1,au2->data, ADR_ADDRESS);
  1262.             au2 = au2->next;
  1263.         }
  1264.         a1->last->next = temp;    /* restore old values */
  1265.         a1->last = temp2;
  1266.         }
  1267.         break;
  1268.     case ADR_ADDRESS:
  1269.     case ADR_ALIAS:
  1270.     case ADR_FILE:
  1271.         add_addresslist(a1,au2->data, au2->type);
  1272.         break;
  1273.     }
  1274.     if (au2)
  1275.         au2 = au2->next;
  1276.     }
  1277. }
  1278.  
  1279. /*
  1280.  * remove addresses in list 2 from list 1.
  1281.  * entries in list 2:
  1282.  *  empty group list: delete whole group from list 1.
  1283.  *  group list: delete selected members from list 1.
  1284.  *  simple address: delete that address.
  1285.  * comments count.
  1286.  */
  1287.  
  1288. remove_addr(a1,a2) 
  1289. addresslist *a1, *a2;
  1290. {
  1291.     addr_unit *au1, *au2, *t1, *t2;
  1292.  
  1293.     au2 = a2->first;
  1294.     
  1295.     while(au2) {            /* all items to delete... */
  1296.     au1 = a1->first;        /* deleting a group */
  1297.     if (au2->type == ADR_GROUP || au2->type == ADR_AL_EXPAND) {
  1298.         if (au2->next->type == ADR_GROUPEND) { /* a whole group */
  1299.         while(au1 && !(au1->type == ADR_GROUP || 
  1300.                    au1->type == ADR_AL_EXPAND ||
  1301.                    !ustrcmp(au1->data, au2->data)))
  1302.             au1 = au1->next;
  1303.         if (au1 != nil) {    /* found the group */
  1304.             t1 = au1;
  1305.             while(t1->type != ADR_GROUPEND) {
  1306.             t1 = t1->next;
  1307.             }            /* now t1 is the end, */
  1308.                     /*  au1 is the start */
  1309.             if (a1->first == au1)
  1310.             a1->first = t1->next;
  1311.             if (a1->last == t1)
  1312.             a1->last = au1->prev;
  1313.             if (au1->prev)
  1314.             au1->prev->next = t1->next;
  1315.             if (t1->next)
  1316.             t1->next->prev = au1->prev;
  1317.             while(au1 != t1) {
  1318.             t2 = au1->next;
  1319.             free_address(au1);
  1320.             au1 = t2;
  1321.             }
  1322.             free_address(au1);
  1323.             au2 = au2->next;
  1324.         }
  1325.         }
  1326.         else {            /* removing selected id's from list */
  1327.         while(au1 && !(au1->type == ADR_GROUP || 
  1328.                    au1->type == ADR_AL_EXPAND ||
  1329.                    !ustrcmp(au1->data, au2->data)))
  1330.             au1 = au1->next;
  1331.         if (au1 != nil) {    /* found the group */
  1332.             au2 = au2->next;
  1333.             while(au2->type != ADR_GROUPEND) {
  1334.             t1 = au1->next;
  1335.             while(t1->type != ADR_GROUPEND &&
  1336.                   addrcmp(t1->data, au2->data)) 
  1337.                 t1 = t1->next;
  1338.             if (t1->type != ADR_GROUPEND) {
  1339.                 t1->next->prev = t1->prev;
  1340.                 t1->prev->next = t1->next;
  1341.                 free_address(t1);
  1342.             }
  1343.             au2 = au2->next;
  1344.             }
  1345.         }
  1346.         }
  1347.     }
  1348.     else {
  1349.         while(au1) {
  1350.         if (au1->type != au2->type  || addrcmp(au1->data, au2->data))
  1351.             au1 = au1->next;
  1352.         else {
  1353.             if (a1->first == au1)
  1354.             a1->first = au1->next;
  1355.             if (a1->last == au1)
  1356.             a1->last = au1->prev;
  1357.             if (au1->prev)
  1358.             au1->prev->next = au1->next;
  1359.             if (au1->next)
  1360.             au1->next->prev = au1->prev;
  1361.             t1 = au1->next;
  1362.             free_address(au1);
  1363.             au1 = t1;
  1364.         }
  1365.         }
  1366.     }
  1367.     au2 = au2->next;
  1368.     }
  1369. }
  1370.  
  1371. /*
  1372.  * this should strip out comments, and check for <mailboxes> which match...
  1373.  */
  1374. addrcmp(a1, a2) 
  1375. char *a1, *a2;
  1376. {
  1377.     char *b, *cp, *uname, *index(), *rindex();
  1378.     int x;
  1379.  
  1380.     if (ustrcmp(a1,a2) == 0)        /* exact match? */
  1381.     return(0);
  1382.                     /* match up to a domain? */
  1383.     if (ustrncmp(a1,a2,strlen(a2)) == 0 && a1[strlen(a2)] == '.' &&
  1384.     index(a1,'@'))
  1385.     return(0);
  1386.  
  1387.     b = malloc(strlen(a1)+1);        /* copy to a workspace. */
  1388.     strcpy(b,a1);
  1389.     
  1390.     if ((cp = index(b,'<')) != nil)    /* if an address in <>, use it */
  1391.     cp++;
  1392.     else cp = b;
  1393.     if ((uname = index(cp,'>')) != nil)
  1394.     *uname = '\0';
  1395.  
  1396.     if (ustrcmp(cp,a2) == 0)  {        /* exact match now? */
  1397.     free(b);
  1398.     return(0);
  1399.     }
  1400.                     /* a match up to a domain? */
  1401.     if (ustrncmp(cp,a2,strlen(a2)) == 0 && cp[strlen(a2)] == '.' &&
  1402.     index(cp,'@')) {
  1403.     free(b);
  1404.     return(0);
  1405.     }
  1406.                     /* find the user name */
  1407.     if ((uname = rindex(cp,'!')) != nil) /* to the right of any '!'s */
  1408.     uname = uname+1;
  1409.     else 
  1410.     uname = cp;
  1411.  
  1412.     if (ustrcmp(uname,a2) == 0)        /* exact match now? */
  1413.     return(0); 
  1414.  
  1415.                     /* without some domains? */
  1416.     if ((ustrncmp(uname,a2,strlen(a2)) == 0) && (uname[strlen(a2)] == '.')
  1417.     && (index(uname,'@') || index(uname,'%'))) {
  1418.     free(b);
  1419.     return(0);
  1420.     }
  1421.  
  1422.     if ((cp = index(uname,'@')) != nil) /* strip off host names */
  1423.     *cp = '\0';
  1424.     if ((cp = index(uname,'%')) != nil)
  1425.     *cp = '\0';
  1426.     x = ustrcmp(uname, a2);        /* match now? */
  1427.     free(b);
  1428.     return(x);
  1429. }
  1430.  
  1431.  
  1432. skip_spaces(str)
  1433. char **str;
  1434. {
  1435.     while(**str == ' ' || **str == '\t')
  1436.     (*str)++;
  1437. }
  1438.