| #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; |
| } |