2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
26 * $TOG: DtMailRc.C /main/9 1998/07/23 18:02:08 mgreess $
28 * RESTRICTED CONFIDENTIAL INFORMATION:
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
38 * Copyright 1993, 1995, 1995 Sun Microsystems, Inc. All rights reserved.
44 * Format of the command description table.
45 * The actual table is declared and initialized
51 #include <sys/param.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"
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 */
71 struct var *DtMail::MailRc::variables[HSHSIZE]; /* Pointer to active var list */
73 typedef DtMail::MailRc::globals DtMailRc_globals;
74 DtMailRc_globals DtMail::MailRc::glob;
76 struct DtMail::MailRc::globals DtMail::MailRc::glob;
78 char *DtMail::MailRc::nullfield;
79 Boolean DtMail::MailRc::clearAliases;
82 #define DTMAIL_FORK vfork
84 #define DTMAIL_FORK fork
87 #define MAXIMUM_PATH_LENGTH 2048
89 /* can't initialize unions */
91 #define c_minargs c_msgflag /* Minimum argcount for RAWLIST */
92 #define c_maxargs c_msgmask /* Max argcount for RAWLIST */
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 */
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 */
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 */
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. */
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, },
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 #elif defined(CSRG_BASED)
160 #define SYSTEM_MAILRC "/etc/mail.rc"
164 DtMail::MailRc::MailRc(DtMailEnv &env, DtMail::Session *session)
172 _parseError = DTME_NoError;
178 char * mail_rc = getenv("MAILRC");
180 _mailrc_name = strdup(mail_rc);
184 GetPasswordEntry(pw);
186 _mailrc_name = (char *)malloc(MAXIMUM_PATH_LENGTH);
188 strcpy(_mailrc_name, pw.pw_dir);
189 strcat(_mailrc_name, "/.mailrc");
198 if ((rval = this->load(_mailrc_name, line)) != 0) {
199 char *msg = new char[MAXIMUM_PATH_LENGTH + LINESIZE];
201 sprintf(msg, "error while reading \'%s\' in %s\n", line,
204 else if (rval == -2) {
205 sprintf(msg, "%s\n", line);
207 else if (rval == -3) {
208 sprintf(msg, "error: no endif for conditional if statement while reading %s\n", _mailrc_name);
209 env.setError(DTME_ResourceParsingNoEndif, NULL);
210 _parseError = DTME_ResourceParsingNoEndif;
213 env.logError(DTM_FALSE, "%s", msg);
220 if ((rval = this->load(SYSTEM_MAILRC, line)) != 0) {
221 char *msg = new char[MAXIMUM_PATH_LENGTH + LINESIZE];
224 sprintf(msg, "error while reading \'%s\' in %s\n",
225 line, SYSTEM_MAILRC);
226 env.logError(DTM_FALSE, "%s", msg);
227 } else if (rval == -2) {
228 /* Ignore error if the system logfile does not exist. */
235 DtMail::MailRc::~MailRc(void)
240 // return true if in list
241 DtMailBoolean DtMail::MailRc::ignore(DtMailEnv &env, const char *name)
244 char *ignore_list = (char *)MailRc::hm_test((struct hash **)glob.g_ignore, (char *)name);
248 if(ignore_list != NULL) // we have a valid ignore list
254 env.setError(DTME_NoObjectValue, NULL);
261 DtMail::MailRc::addIgnore(DtMailEnv & error, const char * name)
264 add_ignore((char *)name);
268 DtMail::MailRc::removeIgnore(DtMailEnv & error, const char * name)
271 if(MailRc::hm_test((struct hash **)glob.g_ignore, (char *)name)) {
272 MailRc::hm_delete((struct hash **)glob.g_ignore, (char *)name);
276 const char *DtMail::MailRc::getAlias(DtMailEnv &env, const char * name)
279 char *return_value = (char *)hm_test((struct hash **)glob.g_alias, (char *)name);
283 if(return_value == NULL)
285 env.setError(DTME_NoObjectValue, NULL);
296 DtMail::MailRc::setAlias(DtMailEnv & error,
302 if (hm_test((struct hash **)glob.g_alias, (char *)name)) {
303 hm_delete((struct hash **)glob.g_alias, (char *)name);
306 add_alias((char *)name, (char *)value);
310 DtMail::MailRc::removeAlias(DtMailEnv & error,
315 if (hm_test((struct hash **)glob.g_alias, (char *)name)) {
316 hm_delete((struct hash **)glob.g_alias, (char *)name);
321 // Adapted from uuencode and uudecode written by Ian Lance Taylor.
323 #define ENCRYPT(c) ((c) ? (((c)&077) + ' ' + 2) : ('`' + 2))
324 #define DECRYPT(c) ((((c) - ' ') & 077) - 2)
327 DtMail::MailRc::encryptedLength(int length)
329 // One for the length, one for the '\0', expansion factor of 4/3.
330 int encrypted_length = (2 + (4 * ((length+2)/3)));
331 return encrypted_length;
335 DtMail::MailRc::encryptValue(char *to, char *from, int buflen)
341 memset(to, 0, buflen);
343 length = strlen(from);
344 to[i++] = ENCRYPT(length);
346 for (p=from; length>0; length-=3, p+=3)
352 ch = ((*p << 4) & 060) | ((p[1] >> 4) & 017);
356 ch = ((p[1] << 2) & 074) | ((p[2] >> 6) & 03);
367 DtMail::MailRc::decryptValue(char *to, char *from, int buflen)
373 memset(to, 0, buflen);
376 length = DECRYPT(*p);
377 if (length<=0) return 1;
379 for (++p; length>0; p+=4, length-=3)
383 ch = DECRYPT(p[0]) << 2 | DECRYPT(p[1]) >> 4;
385 ch = DECRYPT(p[1]) << 4 | DECRYPT(p[2]) >> 2;
387 ch = DECRYPT(p[2]) << 6 | DECRYPT(p[3]);
394 ch = DECRYPT(p[0]) << 2 | DECRYPT(p[1]) >> 4;
399 ch = DECRYPT(p[1]) << 4 | DECRYPT(p[2]) >> 2;
407 // if set with value return no error in env and set value
408 // if set with no value return no error in env and return empty string
409 // if not set, return error in env and leave value alone
411 void DtMail::MailRc::getValue(DtMailEnv &env,
414 DtMailBoolean decrypt)
416 char *get_result = mt_value((char *)var);
421 if (get_result != NULL)
425 int length = encryptedLength(strlen(get_result));
426 *value = (char*) malloc(length);
427 if (decryptValue((char*) *value, get_result, length))
428 strcpy((char*) *value, get_result);
431 *value = strdup((char *)get_result);
434 env.setError(DTME_NoObjectValue, NULL);
438 DtMail::MailRc::setValue(DtMailEnv & error,
441 DtMailBoolean encrypt)
445 if (encrypt && strlen(value))
447 int length = encryptedLength(strlen(value));
448 char *encrypted = (char*) malloc(length);
449 encryptValue(encrypted, (char*) value, length);
450 mt_assign((char*) var, (char*) encrypted);
453 mt_assign((char*) var, (char*) value);
457 DtMail::MailRc::removeValue(DtMailEnv & error,
462 mt_deassign((char *)var);
465 // return alternates list
466 const char * DtMail::MailRc::getAlternates(DtMailEnv &env)
469 register struct hash *h;
470 struct hash **table = (struct hash **)glob.g_alternates;
475 // we don't free this memory...
476 alternate_list = NULL;
480 env.setError(DTME_NoObjectValue, NULL);
489 len = strlen((const char*)h->h_key);
491 if(alternate_list == NULL)
493 alternate_list = (char *)malloc(len + 1); // plus terminator
494 strcpy(alternate_list, (const char*)h->h_key);
498 len += strlen(alternate_list);
499 alternate_list = (char *)realloc(alternate_list,
500 len + 2); // plus terminator
502 strcat(alternate_list, " "); // add space
503 strcat(alternate_list, (const char *)h->h_key);
512 return (alternate_list);
517 DtMail::MailRc::setAlternate(DtMailEnv & error, const char * alt)
521 add_alternates((char *)alt);
525 DtMail::MailRc::removeAlternate(DtMailEnv & error, const char * name)
529 if(MailRc::hm_test((struct hash**)glob.g_alternates, (char *)name)) {
530 MailRc::hm_delete((struct hash **)glob.g_alternates, (char *)name);
535 DtMail::MailRc::update(DtMailEnv & error)
539 // Generate a temporary file.
540 char *tmp_mailrc = new char[MAXIMUM_PATH_LENGTH];
541 strcpy(tmp_mailrc, _mailrc_name);
542 strcat(tmp_mailrc, ".tmp");
544 FILE * outf = fopen(tmp_mailrc, "w+");
546 error.setError(DTME_ObjectCreationFailed);
547 delete [] tmp_mailrc;
551 // Now open the mailrc for input.
552 FILE * inf = fopen(_mailrc_name, "r");
554 // Now we will read the input file, and copy it to the output,
555 // based on type and changes to the mailrc.
557 updateByLine(inf, outf);
562 // Now we need to make a final scan. This will cause new values
563 // to be written and the written flag to be reset for the next
568 hm_scan((struct hash **)glob.g_alias, ngroup, outf);
570 // Alternates and groups we will add to a list, and then
571 // put them out in one batch.
573 DtVirtArray<char *> value_list(32);
575 hm_scan((struct hash **)glob.g_ignore, nigfield, &value_list);
577 if (value_list.length()) {
578 fwrite("ignore ", 1, 7, outf);
579 while (value_list.length()) {
580 char * val = value_list[0];
581 fwrite(val, 1, strlen(val), outf);
582 fwrite(" ", 1, 1, outf);
583 value_list.remove(0);
585 fwrite("\n", 1, 1, outf);
588 hm_scan((struct hash **)glob.g_alternates, nalternates, &value_list);
590 if (value_list.length()) {
591 fwrite("alternates ", 1, 11, outf);
592 while (value_list.length()) {
593 char * val = value_list[0];
594 fwrite(val, 1, strlen(val), outf);
595 fwrite(" ", 1, 1, outf);
596 value_list.remove(0);
598 fwrite("\n", 1, 1, outf);
602 if (rename(tmp_mailrc, _mailrc_name)) {
603 error.setError(DTME_ObjectAccessFailed);
604 delete [] tmp_mailrc;
607 delete [] tmp_mailrc;
610 void DtMail::MailRc::getAliasList(hm_callback stuffing_func, void *client_data)
612 hm_scan((struct hash **)glob.g_alias, stuffing_func, client_data);
616 DtVirtArray<char *> *DtMail::MailRc::getAliasList()
619 DtVirtArray<char *> *value_list = NULL;
621 value_list = new DtVirtArray<char *>(10);
623 hm_scan((struct hash **)glob.g_alias, nalias, value_list);
628 DtVirtArray<char *> *DtMail::MailRc::getIgnoreList()
631 DtVirtArray<char *> *value_list = NULL;
633 value_list = new DtVirtArray<char *>(10);
635 hm_scan((struct hash **)glob.g_ignore, nignorelist, value_list);
641 DtMail::MailRc::updateByLine(FILE * inf, FILE * outf)
643 // We are going to keep two line buffers, unlike the first
644 // pass parser. One is the line, with continuations, verbatim.
645 // If nothing has changed, then we put this into the output
646 // unmodified. The other is the line with continuations stripped
647 // so we can finish parsing and analyzing the line.
649 char *verbatim = new char[LINESIZE];
650 char *for_parser = new char[LINESIZE];
651 char *linebuf = new char[LINESIZE];
659 if (readline(inf, linebuf) <= 0) {
664 int len = strlen(linebuf);
665 if (len == 0 || linebuf[len - 1] != '\\') {
669 strcat(verbatim, linebuf);
670 strcat(verbatim, "\n");
672 linebuf[len - 1] = ' ';
673 strcat(for_parser, linebuf);
679 strcat(verbatim, linebuf);
680 strcat(verbatim, "\n");
681 strcat(for_parser, linebuf);
682 outputLine(verbatim, for_parser, outf);
686 delete [] for_parser;
691 DtMail::MailRc::outputLine(const char * verbatim,
692 const char * for_parser,
695 char *arglist[MAXARGC];
696 const char * start = for_parser;
697 while(*start && isspace(*start)) {
701 // If we have a comment, write it out and move on.
704 fwrite(verbatim, 1, strlen(verbatim), outf);
710 // Pull off the command word.
713 while (*start && !strchr(" \t0123456789$^.:/-+*'\"", *start))
717 if (equal(word, "")) {
718 fwrite(verbatim, 1, strlen(verbatim), outf);
722 struct cmd * com = (struct cmd *)lex(word);
724 // We dont know the command, so just copy the line.
726 fwrite(verbatim, 1, strlen(verbatim), outf);
730 // We will simply rewrite conditionals.
731 if ((com->c_argtype & F)) {
732 fwrite(verbatim, 1, strlen(verbatim), outf);
737 switch (com->c_argtype & ~(F)) {
740 * Just the straight string, with
741 * leading blanks removed.
743 while (strchr(" \t", *start)) {
746 com->c_write(verbatim, (char **)&start, outf);
751 * A vector of strings, in shell style.
753 if ((c = getrawlist((char *)start, arglist,
754 sizeof arglist / sizeof *arglist)) < 0)
756 if (c < com->c_minargs) {
757 fprintf(stderr,"%s requires at least %d arg(s)\n",
758 com->c_name, com->c_minargs);
761 if (c > com->c_maxargs) {
762 fprintf(stderr,"%s takes no more than %d arg(s)\n",
763 com->c_name, com->c_maxargs);
766 com->c_write(verbatim, arglist, outf);
767 freerawlist(arglist);
772 * Just the constant zero, for exiting,
775 com->c_write(verbatim, NULL, outf);
779 fprintf(stderr,"Unknown argtype %#x", com->c_argtype);
780 fprintf(stderr, "NEED TO FIX THIS FOR DTMAIL!\n");
786 // init the global hash structure
787 void DtMail::MailRc::init_globals()
789 glob.g_myname = NULL;
791 glob.g_ignore = this->hm_alloc();
792 glob.g_retain = this->hm_alloc();
793 glob.g_alias = this->hm_alloc();
794 glob.g_alternates = this->hm_alloc();
797 nullfield = new char[3];
799 strcpy(nullfield, "");
801 alternate_list = NULL;
805 // load a "user definintion" file
807 DtMail::MailRc::load(char *name, char* line)
809 register FILE *in, *oldin;
812 if ((in = fopen(name, "r")) == NULL) {
813 sprintf(line, "can not open file %s\n", name);
820 if ((ret = commands(line)) != 0) {
831 * Interpret user commands one by one. If standard input is not a tty,
836 DtMail::MailRc::commands(char* iline)
839 char *linebuf = new char[LINESIZE];
840 char *line = new char[LINESIZE];
844 * Read a line of commands from the current input
845 * and handle end of file specially.
851 if (readline(input, line) <= 0) {
858 // Conditional if statement with no corresponding endif
869 if ((n = strlen(line)) == 0)
875 if (n > LINESIZE - strlen(linebuf) - 1)
877 strcat(linebuf, line);
879 n = LINESIZE - strlen(linebuf) - 1;
880 if (strlen(line) > n) {
882 "Line plus continuation line too long:\n\t%s\n\nplus\n\t%s\n",
888 // Conditional if statement with no corresponding endif
899 strncat(linebuf, line, n);
900 if (execute(linebuf)) {
901 strncpy(iline, linebuf, LINESIZE);
902 iline[LINESIZE-1] = '\0';
914 * Execute a single command. If the command executed
915 * is "quit," then return non-zero so that the caller
916 * will know to return back to main, if he cares.
917 * Contxt is non-zero if called while composing mail.
920 int DtMail::MailRc::execute(char linebuf[])
923 char *arglist[MAXARGC];
932 * Strip the white space away from the beginning
933 * of the command, then scan out a word, which
934 * consists of anything except digits and white space.
936 * Handle ! escapes differently to get the correct
937 * lexical conventions.
941 while (*cp && strchr(" \t", *cp))
944 * Throw away comment lines here.
950 /* the special newcmd header -- "#-" */
953 /* strip whitespace again */
954 while (*++cp && strchr(" \t", *cp));
957 while (*cp && !strchr(" \t0123456789$^.:/-+*'\"", *cp))
962 * Look up the command; if not found, complain.
963 * We ignore blank lines to eliminate confusion.
969 com = (struct cmd *)lex(word);
972 /* this command is OK not to be found; that way
973 * we can extend the .mailrc file with new
974 * commands and not kill old mail and mailtool
979 fprintf(stderr,"Unknown command: \"%s\"\n", word);
986 * See if we should execute the command -- if a conditional
987 * we always execute it, otherwise, check the state of cond.
990 if ((com->c_argtype & F) == 0) {
991 if (cond == CSEND || cond == CTTY )
997 if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode ||
998 cond == CTTY && !intty || cond == CNOTTY && intty)
1006 * Process the arguments to the command, depending
1007 * on the type he expects. Default to an error.
1008 * If we are sourcing an interactive command, it's
1013 switch (com->c_argtype & ~(F)) {
1016 * Just the straight string, with
1017 * leading blanks removed.
1019 while (strchr(" \t", *cp))
1021 e = (*com->c_func)(&cp, this);
1026 * A vector of strings, in shell style.
1028 if ((c = getrawlist(cp, arglist,
1029 sizeof arglist / sizeof *arglist)) < 0)
1031 if (c < com->c_minargs) {
1032 fprintf(stderr,"%s requires at least %d arg(s)\n",
1033 com->c_name, com->c_minargs);
1036 if (c > com->c_maxargs) {
1037 fprintf(stderr,"%s takes no more than %d arg(s)\n",
1038 com->c_name, com->c_maxargs);
1041 e = (*com->c_func)(arglist, this);
1042 freerawlist(arglist);
1047 * Just the constant zero, for exiting,
1050 e = (*com->c_func)(0, this);
1054 fprintf(stderr,"Unknown argtype %#x", com->c_argtype);
1055 fprintf(stderr, "NEED TO FIX THIS FOR DTMAIL!\n");
1060 * Exit the current source file on
1067 /* ZZZ:katin: should we return an error here? */
1072 * Read up a line from the specified input into the line
1073 * buffer. Return the number of characters read. Do not
1074 * include the newline at the end.
1078 DtMail::MailRc::readline(FILE *ibuf, char *linebuf)
1086 for (cp = linebuf; c != '\n' && c != EOF; c = getc(ibuf)) {
1090 "Mail: ignoring NULL characters in mail\n");
1095 if (cp - linebuf < LINESIZE-2)
1099 if (c == EOF && cp == linebuf)
1101 return(cp - linebuf + 1);
1106 * Pop the current input back to the previous level.
1107 * Update the "sourcing" flag as appropriate.
1110 void DtMail::MailRc::unstack()
1113 fprintf(stderr,"\"Source\" stack over-pop.\n");
1119 fprintf(stderr,"Unmatched \"if\"\n");
1120 cond = sstack[ssp].s_cond;
1121 input = sstack[ssp--].s_file;
1126 * Find the correct command in the command table corresponding
1127 * to the passed command "word"
1130 void *DtMail::MailRc::lex(char word[])
1132 register struct cmd *cp;
1134 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
1135 if (DtMail::MailRc::isprefix(word, cp->c_name))
1141 * Determine if as1 is a valid prefix of as2.
1142 * Return true if yep.
1145 int DtMail::MailRc::isprefix(char *as1, char *as2)
1147 register char *s1, *s2;
1151 while (*s1++ == *s2)
1154 return(*--s1 == '\0');
1158 * Scan out the list of string arguments, shell style
1162 int DtMail::MailRc::getrawlist(char line[], char **argv, int argc)
1164 register char **ap, *cp, *cp2;
1165 char linebuf[LINESIZE], quotec;
1166 register char **last;
1170 last = argv + argc - 1;
1171 while (*cp != '\0') {
1172 while (*cp && strchr(" \t", *cp))
1176 while (*cp != '\0') {
1178 if (*cp == quotec) {
1184 if (*cp && strchr(" \t", *cp))
1186 if (*cp && strchr("'\"", *cp))
1197 "Too many elements in the list; excess discarded\n");
1200 *ap++ = strdup((char*)linebuf);
1206 void DtMail::MailRc::freerawlist(char **argv)
1216 * Get the value of a variable and return it.
1217 * Look in the environment if its not available locally.
1220 char *DtMail::MailRc::mt_value(char name[])
1222 register struct var *vp;
1224 // extern char *getenv();
1226 if ((vp = lookup(name, (struct var **)this->variables)) == (struct var *)NULL)
1234 * Locate a variable and return its variable
1237 struct var *DtMail::MailRc::lookup(char *name, struct var **hasharray)
1239 register struct var *vp;
1243 for (vp = hasharray[h]; vp != (struct var *)NULL; vp = vp->v_link)
1244 if (strcmp(vp->v_name, name) == 0)
1246 return ((struct var *)NULL);
1251 * Put add users to a group.
1254 int DtMail::MailRc::group(char **argv, DtMail::MailRc *)
1262 * Insert names from the command list into the group.
1263 * Who cares if there are duplicates? They get tossed
1267 /* compute the size of the buffer */
1269 for (ap = argv+1; *ap != NOSTR; ap++) {
1270 size += strlen(*ap) +1;
1272 buf = (char *)malloc(size);
1274 for (ap = argv+1; *ap != NOSTR; ap++) {
1281 MailRc::add_alias((char *)argv[0], (char *)buf);
1287 DtMail::MailRc::wgroup(const char * verbatim, char ** argv, FILE * outf)
1294 for (ap = argv+1; *ap != NOSTR; ap++) {
1295 size += strlen(*ap) +1;
1297 buf = (char *)malloc(size);
1299 for (ap = argv+1; *ap != NOSTR; ap++) {
1306 char * cur = (char *)hm_test((struct hash **)glob.g_alias, argv[0]);
1307 if (!cur || (!clearAliases && hm_ismarked((struct hash **)glob.g_alias,
1309 // Its been removed or written. Dont write it to the file.
1314 if (strcmp(cur, buf) == 0) {
1315 // It hasnt changed. Write the original to preserve spacing.
1317 fwrite(verbatim, 1, strlen(verbatim), outf);
1320 // It has changed. Create a new alias.
1322 fwrite("alias ", 1, 6, outf);
1323 fwrite(argv[0], 1, strlen(argv[0]), outf);
1324 fwrite(" ", 1, 1, outf);
1325 fwrite(cur, 1, strlen(cur), outf);
1326 fwrite("\n", 1, 1, outf);
1329 hm_mark((struct hash **)glob.g_alias, argv[0]);
1333 DtMail::MailRc::ngroup(char * key, void * data, void * client_data)
1335 char * value = (char *)data;
1336 FILE * outf = (FILE *)client_data;
1338 fwrite("alias ", 1, 6, outf);
1339 fwrite(key, 1, strlen(key), outf);
1340 fwrite(" ", 1, 1, outf);
1341 fwrite(value, 1, strlen(value), outf);
1342 fwrite("\n", 1, 1, outf);
1346 DtMail::MailRc::nalias(char * key, void * data, void * client_data)
1348 DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1349 char *new_alias = NULL;
1350 char *white_space = NULL;
1352 int i, num_spaces = 0;
1353 int key_len = strlen(key);
1354 // figure out whitespace for formatting
1355 // assume 13 for normal sized alias name
1357 if(key_len < 13) // need to add spaces
1359 num_spaces = 13 - key_len;
1361 white_space = (char *)malloc(num_spaces + 1);
1362 white_space[0] = '\0';
1364 for(i = 0; i < num_spaces; i++)
1365 white_space[i] = ' ';
1367 white_space[num_spaces] = '\0';
1369 // strcat(white_space, " ");
1371 /* make an alias string */
1372 m_size = key_len + strlen((char *)white_space)
1373 + strlen((char *)data) + strlen(" = ") + 1;
1374 new_alias = (char *)malloc(m_size);
1376 sprintf(new_alias, "%s%s = %s",key, white_space, (char *) data);
1381 /* make an alias string */
1382 m_size = key_len + strlen((char *)data) + strlen(" = ") + 1;
1383 new_alias = (char *)malloc(m_size);
1385 sprintf(new_alias, "%s = %s",key, (char *) data);
1389 value_list->append(new_alias);
1394 DtMail::MailRc::nignorelist(char * key, void * data, void * client_data)
1397 DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1398 char *new_ignore = NULL;
1400 new_ignore = (char *)malloc(strlen((char *)key) + 2);
1402 sprintf(new_ignore, "%s", key);
1404 value_list->append(new_ignore);
1410 * Set or display a variable value. Syntax is similar to that
1414 int DtMail::MailRc::set(char **arglist, DtMail::MailRc *)
1416 register char *cp, *cp2;
1417 char varbuf[LINESIZE], **ap;
1421 for (ap = arglist; *ap != NOSTR; ap++) {
1424 while (*cp != '=' && *cp != '\0')
1431 if (equal(varbuf, "")) {
1432 fprintf(stderr,"Non-null variable name required\n");
1436 MailRc::mt_assign(varbuf, cp);
1442 DtMail::MailRc::wset(const char * verbatim,
1446 char varbuf[LINESIZE];
1447 char *cp, *cp2, **ap;
1449 for (ap = arglist; *ap != NOSTR; ap++) {
1452 while (*cp != '=' && *cp != '\0')
1459 if (equal(varbuf, "")) {
1463 // Lookup the current value, and see if we should rewrite this
1466 struct var * vp = lookup(varbuf, (struct var **)variables);
1467 if (vp == NULL || vp->v_written) {
1468 // If the original input line was set novar then just write
1469 // it out again. We can not easily track duplicates here.
1471 if (varbuf[0] == 'n' && varbuf[1] == 'o') {
1472 fwrite(verbatim, 1, strlen(verbatim), outf);
1475 // This variable has been unassigned or previously written.
1476 // Remove it from the file, by not writing it.
1481 // Compare the values. If equal, then write the line verbatim to
1482 // preserve the users original spacing and quoting.
1484 if (strcmp(cp, vp->v_value) == 0) {
1485 fwrite(verbatim, 1, strlen(verbatim), outf);
1489 // Write the variable, with a new value.
1491 fwrite("set ", 1, 4, outf);
1492 fwrite(varbuf, 1, strlen(varbuf), outf);
1493 if (strlen(vp->v_value) > 0) {
1494 fwrite("='", 1, 2, outf);
1495 fwrite(vp->v_value, 1, strlen(vp->v_value), outf);
1496 fwrite("'", 1, 1, outf);
1498 fwrite("\n", 1, 1, outf);
1505 * Unset a bunch of variable values.
1508 int DtMail::MailRc::unset(char **arglist, DtMail::MailRc *)
1512 for (ap = arglist; *ap != NOSTR; ap++)
1513 (void) MailRc::mt_deassign(*ap);
1518 DtMail::MailRc::wunset(const char * verbatim,
1523 if(verbatim != NULL && outf != NULL)
1524 fwrite(verbatim, 1, strlen(verbatim), outf);
1528 * Hash the passed string and return an index into
1529 * the variable or group hash table.
1531 int DtMail::MailRc::hash(char *name)
1533 register unsigned h;
1536 for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
1538 return (h % HSHSIZE);
1541 /* manage the alias list */
1542 void DtMail::MailRc::add_alias(char *name, char *value)
1547 /* aliases to the same name get appended to the list */
1548 old = (char*)MailRc::hm_test((struct hash **)glob.g_alias, name);
1553 size = strlen(value) + strlen(old) + 2;
1554 new_ptr = (char *)malloc(size);
1555 sprintf(new_ptr, "%s %s", value, old);
1557 /* delete any old bindings for the name */
1558 MailRc::hm_delete((struct hash**)glob.g_alias, name);
1560 /* add the new binding */
1561 MailRc::hm_add((struct hash**)glob.g_alias, name, new_ptr, size);
1563 /* free up the temporary space */
1566 /* add the new binding */
1567 MailRc::hm_add((struct hash**)glob.g_alias, name, value, strlen(value) +1);
1572 * Assign a value to a mail variable.
1574 void DtMail::MailRc::mt_assign(char *name,char * val)
1578 (void) mt_deassign(name+1);
1579 else if (name[0]=='n' && name[1]=='o')
1580 (void) mt_deassign(name+2);
1581 else if ((val[0] == 'n' || val[0] == 'N') &&
1582 (val[1] == 'o' || val[1] == 'O') && val[2] == '\0') {
1583 (void) mt_deassign(name);
1584 } else mt_puthash(name, vcopy(val), MailRc::variables);
1587 int DtMail::MailRc::mt_deassign(char *s)
1589 register struct var *vp, *vp2;
1592 if ((vp2 = lookup(s, MailRc::variables)) == (struct var *)NULL) {
1596 if (vp2 == MailRc::variables[h]) {
1597 MailRc::variables[h] = MailRc::variables[h]->v_link;
1599 vfree(vp2->v_value);
1603 for (vp = MailRc::variables[h]; vp->v_link != vp2; vp = vp->v_link)
1605 vp->v_link = vp2->v_link;
1607 vfree(vp2->v_value);
1613 * associate val with name in hasharray
1615 void DtMail::MailRc::mt_puthash(char *name, char *val, struct var **hasharray)
1617 register struct var *vp;
1620 vp = lookup(name, hasharray);
1621 if (vp == (struct var *)NULL) {
1623 vp = (struct var *) (calloc(sizeof *vp, 1));
1624 vp->v_name = vcopy(name);
1625 vp->v_link = hasharray[h];
1634 DtMail::MailRc::mt_scan(FILE * outf)
1636 for (int slot = 0; slot < HSHSIZE; slot++) {
1637 for (var * vp = MailRc::variables[slot]; vp != (struct var *)NULL; vp = vp->v_link) {
1638 if (!vp->v_written) {
1639 fwrite("set ", 1, 4, outf);
1640 fwrite(vp->v_name, 1, strlen(vp->v_name), outf);
1641 if (strlen(vp->v_value)) {
1642 fwrite("='", 1, 2, outf);
1643 fwrite(vp->v_value, 1, strlen(vp->v_value), outf);
1644 fwrite("'", 1, 1, outf);
1646 fwrite("\n", 1, 1, outf);
1654 * Copy a variable value into permanent space.
1655 * Do not bother to alloc space for "".
1657 char *DtMail::MailRc::vcopy(char *str)
1660 if (strcmp(str, "") == 0)
1662 return (strdup(str));
1666 * Free up a variable string. We do not bother to allocate
1667 * strings whose value is "" since they are expected to be frequent.
1668 * Thus, we cannot free same!
1670 void DtMail::MailRc::vfree(char *cp)
1673 if (strcmp(cp, "") != 0)
1677 void *DtMail::MailRc::hm_alloc(void)
1679 struct hash **table;
1681 table = (struct hash**)malloc(sizeof (struct hash) * HASHSIZE);
1682 memset(table, '\0', sizeof (struct hash) * HASHSIZE);
1685 void DtMail::MailRc::hm_add(struct hash **table,
1691 register struct hash *h;
1696 index = hash_index(key);
1697 h = (struct hash *)malloc(sizeof (struct hash));
1698 h->h_next = table[index];
1701 h->h_key = strdup(key);
1702 if (size && value != NULL) {
1703 h->h_value = malloc(size);
1704 memcpy(h->h_value, value, size);
1706 h->h_value = nullfield;
1711 void DtMail::MailRc::hm_delete(struct hash **table, char *key)
1714 register struct hash *h;
1715 register struct hash *old;
1720 index = hash_index(key);
1724 if (strcasecmp(h->h_key, key) == 0) {
1725 /* found the match */
1727 table[index] = h->h_next;
1729 old->h_next = h->h_next;
1741 void *DtMail::MailRc::hm_test(struct hash **table, char *key)
1743 register struct hash *h;
1748 h = table[hash_index(key)];
1751 if (strcasecmp(h->h_key, key) == 0) {
1753 return (h->h_value);
1762 void DtMail::MailRc::hm_mark(struct hash **table, char *key)
1764 register struct hash *h;
1769 h = table[hash_index(key)];
1772 if (strcasecmp(h->h_key, key) == 0) {
1781 int DtMail::MailRc::hm_ismarked(struct hash **table, char *key)
1783 register struct hash *h;
1785 if (!table) return 0;
1786 h = table[hash_index(key)];
1789 if (strcasecmp(h->h_key, key) == 0) return(h->h_written);
1797 DtMail::MailRc::hm_scan(struct hash **table, hm_callback callback, void * client_data)
1799 for (int slot = 0; slot < HASHSIZE; slot++) {
1800 for (struct hash * h = table[slot]; h; h = h->h_next) {
1801 if (!h->h_written) {
1802 callback(h->h_key, h->h_value, client_data);
1809 int DtMail::MailRc::hash_index(char *key)
1811 register unsigned h;
1825 return (h % HASHSIZE);
1828 void DtMail::MailRc::free_hash(struct hash *h)
1831 if (h->h_value != nullfield) {
1838 * Add the given header fields to the ignored list.
1839 * If no arguments, print the current list of ignored fields.
1841 int DtMail::MailRc::igfield(char *list[], DtMail::MailRc *)
1845 for (ap = list; *ap != 0; ap++) {
1846 // DP(("igfield: adding %s\n", *ap));
1847 MailRc::add_ignore(*ap);
1853 DtMail::MailRc::wigfield(const char * verbatim, char ** list, FILE * outf)
1856 Boolean ignore_written = FALSE;
1858 // We need to see that every name in this line still exists. If not,
1859 // then we will make a second pass, rewriting the line with only the
1863 for (ap = list; *ap && good; ap++) {
1864 if (!hm_test((struct hash **)glob.g_ignore, *ap)) {
1871 fwrite(verbatim, 1, strlen(verbatim), outf);
1872 for (ap = list; *ap; ap++) {
1873 hm_mark((struct hash **)glob.g_ignore, *ap);
1878 // Create a new ignore line, leaving out the ignores that have
1879 // been removed. Also, dont write any ignores that have been
1880 // previously written.
1882 for (ap = list; *ap; ap++) {
1883 if (hm_test((struct hash **)glob.g_ignore, *ap) &&
1884 !hm_ismarked((struct hash **)glob.g_ignore, *ap)) {
1885 // Only write 'ignore' if there is something in the list
1886 if (!ignore_written) {
1887 fwrite("ignore ", 1, 7, outf);
1888 ignore_written = TRUE;
1890 fwrite(*ap, 1, strlen(*ap), outf);
1891 fwrite(" ", 1, 1, outf);
1892 hm_mark((struct hash **)glob.g_ignore, *ap);
1896 fwrite("\n", 1, 1, outf);
1900 DtMail::MailRc::nigfield(char * key, void *, void * client_data)
1902 DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1904 value_list->append(key);
1907 /* manage the retain/ignore list */
1908 void DtMail::MailRc::add_ignore(char *name)
1910 if(! MailRc::hm_test((struct hash **)glob.g_ignore, name)) {
1911 /* name is not already there... */
1912 MailRc::hm_add((struct hash **)glob.g_ignore, name, NULL, 0);
1918 * Set the list of alternate names.
1920 int DtMail::MailRc::alternates(char **namelist, DtMail::MailRc *)
1922 while (*namelist != NULL)
1923 MailRc::add_alternates(*namelist++);
1928 DtMail::MailRc::walternates(const char * verbatim, char ** list, FILE * outf)
1930 // This is like ignores. We need to make sure all of the alternates
1931 // on this command are still in the database.
1935 for (ap = list; *ap && good; ap++) {
1936 if (!hm_test((struct hash **)glob.g_alternates, *ap)) {
1943 fwrite(verbatim, 1, strlen(verbatim), outf);
1944 for (ap = list; *ap; ap++) {
1945 hm_mark((struct hash **)glob.g_alternates, *ap);
1950 // Write out the alternates that still exist and have not been
1951 // previously written.
1953 Boolean written = FALSE;
1954 for (ap = list; *ap; ap++) {
1955 if (hm_test((struct hash **)glob.g_alternates, *ap) &&
1956 !hm_ismarked((struct hash **)glob.g_alternates, *ap)) {
1958 fwrite("alternates ", 1, 11, outf);
1961 fwrite(*ap, 1, strlen(*ap), outf);
1962 fwrite(" ", 1, 1, outf);
1963 hm_mark((struct hash **)glob.g_alternates, *ap);
1969 DtMail::MailRc::nalternates(char * key, void *, void * client_data)
1971 DtVirtArray<char *> *value_list = (DtVirtArray<char *> *)client_data;
1973 value_list->append(key);
1976 /* manage the alternate name list */
1977 void DtMail::MailRc::add_alternates(char *name)
1979 if(! MailRc::hm_test((struct hash**)glob.g_alternates, name)) {
1980 /* name is not already there... */
1981 MailRc::hm_add((struct hash **)glob.g_alternates, name, NULL, 0);
1986 * Determine the current folder directory name.
1989 DtMail::MailRc::getfolderdir(char *name, size_t buffsize)
1993 if ((folder = mt_value("folder")) == NOSTR)
1996 snprintf(name, buffsize, "%s", folder);
1998 snprintf(name, buffsize, "%s/%s", mt_value("HOME"), folder);
2003 * Take a file name, possibly with shell meta characters
2004 * in it and expand it by using "sh -c echo filename"
2005 * Return the file name as a dynamic string.
2009 DtMail::MailRc::expand(char *name)
2011 char *xname = new char[LINESIZE];
2012 char *cmdbuf = new char[LINESIZE];
2014 register int pid, l;
2015 register char *cp, *Shell;
2018 char *retchr = NULL;
2020 if (name[0] == '+' && getfolderdir(cmdbuf, LINESIZE) >= 0) {
2021 sprintf(xname, "%s/%s", cmdbuf, name + 1);
2022 str = expand(xname);
2027 if (!strpbrk(name, "~{[*?$`'\"\\")) {
2028 return(strdup(name));
2030 if (pipe(pivec) < 0) {
2034 return(strdup(name));
2036 sprintf(cmdbuf, "echo %s", name);
2037 if ((pid = DTMAIL_FORK()) == 0) {
2038 Shell = mt_value("SHELL");
2039 if (Shell == NOSTR || *Shell=='\0')
2046 execlp(Shell, Shell, "-c", cmdbuf, (char *)0);
2058 l = read(pivec[0], xname, LINESIZE);
2060 while (wait(&s) != pid);
2063 if (s != 0 && s != SIGPIPE) {
2064 fprintf(stderr, "Echo failed\n");
2072 fprintf(stderr, "%s: No match\n", name);
2075 if (l == LINESIZE) {
2076 fprintf(stderr, "Buffer overflow expanding %s\n", name);
2080 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
2083 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
2084 fprintf(stderr, "%s: Ambiguous\n", name);
2088 retchr = strdup(xname);
2101 DtMail::MailRc::source(char **arglist, DtMail::MailRc *self)
2103 char *fname = arglist[0];
2107 /* if any of the three if test failed, return 0,
2108 * since we have not updated the input and stack pointer yet
2110 if ((cp = self->expand(fname)) == NOSTR)
2113 if ((fi = fopen(cp, "r")) == NULL) {
2119 if (self->ssp >= NOFILE - 2) {
2123 self->sstack[++(self->ssp)].s_file = self->input;
2124 self->sstack[self->ssp].s_cond = self->cond;
2133 DtMail::MailRc::wsource(const char * verbatim, char ** arglist, FILE * outf)
2136 if(verbatim != NULL && outf != NULL)
2137 fwrite(verbatim, 1, strlen(verbatim), outf);
2141 DtMail::MailRc::ifcmd(char **arglist, DtMail::MailRc *self)
2145 if (self->cond != CANY) {
2146 fprintf(stderr,"Illegal nested \"if\"\n");
2165 fprintf(stderr,"Unrecognized if-keyword: \"%s\"\n",
2173 DtMail::MailRc::wifcmd(const char * verbatim, char ** arglist, FILE * outf)
2176 if(verbatim != NULL && outf != NULL)
2177 fwrite(verbatim, 1, strlen(verbatim), outf);
2182 DtMail::MailRc::elsecmd(char **, DtMail::MailRc *self)
2185 switch (self->cond) {
2187 fprintf(stderr, "\"Else\" without matching \"if\"\n");
2199 self->cond = CNOTTY;
2207 fprintf(stderr,"invalid condition encountered\n");
2215 DtMail::MailRc::welsecmd(const char * verbatim, char ** arglist, FILE * outf)
2218 if(verbatim != NULL && outf != NULL)
2219 fwrite(verbatim, 1, strlen(verbatim), outf);
2224 DtMail::MailRc::endifcmd(char **, DtMail::MailRc *self)
2227 if (self->cond == CANY) {
2228 fprintf(stderr,"\"Endif\" without matching \"if\"\n");
2236 DtMail::MailRc::wendifcmd(const char * verbatim, char ** arglist, FILE * outf)
2239 if(verbatim != NULL && outf != NULL)
2240 fwrite(verbatim, 1, strlen(verbatim), outf);
2244 DtMail::MailRc::clearaliases(char **, DtMail::MailRc *)
2246 DtVirtArray<char *> *value_list = NULL;
2249 value_list = new DtVirtArray<char *>(10);
2251 hm_scan((struct hash **)glob.g_alias, nalias, value_list);
2253 while (value_list->length()) {
2254 char *buf, *val = (*value_list)[0];
2255 if ((buf = strchr(val, ' ')) != NULL)
2257 if (hm_test((struct hash **)glob.g_alias, val))
2258 hm_delete((struct hash **)glob.g_alias, val);
2259 value_list->remove(0);
2266 DtMail::MailRc::wclearaliases(const char *, char **, FILE *)