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