% mailalias.sl -*- mode: slang; mode: fold -*- % % expand any aliases in the To, Cc, Bcc, From, Reply-To: lines % % - use Pine-style (TAB delimited) aliases % - use UCB Mail (space delimited) aliases % % Pine (.addressbook) style: % alias-name[TAB]Family Name(s), First Name(s)[TAB]e-mail-address % alias-name[TAB]space for name[TAB]e-mail-address % % UCB Mail (.mailrc) style % alias alias-name e-mail-address % group alias-name address1 address2 ... % % The mail entry point: mailalias_expand (); % relies on the X-Mailer: field to indicate the end of the header % % add to your mail_hook() % % define mail_hook () % { % local_setkey ("mailalias_expand", "^C^E"); % ... % } % %{{{ Public Variables %!% where to find the [TAB] delimited mail alias list custom_variable ("Mailaliases", dircat (getenv ("HOME"), ".addressbook")); %!% where to find the [SPACE] delimited mail alias list !if (is_defined ("Mailrc")) { variable Mailrc = getenv ("MAILRC"); if (Mailrc == NULL) Mailrc = dircat (getenv ("HOME"), ".mailrc"); } %}}} %{{{ Private Variables static variable Mailalias_buf = " *mailalias*"; %}}} %{{{ Private Functions %{{{ forward reference define mailalias_xAlias(); %}}} % take a comma-delimited list of aliases and return a list of fully % expanded names and addresses define mailalias_xList (raw) %{{{ { variable str, list = Null_String; if (strlen (Mailrc)) raw = strtrans (raw, " ", ","); raw = strcompress (raw, ","); foreach (strchop (raw, ',', 0)) { str = mailalias_xAlias (strtrim (())); if (strlen (str)) { if (strlen (list)) list += ",\n\t"; list += str; } } strtrim (list); % return on the stack } %}}} % expands alias => "Full Name" % quotes any names that contain '@' to % for a [TAB] delimited field define mailalias_xAlias (alias) %{{{ { variable name, addr = Null_String; variable name0, name1; !if (strlen (alias)) return addr; if (is_substr (alias, char ('@'))) { % "" if (is_substr (alias, "<")) alias; else "<" + alias + ">"; return; } bob (); !if (right (bol_fsearch (alias + "\t"))) return alias; % hand-rolled functions, regular expressions are too inconsistent % and do not work across lines USER_BLOCK0 { push_mark (); % extract a field skip_chars ("^\t"); % next field bufsubstr (); % return the field on the stack go_right (1); % skip TAB char } name = X_USER_BLOCK0; % fullname or surname name0 = extract_element (name, 0, ','); name1 = extract_element (name, 1, ','); if (name1 == NULL) name1 = ""; name = strtrim (strtrim (name1) + " " + strtrim (name0)); % "quote" name in case it contains one of `,.@' name = "\"" + name + "\" "; if (looking_at_char ('(')) % distribution list (with parentheses) { name = Null_String; % ignore name of distribution list? go_right (1); push_mark (); !if (fsearch_char (')')) skip_chars ("^\t "); % messed-up entry? addr = mailalias_xList (bufsubstr ()); } else { addr = X_USER_BLOCK0; addr = "<" + addr + ">"; } name + addr; % "Full Name" } %}}} % expands alias => user@machine.domain % for a [SPACE] delimited field define mailrc_xAlias (alias) %{{{ { variable name, addr; variable n; alias = strtrim (alias); if (not(strlen(alias)) or is_substr (alias, char ('@'))) return alias; % look for "alias name" name = "alias " + alias + " "; bob (); % Convert all tabs to spaces replace ("\t", " "); n = bol_fsearch (name); !if (n) { % look for "group name" name = "group " + alias + " "; n = bol_fsearch (name); !if (n) return alias; } go_right (n); addr = ""; % hand-rolled functions, regular expressions are too inconsistent forever { skip_white (); if (eolp ()) break; push_mark (); skip_chars ("^ "); % next address name = bufsubstr (); % address if (strlen (addr)) addr += ",\n\t"; addr += name; } addr; } %}}} % look for the addresses that follow FIELD and are before the next one. define mailalias_xField (field) %{{{ { variable n, addr, buf = "*mail*"; bob (); !if (right (bol_fsearch (field))) return; % not there - trouble! push_mark (); !if (re_fsearch ("^[-A-Za-z]+:")) % search for next header line { pop_mark_1 (); return; } exchange_point_and_mark (); addr = bufsubstr (); n = strlen (addr); deln (n--, n); % don't delete newline character setbuf (Mailalias_buf); addr = "\t" + mailalias_xList (addr); sw2buf (buf); insert (addr); } %}}} %}}} %!% expands "To, Cc, Bcc" mail header fields using the aliases/addresses %!% that are defined in Mailaliases define mailalias_expand () %{{{ { variable buf = "*mail*"; if (strcmp (buf, whatbuf)) error ("Mail mode only"); push_spot_bob (); !if (bol_fsearch ("X-Mailer:")) { !if (bol_fsearch ("Subject:")) { pop_spot (); error ("Failed to find text divider or Subject."); } } push_mark (); bob (); narrow (); setbuf (Mailalias_buf); if (insert_file (Mailaliases) > 0) { variable header; set_buffer_modified_flag (0); % mark unmodified sw2buf (buf); % order isn't important Null_String; "From:"; "Reply-To:"; "Bcc:"; "Cc:"; "To:"; while (header = (), strlen (header)) mailalias_xField (header); } else { sw2buf (buf); } delbuf (Mailalias_buf); widen (); pop_spot (); } %}}} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %{{{ check that everything is okay, unload as appropriate #ifexists stat_file % If the files exist, they may be empty. Here is a quick check for that. foreach ([&Mailaliases, &Mailrc]) { $1 = (); $2 = stat_file (@$1); if ($2 != NULL) { if ($2.st_size == 0) @$1 = ""; } } #endif %verror (Mailaliases); % ~/.addressbook is okay, un-load Mailrc stuff if (1 == file_status (Mailaliases)) { eval (".()mailrc_xAlias"); Mailrc = Null_String; } % ~/.mailrc is okay, then un-load addressbook stuff & over-load definition else if (1 == file_status (Mailrc)) { eval (".(mailrc_xAlias) mailalias_xAlias"); Mailaliases = Mailrc; } % nothing seems to have worked, so get rid of everything else { eval (".()mailrc_xAlias"); eval (".()mailalias_xAlias"); eval (".()mailalias_xList"); eval (".()mailalias_xField"); eval (".()mailalias_expand"); Mailaliases = Null_String; } %}}}