Add GNU LGPL headers to all .c .C and .h files
[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 librararies 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(__uxp__)
158 #define SYSTEM_MAILRC   "/etc/mail/mailx.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 DtMail::MailRc::readline(FILE *ibuf, char *linebuf)
1076 {
1077     register char *cp;
1078     register int c;
1079     int seennulls = 0;
1080     
1081     clearerr(ibuf);
1082     c = getc(ibuf);
1083     for (cp = linebuf; c != '\n' && c != EOF; c = getc(ibuf)) {
1084         if (c == 0) {
1085             if (!seennulls) {
1086                 fprintf(stderr,
1087                         "Mail: ignoring NULL characters in mail\n");
1088                 seennulls++;
1089             }
1090             continue;
1091         }
1092         if (cp - linebuf < LINESIZE-2)
1093             *cp++ = c;
1094     }
1095     *cp = 0;
1096     if (c == EOF && cp == linebuf)
1097         return(0);
1098     return(cp - linebuf + 1);
1099 }
1100
1101
1102 /*
1103  * Pop the current input back to the previous level.
1104  * Update the "sourcing" flag as appropriate.
1105  */
1106
1107 void DtMail::MailRc::unstack()
1108 {
1109     if (ssp < 0) {
1110         fprintf(stderr,"\"Source\" stack over-pop.\n");
1111         sourcing = 0;
1112         return;
1113     }
1114     fclose(input);
1115     if (cond != CANY)
1116         fprintf(stderr,"Unmatched \"if\"\n");
1117     cond = sstack[ssp].s_cond;
1118     input = sstack[ssp--].s_file;
1119     if (ssp < 0)
1120         sourcing = 0;
1121 }
1122 /*
1123  * Find the correct command in the command table corresponding
1124  * to the passed command "word"
1125  */
1126
1127 void *DtMail::MailRc::lex(char word[])
1128 {
1129     register struct cmd *cp;
1130
1131     for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
1132         if (DtMail::MailRc::isprefix(word, cp->c_name))
1133             return(cp);
1134     return(NONE);
1135 }
1136
1137 /*
1138  * Determine if as1 is a valid prefix of as2.
1139  * Return true if yep.
1140  */
1141
1142 int DtMail::MailRc::isprefix(char *as1, char *as2)
1143 {
1144     register char *s1, *s2;
1145     
1146     s1 = as1;
1147     s2 = as2;
1148     while (*s1++ == *s2)
1149         if (*s2++ == '\0')
1150             return(1);
1151     return(*--s1 == '\0');
1152 }
1153
1154 /*
1155  * Scan out the list of string arguments, shell style
1156  * for a RAWLIST.
1157  */
1158
1159 int DtMail::MailRc::getrawlist(char line[], char **argv, int argc)
1160 {
1161     register char **ap, *cp, *cp2;
1162     char linebuf[LINESIZE], quotec;
1163     register char **last;
1164     
1165     ap = argv;
1166     cp = line;
1167     last = argv + argc - 1;
1168     while (*cp != '\0') {
1169         while (*cp && strchr(" \t", *cp))
1170             cp++;
1171         cp2 = linebuf;
1172         quotec = 0;
1173         while (*cp != '\0') {
1174             if (quotec) {
1175                 if (*cp == quotec) {
1176                     quotec=0;
1177                     cp++;
1178                 } else
1179                     *cp2++ = *cp++;
1180             } else {
1181                 if (*cp && strchr(" \t", *cp))
1182                     break;
1183                 if (*cp && strchr("'\"", *cp))
1184                     quotec = *cp++;
1185                 else
1186                     *cp2++ = *cp++;
1187             }
1188         }
1189         *cp2 = '\0';
1190         if (cp2 == linebuf)
1191             break;
1192         if (ap >= last) {
1193             fprintf(stderr,
1194                     "Too many elements in the list; excess discarded\n");
1195             break;
1196         }
1197         *ap++ = strdup((char*)linebuf);
1198     }
1199     *ap = NOSTR;
1200     return(ap-argv);
1201 }
1202
1203 void DtMail::MailRc::freerawlist(char **argv)
1204 {
1205     
1206     while(*argv) {
1207         free((char*)*argv);
1208         argv++;
1209     }
1210 }
1211
1212 /*
1213  * Get the value of a variable and return it.
1214  * Look in the environment if its not available locally.
1215  */
1216
1217 char *DtMail::MailRc::mt_value(char name[])
1218 {
1219     register struct var *vp;
1220     register char *cp;
1221 //        extern char *getenv();
1222     
1223     if ((vp = lookup(name, (struct var **)this->variables)) == (struct var *)NULL)
1224         cp = getenv(name);
1225     else
1226         cp = vp->v_value;
1227     return (cp);
1228 }
1229
1230 /*
1231  * Locate a variable and return its variable
1232  * node.
1233  */
1234 struct var *DtMail::MailRc::lookup(char *name, struct var **hasharray)
1235 {
1236     register struct var *vp;
1237     register int h;
1238     
1239     h = hash(name);
1240     for (vp = hasharray[h]; vp != (struct var *)NULL; vp = vp->v_link)
1241         if (strcmp(vp->v_name, name) == 0)
1242             return (vp);
1243     return ((struct var *)NULL);
1244 }
1245
1246
1247 /*
1248  * Put add users to a group.
1249  */
1250
1251 int DtMail::MailRc::group(char **argv, DtMail::MailRc *)
1252 {
1253     int size;
1254     char *buf;
1255     char *s;
1256     char **ap;
1257     
1258     /*
1259      * Insert names from the command list into the group.
1260      * Who cares if there are duplicates?  They get tossed
1261      * later anyway.
1262      */
1263     
1264     /* compute the size of the buffer */
1265     size = 0;
1266     for (ap = argv+1; *ap != NOSTR; ap++) {
1267         size += strlen(*ap) +1;
1268     }
1269     buf = (char *)malloc(size);
1270     s = buf;
1271     for (ap = argv+1; *ap != NOSTR; ap++) {
1272         strcpy(s, *ap);
1273         s += strlen(s);
1274         *s++ = ' ';
1275     }
1276     *--s = '\0';
1277     
1278     MailRc::add_alias((char *)argv[0], (char *)buf);
1279     free(buf);
1280     return(0);
1281 }
1282
1283 void
1284 DtMail::MailRc::wgroup(const char * verbatim, char ** argv, FILE * outf)
1285 {
1286     int size = 0;
1287     char * buf;
1288     char * s;
1289     char ** ap;
1290
1291     for (ap = argv+1; *ap != NOSTR; ap++) {
1292         size += strlen(*ap) +1;
1293     }
1294     buf = (char *)malloc(size);
1295     s = buf;
1296     for (ap = argv+1; *ap != NOSTR; ap++) {
1297         strcpy(s, *ap);
1298         s += strlen(s);
1299         *s++ = ' ';
1300     }
1301     *--s = '\0';
1302
1303     char * cur = (char *)hm_test((struct hash **)glob.g_alias, argv[0]);
1304     if (!cur || (!clearAliases && hm_ismarked((struct hash **)glob.g_alias, 
1305                         argv[0]))) {
1306         // Its been removed or written. Dont write it to the file.
1307         free(buf);
1308         return;
1309     }
1310
1311     if (strcmp(cur, buf) == 0) {
1312         // It hasnt changed. Write the original to preserve spacing.
1313         //
1314         fwrite(verbatim, 1, strlen(verbatim), outf);
1315     }
1316     else {
1317         // It has changed. Create a new alias.
1318         //
1319         fwrite("alias ", 1, 6, outf);
1320         fwrite(argv[0], 1, strlen(argv[0]), outf);
1321         fwrite(" ", 1, 1, outf);
1322         fwrite(cur, 1, strlen(cur), outf);
1323         fwrite("\n", 1, 1, outf);
1324     }
1325
1326     hm_mark((struct hash **)glob.g_alias, argv[0]);
1327 }
1328
1329 void
1330 DtMail::MailRc::ngroup(char * key, void * data, void * client_data)
1331 {
1332     char * value = (char *)data;
1333     FILE * outf = (FILE *)client_data;
1334
1335     fwrite("alias ", 1, 6, outf);
1336     fwrite(key, 1, strlen(key), outf);
1337     fwrite(" ", 1, 1, outf);
1338     fwrite(value, 1, strlen(value), outf);
1339     fwrite("\n", 1, 1, outf);
1340 }
1341
1342 void
1343 DtMail::MailRc::nalias(char * key, void * data, void * client_data)
1344 {
1345     DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1346     char *new_alias = NULL;
1347     char *white_space = NULL;
1348     int m_size = 0;
1349     int  i, num_spaces = 0;
1350     int key_len = strlen(key);
1351     // figure out whitespace for formatting
1352     // assume 13 for normal sized alias name
1353
1354     if(key_len < 13)  // need to add spaces
1355       {
1356         num_spaces = 13 - key_len;
1357
1358         white_space = (char *)malloc(num_spaces + 1);
1359         white_space[0] = '\0';
1360
1361         for(i = 0; i < num_spaces; i++)
1362           white_space[i] = ' ';
1363
1364         white_space[num_spaces] = '\0';
1365
1366 //        strcat(white_space, " ");
1367
1368         /* make an alias string */
1369         m_size = key_len + strlen((char *)white_space)
1370                  + strlen((char *)data) + strlen(" = ") + 1;
1371         new_alias = (char *)malloc(m_size);
1372     
1373         sprintf(new_alias, "%s%s = %s",key, white_space, data);
1374
1375       }
1376     else
1377       {
1378                 /* make an alias string */
1379         m_size = key_len + strlen((char *)data) + strlen(" = ") + 1;
1380         new_alias = (char *)malloc(m_size);
1381     
1382         sprintf(new_alias, "%s = %s",key, data);
1383
1384       }
1385     
1386     value_list->append(new_alias);
1387
1388 }
1389
1390 void
1391 DtMail::MailRc::nignorelist(char * key, void * data, void * client_data)
1392 {
1393     data = data;
1394     DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1395     char *new_ignore = NULL;
1396
1397     new_ignore = (char *)malloc(strlen((char *)key) + 2);
1398
1399     sprintf(new_ignore, "%s", key);
1400
1401     value_list->append(new_ignore);
1402
1403 }
1404
1405
1406 /*
1407  * Set or display a variable value.  Syntax is similar to that
1408  * of csh.
1409  */
1410
1411 int DtMail::MailRc::set(char **arglist, DtMail::MailRc *)
1412 {
1413     register char *cp, *cp2;
1414     char varbuf[LINESIZE], **ap;
1415     int errs;
1416     
1417     errs = 0;
1418     for (ap = arglist; *ap != NOSTR; ap++) {
1419         cp = *ap;
1420         cp2 = varbuf;
1421         while (*cp != '=' && *cp != '\0')
1422             *cp2++ = *cp++;
1423         *cp2 = '\0';
1424         if (*cp == '\0')
1425             cp = "";
1426         else
1427             cp++;
1428         if (equal(varbuf, "")) {
1429             fprintf(stderr,"Non-null variable name required\n");
1430             errs++;
1431             continue;
1432         }
1433         MailRc::mt_assign(varbuf, cp);
1434     }
1435     return(errs);
1436 }
1437
1438 void
1439 DtMail::MailRc::wset(const char * verbatim,
1440                      char ** arglist,
1441                      FILE * outf)
1442 {
1443     char varbuf[LINESIZE];
1444     char *cp, *cp2, **ap;
1445
1446     for (ap = arglist; *ap != NOSTR; ap++) {
1447         cp = *ap;
1448         cp2 = varbuf;
1449         while (*cp != '=' && *cp != '\0')
1450             *cp2++ = *cp++;
1451         *cp2 = '\0';
1452         if (*cp == '\0')
1453             cp = "";
1454         else
1455             cp++;
1456         if (equal(varbuf, "")) {
1457             continue;
1458         }
1459
1460         // Lookup the current value, and see if we should rewrite this
1461         // variable.
1462         //
1463         struct var * vp = lookup(varbuf, (struct var **)variables);
1464         if (vp == NULL || vp->v_written) {
1465             // If the original input line was set novar then just write
1466             // it out again. We can not easily track duplicates here.
1467             //
1468             if (varbuf[0] == 'n' && varbuf[1] == 'o') {
1469                 fwrite(verbatim, 1, strlen(verbatim), outf);
1470             }
1471
1472             // This variable has been unassigned or previously written.
1473             // Remove it from the file, by not writing it.
1474             //
1475             continue;
1476         }
1477
1478         // Compare the values. If equal, then write the line verbatim to
1479         // preserve the users original spacing and quoting.
1480         //
1481         if (strcmp(cp, vp->v_value) == 0) {
1482             fwrite(verbatim, 1, strlen(verbatim), outf);
1483             vp->v_written = 1;
1484         }
1485         else {
1486             // Write the variable, with a new value.
1487             //
1488             fwrite("set ", 1, 4, outf);
1489             fwrite(varbuf, 1, strlen(varbuf), outf);
1490             if (strlen(vp->v_value) > 0) {
1491                 fwrite("='", 1, 2, outf);
1492                 fwrite(vp->v_value, 1, strlen(vp->v_value), outf);
1493                 fwrite("'", 1, 1, outf);
1494             }
1495             fwrite("\n", 1, 1, outf);
1496             vp->v_written = 1;
1497         }
1498     }
1499 }
1500
1501 /*
1502  * Unset a bunch of variable values.
1503  */
1504
1505 int DtMail::MailRc::unset(char **arglist, DtMail::MailRc *)
1506 {
1507         register char **ap;
1508
1509         for (ap = arglist; *ap != NOSTR; ap++)
1510                 (void) MailRc::mt_deassign(*ap);
1511         return(0);
1512 }
1513
1514 void
1515 DtMail::MailRc::wunset(const char * verbatim,
1516                      char ** arglist,
1517                      FILE * outf)
1518 {
1519   arglist = arglist;
1520   if(verbatim != NULL && outf != NULL)
1521     fwrite(verbatim, 1, strlen(verbatim), outf);
1522 }
1523
1524 /*
1525  * Hash the passed string and return an index into
1526  * the variable or group hash table.
1527  */
1528 int DtMail::MailRc::hash(char *name)
1529 {
1530     register unsigned h;
1531     register char *cp;
1532     
1533     for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
1534         ;
1535     return (h % HSHSIZE);
1536 }
1537
1538 /* manage the alias list */
1539 void DtMail::MailRc::add_alias(char *name, char *value)
1540 {
1541     char *old;
1542     char *new_ptr;
1543     
1544     /* aliases to the same name get appended to the list */
1545     old = (char*)MailRc::hm_test((struct hash **)glob.g_alias, name);
1546     if (old) {
1547         
1548         int size;
1549         
1550         size = strlen(value) + strlen(old) + 2;
1551         new_ptr = (char *)malloc(size);
1552         sprintf(new_ptr, "%s %s", value, old);
1553         
1554         /* delete any old bindings for the name */
1555         MailRc::hm_delete((struct hash**)glob.g_alias, name);
1556         
1557         /* add the new binding */
1558         MailRc::hm_add((struct hash**)glob.g_alias, name, new_ptr, size);
1559         
1560         /* free up the temporary space */
1561         free(new_ptr);
1562     } else {
1563         /* add the new binding */
1564         MailRc::hm_add((struct hash**)glob.g_alias, name, value, strlen(value) +1);
1565     }
1566 }
1567
1568 /*
1569  * Assign a value to a mail variable.
1570  */
1571 void DtMail::MailRc::mt_assign(char *name,char * val)
1572 {
1573     
1574     if (name[0]=='-')
1575         (void) mt_deassign(name+1);
1576     else if (name[0]=='n' && name[1]=='o')
1577         (void) mt_deassign(name+2);
1578     else if ((val[0] == 'n' || val[0] == 'N') &&
1579              (val[1] == 'o' || val[1] == 'O') && val[2] == '\0') {
1580         (void) mt_deassign(name);
1581     } else mt_puthash(name, vcopy(val), MailRc::variables);
1582 }
1583
1584 int DtMail::MailRc::mt_deassign(char *s)
1585 {
1586     register struct var *vp, *vp2;
1587     register int h;
1588     
1589     if ((vp2 = lookup(s, MailRc::variables)) == (struct var *)NULL) {
1590         return (1);
1591     }
1592     h = hash(s);
1593     if (vp2 == MailRc::variables[h]) {
1594         MailRc::variables[h] = MailRc::variables[h]->v_link;
1595         vfree(vp2->v_name);
1596         vfree(vp2->v_value);
1597         free((char *)vp2);
1598         return (0);
1599     }
1600     for (vp = MailRc::variables[h]; vp->v_link != vp2; vp = vp->v_link)
1601         ;
1602     vp->v_link = vp2->v_link;
1603     vfree(vp2->v_name);
1604     vfree(vp2->v_value);
1605     free((char *)vp2);
1606     return (0);
1607 }
1608
1609 /*
1610  * associate val with name in hasharray
1611  */
1612 void DtMail::MailRc::mt_puthash(char *name, char *val, struct var **hasharray)
1613 {
1614     register struct var *vp;
1615     register int h;
1616     
1617     vp = lookup(name, hasharray);
1618     if (vp == (struct var *)NULL) {
1619         h = hash(name);
1620         vp = (struct var *) (calloc(sizeof *vp, 1));
1621         vp->v_name = vcopy(name);
1622         vp->v_link = hasharray[h];
1623         vp->v_written = 0;
1624         hasharray[h] = vp;
1625     } else
1626         vfree(vp->v_value);
1627     vp->v_value = val;
1628 }
1629
1630 void
1631 DtMail::MailRc::mt_scan(FILE * outf)
1632 {
1633     for (int slot = 0; slot < HSHSIZE; slot++) {
1634         for (var * vp = MailRc::variables[slot]; vp != (struct var *)NULL; vp = vp->v_link) {
1635             if (!vp->v_written) {
1636                 fwrite("set ", 1, 4, outf);
1637                 fwrite(vp->v_name, 1, strlen(vp->v_name), outf);
1638                 if (strlen(vp->v_value)) {
1639                     fwrite("='", 1, 2, outf);
1640                     fwrite(vp->v_value, 1, strlen(vp->v_value), outf);
1641                     fwrite("'", 1, 1, outf);
1642                 }
1643                 fwrite("\n", 1, 1, outf);
1644             }
1645             vp->v_written = 0;
1646         }
1647     }
1648 }
1649
1650 /*
1651  * Copy a variable value into permanent space.
1652  * Do not bother to alloc space for "".
1653  */
1654 char *DtMail::MailRc::vcopy(char *str)
1655 {
1656     
1657     if (strcmp(str, "") == 0)
1658         return ("");
1659     return (strdup(str));
1660 }
1661
1662 /*
1663  * Free up a variable string.  We do not bother to allocate
1664  * strings whose value is "" since they are expected to be frequent.
1665  * Thus, we cannot free same!
1666  */
1667 void DtMail::MailRc::vfree(char *cp)
1668 {
1669     
1670     if (strcmp(cp, "") != 0)
1671         free(cp);
1672 }
1673
1674 void *DtMail::MailRc::hm_alloc(void)
1675 {
1676     struct hash **table;
1677     
1678     table = (struct hash**)malloc(sizeof (struct hash) * HASHSIZE);
1679     memset(table, '\0', sizeof (struct hash) * HASHSIZE);
1680     return (table);
1681 }
1682 void DtMail::MailRc::hm_add(struct hash **table, 
1683                             char *key, 
1684                             void *value, 
1685                             int size)
1686 {
1687     int index;
1688     register struct hash *h;
1689     
1690     if (!table)
1691         return;
1692     
1693     index = hash_index(key);
1694     h = (struct hash *)malloc(sizeof (struct hash));
1695     h->h_next = table[index];
1696     h->h_written = 0;
1697     table[index] = h;
1698     h->h_key = strdup(key);
1699     if (size && value != NULL) {
1700         h->h_value = malloc(size);
1701         memcpy(h->h_value, value, size);
1702     } else {
1703         h->h_value = nullfield;
1704     }
1705 }
1706
1707
1708 void DtMail::MailRc::hm_delete(struct hash **table, char *key)
1709 {
1710     register int index;
1711     register struct hash *h;
1712     register struct hash *old;
1713     
1714     if (!table)
1715         return;
1716     
1717     index = hash_index(key);
1718     old = NULL;
1719     h = table[index];
1720     while (h) {
1721         if (strcasecmp(h->h_key, key) == 0) {
1722             /* found the match */
1723             if (old == NULL)
1724                 table[index] = h->h_next;
1725             else
1726                 old->h_next = h->h_next;
1727             
1728             free_hash(h);
1729             break;
1730         }
1731         
1732         old = h;
1733         h = h->h_next;
1734     }
1735 }
1736
1737
1738 void *DtMail::MailRc::hm_test(struct hash **table, char *key)
1739 {
1740     register struct hash *h;
1741     
1742     if (!table)
1743         return (NULL);
1744     
1745     h = table[hash_index(key)];
1746     
1747     while (h) {
1748         if (strcasecmp(h->h_key, key) == 0) {
1749             /* found a match */
1750             return (h->h_value);
1751         }
1752         
1753         h = h->h_next;
1754     }
1755     
1756     return (NULL);
1757 }
1758
1759 void DtMail::MailRc::hm_mark(struct hash **table, char *key)
1760 {
1761     register struct hash *h;
1762     
1763     if (!table)
1764         return;
1765     
1766     h = table[hash_index(key)];
1767     
1768     while (h) {
1769         if (strcasecmp(h->h_key, key) == 0) {
1770             h->h_written = 1;
1771             return;
1772         }
1773         
1774         h = h->h_next;
1775     }
1776 }
1777
1778 int DtMail::MailRc::hm_ismarked(struct hash **table, char *key)
1779 {
1780     register struct hash *h;
1781     
1782     if (!table) return 0;
1783     h = table[hash_index(key)];
1784     while (h)
1785     {
1786         if (strcasecmp(h->h_key, key) == 0) return(h->h_written);
1787         h = h->h_next;
1788     }
1789
1790     return 0;
1791 }
1792
1793 void
1794 DtMail::MailRc::hm_scan(struct hash **table, hm_callback callback, void * client_data)
1795 {
1796     for (int slot = 0; slot < HASHSIZE; slot++) {
1797         for (struct hash * h = table[slot]; h; h = h->h_next) {
1798             if (!h->h_written) {
1799                 callback(h->h_key, h->h_value, client_data);
1800             }
1801             h->h_written = 0;
1802         }
1803     }
1804 }
1805
1806 int DtMail::MailRc::hash_index(char *key)
1807 {
1808     register unsigned h;
1809     register char *s;
1810     register c;
1811     
1812     s = key;
1813     h = 0;
1814     while (*s) {
1815         c = *s++;
1816         if (isupper(c)) {
1817             c = tolower(c);
1818         }
1819         h = (h << 2) + c;
1820     }
1821     
1822     return (h % HASHSIZE);
1823 }
1824
1825 void DtMail::MailRc::free_hash(struct hash *h)
1826 {
1827     free(h->h_key);
1828     if (h->h_value != nullfield) {
1829         free(h->h_value);
1830     }
1831     free(h);
1832 }
1833
1834 /*
1835  * Add the given header fields to the ignored list.
1836  * If no arguments, print the current list of ignored fields.
1837  */
1838 int DtMail::MailRc::igfield(char *list[], DtMail::MailRc *)
1839 {
1840     char **ap;
1841     
1842     for (ap = list; *ap != 0; ap++) {
1843 //                DP(("igfield: adding %s\n", *ap));
1844         MailRc::add_ignore(*ap);
1845     }
1846     return(0);
1847 }
1848
1849 void
1850 DtMail::MailRc::wigfield(const char * verbatim, char ** list, FILE * outf)
1851 {
1852     char ** ap;
1853     Boolean ignore_written = FALSE;
1854
1855     // We need to see that every name in this line still exists. If not,
1856     // then we will make a second pass, rewriting the line with only the
1857     // valid ignores.
1858     //
1859     int good = 1;
1860     for (ap = list; *ap && good; ap++) {
1861         if (!hm_test((struct hash **)glob.g_ignore, *ap)) {
1862             good = 0;
1863             break;
1864         }
1865     }
1866
1867     if (good) {
1868         fwrite(verbatim, 1, strlen(verbatim), outf);
1869         for (ap = list; *ap; ap++) {
1870             hm_mark((struct hash **)glob.g_ignore, *ap);
1871         }
1872         return;
1873     }
1874
1875     // Create a new ignore line, leaving out the ignores that have
1876     // been removed. Also, dont write any ignores that have been
1877     // previously written.
1878     //
1879     for (ap = list; *ap; ap++) {
1880         if (hm_test((struct hash **)glob.g_ignore, *ap) &&
1881         !hm_ismarked((struct hash **)glob.g_ignore, *ap)) {
1882                 // Only write 'ignore' if there is something in the list
1883                 if (!ignore_written) {
1884                         fwrite("ignore ", 1, 7, outf);
1885                         ignore_written = TRUE;
1886                 }
1887                 fwrite(*ap, 1, strlen(*ap), outf);
1888                 fwrite(" ", 1, 1, outf);
1889                 hm_mark((struct hash **)glob.g_ignore, *ap);
1890         }
1891      }
1892      if (ignore_written)
1893         fwrite("\n", 1, 1, outf);
1894 }
1895
1896 void
1897 DtMail::MailRc::nigfield(char * key, void *, void * client_data)
1898 {
1899     DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1900
1901     value_list->append(key);
1902 }
1903
1904 /* manage the retain/ignore list */
1905 void DtMail::MailRc::add_ignore(char *name)
1906 {
1907     if(! MailRc::hm_test((struct hash **)glob.g_ignore, name)) {
1908         /* name is not already there... */
1909         MailRc::hm_add((struct hash **)glob.g_ignore, name, NULL, 0);
1910     }
1911 }
1912
1913
1914 /*
1915  * Set the list of alternate names.
1916  */
1917 int DtMail::MailRc::alternates(char **namelist, DtMail::MailRc *)
1918 {
1919     while (*namelist != NULL)
1920         MailRc::add_alternates(*namelist++);
1921     return(0);
1922 }
1923
1924 void
1925 DtMail::MailRc::walternates(const char * verbatim, char ** list, FILE * outf)
1926 {
1927     // This is like ignores. We need to make sure all of the alternates
1928     // on this command are still in the database.
1929     //
1930     char ** ap;
1931     int good = 1;
1932     for (ap = list; *ap && good; ap++) {
1933         if (!hm_test((struct hash **)glob.g_alternates, *ap)) {
1934             good = 0;
1935             break;
1936         }
1937     }
1938
1939     if (good) {
1940         fwrite(verbatim, 1, strlen(verbatim), outf);
1941         for (ap = list; *ap; ap++) {
1942             hm_mark((struct hash **)glob.g_alternates, *ap);
1943         }
1944         return;
1945     }
1946
1947     // Write out the alternates that still exist and have not been
1948     // previously written.
1949     //
1950     Boolean written = FALSE;
1951     for (ap = list; *ap; ap++) {
1952         if (hm_test((struct hash **)glob.g_alternates, *ap) &&
1953             !hm_ismarked((struct hash **)glob.g_alternates, *ap)) {
1954             if (!written) {
1955                 fwrite("alternates ", 1, 11, outf);
1956                 written=TRUE;
1957             }
1958             fwrite(*ap, 1, strlen(*ap), outf);
1959             fwrite(" ", 1, 1, outf);
1960             hm_mark((struct hash **)glob.g_alternates, *ap);
1961         }
1962     }
1963 }
1964
1965 void
1966 DtMail::MailRc::nalternates(char * key, void *, void * client_data)
1967 {
1968     DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1969
1970     value_list->append(key);
1971 }
1972
1973 /* manage the alternate name list */
1974 void DtMail::MailRc::add_alternates(char *name)
1975 {
1976     if(! MailRc::hm_test((struct hash**)glob.g_alternates, name)) {
1977         /* name is not already there... */
1978         MailRc::hm_add((struct hash **)glob.g_alternates, name, NULL, 0);
1979     }
1980 }
1981
1982 /*
1983  * Determine the current folder directory name.
1984  */
1985 int
1986 DtMail::MailRc::getfolderdir(char *name)
1987 {
1988         char *folder;
1989
1990         if ((folder = mt_value("folder")) == NOSTR)
1991                 return(-1);
1992         if (*folder == '/')
1993                 strcpy(name, folder);
1994         else
1995                 sprintf(name, "%s/%s", mt_value("HOME"), folder);
1996         return(0);
1997 }
1998
1999 /*
2000  * Take a file name, possibly with shell meta characters
2001  * in it and expand it by using "sh -c echo filename"
2002  * Return the file name as a dynamic string.
2003  */
2004
2005 char *
2006 DtMail::MailRc::expand(char *name)
2007 {
2008         char *xname = new char[LINESIZE];
2009         char *cmdbuf = new char[LINESIZE];
2010         char *str;
2011         register int pid, l;
2012         register char *cp, *Shell;
2013         int s, pivec[2];
2014         struct stat sbuf;
2015
2016         if (name[0] == '+' && getfolderdir(cmdbuf) >= 0) {
2017                 sprintf(xname, "%s/%s", cmdbuf, name + 1);
2018                 str = expand(xname);
2019                 delete [] xname;
2020                 delete [] cmdbuf;
2021                 return str;
2022         }
2023         if (!strpbrk(name, "~{[*?$`'\"\\")) {
2024                 return(strdup(name));
2025         }
2026         if (pipe(pivec) < 0) {
2027                 perror("pipe");
2028                 delete [] xname;
2029                 delete [] cmdbuf;
2030                 return(strdup(name));
2031         }
2032         sprintf(cmdbuf, "echo %s", name);
2033         if ((pid = DTMAIL_FORK()) == 0) {
2034                 Shell = mt_value("SHELL");
2035                 if (Shell == NOSTR || *Shell=='\0')
2036                         Shell = "/bin/sh";
2037                 close(pivec[0]);
2038                 close(1);
2039                 dup(pivec[1]);
2040                 close(pivec[1]);
2041                 close(2);
2042                 execlp(Shell, Shell, "-c", cmdbuf, (char *)0);
2043                 _exit(1);
2044         }
2045         if (pid == -1) {
2046                 perror("fork");
2047                 close(pivec[0]);
2048                 close(pivec[1]);
2049                 delete [] xname;
2050                 delete [] cmdbuf;
2051                 return(NOSTR);
2052         }
2053         close(pivec[1]);
2054         l = read(pivec[0], xname, LINESIZE);
2055         close(pivec[0]);
2056         while (wait(&s) != pid);
2057                 ;
2058         s &= 0377;
2059         if (s != 0 && s != SIGPIPE) {
2060                 fprintf(stderr, "Echo failed\n");
2061                 goto err;
2062         }
2063         if (l < 0) {
2064                 perror("read");
2065                 goto err;
2066         }
2067         if (l == 0) {
2068                 fprintf(stderr, "%s: No match\n", name);
2069                 goto err;
2070         }
2071         if (l == LINESIZE) {
2072                 fprintf(stderr, "Buffer overflow expanding %s\n", name);
2073                 goto err;
2074         }
2075         xname[l] = 0;
2076         for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
2077                 ;
2078         *++cp = '\0';
2079         if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
2080                 fprintf(stderr, "%s: Ambiguous\n", name);
2081                 goto err;
2082         }
2083
2084         delete [] xname;
2085         delete [] cmdbuf;
2086         return(strdup(xname));
2087
2088 err:
2089         fflush(stderr);
2090         delete [] xname;
2091         delete [] cmdbuf;
2092         return(NOSTR);
2093 }
2094
2095 int
2096 DtMail::MailRc::source(char **arglist, DtMail::MailRc *self)
2097 {
2098   char *fname = arglist[0];
2099   FILE *fi;
2100   char *cp;
2101
2102   /* if any of the three if test failed, return 0,
2103    * since we have not updated the input and stack pointer yet
2104    */
2105   if ((cp = self->expand(fname)) == NOSTR)
2106     return(0);
2107
2108   if ((fi = fopen(cp, "r")) == NULL) {
2109     free(cp);
2110     return(0);
2111   }
2112   free(cp);
2113
2114   if (self->ssp >= NOFILE - 2) {
2115     fclose(fi);
2116     return(0);
2117   }
2118   self->sstack[++(self->ssp)].s_file = self->input;
2119   self->sstack[self->ssp].s_cond = self->cond;
2120   self->cond = CANY;
2121   self->input = fi;
2122   self->sourcing++;
2123
2124   return(0);
2125 }
2126
2127 void
2128 DtMail::MailRc::wsource(const char * verbatim, char ** arglist, FILE * outf)
2129 {
2130   arglist = arglist;
2131   if(verbatim != NULL && outf != NULL)
2132     fwrite(verbatim, 1, strlen(verbatim), outf);
2133 }
2134
2135 int
2136 DtMail::MailRc::ifcmd(char **arglist, DtMail::MailRc *self)
2137 {
2138         register char *cp;
2139
2140         if (self->cond != CANY) {
2141                 fprintf(stderr,"Illegal nested \"if\"\n");
2142                 return(1);
2143         }
2144         self->cond = CANY;
2145         cp = arglist[0];
2146         switch (*cp) {
2147         case 'r': case 'R':
2148                 self->cond = CRCV;
2149                 break;
2150
2151         case 's': case 'S':
2152                 self->cond = CSEND;
2153                 break;
2154
2155         case 't': case 'T':
2156                 self->cond = CTTY;
2157                 break;
2158
2159         default:
2160                 fprintf(stderr,"Unrecognized if-keyword: \"%s\"\n",
2161                         cp);
2162                 return(1);
2163         }
2164         return(0);
2165 }
2166
2167 void
2168 DtMail::MailRc::wifcmd(const char * verbatim, char ** arglist, FILE * outf)
2169 {
2170   arglist = arglist;
2171   if(verbatim != NULL && outf != NULL)
2172     fwrite(verbatim, 1, strlen(verbatim), outf);
2173 }
2174
2175
2176 int
2177 DtMail::MailRc::elsecmd(char **, DtMail::MailRc *self)
2178 {
2179
2180         switch (self->cond) {
2181         case CANY:
2182                 fprintf(stderr, "\"Else\" without matching \"if\"\n");
2183                 return(1);
2184
2185         case CSEND:
2186                 self->cond = CRCV;
2187                 break;
2188
2189         case CRCV:
2190                 self->cond = CSEND;
2191                 break;
2192
2193         case CTTY:
2194                 self->cond = CNOTTY;
2195                 break;
2196
2197         case CNOTTY:
2198                 self->cond = CTTY;
2199                 break;
2200
2201         default:
2202                 fprintf(stderr,"invalid condition encountered\n");
2203                 self->cond = CANY;
2204                 break;
2205         }
2206         return(0);
2207 }
2208
2209 void
2210 DtMail::MailRc::welsecmd(const char * verbatim, char ** arglist, FILE * outf)
2211 {
2212   arglist = arglist;
2213   if(verbatim != NULL && outf != NULL)
2214     fwrite(verbatim, 1, strlen(verbatim), outf);
2215 }
2216
2217
2218 int
2219 DtMail::MailRc::endifcmd(char **, DtMail::MailRc *self)
2220 {
2221
2222         if (self->cond == CANY) {
2223                 fprintf(stderr,"\"Endif\" without matching \"if\"\n");
2224                 return(1);
2225         }
2226         self->cond = CANY;
2227         return(0);
2228 }
2229
2230 void
2231 DtMail::MailRc::wendifcmd(const char * verbatim, char ** arglist, FILE * outf)
2232 {
2233   arglist = arglist;
2234   if(verbatim != NULL && outf != NULL)
2235     fwrite(verbatim, 1, strlen(verbatim), outf);
2236 }
2237
2238 int
2239 DtMail::MailRc::clearaliases(char **, DtMail::MailRc *)
2240 {
2241         DtVirtArray<char *>   *value_list = NULL;
2242         DtMailEnv error;
2243         
2244         value_list = new DtVirtArray<char *>(10);
2245
2246         hm_scan((struct hash **)glob.g_alias, nalias, value_list);
2247
2248         while (value_list->length()) {
2249                 char *buf, *val = (*value_list)[0];
2250                 if ((buf = strchr(val, ' ')) != NULL)
2251                         *buf = '\0';
2252                 if (hm_test((struct hash **)glob.g_alias, val))
2253                         hm_delete((struct hash **)glob.g_alias, val);
2254                 value_list->remove(0);
2255         }
2256         delete value_list;
2257         clearAliases = 1;
2258         return 0;
2259 }
2260 void
2261 DtMail::MailRc::wclearaliases(const char *, char **, FILE *)
2262 {
2263 }