Remove UXPDS support
[oweals/cde.git] / cde / programs / dtmail / libDtMail / Common / DtMailRc.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *      $TOG: DtMailRc.C /main/9 1998/07/23 18:02:08 mgreess $
27  *
28  *      RESTRICTED CONFIDENTIAL INFORMATION:
29  *      
30  *      The information in this document is subject to special
31  *      restrictions in a confidential disclosure agreement between
32  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
33  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
34  *      Sun's specific written approval.  This document and all copies
35  *      and derivative works thereof must be returned or destroyed at
36  *      Sun's request.
37  *
38  *      Copyright 1993, 1995, 1995 Sun Microsystems, Inc.  All rights reserved.
39  *
40  *+ENOTICE
41  */
42
43 /*
44  * Format of the command description table.
45  * The actual table is declared and initialized
46  * in lex.c
47  */
48
49 #include <stdlib.h>
50 #include <signal.h>
51 #include <sys/param.h>
52 #include <sys/wait.h>
53 #include <ctype.h>
54 #include <pwd.h>
55 #include <DtMail/DtMail.hh>
56 #include <DtMail/DtMailError.hh>
57 #include <DtMail/Threads.hh>
58 #include <DtMail/IO.hh>
59 #include <EUSCompat.h>
60 #include "str_utils.h"
61
62 struct cmd {
63     char *c_name;   /* Name of command */
64     int (*c_func)(char **, DtMail::MailRc *);     /* Implementor of the command */
65     void (*c_write)(const char * verbatim, char ** args, FILE * outf);
66     short c_argtype;     /* Type of arglist (see below) */
67     short c_msgflag;     /* Required flags of messages */
68     short c_msgmask;     /* Relevant flags of messages */
69 };
70
71 struct var *DtMail::MailRc::variables[HSHSIZE]; /* Pointer to active var list */
72 #ifdef __osf__
73 typedef DtMail::MailRc::globals DtMailRc_globals;
74 DtMailRc_globals  DtMail::MailRc::glob;
75 #else
76 struct DtMail::MailRc::globals DtMail::MailRc::glob;
77 #endif
78 char *DtMail::MailRc::nullfield;
79 Boolean DtMail::MailRc::clearAliases;
80
81 #ifdef HAS_VFORK
82   #define DTMAIL_FORK   vfork
83 #else
84   #define DTMAIL_FORK   fork
85 #endif
86
87 #define MAXIMUM_PATH_LENGTH        2048
88
89 /* can't initialize unions */
90
91 #define c_minargs c_msgflag             /* Minimum argcount for RAWLIST */
92 #define c_maxargs c_msgmask             /* Max argcount for RAWLIST */
93 #define HASHSIZE 120
94
95
96 // defines needed by mail-parse
97 #define LINESIZE 10240        /* max readable line width */
98 #define MAXARGC  1024    /* Maximum list of raw strings */
99 #define equal(a, b)   (strcmp(a,b)==0)/* A nice function to string compare */
100 #define NONE  ((struct cmd *) 0) /* The nil pointer to command tab */
101
102 #define CANY            0               /* Execute in send or receive mode */
103 #define CRCV            1               /* Execute in receive mode only */
104 #define CSEND           2               /* Execute in send mode only */
105 #define CTTY            3               /* Execute if attached to a tty only */
106 #define CNOTTY          4               /* Execute if not attached to a tty */
107
108 #define STRLIST  1       /* A pure string */
109 #define RAWLIST  2       /* Shell string list */
110 #define F 01000  /* Is a conditional command */
111 #define NOLIST   3       /* Just plain 0 */
112 #define CANY            0               /* Execute in send or receive mode */
113 #define NOSTR           ((char *) 0)    /* Null string pointer */
114 // #define NOFILE_DTMAIL          20      /* this define is here for      */
115 //                                 /* compatibility purposes only  */
116 //                                 /* and will be removed in a     */
117 //                                 /* later release                */
118
119
120
121
122 struct var {
123     struct  var *v_link;            /* Forward link to next variable */
124     char    *v_name;                /* The variable's name */
125     char    *v_value;               /* And it's current value */
126     int     v_written;              /* It has been written on this pass. */
127 };      
128
129 struct hash {
130     struct hash *h_next;
131     char *h_key;
132     void *h_value;
133     int h_written;
134 };
135
136 struct cmd cmdtab[] = {
137 { "unset",      DtMail::MailRc::unset, DtMail::MailRc::wunset,     RAWLIST,        1,      1000, },
138 { "alias",      DtMail::MailRc::group, DtMail::MailRc::wgroup, RAWLIST, 2,      1000, },
139 { "group",      DtMail::MailRc::group, DtMail::MailRc::wgroup, RAWLIST, 2,      1000, },
140 { "set",        DtMail::MailRc::set,   DtMail::MailRc::wset, RAWLIST,   1,      1000,},
141 { "ignore",     DtMail::MailRc::igfield, DtMail::MailRc::wigfield, RAWLIST,        1,      1000, },
142 { "source",     DtMail::MailRc::source, DtMail::MailRc::wsource, RAWLIST,        1,      1000, },
143 { "if",         DtMail::MailRc::ifcmd, DtMail::MailRc::wifcmd, F|RAWLIST, 1, 1, },
144 { "else",       DtMail::MailRc::elsecmd, DtMail::MailRc::welsecmd, F|RAWLIST, 0, 0, },
145 { "endif",      DtMail::MailRc::endifcmd, DtMail::MailRc::wendifcmd, F|RAWLIST, 0, 0, },
146 { "alternates", DtMail::MailRc::alternates, DtMail::MailRc::walternates, RAWLIST,        1,      1000, },
147 { "clearaliases", DtMail::MailRc::clearaliases, DtMail::MailRc::wclearaliases, RAWLIST, 0, 0, },
148 { 0, 0,         0,              0,      0,      0 }
149 };
150
151 #if defined(sun)
152 #define SYSTEM_MAILRC   "/etc/mail/mail.rc"
153 #elif defined(_AIX) || defined(__osf__) || defined(linux)
154 #define SYSTEM_MAILRC   "/usr/share/lib/Mail.rc"
155 #elif defined(USL) || defined(__hpux)
156 #define SYSTEM_MAILRC   "/usr/share/lib/mailx.rc"
157 #elif defined(CSRG_BASED)
158 #define SYSTEM_MAILRC   "/etc/mail.rc"
159 #endif
160
161 // constructor
162 DtMail::MailRc::MailRc(DtMailEnv &env, DtMail::Session *session)
163 {
164   //char *line = NULL;
165    
166     session = session;
167
168
169     env.clear();
170     _parseError = DTME_NoError;
171
172     input = NULL;
173
174     init_globals();
175     
176     char * mail_rc = getenv("MAILRC");
177     if (mail_rc) {
178         _mailrc_name = strdup(mail_rc);
179     }
180     else {
181         passwd pw;
182         GetPasswordEntry(pw);
183         
184         _mailrc_name = (char *)malloc(MAXIMUM_PATH_LENGTH);
185         
186         strcpy(_mailrc_name, pw.pw_dir);
187         strcat(_mailrc_name, "/.mailrc");
188     }
189     
190     cond = CANY;
191   
192     char line[LINESIZE];
193     int rval = 0;
194     line[0] = '\0';
195     
196     if ((rval = this->load(_mailrc_name, line)) != 0) {
197         char *msg = new char[MAXIMUM_PATH_LENGTH + LINESIZE];
198         if (rval == -1) {
199             sprintf(msg, "error while reading \'%s\' in %s\n", line,
200                         _mailrc_name);
201         }
202         else if (rval == -2) {
203             sprintf(msg, "%s\n", line);
204         }
205         else if (rval == -3) {
206             sprintf(msg, "error: no endif for conditional if statement while reading %s\n", _mailrc_name);
207             env.setError(DTME_ResourceParsingNoEndif, NULL);
208             _parseError = DTME_ResourceParsingNoEndif;
209         }
210                 
211         env.logError(DTM_FALSE, "%s", msg);
212         delete [] msg;
213     }
214
215     rval = 0;
216     line[0] = '\0';
217
218     if ((rval = this->load(SYSTEM_MAILRC, line)) != 0) {
219         char *msg = new char[MAXIMUM_PATH_LENGTH + LINESIZE];
220
221         if (rval == -1) {
222             sprintf(msg, "error while reading \'%s\' in %s\n",
223                     line, SYSTEM_MAILRC);
224             env.logError(DTM_FALSE, "%s", msg);
225         } else if (rval == -2) {
226             /* Ignore error if the system logfile does not exist. */
227         }
228         delete [] msg;
229     }
230     
231 }
232
233 DtMail::MailRc::~MailRc(void)
234 {
235     free(_mailrc_name);
236 }
237
238 // return true if in list
239 DtMailBoolean DtMail::MailRc::ignore(DtMailEnv &env, const char *name)
240 {
241     
242     char *ignore_list = (char *)MailRc::hm_test((struct hash **)glob.g_ignore, (char *)name);
243     
244     env.clear();
245     
246     if(ignore_list != NULL)  // we have a valid ignore list
247       {
248           return DTM_TRUE;
249       }
250     else
251       {
252           env.setError(DTME_NoObjectValue, NULL);
253           return DTM_FALSE;
254       }
255     
256 }
257
258 void
259 DtMail::MailRc::addIgnore(DtMailEnv & error, const char * name)
260 {
261     error.clear();
262     add_ignore((char *)name);
263 }
264
265 void
266 DtMail::MailRc::removeIgnore(DtMailEnv & error, const char * name)
267 {
268     error.clear();
269     if(MailRc::hm_test((struct hash **)glob.g_ignore, (char *)name)) {
270         MailRc::hm_delete((struct hash **)glob.g_ignore, (char *)name);
271     }
272 }
273
274 const char *DtMail::MailRc::getAlias(DtMailEnv &env, const char * name)
275 {
276     
277     char *return_value = (char *)hm_test((struct hash **)glob.g_alias, (char *)name);
278     
279     env.clear();
280     
281     if(return_value == NULL)
282       {
283           env.setError(DTME_NoObjectValue, NULL);
284           return NULL;
285       }
286     else
287       {
288           return return_value;
289       }
290     
291 }
292
293 void
294 DtMail::MailRc::setAlias(DtMailEnv & error,
295                          const char * name,
296                          const char * value)
297 {
298     error.clear();
299
300     if (hm_test((struct hash **)glob.g_alias, (char *)name)) {
301         hm_delete((struct hash **)glob.g_alias, (char *)name);
302     }
303
304     add_alias((char *)name, (char *)value);
305 }
306
307 void
308 DtMail::MailRc::removeAlias(DtMailEnv & error,
309                             const char * name)
310 {
311     error.clear();
312
313     if (hm_test((struct hash **)glob.g_alias, (char *)name)) {
314         hm_delete((struct hash **)glob.g_alias, (char *)name);
315     }
316 }
317
318 // 
319 // Adapted from uuencode and uudecode written by Ian Lance Taylor.
320 //
321 #define ENCRYPT(c) ((c) ? (((c)&077) + ' ' + 2) : ('`' + 2))
322 #define DECRYPT(c) ((((c) - ' ') & 077) - 2)
323
324 int 
325 DtMail::MailRc::encryptedLength(int length)
326 {
327     // One for the length, one for the '\0', expansion factor of 4/3.
328     int encrypted_length = (2 + (4 * ((length+2)/3)));
329     return encrypted_length;
330 }
331
332 void 
333 DtMail::MailRc::encryptValue(char *to, char *from, int buflen)
334 {
335     int         ch, length;
336     char        *p;
337     int         i=0;
338   
339     memset(to, 0, buflen);
340
341     length = strlen(from);
342     to[i++] = ENCRYPT(length);
343
344     for (p=from; length>0; length-=3, p+=3)
345     {
346         ch = *p >> 2;
347         ch = ENCRYPT(ch);
348         to[i++] = (char) ch;
349
350         ch = ((*p << 4) & 060) | ((p[1] >> 4) & 017);
351         ch = ENCRYPT(ch);
352         to[i++] = (char) ch;
353
354         ch = ((p[1] << 2) & 074) | ((p[2] >> 6) & 03);
355         ch = ENCRYPT(ch);
356         to[i++] = (char) ch;
357
358         ch = p[2] & 077;
359         ch = ENCRYPT(ch);   
360         to[i++] = (char) ch;
361     }
362 }
363
364 int
365 DtMail::MailRc::decryptValue(char *to, char *from, int buflen)
366 {
367     int         ch, length;
368     int         i=0;
369     char        *p;
370    
371     memset(to, 0, buflen);
372     p = from;
373
374     length = DECRYPT(*p);
375     if (length<=0) return 1;
376
377     for (++p; length>0; p+=4, length-=3)
378     {
379         if (length>=3)
380         {
381             ch = DECRYPT(p[0]) << 2 | DECRYPT(p[1]) >> 4;
382             to[i++] = (char) ch;
383             ch = DECRYPT(p[1]) << 4 | DECRYPT(p[2]) >> 2;
384             to[i++] = (char) ch;
385             ch = DECRYPT(p[2]) << 6 | DECRYPT(p[3]);
386             to[i++] = (char) ch;
387         }
388         else
389         {
390             if (length>=1)
391             {
392                 ch = DECRYPT(p[0]) << 2 | DECRYPT(p[1]) >> 4;
393                 to[i++] = (char) ch;
394             }
395             if (length>=2)
396             {
397                 ch = DECRYPT(p[1]) << 4 | DECRYPT(p[2]) >> 2;
398                 to[i++] = (char) ch;
399             }
400         }
401     }
402     return 0;
403 }
404
405 // if set with value return no error in env and set value
406 // if set with no value return no error in env and return empty string
407 // if not set, return error in env and leave value alone
408
409 void DtMail::MailRc::getValue(DtMailEnv &env, 
410                               const char * var, 
411                               const char ** value,
412                               DtMailBoolean decrypt)
413 {
414     char *get_result = mt_value((char *)var);
415     
416     *value = NULL;
417     env.clear();
418     
419     if (get_result != NULL)
420     {
421         if (decrypt)
422         {
423             int length = encryptedLength(strlen(get_result));
424             *value = (char*) malloc(length);
425             if (decryptValue((char*) *value, get_result, length))
426               strcpy((char*) *value, get_result);
427         }
428         else
429           *value = strdup((char *)get_result);
430     }
431     else
432       env.setError(DTME_NoObjectValue, NULL);
433 }
434
435 void
436 DtMail::MailRc::setValue(DtMailEnv & error,
437                          const char * var,
438                          const char * value,
439                          DtMailBoolean encrypt)
440 {
441     error.clear();
442
443     if (encrypt && strlen(value))
444     {
445         int     length = encryptedLength(strlen(value));
446         char    *encrypted = (char*) malloc(length);
447         encryptValue(encrypted, (char*) value, length);
448         mt_assign((char*) var, (char*) encrypted);
449     }
450     else
451       mt_assign((char*) var, (char*) value);
452 }
453
454 void
455 DtMail::MailRc::removeValue(DtMailEnv & error,
456                             const char * var)
457 {
458     error.clear();
459
460     mt_deassign((char *)var);
461 }
462
463 // return alternates list
464 const char * DtMail::MailRc::getAlternates(DtMailEnv &env)
465 {
466     register int    i;
467     register struct hash *h;
468     struct hash **table = (struct hash **)glob.g_alternates;
469     int len;
470     
471     env.clear();
472     
473     // we don't free this memory...
474     alternate_list = NULL;
475     
476     if(table == NULL)
477       {
478           env.setError(DTME_NoObjectValue, NULL);
479           return NULL;
480       }
481     
482     i = HASHSIZE;
483     while (--i >= 0) {
484         
485         h = *table++;
486         while (h) {
487             len = strlen((const char*)h->h_key);
488             
489             if(alternate_list == NULL)
490               {
491                   alternate_list = (char *)malloc(len + 1); // plus terminator
492                   strcpy(alternate_list, (const char*)h->h_key);
493               }
494             else
495               {
496                   len += strlen(alternate_list);
497                   alternate_list = (char *)realloc(alternate_list, 
498                                                    len + 2); // plus terminator
499                   // and space
500                   strcat(alternate_list, " "); // add space
501                   strcat(alternate_list, (const char *)h->h_key);
502                   
503               }
504             
505             h = h->h_next;
506         }
507         
508     }
509     
510     return (alternate_list);
511     
512 }
513
514 void
515 DtMail::MailRc::setAlternate(DtMailEnv & error, const char * alt)
516 {
517     error.clear();
518
519     add_alternates((char *)alt);
520 }
521
522 void
523 DtMail::MailRc::removeAlternate(DtMailEnv & error, const char * name)
524 {
525     error.clear();
526
527     if(MailRc::hm_test((struct hash**)glob.g_alternates, (char *)name)) {
528         MailRc::hm_delete((struct hash **)glob.g_alternates, (char *)name);
529     }
530 }
531
532 void
533 DtMail::MailRc::update(DtMailEnv & error)
534 {
535     error.clear();
536     
537     // Generate a temporary file.
538     char *tmp_mailrc = new char[MAXIMUM_PATH_LENGTH];
539     strcpy(tmp_mailrc, _mailrc_name);
540     strcat(tmp_mailrc, ".tmp");
541     
542     FILE * outf = fopen(tmp_mailrc, "w+");
543     if (outf == NULL) {
544         error.setError(DTME_ObjectCreationFailed);
545         delete [] tmp_mailrc;
546         return;
547     }
548     
549     // Now open the mailrc for input.
550     FILE * inf = fopen(_mailrc_name, "r");
551     if (inf != NULL) {
552         // Now we will read the input file, and copy it to the output,
553         // based on type and changes to the mailrc.
554         //
555         updateByLine(inf, outf);
556         
557         fclose(inf);
558     }
559     
560     // Now we need to make a final scan. This will cause new values
561     // to be written and the written flag to be reset for the next
562     // update.
563     //
564     mt_scan(outf);
565
566     hm_scan((struct hash **)glob.g_alias, ngroup, outf);
567
568     // Alternates and groups we will add to a list, and then
569     // put them out in one batch.
570     //
571     DtVirtArray<char *> value_list(32);
572
573     hm_scan((struct hash **)glob.g_ignore, nigfield, &value_list);
574
575     if (value_list.length()) {
576         fwrite("ignore ", 1, 7, outf);
577         while (value_list.length()) {
578             char * val = value_list[0];
579             fwrite(val, 1, strlen(val), outf);
580             fwrite(" ", 1, 1, outf);
581             value_list.remove(0);
582         }
583         fwrite("\n", 1, 1, outf);
584     }
585
586     hm_scan((struct hash **)glob.g_alternates, nalternates, &value_list);
587
588     if (value_list.length()) {
589         fwrite("alternates ", 1, 11, outf);
590         while (value_list.length()) {
591             char * val = value_list[0];
592             fwrite(val, 1, strlen(val), outf);
593             fwrite(" ", 1, 1, outf);
594             value_list.remove(0);
595         }
596         fwrite("\n", 1, 1, outf);
597     }
598
599     fclose(outf);
600     if (rename(tmp_mailrc, _mailrc_name)) {
601         error.setError(DTME_ObjectAccessFailed);
602         delete [] tmp_mailrc;
603         return;
604     }
605     delete [] tmp_mailrc;
606 }
607
608 void DtMail::MailRc::getAliasList(hm_callback stuffing_func, void *client_data)
609 {
610   hm_scan((struct hash **)glob.g_alias, stuffing_func, client_data);
611 }
612
613
614 DtVirtArray<char *> *DtMail::MailRc::getAliasList()
615 {       
616
617   DtVirtArray<char *>   *value_list = NULL;
618
619   value_list = new DtVirtArray<char *>(10);
620
621   hm_scan((struct hash **)glob.g_alias, nalias, value_list);
622
623   return value_list;
624 }
625
626 DtVirtArray<char *> *DtMail::MailRc::getIgnoreList()
627 {       
628
629   DtVirtArray<char *>   *value_list = NULL;
630
631   value_list = new DtVirtArray<char *>(10);
632
633   hm_scan((struct hash **)glob.g_ignore, nignorelist, value_list);
634
635   return value_list;
636 }
637
638 void
639 DtMail::MailRc::updateByLine(FILE * inf, FILE * outf)
640 {
641     // We are going to keep two line buffers, unlike the first
642     // pass parser. One is the line, with continuations, verbatim.
643     // If nothing has changed, then we put this into the output
644     // unmodified. The other is the line with continuations stripped
645     // so we can finish parsing and analyzing the line.
646     //
647     char *verbatim = new char[LINESIZE];
648     char *for_parser = new char[LINESIZE];
649     char *linebuf = new char[LINESIZE];
650     int  at_eof = 0;
651
652     while(!at_eof) {
653         verbatim[0] = 0;
654         for_parser[0] = 0;
655
656         while(1) {
657             if (readline(inf, linebuf) <= 0) {
658                 at_eof = 1;
659                 break;
660             }
661
662             int len = strlen(linebuf);
663             if (len == 0 || linebuf[len - 1] != '\\') {
664                 break;
665             }
666
667             strcat(verbatim, linebuf);
668             strcat(verbatim, "\n");
669
670             linebuf[len - 1] = ' ';
671             strcat(for_parser, linebuf);
672         }
673         if (at_eof) {
674             break;
675         }
676
677         strcat(verbatim, linebuf);
678         strcat(verbatim, "\n");
679         strcat(for_parser, linebuf);
680         outputLine(verbatim, for_parser, outf);
681     }
682
683     delete [] verbatim;
684     delete [] for_parser;
685     delete [] linebuf;
686 }
687
688 void
689 DtMail::MailRc::outputLine(const char * verbatim,
690                            const char * for_parser,
691                            FILE * outf)
692 {
693     char *arglist[MAXARGC];
694     const char * start = for_parser;
695     while(*start && isspace(*start)) {
696         start++;
697     }
698
699     // If we have a comment, write it out and move on.
700     //
701     if (*start == '#') {
702         fwrite(verbatim, 1, strlen(verbatim), outf);
703         return;
704     }
705
706     char word[LINESIZE];
707
708     // Pull off the command word.
709     //
710     char * cp2 = word;
711     while (*start && !strchr(" \t0123456789$^.:/-+*'\"", *start))
712         *cp2++ = *start++;
713     *cp2 = '\0';
714
715     if (equal(word, "")) {
716         fwrite(verbatim, 1, strlen(verbatim), outf);
717         return;
718     }
719
720     struct cmd * com = (struct cmd *)lex(word);
721     if (com == NONE) {
722         // We dont know the command, so just copy the line.
723         //
724         fwrite(verbatim, 1, strlen(verbatim), outf);
725         return;
726     }
727
728     // We will simply rewrite conditionals.
729     if ((com->c_argtype & F)) {
730         fwrite(verbatim, 1, strlen(verbatim), outf);
731         return;
732     }
733
734     int c;
735     switch (com->c_argtype & ~(F)) {
736       case STRLIST:
737         /*
738          * Just the straight string, with
739          * leading blanks removed.
740          */
741         while (strchr(" \t", *start)) {
742             start++;
743         }
744         com->c_write(verbatim, (char **)&start, outf);
745         break;
746         
747       case RAWLIST:
748         /*
749          * A vector of strings, in shell style.
750          */
751         if ((c = getrawlist((char *)start, arglist,
752                             sizeof arglist / sizeof *arglist)) < 0)
753             break;
754         if (c < com->c_minargs) {
755             fprintf(stderr,"%s requires at least %d arg(s)\n",
756                     com->c_name, com->c_minargs);
757             break;
758         }
759         if (c > com->c_maxargs) {
760             fprintf(stderr,"%s takes no more than %d arg(s)\n",
761                     com->c_name, com->c_maxargs);
762             break;
763         }
764         com->c_write(verbatim, arglist, outf);
765         freerawlist(arglist);
766         break;
767         
768       case NOLIST:
769         /*
770          * Just the constant zero, for exiting,
771          * eg.
772          */
773         com->c_write(verbatim, NULL, outf);
774         break;
775         
776       default:
777         fprintf(stderr,"Unknown argtype %#x", com->c_argtype);
778         fprintf(stderr, "NEED TO FIX THIS FOR DTMAIL!\n");
779 /*              mt_done(1); */
780     }
781
782 }
783
784 // init the global hash structure
785 void DtMail::MailRc::init_globals()
786 {
787     glob.g_myname = NULL; 
788     
789     glob.g_ignore = this->hm_alloc(); 
790     glob.g_retain = this->hm_alloc(); 
791     glob.g_alias = this->hm_alloc(); 
792     glob.g_alternates = this->hm_alloc(); 
793     ssp = -1;  
794     
795     nullfield = new char[3];
796     
797     strcpy(nullfield, "");
798     
799     alternate_list = NULL;
800     
801 }
802
803 // load a "user definintion" file
804 int
805 DtMail::MailRc::load(char *name, char* line)
806 {
807     register FILE *in, *oldin;
808     int ret=0;
809     
810     if ((in = fopen(name, "r")) == NULL) {
811         sprintf(line, "can not open file %s\n", name);
812         return(-2);
813     }
814     line[0] = '\0';
815     oldin = input;
816     input = in;
817     sourcing = 0;
818     if ((ret = commands(line)) != 0) {
819         input = oldin;
820         fclose(in);
821         return ret;
822     }
823     input = oldin;
824     fclose(in);
825     return ret;
826 }
827
828 /*
829  * Interpret user commands one by one.  If standard input is not a tty,
830  * print no prompt.
831  */
832
833 int
834 DtMail::MailRc::commands(char* iline)
835 {
836     register int n;
837     char *linebuf = new char[LINESIZE];
838     char *line = new char[LINESIZE];
839     
840     for (;;) {
841         /*
842          * Read a line of commands from the current input
843          * and handle end of file specially.
844          */
845         
846         n = 0;
847         linebuf[0] = '\0';
848         for (;;) {
849             if (readline(input, line) <= 0) {
850                 if (n != 0)
851                     break;
852                 if (sourcing) {
853                     unstack();
854                     goto more;
855                 }
856                 // Conditional if statement with no corresponding endif 
857                 if (cond != CANY)
858                 {
859                     delete [] linebuf;
860                     delete [] line;
861                     return(-3);
862                 }
863                 delete [] linebuf;
864                 delete [] line;
865                 return 0;
866             }
867             if ((n = strlen(line)) == 0)
868                 break;
869             n--;
870             if (line[n] != '\\')
871                 break;
872             line[n++] = ' ';
873             if (n > LINESIZE - strlen(linebuf) - 1)
874                 break;
875             strcat(linebuf, line);
876         }
877         n = LINESIZE - strlen(linebuf) - 1;
878         if (strlen(line) > n) {
879             fprintf(stderr,
880                     "Line plus continuation line too long:\n\t%s\n\nplus\n\t%s\n",
881                     linebuf, line);
882             if (sourcing) {
883                 unstack();
884                 goto more;
885             }
886             // Conditional if statement with no corresponding endif 
887             if (cond != CANY)
888             {
889                 delete [] linebuf;
890                 delete [] line;
891                 return(-3);
892             }
893             delete [] linebuf;
894             delete [] line;
895             return 0;
896         }
897         strncat(linebuf, line, n);
898         if (execute(linebuf)) {
899                 strncpy(iline, linebuf, LINESIZE);
900                 iline[LINESIZE-1] = '\0';
901             delete [] linebuf;
902             delete [] line;
903             return(-1);
904         }
905       more:             ;
906     }
907     delete [] linebuf;
908     delete [] line;
909 }
910
911 /*
912  * Execute a single command.  If the command executed
913  * is "quit," then return non-zero so that the caller
914  * will know to return back to main, if he cares.
915  * Contxt is non-zero if called while composing mail.
916  */
917
918 int DtMail::MailRc::execute(char linebuf[])
919 {
920     char word[LINESIZE];
921     char *arglist[MAXARGC];
922     struct cmd *com;
923     char *cp, *cp2;
924     int c;
925 //      int muvec[2];
926     int e;
927     int newcmd = 0;
928     
929     /*
930      * Strip the white space away from the beginning
931      * of the command, then scan out a word, which
932      * consists of anything except digits and white space.
933      *
934      * Handle ! escapes differently to get the correct
935      * lexical conventions.
936      */
937     
938     cp = linebuf;
939     while (*cp && strchr(" \t", *cp))
940         cp++;
941     /*
942      * Throw away comment lines here.
943      */
944     if (*cp == '#') {
945         if (*++cp != '-') {
946             return(0);
947         }
948         /* the special newcmd header -- "#-" */
949         newcmd = 1;
950         
951         /* strip whitespace again */
952         while (*++cp && strchr(" \t", *cp));
953     }
954     cp2 = word;
955     while (*cp && !strchr(" \t0123456789$^.:/-+*'\"", *cp))
956         *cp2++ = *cp++;
957     *cp2 = '\0';
958     
959     /*
960      * Look up the command; if not found, complain.
961      * We ignore blank lines to eliminate confusion.
962      */
963     
964     if (equal(word, ""))
965         return(0);
966     
967     com = (struct cmd *)lex(word);
968     if (com == NONE) {
969         if (newcmd) {
970             /* this command is OK not to be found; that way
971              * we can extend the .mailrc file with new
972              * commands and not kill old mail and mailtool
973              * programs.
974              */
975             return(0);
976         }
977         fprintf(stderr,"Unknown command: \"%s\"\n", word);
978         if (sourcing)
979             unstack();
980         return(1);
981     }
982     
983     /*
984      * See if we should execute the command -- if a conditional
985      * we always execute it, otherwise, check the state of cond.  
986      */
987     
988     if ((com->c_argtype & F) == 0) {
989         if (cond == CSEND || cond == CTTY )
990           {
991               return(0);
992           }
993         
994 #ifdef undef
995         if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode ||
996             cond == CTTY && !intty || cond == CNOTTY && intty)
997           {
998               return(0);
999           }
1000 #endif 
1001     }
1002     
1003     /*
1004      * Process the arguments to the command, depending
1005      * on the type he expects.  Default to an error.
1006      * If we are sourcing an interactive command, it's
1007      * an error.
1008      */
1009     
1010     e = 1;
1011     switch (com->c_argtype & ~(F)) {
1012       case STRLIST:
1013         /*
1014          * Just the straight string, with
1015          * leading blanks removed.
1016          */
1017         while (strchr(" \t", *cp))
1018             cp++;
1019         e = (*com->c_func)(&cp, this);
1020         break;
1021         
1022       case RAWLIST:
1023         /*
1024          * A vector of strings, in shell style.
1025          */
1026         if ((c = getrawlist(cp, arglist,
1027                             sizeof arglist / sizeof *arglist)) < 0)
1028             break;
1029         if (c < com->c_minargs) {
1030             fprintf(stderr,"%s requires at least %d arg(s)\n",
1031                     com->c_name, com->c_minargs);
1032             break;
1033         }
1034         if (c > com->c_maxargs) {
1035             fprintf(stderr,"%s takes no more than %d arg(s)\n",
1036                     com->c_name, com->c_maxargs);
1037             break;
1038         }
1039         e = (*com->c_func)(arglist, this);
1040         freerawlist(arglist);
1041         break;
1042         
1043       case NOLIST:
1044         /*
1045          * Just the constant zero, for exiting,
1046          * eg.
1047          */
1048         e = (*com->c_func)(0, this);
1049         break;
1050         
1051       default:
1052         fprintf(stderr,"Unknown argtype %#x", com->c_argtype);
1053         fprintf(stderr, "NEED TO FIX THIS FOR DTMAIL!\n");
1054 /*              mt_done(1); */
1055     }
1056     
1057     /*
1058      * Exit the current source file on
1059      * error.
1060      */
1061     
1062     if (e && sourcing)
1063         unstack();
1064     
1065     /* ZZZ:katin: should we return an error here? */
1066     return(e);
1067 }
1068
1069 /*
1070  * Read up a line from the specified input into the line
1071  * buffer.  Return the number of characters read.  Do not
1072  * include the newline at the end.
1073  */
1074
1075 int
1076 DtMail::MailRc::readline(FILE *ibuf, char *linebuf)
1077 {
1078     register char *cp;
1079     register int c;
1080     int seennulls = 0;
1081     
1082     clearerr(ibuf);
1083     c = getc(ibuf);
1084     for (cp = linebuf; c != '\n' && c != EOF; c = getc(ibuf)) {
1085         if (c == 0) {
1086             if (!seennulls) {
1087                 fprintf(stderr,
1088                         "Mail: ignoring NULL characters in mail\n");
1089                 seennulls++;
1090             }
1091             continue;
1092         }
1093         if (cp - linebuf < LINESIZE-2)
1094             *cp++ = c;
1095     }
1096     *cp = 0;
1097     if (c == EOF && cp == linebuf)
1098         return(0);
1099     return(cp - linebuf + 1);
1100 }
1101
1102
1103 /*
1104  * Pop the current input back to the previous level.
1105  * Update the "sourcing" flag as appropriate.
1106  */
1107
1108 void DtMail::MailRc::unstack()
1109 {
1110     if (ssp < 0) {
1111         fprintf(stderr,"\"Source\" stack over-pop.\n");
1112         sourcing = 0;
1113         return;
1114     }
1115     fclose(input);
1116     if (cond != CANY)
1117         fprintf(stderr,"Unmatched \"if\"\n");
1118     cond = sstack[ssp].s_cond;
1119     input = sstack[ssp--].s_file;
1120     if (ssp < 0)
1121         sourcing = 0;
1122 }
1123 /*
1124  * Find the correct command in the command table corresponding
1125  * to the passed command "word"
1126  */
1127
1128 void *DtMail::MailRc::lex(char word[])
1129 {
1130     register struct cmd *cp;
1131
1132     for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
1133         if (DtMail::MailRc::isprefix(word, cp->c_name))
1134             return(cp);
1135     return(NONE);
1136 }
1137
1138 /*
1139  * Determine if as1 is a valid prefix of as2.
1140  * Return true if yep.
1141  */
1142
1143 int DtMail::MailRc::isprefix(char *as1, char *as2)
1144 {
1145     register char *s1, *s2;
1146     
1147     s1 = as1;
1148     s2 = as2;
1149     while (*s1++ == *s2)
1150         if (*s2++ == '\0')
1151             return(1);
1152     return(*--s1 == '\0');
1153 }
1154
1155 /*
1156  * Scan out the list of string arguments, shell style
1157  * for a RAWLIST.
1158  */
1159
1160 int DtMail::MailRc::getrawlist(char line[], char **argv, int argc)
1161 {
1162     register char **ap, *cp, *cp2;
1163     char linebuf[LINESIZE], quotec;
1164     register char **last;
1165     
1166     ap = argv;
1167     cp = line;
1168     last = argv + argc - 1;
1169     while (*cp != '\0') {
1170         while (*cp && strchr(" \t", *cp))
1171             cp++;
1172         cp2 = linebuf;
1173         quotec = 0;
1174         while (*cp != '\0') {
1175             if (quotec) {
1176                 if (*cp == quotec) {
1177                     quotec=0;
1178                     cp++;
1179                 } else
1180                     *cp2++ = *cp++;
1181             } else {
1182                 if (*cp && strchr(" \t", *cp))
1183                     break;
1184                 if (*cp && strchr("'\"", *cp))
1185                     quotec = *cp++;
1186                 else
1187                     *cp2++ = *cp++;
1188             }
1189         }
1190         *cp2 = '\0';
1191         if (cp2 == linebuf)
1192             break;
1193         if (ap >= last) {
1194             fprintf(stderr,
1195                     "Too many elements in the list; excess discarded\n");
1196             break;
1197         }
1198         *ap++ = strdup((char*)linebuf);
1199     }
1200     *ap = NOSTR;
1201     return(ap-argv);
1202 }
1203
1204 void DtMail::MailRc::freerawlist(char **argv)
1205 {
1206     
1207     while(*argv) {
1208         free((char*)*argv);
1209         argv++;
1210     }
1211 }
1212
1213 /*
1214  * Get the value of a variable and return it.
1215  * Look in the environment if its not available locally.
1216  */
1217
1218 char *DtMail::MailRc::mt_value(char name[])
1219 {
1220     register struct var *vp;
1221     register char *cp;
1222 //        extern char *getenv();
1223     
1224     if ((vp = lookup(name, (struct var **)this->variables)) == (struct var *)NULL)
1225         cp = getenv(name);
1226     else
1227         cp = vp->v_value;
1228     return (cp);
1229 }
1230
1231 /*
1232  * Locate a variable and return its variable
1233  * node.
1234  */
1235 struct var *DtMail::MailRc::lookup(char *name, struct var **hasharray)
1236 {
1237     register struct var *vp;
1238     register int h;
1239     
1240     h = hash(name);
1241     for (vp = hasharray[h]; vp != (struct var *)NULL; vp = vp->v_link)
1242         if (strcmp(vp->v_name, name) == 0)
1243             return (vp);
1244     return ((struct var *)NULL);
1245 }
1246
1247
1248 /*
1249  * Put add users to a group.
1250  */
1251
1252 int DtMail::MailRc::group(char **argv, DtMail::MailRc *)
1253 {
1254     int size;
1255     char *buf;
1256     char *s;
1257     char **ap;
1258     
1259     /*
1260      * Insert names from the command list into the group.
1261      * Who cares if there are duplicates?  They get tossed
1262      * later anyway.
1263      */
1264     
1265     /* compute the size of the buffer */
1266     size = 0;
1267     for (ap = argv+1; *ap != NOSTR; ap++) {
1268         size += strlen(*ap) +1;
1269     }
1270     buf = (char *)malloc(size);
1271     s = buf;
1272     for (ap = argv+1; *ap != NOSTR; ap++) {
1273         strcpy(s, *ap);
1274         s += strlen(s);
1275         *s++ = ' ';
1276     }
1277     *--s = '\0';
1278     
1279     MailRc::add_alias((char *)argv[0], (char *)buf);
1280     free(buf);
1281     return(0);
1282 }
1283
1284 void
1285 DtMail::MailRc::wgroup(const char * verbatim, char ** argv, FILE * outf)
1286 {
1287     int size = 0;
1288     char * buf;
1289     char * s;
1290     char ** ap;
1291
1292     for (ap = argv+1; *ap != NOSTR; ap++) {
1293         size += strlen(*ap) +1;
1294     }
1295     buf = (char *)malloc(size);
1296     s = buf;
1297     for (ap = argv+1; *ap != NOSTR; ap++) {
1298         strcpy(s, *ap);
1299         s += strlen(s);
1300         *s++ = ' ';
1301     }
1302     *--s = '\0';
1303
1304     char * cur = (char *)hm_test((struct hash **)glob.g_alias, argv[0]);
1305     if (!cur || (!clearAliases && hm_ismarked((struct hash **)glob.g_alias, 
1306                         argv[0]))) {
1307         // Its been removed or written. Dont write it to the file.
1308         free(buf);
1309         return;
1310     }
1311
1312     if (strcmp(cur, buf) == 0) {
1313         // It hasnt changed. Write the original to preserve spacing.
1314         //
1315         fwrite(verbatim, 1, strlen(verbatim), outf);
1316     }
1317     else {
1318         // It has changed. Create a new alias.
1319         //
1320         fwrite("alias ", 1, 6, outf);
1321         fwrite(argv[0], 1, strlen(argv[0]), outf);
1322         fwrite(" ", 1, 1, outf);
1323         fwrite(cur, 1, strlen(cur), outf);
1324         fwrite("\n", 1, 1, outf);
1325     }
1326
1327     hm_mark((struct hash **)glob.g_alias, argv[0]);
1328 }
1329
1330 void
1331 DtMail::MailRc::ngroup(char * key, void * data, void * client_data)
1332 {
1333     char * value = (char *)data;
1334     FILE * outf = (FILE *)client_data;
1335
1336     fwrite("alias ", 1, 6, outf);
1337     fwrite(key, 1, strlen(key), outf);
1338     fwrite(" ", 1, 1, outf);
1339     fwrite(value, 1, strlen(value), outf);
1340     fwrite("\n", 1, 1, outf);
1341 }
1342
1343 void
1344 DtMail::MailRc::nalias(char * key, void * data, void * client_data)
1345 {
1346     DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1347     char *new_alias = NULL;
1348     char *white_space = NULL;
1349     int m_size = 0;
1350     int  i, num_spaces = 0;
1351     int key_len = strlen(key);
1352     // figure out whitespace for formatting
1353     // assume 13 for normal sized alias name
1354
1355     if(key_len < 13)  // need to add spaces
1356       {
1357         num_spaces = 13 - key_len;
1358
1359         white_space = (char *)malloc(num_spaces + 1);
1360         white_space[0] = '\0';
1361
1362         for(i = 0; i < num_spaces; i++)
1363           white_space[i] = ' ';
1364
1365         white_space[num_spaces] = '\0';
1366
1367 //        strcat(white_space, " ");
1368
1369         /* make an alias string */
1370         m_size = key_len + strlen((char *)white_space)
1371                  + strlen((char *)data) + strlen(" = ") + 1;
1372         new_alias = (char *)malloc(m_size);
1373     
1374         sprintf(new_alias, "%s%s = %s",key, white_space, (char *) data);
1375
1376       }
1377     else
1378       {
1379                 /* make an alias string */
1380         m_size = key_len + strlen((char *)data) + strlen(" = ") + 1;
1381         new_alias = (char *)malloc(m_size);
1382     
1383         sprintf(new_alias, "%s = %s",key, (char *) data);
1384
1385       }
1386     
1387     value_list->append(new_alias);
1388
1389 }
1390
1391 void
1392 DtMail::MailRc::nignorelist(char * key, void * data, void * client_data)
1393 {
1394     data = data;
1395     DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1396     char *new_ignore = NULL;
1397
1398     new_ignore = (char *)malloc(strlen((char *)key) + 2);
1399
1400     sprintf(new_ignore, "%s", key);
1401
1402     value_list->append(new_ignore);
1403
1404 }
1405
1406
1407 /*
1408  * Set or display a variable value.  Syntax is similar to that
1409  * of csh.
1410  */
1411
1412 int DtMail::MailRc::set(char **arglist, DtMail::MailRc *)
1413 {
1414     register char *cp, *cp2;
1415     char varbuf[LINESIZE], **ap;
1416     int errs;
1417     
1418     errs = 0;
1419     for (ap = arglist; *ap != NOSTR; ap++) {
1420         cp = *ap;
1421         cp2 = varbuf;
1422         while (*cp != '=' && *cp != '\0')
1423             *cp2++ = *cp++;
1424         *cp2 = '\0';
1425         if (*cp == '\0')
1426             cp = "";
1427         else
1428             cp++;
1429         if (equal(varbuf, "")) {
1430             fprintf(stderr,"Non-null variable name required\n");
1431             errs++;
1432             continue;
1433         }
1434         MailRc::mt_assign(varbuf, cp);
1435     }
1436     return(errs);
1437 }
1438
1439 void
1440 DtMail::MailRc::wset(const char * verbatim,
1441                      char ** arglist,
1442                      FILE * outf)
1443 {
1444     char varbuf[LINESIZE];
1445     char *cp, *cp2, **ap;
1446
1447     for (ap = arglist; *ap != NOSTR; ap++) {
1448         cp = *ap;
1449         cp2 = varbuf;
1450         while (*cp != '=' && *cp != '\0')
1451             *cp2++ = *cp++;
1452         *cp2 = '\0';
1453         if (*cp == '\0')
1454             cp = "";
1455         else
1456             cp++;
1457         if (equal(varbuf, "")) {
1458             continue;
1459         }
1460
1461         // Lookup the current value, and see if we should rewrite this
1462         // variable.
1463         //
1464         struct var * vp = lookup(varbuf, (struct var **)variables);
1465         if (vp == NULL || vp->v_written) {
1466             // If the original input line was set novar then just write
1467             // it out again. We can not easily track duplicates here.
1468             //
1469             if (varbuf[0] == 'n' && varbuf[1] == 'o') {
1470                 fwrite(verbatim, 1, strlen(verbatim), outf);
1471             }
1472
1473             // This variable has been unassigned or previously written.
1474             // Remove it from the file, by not writing it.
1475             //
1476             continue;
1477         }
1478
1479         // Compare the values. If equal, then write the line verbatim to
1480         // preserve the users original spacing and quoting.
1481         //
1482         if (strcmp(cp, vp->v_value) == 0) {
1483             fwrite(verbatim, 1, strlen(verbatim), outf);
1484             vp->v_written = 1;
1485         }
1486         else {
1487             // Write the variable, with a new value.
1488             //
1489             fwrite("set ", 1, 4, outf);
1490             fwrite(varbuf, 1, strlen(varbuf), outf);
1491             if (strlen(vp->v_value) > 0) {
1492                 fwrite("='", 1, 2, outf);
1493                 fwrite(vp->v_value, 1, strlen(vp->v_value), outf);
1494                 fwrite("'", 1, 1, outf);
1495             }
1496             fwrite("\n", 1, 1, outf);
1497             vp->v_written = 1;
1498         }
1499     }
1500 }
1501
1502 /*
1503  * Unset a bunch of variable values.
1504  */
1505
1506 int DtMail::MailRc::unset(char **arglist, DtMail::MailRc *)
1507 {
1508         register char **ap;
1509
1510         for (ap = arglist; *ap != NOSTR; ap++)
1511                 (void) MailRc::mt_deassign(*ap);
1512         return(0);
1513 }
1514
1515 void
1516 DtMail::MailRc::wunset(const char * verbatim,
1517                      char ** arglist,
1518                      FILE * outf)
1519 {
1520   arglist = arglist;
1521   if(verbatim != NULL && outf != NULL)
1522     fwrite(verbatim, 1, strlen(verbatim), outf);
1523 }
1524
1525 /*
1526  * Hash the passed string and return an index into
1527  * the variable or group hash table.
1528  */
1529 int DtMail::MailRc::hash(char *name)
1530 {
1531     register unsigned h;
1532     register char *cp;
1533     
1534     for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
1535         ;
1536     return (h % HSHSIZE);
1537 }
1538
1539 /* manage the alias list */
1540 void DtMail::MailRc::add_alias(char *name, char *value)
1541 {
1542     char *old;
1543     char *new_ptr;
1544     
1545     /* aliases to the same name get appended to the list */
1546     old = (char*)MailRc::hm_test((struct hash **)glob.g_alias, name);
1547     if (old) {
1548         
1549         int size;
1550         
1551         size = strlen(value) + strlen(old) + 2;
1552         new_ptr = (char *)malloc(size);
1553         sprintf(new_ptr, "%s %s", value, old);
1554         
1555         /* delete any old bindings for the name */
1556         MailRc::hm_delete((struct hash**)glob.g_alias, name);
1557         
1558         /* add the new binding */
1559         MailRc::hm_add((struct hash**)glob.g_alias, name, new_ptr, size);
1560         
1561         /* free up the temporary space */
1562         free(new_ptr);
1563     } else {
1564         /* add the new binding */
1565         MailRc::hm_add((struct hash**)glob.g_alias, name, value, strlen(value) +1);
1566     }
1567 }
1568
1569 /*
1570  * Assign a value to a mail variable.
1571  */
1572 void DtMail::MailRc::mt_assign(char *name,char * val)
1573 {
1574     
1575     if (name[0]=='-')
1576         (void) mt_deassign(name+1);
1577     else if (name[0]=='n' && name[1]=='o')
1578         (void) mt_deassign(name+2);
1579     else if ((val[0] == 'n' || val[0] == 'N') &&
1580              (val[1] == 'o' || val[1] == 'O') && val[2] == '\0') {
1581         (void) mt_deassign(name);
1582     } else mt_puthash(name, vcopy(val), MailRc::variables);
1583 }
1584
1585 int DtMail::MailRc::mt_deassign(char *s)
1586 {
1587     register struct var *vp, *vp2;
1588     register int h;
1589     
1590     if ((vp2 = lookup(s, MailRc::variables)) == (struct var *)NULL) {
1591         return (1);
1592     }
1593     h = hash(s);
1594     if (vp2 == MailRc::variables[h]) {
1595         MailRc::variables[h] = MailRc::variables[h]->v_link;
1596         vfree(vp2->v_name);
1597         vfree(vp2->v_value);
1598         free((char *)vp2);
1599         return (0);
1600     }
1601     for (vp = MailRc::variables[h]; vp->v_link != vp2; vp = vp->v_link)
1602         ;
1603     vp->v_link = vp2->v_link;
1604     vfree(vp2->v_name);
1605     vfree(vp2->v_value);
1606     free((char *)vp2);
1607     return (0);
1608 }
1609
1610 /*
1611  * associate val with name in hasharray
1612  */
1613 void DtMail::MailRc::mt_puthash(char *name, char *val, struct var **hasharray)
1614 {
1615     register struct var *vp;
1616     register int h;
1617     
1618     vp = lookup(name, hasharray);
1619     if (vp == (struct var *)NULL) {
1620         h = hash(name);
1621         vp = (struct var *) (calloc(sizeof *vp, 1));
1622         vp->v_name = vcopy(name);
1623         vp->v_link = hasharray[h];
1624         vp->v_written = 0;
1625         hasharray[h] = vp;
1626     } else
1627         vfree(vp->v_value);
1628     vp->v_value = val;
1629 }
1630
1631 void
1632 DtMail::MailRc::mt_scan(FILE * outf)
1633 {
1634     for (int slot = 0; slot < HSHSIZE; slot++) {
1635         for (var * vp = MailRc::variables[slot]; vp != (struct var *)NULL; vp = vp->v_link) {
1636             if (!vp->v_written) {
1637                 fwrite("set ", 1, 4, outf);
1638                 fwrite(vp->v_name, 1, strlen(vp->v_name), outf);
1639                 if (strlen(vp->v_value)) {
1640                     fwrite("='", 1, 2, outf);
1641                     fwrite(vp->v_value, 1, strlen(vp->v_value), outf);
1642                     fwrite("'", 1, 1, outf);
1643                 }
1644                 fwrite("\n", 1, 1, outf);
1645             }
1646             vp->v_written = 0;
1647         }
1648     }
1649 }
1650
1651 /*
1652  * Copy a variable value into permanent space.
1653  * Do not bother to alloc space for "".
1654  */
1655 char *DtMail::MailRc::vcopy(char *str)
1656 {
1657     
1658     if (strcmp(str, "") == 0)
1659         return ("");
1660     return (strdup(str));
1661 }
1662
1663 /*
1664  * Free up a variable string.  We do not bother to allocate
1665  * strings whose value is "" since they are expected to be frequent.
1666  * Thus, we cannot free same!
1667  */
1668 void DtMail::MailRc::vfree(char *cp)
1669 {
1670     
1671     if (strcmp(cp, "") != 0)
1672         free(cp);
1673 }
1674
1675 void *DtMail::MailRc::hm_alloc(void)
1676 {
1677     struct hash **table;
1678     
1679     table = (struct hash**)malloc(sizeof (struct hash) * HASHSIZE);
1680     memset(table, '\0', sizeof (struct hash) * HASHSIZE);
1681     return (table);
1682 }
1683 void DtMail::MailRc::hm_add(struct hash **table, 
1684                             char *key, 
1685                             void *value, 
1686                             int size)
1687 {
1688     int index;
1689     register struct hash *h;
1690     
1691     if (!table)
1692         return;
1693     
1694     index = hash_index(key);
1695     h = (struct hash *)malloc(sizeof (struct hash));
1696     h->h_next = table[index];
1697     h->h_written = 0;
1698     table[index] = h;
1699     h->h_key = strdup(key);
1700     if (size && value != NULL) {
1701         h->h_value = malloc(size);
1702         memcpy(h->h_value, value, size);
1703     } else {
1704         h->h_value = nullfield;
1705     }
1706 }
1707
1708
1709 void DtMail::MailRc::hm_delete(struct hash **table, char *key)
1710 {
1711     register int index;
1712     register struct hash *h;
1713     register struct hash *old;
1714     
1715     if (!table)
1716         return;
1717     
1718     index = hash_index(key);
1719     old = NULL;
1720     h = table[index];
1721     while (h) {
1722         if (strcasecmp(h->h_key, key) == 0) {
1723             /* found the match */
1724             if (old == NULL)
1725                 table[index] = h->h_next;
1726             else
1727                 old->h_next = h->h_next;
1728             
1729             free_hash(h);
1730             break;
1731         }
1732         
1733         old = h;
1734         h = h->h_next;
1735     }
1736 }
1737
1738
1739 void *DtMail::MailRc::hm_test(struct hash **table, char *key)
1740 {
1741     register struct hash *h;
1742     
1743     if (!table)
1744         return (NULL);
1745     
1746     h = table[hash_index(key)];
1747     
1748     while (h) {
1749         if (strcasecmp(h->h_key, key) == 0) {
1750             /* found a match */
1751             return (h->h_value);
1752         }
1753         
1754         h = h->h_next;
1755     }
1756     
1757     return (NULL);
1758 }
1759
1760 void DtMail::MailRc::hm_mark(struct hash **table, char *key)
1761 {
1762     register struct hash *h;
1763     
1764     if (!table)
1765         return;
1766     
1767     h = table[hash_index(key)];
1768     
1769     while (h) {
1770         if (strcasecmp(h->h_key, key) == 0) {
1771             h->h_written = 1;
1772             return;
1773         }
1774         
1775         h = h->h_next;
1776     }
1777 }
1778
1779 int DtMail::MailRc::hm_ismarked(struct hash **table, char *key)
1780 {
1781     register struct hash *h;
1782     
1783     if (!table) return 0;
1784     h = table[hash_index(key)];
1785     while (h)
1786     {
1787         if (strcasecmp(h->h_key, key) == 0) return(h->h_written);
1788         h = h->h_next;
1789     }
1790
1791     return 0;
1792 }
1793
1794 void
1795 DtMail::MailRc::hm_scan(struct hash **table, hm_callback callback, void * client_data)
1796 {
1797     for (int slot = 0; slot < HASHSIZE; slot++) {
1798         for (struct hash * h = table[slot]; h; h = h->h_next) {
1799             if (!h->h_written) {
1800                 callback(h->h_key, h->h_value, client_data);
1801             }
1802             h->h_written = 0;
1803         }
1804     }
1805 }
1806
1807 int DtMail::MailRc::hash_index(char *key)
1808 {
1809     register unsigned h;
1810     register char *s;
1811     register int c;
1812     
1813     s = key;
1814     h = 0;
1815     while (*s) {
1816         c = *s++;
1817         if (isupper(c)) {
1818             c = tolower(c);
1819         }
1820         h = (h << 2) + c;
1821     }
1822     
1823     return (h % HASHSIZE);
1824 }
1825
1826 void DtMail::MailRc::free_hash(struct hash *h)
1827 {
1828     free(h->h_key);
1829     if (h->h_value != nullfield) {
1830         free(h->h_value);
1831     }
1832     free(h);
1833 }
1834
1835 /*
1836  * Add the given header fields to the ignored list.
1837  * If no arguments, print the current list of ignored fields.
1838  */
1839 int DtMail::MailRc::igfield(char *list[], DtMail::MailRc *)
1840 {
1841     char **ap;
1842     
1843     for (ap = list; *ap != 0; ap++) {
1844 //                DP(("igfield: adding %s\n", *ap));
1845         MailRc::add_ignore(*ap);
1846     }
1847     return(0);
1848 }
1849
1850 void
1851 DtMail::MailRc::wigfield(const char * verbatim, char ** list, FILE * outf)
1852 {
1853     char ** ap;
1854     Boolean ignore_written = FALSE;
1855
1856     // We need to see that every name in this line still exists. If not,
1857     // then we will make a second pass, rewriting the line with only the
1858     // valid ignores.
1859     //
1860     int good = 1;
1861     for (ap = list; *ap && good; ap++) {
1862         if (!hm_test((struct hash **)glob.g_ignore, *ap)) {
1863             good = 0;
1864             break;
1865         }
1866     }
1867
1868     if (good) {
1869         fwrite(verbatim, 1, strlen(verbatim), outf);
1870         for (ap = list; *ap; ap++) {
1871             hm_mark((struct hash **)glob.g_ignore, *ap);
1872         }
1873         return;
1874     }
1875
1876     // Create a new ignore line, leaving out the ignores that have
1877     // been removed. Also, dont write any ignores that have been
1878     // previously written.
1879     //
1880     for (ap = list; *ap; ap++) {
1881         if (hm_test((struct hash **)glob.g_ignore, *ap) &&
1882         !hm_ismarked((struct hash **)glob.g_ignore, *ap)) {
1883                 // Only write 'ignore' if there is something in the list
1884                 if (!ignore_written) {
1885                         fwrite("ignore ", 1, 7, outf);
1886                         ignore_written = TRUE;
1887                 }
1888                 fwrite(*ap, 1, strlen(*ap), outf);
1889                 fwrite(" ", 1, 1, outf);
1890                 hm_mark((struct hash **)glob.g_ignore, *ap);
1891         }
1892      }
1893      if (ignore_written)
1894         fwrite("\n", 1, 1, outf);
1895 }
1896
1897 void
1898 DtMail::MailRc::nigfield(char * key, void *, void * client_data)
1899 {
1900     DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1901
1902     value_list->append(key);
1903 }
1904
1905 /* manage the retain/ignore list */
1906 void DtMail::MailRc::add_ignore(char *name)
1907 {
1908     if(! MailRc::hm_test((struct hash **)glob.g_ignore, name)) {
1909         /* name is not already there... */
1910         MailRc::hm_add((struct hash **)glob.g_ignore, name, NULL, 0);
1911     }
1912 }
1913
1914
1915 /*
1916  * Set the list of alternate names.
1917  */
1918 int DtMail::MailRc::alternates(char **namelist, DtMail::MailRc *)
1919 {
1920     while (*namelist != NULL)
1921         MailRc::add_alternates(*namelist++);
1922     return(0);
1923 }
1924
1925 void
1926 DtMail::MailRc::walternates(const char * verbatim, char ** list, FILE * outf)
1927 {
1928     // This is like ignores. We need to make sure all of the alternates
1929     // on this command are still in the database.
1930     //
1931     char ** ap;
1932     int good = 1;
1933     for (ap = list; *ap && good; ap++) {
1934         if (!hm_test((struct hash **)glob.g_alternates, *ap)) {
1935             good = 0;
1936             break;
1937         }
1938     }
1939
1940     if (good) {
1941         fwrite(verbatim, 1, strlen(verbatim), outf);
1942         for (ap = list; *ap; ap++) {
1943             hm_mark((struct hash **)glob.g_alternates, *ap);
1944         }
1945         return;
1946     }
1947
1948     // Write out the alternates that still exist and have not been
1949     // previously written.
1950     //
1951     Boolean written = FALSE;
1952     for (ap = list; *ap; ap++) {
1953         if (hm_test((struct hash **)glob.g_alternates, *ap) &&
1954             !hm_ismarked((struct hash **)glob.g_alternates, *ap)) {
1955             if (!written) {
1956                 fwrite("alternates ", 1, 11, outf);
1957                 written=TRUE;
1958             }
1959             fwrite(*ap, 1, strlen(*ap), outf);
1960             fwrite(" ", 1, 1, outf);
1961             hm_mark((struct hash **)glob.g_alternates, *ap);
1962         }
1963     }
1964 }
1965
1966 void
1967 DtMail::MailRc::nalternates(char * key, void *, void * client_data)
1968 {
1969     DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1970
1971     value_list->append(key);
1972 }
1973
1974 /* manage the alternate name list */
1975 void DtMail::MailRc::add_alternates(char *name)
1976 {
1977     if(! MailRc::hm_test((struct hash**)glob.g_alternates, name)) {
1978         /* name is not already there... */
1979         MailRc::hm_add((struct hash **)glob.g_alternates, name, NULL, 0);
1980     }
1981 }
1982
1983 /*
1984  * Determine the current folder directory name.
1985  */
1986 int
1987 DtMail::MailRc::getfolderdir(char *name, size_t buffsize)
1988 {
1989         char *folder;
1990
1991         if ((folder = mt_value("folder")) == NOSTR)
1992                 return(-1);
1993         if (*folder == '/')
1994                 snprintf(name, buffsize, "%s", folder);
1995         else
1996                 snprintf(name, buffsize, "%s/%s", mt_value("HOME"), folder);
1997         return(0);
1998 }
1999
2000 /*
2001  * Take a file name, possibly with shell meta characters
2002  * in it and expand it by using "sh -c echo filename"
2003  * Return the file name as a dynamic string.
2004  */
2005
2006 char *
2007 DtMail::MailRc::expand(char *name)
2008 {
2009         char *xname = new char[LINESIZE];
2010         char *cmdbuf = new char[LINESIZE];
2011         char *str;
2012         register int pid, l;
2013         register char *cp, *Shell;
2014         int s, pivec[2];
2015         struct stat sbuf;
2016         char *retchr = NULL;
2017
2018         if (name[0] == '+' && getfolderdir(cmdbuf, LINESIZE) >= 0) {
2019                 sprintf(xname, "%s/%s", cmdbuf, name + 1);
2020                 str = expand(xname);
2021                 delete [] xname;
2022                 delete [] cmdbuf;
2023                 return str;
2024         }
2025         if (!strpbrk(name, "~{[*?$`'\"\\")) {
2026                 return(strdup(name));
2027         }
2028         if (pipe(pivec) < 0) {
2029                 perror("pipe");
2030                 delete [] xname;
2031                 delete [] cmdbuf;
2032                 return(strdup(name));
2033         }
2034         sprintf(cmdbuf, "echo %s", name);
2035         if ((pid = DTMAIL_FORK()) == 0) {
2036                 Shell = mt_value("SHELL");
2037                 if (Shell == NOSTR || *Shell=='\0')
2038                         Shell = "/bin/sh";
2039                 close(pivec[0]);
2040                 close(1);
2041                 dup(pivec[1]);
2042                 close(pivec[1]);
2043                 close(2);
2044                 execlp(Shell, Shell, "-c", cmdbuf, (char *)0);
2045                 _exit(1);
2046         }
2047         if (pid == -1) {
2048                 perror("fork");
2049                 close(pivec[0]);
2050                 close(pivec[1]);
2051                 delete [] xname;
2052                 delete [] cmdbuf;
2053                 return(NOSTR);
2054         }
2055         close(pivec[1]);
2056         l = read(pivec[0], xname, LINESIZE);
2057         close(pivec[0]);
2058         while (wait(&s) != pid);
2059                 ;
2060         s &= 0377;
2061         if (s != 0 && s != SIGPIPE) {
2062                 fprintf(stderr, "Echo failed\n");
2063                 goto err;
2064         }
2065         if (l < 0) {
2066                 perror("read");
2067                 goto err;
2068         }
2069         if (l == 0) {
2070                 fprintf(stderr, "%s: No match\n", name);
2071                 goto err;
2072         }
2073         if (l == LINESIZE) {
2074                 fprintf(stderr, "Buffer overflow expanding %s\n", name);
2075                 goto err;
2076         }
2077         xname[l] = 0;
2078         for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
2079                 ;
2080         *++cp = '\0';
2081         if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
2082                 fprintf(stderr, "%s: Ambiguous\n", name);
2083                 goto err;
2084         }
2085
2086         retchr = strdup(xname);
2087         delete [] xname;
2088         delete [] cmdbuf;
2089         return(retchr);
2090
2091 err:
2092         fflush(stderr);
2093         delete [] xname;
2094         delete [] cmdbuf;
2095         return(NOSTR);
2096 }
2097
2098 int
2099 DtMail::MailRc::source(char **arglist, DtMail::MailRc *self)
2100 {
2101   char *fname = arglist[0];
2102   FILE *fi;
2103   char *cp;
2104
2105   /* if any of the three if test failed, return 0,
2106    * since we have not updated the input and stack pointer yet
2107    */
2108   if ((cp = self->expand(fname)) == NOSTR)
2109     return(0);
2110
2111   if ((fi = fopen(cp, "r")) == NULL) {
2112     free(cp);
2113     return(0);
2114   }
2115   free(cp);
2116
2117   if (self->ssp >= NOFILE - 2) {
2118     fclose(fi);
2119     return(0);
2120   }
2121   self->sstack[++(self->ssp)].s_file = self->input;
2122   self->sstack[self->ssp].s_cond = self->cond;
2123   self->cond = CANY;
2124   self->input = fi;
2125   self->sourcing++;
2126
2127   return(0);
2128 }
2129
2130 void
2131 DtMail::MailRc::wsource(const char * verbatim, char ** arglist, FILE * outf)
2132 {
2133   arglist = arglist;
2134   if(verbatim != NULL && outf != NULL)
2135     fwrite(verbatim, 1, strlen(verbatim), outf);
2136 }
2137
2138 int
2139 DtMail::MailRc::ifcmd(char **arglist, DtMail::MailRc *self)
2140 {
2141         register char *cp;
2142
2143         if (self->cond != CANY) {
2144                 fprintf(stderr,"Illegal nested \"if\"\n");
2145                 return(1);
2146         }
2147         self->cond = CANY;
2148         cp = arglist[0];
2149         switch (*cp) {
2150         case 'r': case 'R':
2151                 self->cond = CRCV;
2152                 break;
2153
2154         case 's': case 'S':
2155                 self->cond = CSEND;
2156                 break;
2157
2158         case 't': case 'T':
2159                 self->cond = CTTY;
2160                 break;
2161
2162         default:
2163                 fprintf(stderr,"Unrecognized if-keyword: \"%s\"\n",
2164                         cp);
2165                 return(1);
2166         }
2167         return(0);
2168 }
2169
2170 void
2171 DtMail::MailRc::wifcmd(const char * verbatim, char ** arglist, FILE * outf)
2172 {
2173   arglist = arglist;
2174   if(verbatim != NULL && outf != NULL)
2175     fwrite(verbatim, 1, strlen(verbatim), outf);
2176 }
2177
2178
2179 int
2180 DtMail::MailRc::elsecmd(char **, DtMail::MailRc *self)
2181 {
2182
2183         switch (self->cond) {
2184         case CANY:
2185                 fprintf(stderr, "\"Else\" without matching \"if\"\n");
2186                 return(1);
2187
2188         case CSEND:
2189                 self->cond = CRCV;
2190                 break;
2191
2192         case CRCV:
2193                 self->cond = CSEND;
2194                 break;
2195
2196         case CTTY:
2197                 self->cond = CNOTTY;
2198                 break;
2199
2200         case CNOTTY:
2201                 self->cond = CTTY;
2202                 break;
2203
2204         default:
2205                 fprintf(stderr,"invalid condition encountered\n");
2206                 self->cond = CANY;
2207                 break;
2208         }
2209         return(0);
2210 }
2211
2212 void
2213 DtMail::MailRc::welsecmd(const char * verbatim, char ** arglist, FILE * outf)
2214 {
2215   arglist = arglist;
2216   if(verbatim != NULL && outf != NULL)
2217     fwrite(verbatim, 1, strlen(verbatim), outf);
2218 }
2219
2220
2221 int
2222 DtMail::MailRc::endifcmd(char **, DtMail::MailRc *self)
2223 {
2224
2225         if (self->cond == CANY) {
2226                 fprintf(stderr,"\"Endif\" without matching \"if\"\n");
2227                 return(1);
2228         }
2229         self->cond = CANY;
2230         return(0);
2231 }
2232
2233 void
2234 DtMail::MailRc::wendifcmd(const char * verbatim, char ** arglist, FILE * outf)
2235 {
2236   arglist = arglist;
2237   if(verbatim != NULL && outf != NULL)
2238     fwrite(verbatim, 1, strlen(verbatim), outf);
2239 }
2240
2241 int
2242 DtMail::MailRc::clearaliases(char **, DtMail::MailRc *)
2243 {
2244         DtVirtArray<char *>   *value_list = NULL;
2245         DtMailEnv error;
2246         
2247         value_list = new DtVirtArray<char *>(10);
2248
2249         hm_scan((struct hash **)glob.g_alias, nalias, value_list);
2250
2251         while (value_list->length()) {
2252                 char *buf, *val = (*value_list)[0];
2253                 if ((buf = strchr(val, ' ')) != NULL)
2254                         *buf = '\0';
2255                 if (hm_test((struct hash **)glob.g_alias, val))
2256                         hm_delete((struct hash **)glob.g_alias, val);
2257                 value_list->remove(0);
2258         }
2259         delete value_list;
2260         clearAliases = 1;
2261         return 0;
2262 }
2263 void
2264 DtMail::MailRc::wclearaliases(const char *, char **, FILE *)
2265 {
2266 }