xref: /plan9-contrib/sys/src/cmd/upas/send/dest.c (revision 25f7656a92d4745f0ae4bb0ac3376f038ab1f088)
1 #include "common.h"
2 #include "send.h"
3 
4 static String* s_parseq(String*, String*);
5 
6 /* exports */
7 dest *dlist;
8 
9 extern dest*
d_new(String * addr)10 d_new(String *addr)
11 {
12 	dest *dp;
13 
14 	dp = (dest *)mallocz(sizeof(dest), 1);
15 	if (dp == 0) {
16 		perror("d_new");
17 		exit(1);
18 	}
19 	dp->same = dp;
20 	dp->nsame = 1;
21 	dp->nchar = 0;
22 	dp->next = dp;
23 	dp->addr = escapespecial(addr);
24 	dp->parent = 0;
25 	dp->repl1 = dp->repl2 = 0;
26 	dp->status = d_undefined;
27 	return dp;
28 }
29 
30 extern void
d_free(dest * dp)31 d_free(dest *dp)
32 {
33 	if (dp != 0) {
34 		s_free(dp->addr);
35 		s_free(dp->repl1);
36 		s_free(dp->repl2);
37 		free((char *)dp);
38 	}
39 }
40 
41 /* The following routines manipulate an ordered list of items.  Insertions
42  * are always to the end of the list.  Deletions are from the beginning.
43  *
44  * The list are circular witht the `head' of the list being the last item
45  * added.
46  */
47 
48 /*  Get first element from a circular list linked via 'next'. */
49 extern dest *
d_rm(dest ** listp)50 d_rm(dest **listp)
51 {
52 	dest *dp;
53 
54 	if (*listp == 0)
55 		return 0;
56 	dp = (*listp)->next;
57 	if (dp == *listp)
58 		*listp = 0;
59 	else
60 		(*listp)->next = dp->next;
61 	dp->next = dp;
62 	return dp;
63 }
64 
65 /*  Insert a new entry at the end of the list linked via 'next'. */
66 extern void
d_insert(dest ** listp,dest * new)67 d_insert(dest **listp, dest *new)
68 {
69 	dest *head;
70 
71 	if (*listp == 0) {
72 		*listp = new;
73 		return;
74 	}
75 	if (new == 0)
76 		return;
77 	head = new->next;
78 	new->next = (*listp)->next;
79 	(*listp)->next = head;
80 	*listp = new;
81 	return;
82 }
83 
84 /*  Get first element from a circular list linked via 'same'. */
85 extern dest *
d_rm_same(dest ** listp)86 d_rm_same(dest **listp)
87 {
88 	dest *dp;
89 
90 	if (*listp == 0)
91 		return 0;
92 	dp = (*listp)->same;
93 	if (dp == *listp)
94 		*listp = 0;
95 	else
96 		(*listp)->same = dp->same;
97 	dp->same = dp;
98 	return dp;
99 }
100 
101 /* Look for a duplicate on the same list */
102 int
d_same_dup(dest * dp,dest * new)103 d_same_dup(dest *dp, dest *new)
104 {
105 	dest *first = dp;
106 
107 	if(new->repl2 == 0)
108 		return 1;
109 	do {
110 		if(strcmp(s_to_c(dp->repl2), s_to_c(new->repl2))==0)
111 			return 1;
112 		dp = dp->same;
113 	} while(dp != first);
114 	return 0;
115 }
116 
117 /* Insert an entry into the corresponding list linked by 'same'.  Note that
118  * the basic structure is a list of lists.
119  */
120 extern void
d_same_insert(dest ** listp,dest * new)121 d_same_insert(dest **listp, dest *new)
122 {
123 	dest *dp;
124 	int len;
125 
126 	if(new->status == d_pipe || new->status == d_cat) {
127 		len = new->repl2 ? strlen(s_to_c(new->repl2)) : 0;
128 		if(*listp != 0){
129 			dp = (*listp)->next;
130 			do {
131 				if(dp->status == new->status
132 				&& strcmp(s_to_c(dp->repl1), s_to_c(new->repl1))==0){
133 					/* remove duplicates */
134 					if(d_same_dup(dp, new))
135 						return;
136 					/* add to chain if chain small enough */
137 					if(dp->nsame < MAXSAME
138 					&& dp->nchar + len < MAXSAMECHAR){
139 						new->same = dp->same;
140 						dp->same = new;
141 						dp->nchar += len + 1;
142 						dp->nsame++;
143 						return;
144 					}
145 				}
146 				dp = dp->next;
147 			} while (dp != (*listp)->next);
148 		}
149 		new->nchar = strlen(s_to_c(new->repl1)) + len + 1;
150 	}
151 	new->next = new;
152 	d_insert(listp, new);
153 }
154 
155 /*
156  *  Form a To: if multiple destinations.
157  *  The local! and !local! checks are artificial intelligence,
158  *  there should be a better way.
159  */
160 extern String*
d_to(dest * list)161 d_to(dest *list)
162 {
163 	dest *np, *sp;
164 	String *s;
165 	int i, n;
166 	char *cp;
167 
168 	s = s_new();
169 	s_append(s, "To: ");
170 	np = list;
171 	i = n = 0;
172 	do {
173 		np = np->next;
174 		sp = np;
175 		do {
176 			sp = sp->same;
177 			cp = s_to_c(sp->addr);
178 
179 			/* hack to get local! out of the names */
180 			if(strncmp(cp, "local!", 6) == 0)
181 				cp += 6;
182 
183 			if(n > 20){	/* 20 to appease mailers complaining about long lines */
184 				s_append(s, "\n\t");
185 				n = 0;
186 			}
187 			if(i != 0){
188 				s_append(s, ", ");
189 				n += 2;
190 			}
191 			s_append(s, cp);
192 			n += strlen(cp);
193 			i++;
194 		} while(sp != np);
195 	} while(np != list);
196 
197 	return unescapespecial(s);
198 }
199 
200 /* expand a String of destinations into a linked list of destiniations */
201 extern dest *
s_to_dest(String * sp,dest * parent)202 s_to_dest(String *sp, dest *parent)
203 {
204 	String *addr;
205 	dest *list=0;
206 	dest *new;
207 
208 	if (sp == 0)
209 		return 0;
210 	addr = s_new();
211 	while (s_parseq(sp, addr)!=0) {
212 		addr = escapespecial(addr);
213 		if(shellchars(s_to_c(addr))){
214 			while(new = d_rm(&list))
215 				d_free(new);
216 			break;
217 		}
218 		new = d_new(addr);
219 		new->parent = parent;
220 		new->authorized = parent->authorized;
221 		d_insert(&list, new);
222 		addr = s_new();
223 	}
224 	s_free(addr);
225 	return list;
226 }
227 
228 #define isspace(c) ((c)==' ' || (c)=='\t' || (c)=='\n')
229 
230 /*  Get the next field from a String.  The field is delimited by white space.
231  *  Anything delimited by double quotes is included in the string.
232  */
233 static String*
s_parseq(String * from,String * to)234 s_parseq(String *from, String *to)
235 {
236 	int c;
237 
238 	if (*from->ptr == '\0')
239 		return 0;
240 	if (to == 0)
241 		to = s_new();
242 	for (c = *from->ptr;!isspace(c) && c != 0; c = *(++from->ptr)){
243 		s_putc(to, c);
244 		if(c == '"'){
245 			for (c = *(++from->ptr); c && c != '"'; c = *(++from->ptr))
246 				s_putc(to, *from->ptr);
247 			s_putc(to, '"');
248 			if(c == 0)
249 				break;
250 		}
251 	}
252 	s_terminate(to);
253 
254 	/* crunch trailing white */
255 	while(isspace(*from->ptr))
256 		from->ptr++;
257 
258 	return to;
259 }
260