261 lines
4.7 KiB
C
261 lines
4.7 KiB
C
#include "common.h"
|
|
#include "send.h"
|
|
|
|
static String* s_parseq(String*, String*);
|
|
|
|
/* exports */
|
|
dest *dlist;
|
|
|
|
extern dest*
|
|
d_new(String *addr)
|
|
{
|
|
dest *dp;
|
|
|
|
dp = (dest *)mallocz(sizeof(dest), 1);
|
|
if (dp == 0) {
|
|
perror("d_new");
|
|
exit(1);
|
|
}
|
|
dp->same = dp;
|
|
dp->nsame = 1;
|
|
dp->nchar = 0;
|
|
dp->next = dp;
|
|
dp->addr = escapespecial(addr);
|
|
dp->parent = 0;
|
|
dp->repl1 = dp->repl2 = 0;
|
|
dp->status = d_undefined;
|
|
return dp;
|
|
}
|
|
|
|
extern void
|
|
d_free(dest *dp)
|
|
{
|
|
if (dp != 0) {
|
|
s_free(dp->addr);
|
|
s_free(dp->repl1);
|
|
s_free(dp->repl2);
|
|
free((char *)dp);
|
|
}
|
|
}
|
|
|
|
/* The following routines manipulate an ordered list of items. Insertions
|
|
* are always to the end of the list. Deletions are from the beginning.
|
|
*
|
|
* The list are circular witht the `head' of the list being the last item
|
|
* added.
|
|
*/
|
|
|
|
/* Get first element from a circular list linked via 'next'. */
|
|
extern dest *
|
|
d_rm(dest **listp)
|
|
{
|
|
dest *dp;
|
|
|
|
if (*listp == 0)
|
|
return 0;
|
|
dp = (*listp)->next;
|
|
if (dp == *listp)
|
|
*listp = 0;
|
|
else
|
|
(*listp)->next = dp->next;
|
|
dp->next = dp;
|
|
return dp;
|
|
}
|
|
|
|
/* Insert a new entry at the end of the list linked via 'next'. */
|
|
extern void
|
|
d_insert(dest **listp, dest *new)
|
|
{
|
|
dest *head;
|
|
|
|
if (*listp == 0) {
|
|
*listp = new;
|
|
return;
|
|
}
|
|
if (new == 0)
|
|
return;
|
|
head = new->next;
|
|
new->next = (*listp)->next;
|
|
(*listp)->next = head;
|
|
*listp = new;
|
|
return;
|
|
}
|
|
|
|
/* Get first element from a circular list linked via 'same'. */
|
|
extern dest *
|
|
d_rm_same(dest **listp)
|
|
{
|
|
dest *dp;
|
|
|
|
if (*listp == 0)
|
|
return 0;
|
|
dp = (*listp)->same;
|
|
if (dp == *listp)
|
|
*listp = 0;
|
|
else
|
|
(*listp)->same = dp->same;
|
|
dp->same = dp;
|
|
return dp;
|
|
}
|
|
|
|
/* Look for a duplicate on the same list */
|
|
int
|
|
d_same_dup(dest *dp, dest *new)
|
|
{
|
|
dest *first = dp;
|
|
|
|
if(new->repl2 == 0)
|
|
return 1;
|
|
do {
|
|
if(strcmp(s_to_c(dp->repl2), s_to_c(new->repl2))==0)
|
|
return 1;
|
|
dp = dp->same;
|
|
} while(dp != first);
|
|
return 0;
|
|
}
|
|
|
|
/* Insert an entry into the corresponding list linked by 'same'. Note that
|
|
* the basic structure is a list of lists.
|
|
*/
|
|
extern void
|
|
d_same_insert(dest **listp, dest *new)
|
|
{
|
|
dest *dp;
|
|
int len;
|
|
|
|
if(new->status == d_pipe || new->status == d_cat) {
|
|
len = new->repl2 ? strlen(s_to_c(new->repl2)) : 0;
|
|
if(*listp != 0){
|
|
dp = (*listp)->next;
|
|
do {
|
|
if(dp->status == new->status
|
|
&& strcmp(s_to_c(dp->repl1), s_to_c(new->repl1))==0){
|
|
/* remove duplicates */
|
|
if(d_same_dup(dp, new))
|
|
return;
|
|
/* add to chain if chain small enough */
|
|
if(dp->nsame < MAXSAME
|
|
&& dp->nchar + len < MAXSAMECHAR){
|
|
new->same = dp->same;
|
|
dp->same = new;
|
|
dp->nchar += len + 1;
|
|
dp->nsame++;
|
|
return;
|
|
}
|
|
}
|
|
dp = dp->next;
|
|
} while (dp != (*listp)->next);
|
|
}
|
|
new->nchar = strlen(s_to_c(new->repl1)) + len + 1;
|
|
}
|
|
new->next = new;
|
|
d_insert(listp, new);
|
|
}
|
|
|
|
/*
|
|
* Form a To: if multiple destinations.
|
|
* The local! and !local! checks are artificial intelligence,
|
|
* there should be a better way.
|
|
*/
|
|
extern String*
|
|
d_to(dest *list)
|
|
{
|
|
dest *np, *sp;
|
|
String *s;
|
|
int i, n;
|
|
char *cp;
|
|
|
|
s = s_new();
|
|
s_append(s, "To: ");
|
|
np = list;
|
|
i = n = 0;
|
|
do {
|
|
np = np->next;
|
|
sp = np;
|
|
do {
|
|
sp = sp->same;
|
|
cp = s_to_c(sp->addr);
|
|
|
|
/* hack to get local! out of the names */
|
|
if(strncmp(cp, "local!", 6) == 0)
|
|
cp += 6;
|
|
|
|
if(n > 20){ /* 20 to appease mailers complaining about long lines */
|
|
s_append(s, "\n\t");
|
|
n = 0;
|
|
}
|
|
if(i != 0){
|
|
s_append(s, ", ");
|
|
n += 2;
|
|
}
|
|
s_append(s, cp);
|
|
n += strlen(cp);
|
|
i++;
|
|
} while(sp != np);
|
|
} while(np != list);
|
|
|
|
return unescapespecial(s);
|
|
}
|
|
|
|
/* expand a String of destinations into a linked list of destiniations */
|
|
extern dest *
|
|
s_to_dest(String *sp, dest *parent)
|
|
{
|
|
String *addr;
|
|
dest *list=0;
|
|
dest *new;
|
|
|
|
if (sp == 0)
|
|
return 0;
|
|
addr = s_new();
|
|
while (s_parseq(sp, addr)!=0) {
|
|
addr = escapespecial(addr);
|
|
if(shellchars(s_to_c(addr))){
|
|
while(new = d_rm(&list))
|
|
d_free(new);
|
|
break;
|
|
}
|
|
new = d_new(addr);
|
|
new->parent = parent;
|
|
new->authorized = parent->authorized;
|
|
d_insert(&list, new);
|
|
addr = s_new();
|
|
}
|
|
s_free(addr);
|
|
return list;
|
|
}
|
|
|
|
#undef isspace
|
|
#define isspace(c) ((c)==' ' || (c)=='\t' || (c)=='\n')
|
|
|
|
/* Get the next field from a String. The field is delimited by white space.
|
|
* Anything delimited by double quotes is included in the string.
|
|
*/
|
|
static String*
|
|
s_parseq(String *from, String *to)
|
|
{
|
|
int c;
|
|
|
|
if (*from->ptr == '\0')
|
|
return 0;
|
|
if (to == 0)
|
|
to = s_new();
|
|
for (c = *from->ptr;!isspace(c) && c != 0; c = *(++from->ptr)){
|
|
s_putc(to, c);
|
|
if(c == '"'){
|
|
for (c = *(++from->ptr); c && c != '"'; c = *(++from->ptr))
|
|
s_putc(to, *from->ptr);
|
|
s_putc(to, '"');
|
|
if(c == 0)
|
|
break;
|
|
}
|
|
}
|
|
s_terminate(to);
|
|
|
|
/* crunch trailing white */
|
|
while(isspace(*from->ptr))
|
|
from->ptr++;
|
|
|
|
return to;
|
|
}
|