Commit | Line | Data |
---|---|---|
44b61824 | 1 | /*- |
e1a31032 KM |
2 | * Copyright (c) 1989 Jan-Simon Pendry |
3 | * Copyright (c) 1989 Imperial College of Science, Technology & Medicine | |
ad787160 C |
4 | * Copyright (c) 1989, 1993 |
5 | * The Regents of the University of California. All rights reserved. | |
e1a31032 KM |
6 | * |
7 | * This code is derived from software contributed to Berkeley by | |
8 | * Jan-Simon Pendry at Imperial College, London. | |
9 | * | |
ad787160 C |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. All advertising materials mentioning features or use of this software | |
19 | * must display the following acknowledgement: | |
20 | * This product includes software developed by the University of | |
21 | * California, Berkeley and its contributors. | |
22 | * 4. Neither the name of the University nor the names of its contributors | |
23 | * may be used to endorse or promote products derived from this software | |
24 | * without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
36 | * SUCH DAMAGE. | |
e1a31032 | 37 | * |
00737562 | 38 | * $Id: opts.c,v 5.2.2.3 1992/05/31 16:34:13 jsp Exp $ |
e1a31032 KM |
39 | */ |
40 | ||
44b61824 | 41 | #ifndef lint |
ad787160 | 42 | static char sccsid[] = "@(#)opts.c 8.1 (Berkeley) 6/6/93"; |
44b61824 KB |
43 | #endif /* not lint */ |
44 | ||
e1a31032 KM |
45 | #include "am.h" |
46 | ||
8d2991d5 | 47 | extern char *getenv P((const char *)); |
e1a31032 KM |
48 | |
49 | /* | |
50 | * static copy of the options with | |
51 | * which to play | |
52 | */ | |
53 | static struct am_opts fs_static; | |
54 | ||
55 | static char *opt_host = hostname; | |
56 | static char *opt_hostd = hostd; | |
57 | static char nullstr[] = ""; | |
58 | static char *opt_key = nullstr; | |
59 | static char *opt_map = nullstr; | |
60 | static char *opt_path = nullstr; | |
61 | ||
62 | static char *vars[8]; | |
63 | ||
64 | /* | |
65 | * Length of longest option name | |
66 | */ | |
2664d859 | 67 | #define NLEN 16 /* conservative */ |
e1a31032 KM |
68 | #define S(x) (x) , (sizeof(x)-1) |
69 | static struct opt { | |
70 | char *name; /* Name of the option */ | |
71 | int nlen; /* Length of option name */ | |
72 | char **optp; /* Pointer to option value string */ | |
73 | char **sel_p; /* Pointer to selector value string */ | |
74 | } opt_fields[] = { | |
75 | /* Options in something corresponding to frequency of use */ | |
76 | { S("opts"), &fs_static.opt_opts, 0 }, | |
77 | { S("host"), 0, &opt_host }, | |
78 | { S("hostd"), 0, &opt_hostd }, | |
79 | { S("type"), &fs_static.opt_type, 0 }, | |
80 | { S("rhost"), &fs_static.opt_rhost, 0 }, | |
81 | { S("rfs"), &fs_static.opt_rfs, 0 }, | |
82 | { S("fs"), &fs_static.opt_fs, 0 }, | |
83 | { S("key"), 0, &opt_key }, | |
84 | { S("map"), 0, &opt_map }, | |
85 | { S("sublink"), &fs_static.opt_sublink, 0 }, | |
86 | { S("arch"), 0, &arch }, | |
87 | { S("dev"), &fs_static.opt_dev, 0 }, | |
88 | { S("pref"), &fs_static.opt_pref, 0 }, | |
89 | { S("path"), 0, &opt_path }, | |
90 | { S("autodir"), 0, &auto_dir }, | |
91 | { S("delay"), &fs_static.opt_delay, 0 }, | |
92 | { S("domain"), 0, &hostdomain }, | |
93 | { S("karch"), 0, &karch }, | |
94 | { S("cluster"), 0, &cluster }, | |
2664d859 | 95 | { S("wire"), 0, &wire }, |
e1a31032 KM |
96 | { S("byte"), 0, &endian }, |
97 | { S("os"), 0, &op_sys }, | |
332f0791 | 98 | { S("remopts"), &fs_static.opt_remopts, 0 }, |
e1a31032 KM |
99 | { S("mount"), &fs_static.opt_mount, 0 }, |
100 | { S("unmount"), &fs_static.opt_unmount, 0 }, | |
101 | { S("cache"), &fs_static.opt_cache, 0 }, | |
102 | { S("user"), &fs_static.opt_user, 0 }, | |
103 | { S("group"), &fs_static.opt_group, 0 }, | |
104 | { S("var0"), &vars[0], 0 }, | |
105 | { S("var1"), &vars[1], 0 }, | |
106 | { S("var2"), &vars[2], 0 }, | |
107 | { S("var3"), &vars[3], 0 }, | |
108 | { S("var4"), &vars[4], 0 }, | |
109 | { S("var5"), &vars[5], 0 }, | |
110 | { S("var6"), &vars[6], 0 }, | |
111 | { S("var7"), &vars[7], 0 }, | |
112 | { 0, 0, 0, 0 }, | |
113 | }; | |
114 | ||
115 | typedef struct opt_apply opt_apply; | |
116 | struct opt_apply { | |
117 | char **opt; | |
118 | char *val; | |
119 | }; | |
120 | ||
121 | /* | |
122 | * Specially expand the remote host name first | |
123 | */ | |
124 | static opt_apply rhost_expansion[] = { | |
125 | { &fs_static.opt_rhost, "${host}" }, | |
126 | { 0, 0 }, | |
127 | }; | |
128 | /* | |
129 | * List of options which need to be expanded | |
130 | * Note that this the order here _may_ be important. | |
131 | */ | |
132 | static opt_apply expansions[] = { | |
133 | /* { &fs_static.opt_dir, 0 }, */ | |
134 | { &fs_static.opt_sublink, 0 }, | |
135 | { &fs_static.opt_rfs, "${path}" }, | |
136 | { &fs_static.opt_fs, "${autodir}/${rhost}${rfs}" }, | |
137 | { &fs_static.opt_opts, "rw" }, | |
332f0791 | 138 | { &fs_static.opt_remopts, "${opts}" }, |
e1a31032 KM |
139 | { &fs_static.opt_mount, 0 }, |
140 | { &fs_static.opt_unmount, 0 }, | |
141 | { 0, 0 }, | |
142 | }; | |
143 | ||
144 | /* | |
145 | * List of options which need to be free'ed before re-use | |
146 | */ | |
147 | static opt_apply to_free[] = { | |
148 | { &fs_static.fs_glob, 0 }, | |
149 | { &fs_static.fs_local, 0 }, | |
150 | { &fs_static.fs_mtab, 0 }, | |
151 | /* { &fs_static.opt_dir, 0 }, */ | |
152 | { &fs_static.opt_sublink, 0 }, | |
153 | { &fs_static.opt_rfs, 0 }, | |
154 | { &fs_static.opt_fs, 0 }, | |
155 | { &fs_static.opt_rhost, 0 }, | |
156 | { &fs_static.opt_opts, 0 }, | |
332f0791 | 157 | { &fs_static.opt_remopts, 0 }, |
e1a31032 KM |
158 | { &fs_static.opt_mount, 0 }, |
159 | { &fs_static.opt_unmount, 0 }, | |
160 | { &vars[0], 0 }, | |
161 | { &vars[1], 0 }, | |
162 | { &vars[2], 0 }, | |
163 | { &vars[3], 0 }, | |
164 | { &vars[4], 0 }, | |
165 | { &vars[5], 0 }, | |
166 | { &vars[6], 0 }, | |
167 | { &vars[7], 0 }, | |
168 | { 0, 0 }, | |
169 | }; | |
170 | ||
171 | /* | |
172 | * Skip to next option in the string | |
173 | */ | |
174 | static char *opt P((char**)); | |
175 | static char *opt(p) | |
176 | char **p; | |
177 | { | |
178 | char *cp = *p; | |
179 | char *dp = cp; | |
180 | char *s = cp; | |
181 | ||
182 | top: | |
183 | while (*cp && *cp != ';') { | |
184 | if (*cp == '\"') { | |
185 | /* | |
186 | * Skip past string | |
187 | */ | |
188 | cp++; | |
189 | while (*cp && *cp != '\"') | |
190 | *dp++ = *cp++; | |
191 | if (*cp) | |
192 | cp++; | |
193 | } else { | |
194 | *dp++ = *cp++; | |
195 | } | |
196 | } | |
197 | ||
198 | /* | |
199 | * Skip past any remaining ';'s | |
200 | */ | |
201 | while (*cp == ';') | |
202 | cp++; | |
203 | ||
204 | /* | |
205 | * If we have a zero length string | |
206 | * and there are more fields, then | |
207 | * parse the next one. This allows | |
208 | * sequences of empty fields. | |
209 | */ | |
210 | if (*cp && dp == s) | |
211 | goto top; | |
212 | ||
213 | *dp = '\0'; | |
214 | ||
215 | *p = cp; | |
216 | return s; | |
217 | } | |
218 | ||
2664d859 JSP |
219 | static int eval_opts P((char*, char*)); |
220 | static int eval_opts(opts, mapkey) | |
e1a31032 | 221 | char *opts; |
2664d859 | 222 | char *mapkey; |
e1a31032 KM |
223 | { |
224 | /* | |
225 | * Fill in the global structure fs_static by | |
226 | * cracking the string opts. opts may be | |
227 | * scribbled on at will. | |
228 | */ | |
229 | char *o = opts; | |
230 | char *f; | |
231 | ||
232 | /* | |
233 | * For each user-specified option | |
234 | */ | |
235 | while (*(f = opt(&o))) { | |
236 | struct opt *op; | |
237 | enum vs_opt { OldSyn, SelEQ, SelNE, VarAss } vs_opt; | |
238 | char *eq = strchr(f, '='); | |
239 | char *opt; | |
240 | if (!eq || eq[1] == '\0' || eq == f) { | |
241 | /* | |
242 | * No value, just continue | |
243 | */ | |
2664d859 | 244 | plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f); |
e1a31032 KM |
245 | continue; |
246 | } | |
247 | ||
248 | /* | |
249 | * Check what type of operation is happening | |
250 | * !=, =! is SelNE | |
251 | * == is SelEQ | |
252 | * := is VarAss | |
253 | * = is OldSyn (either SelEQ or VarAss) | |
254 | */ | |
255 | if (eq[-1] == '!') { /* != */ | |
256 | vs_opt = SelNE; | |
257 | eq[-1] = '\0'; | |
258 | opt = eq + 1; | |
259 | } else if (eq[-1] == ':') { /* := */ | |
260 | vs_opt = VarAss; | |
261 | eq[-1] = '\0'; | |
262 | opt = eq + 1; | |
263 | } else if (eq[1] == '=') { /* == */ | |
264 | vs_opt = SelEQ; | |
265 | eq[0] = '\0'; | |
266 | opt = eq + 2; | |
267 | } else if (eq[1] == '!') { /* =! */ | |
268 | vs_opt = SelNE; | |
269 | eq[0] = '\0'; | |
270 | opt = eq + 2; | |
271 | } else { /* = */ | |
272 | vs_opt = OldSyn; | |
273 | eq[0] = '\0'; | |
274 | opt = eq + 1; | |
275 | } | |
276 | ||
277 | /* | |
278 | * For each recognised option | |
279 | */ | |
280 | for (op = opt_fields; op->name; op++) { | |
281 | /* | |
282 | * Check whether they match | |
283 | */ | |
284 | if (FSTREQ(op->name, f)) { | |
285 | switch (vs_opt) { | |
286 | #if AMD_COMPAT <= 5000108 | |
287 | case OldSyn: | |
2664d859 | 288 | plog(XLOG_WARNING, "key %s: Old syntax selector found: %s=%s", mapkey, f, opt); |
e1a31032 KM |
289 | if (!op->sel_p) { |
290 | *op->optp = opt; | |
291 | break; | |
292 | } | |
293 | /* fall through ... */ | |
294 | #endif /* 5000108 */ | |
295 | case SelEQ: | |
296 | case SelNE: | |
297 | if (op->sel_p && (STREQ(*op->sel_p, opt) == (vs_opt == SelNE))) { | |
2664d859 JSP |
298 | plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s", |
299 | mapkey, | |
e1a31032 KM |
300 | op->name, |
301 | *op->sel_p, | |
302 | vs_opt == SelNE ? "not " : "", | |
303 | opt); | |
304 | return 0; | |
305 | } | |
306 | break; | |
307 | ||
308 | case VarAss: | |
309 | if (op->sel_p) { | |
2664d859 | 310 | plog(XLOG_USER, "key %s: Can't assign to a selector (%s)", mapkey, op->name); |
e1a31032 KM |
311 | return 0; |
312 | } | |
313 | *op->optp = opt; | |
314 | break; | |
315 | } | |
316 | break; | |
317 | } | |
318 | } | |
319 | ||
320 | if (!op->name) | |
2664d859 | 321 | plog(XLOG_USER, "key %s: Unrecognised key/option \"%s\"", mapkey, f); |
e1a31032 KM |
322 | } |
323 | ||
324 | return 1; | |
325 | } | |
326 | ||
327 | /* | |
328 | * Free an option | |
329 | */ | |
330 | static void free_op P((opt_apply*, int)); | |
331 | /*ARGSUSED*/ | |
332 | static void free_op(p, b) | |
333 | opt_apply *p; | |
334 | int b; | |
335 | { | |
336 | if (*p->opt) { | |
337 | free(*p->opt); | |
338 | *p->opt = 0; | |
339 | } | |
340 | } | |
341 | ||
2664d859 JSP |
342 | /* |
343 | * Normalize slashes in the string. | |
344 | */ | |
345 | void normalize_slash P((char *p)); | |
346 | void normalize_slash(p) | |
347 | char *p; | |
348 | { | |
349 | char *f = strchr(p, '/'); | |
00737562 | 350 | char *f0 = f; |
2664d859 JSP |
351 | if (f) { |
352 | char *t = f; | |
353 | do { | |
354 | /* assert(*f == '/'); */ | |
00737562 | 355 | if (f == f0 && f[0] == '/' && f[1] == '/') { |
332f0791 JSP |
356 | /* copy double slash iff first */ |
357 | *t++ = *f++; | |
358 | *t++ = *f++; | |
359 | } else { | |
360 | /* copy a single / across */ | |
361 | *t++ = *f++; | |
362 | } | |
2664d859 JSP |
363 | |
364 | /* assert(f[-1] == '/'); */ | |
365 | /* skip past more /'s */ | |
366 | while (*f == '/') | |
367 | f++; | |
368 | ||
369 | /* assert(*f != '/'); */ | |
370 | /* keep copying up to next / */ | |
00737562 | 371 | while (*f && *f != '/') { |
2664d859 | 372 | *t++ = *f++; |
00737562 | 373 | } |
2664d859 JSP |
374 | |
375 | /* assert(*f == 0 || *f == '/'); */ | |
376 | ||
377 | } while (*f); | |
378 | *t = 0; /* derived from fix by Steven Glassman */ | |
379 | } | |
380 | } | |
381 | ||
e1a31032 KM |
382 | /* |
383 | * Macro-expand an option. Note that this does not | |
384 | * handle recursive expansions. They will go badly wrong. | |
385 | * If sel is true then old expand selectors, otherwise | |
386 | * don't expand selectors. | |
387 | */ | |
388 | static void expand_op P((opt_apply*, int)); | |
389 | static void expand_op(p, sel_p) | |
390 | opt_apply *p; | |
391 | int sel_p; | |
392 | { | |
393 | /* | |
394 | * The BUFSPACE macros checks that there is enough space | |
395 | * left in the expansion buffer. If there isn't then we | |
396 | * give up completely. This is done to avoid crashing the | |
397 | * automounter itself (which would be a bad thing to do). | |
398 | */ | |
399 | #define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN) | |
400 | static char expand_error[] = "No space to expand \"%s\""; | |
401 | ||
402 | char expbuf[MAXPATHLEN+1]; | |
403 | char nbuf[NLEN+1]; | |
404 | char *ep = expbuf; | |
405 | char *cp = *p->opt; | |
406 | char *dp; | |
407 | #ifdef DEBUG | |
408 | char *cp_orig = *p->opt; | |
409 | #endif /* DEBUG */ | |
410 | struct opt *op; | |
411 | ||
412 | while (dp = strchr(cp, '$')) { | |
413 | char ch; | |
414 | /* | |
415 | * First copy up to the $ | |
416 | */ | |
417 | { int len = dp - cp; | |
418 | if (BUFSPACE(ep, len)) { | |
419 | strncpy(ep, cp, len); | |
420 | ep += len; | |
421 | } else { | |
422 | plog(XLOG_ERROR, expand_error, *p->opt); | |
423 | goto out; | |
424 | } | |
425 | } | |
426 | cp = dp + 1; | |
427 | ch = *cp++; | |
428 | if (ch == '$') { | |
429 | if (BUFSPACE(ep, 1)) { | |
430 | *ep++ = '$'; | |
431 | } else { | |
432 | plog(XLOG_ERROR, expand_error, *p->opt); | |
433 | goto out; | |
434 | } | |
435 | } else if (ch == '{') { | |
436 | /* Expansion... */ | |
2664d859 | 437 | enum { E_All, E_Dir, E_File, E_Domain, E_Host } todo; |
e1a31032 KM |
438 | /* |
439 | * Find closing brace | |
440 | */ | |
441 | char *br_p = strchr(cp, '}'); | |
442 | int len; | |
443 | /* | |
444 | * Check we found it | |
445 | */ | |
446 | if (!br_p) { | |
447 | /* | |
448 | * Just give up | |
449 | */ | |
450 | plog(XLOG_USER, "No closing '}' in \"%s\"", *p->opt); | |
451 | goto out; | |
452 | } | |
453 | len = br_p - cp; | |
454 | /* | |
455 | * Figure out which part of the variable to grab. | |
456 | */ | |
457 | if (*cp == '/') { | |
458 | /* | |
459 | * Just take the last component | |
460 | */ | |
461 | todo = E_File; | |
462 | cp++; | |
463 | --len; | |
464 | } else if (br_p[-1] == '/') { | |
465 | /* | |
466 | * Take all but the last component | |
467 | */ | |
468 | todo = E_Dir; | |
469 | --len; | |
2664d859 JSP |
470 | } else if (*cp == '.') { |
471 | /* | |
472 | * Take domain name | |
473 | */ | |
474 | todo = E_Domain; | |
475 | cp++; | |
476 | --len; | |
477 | } else if (br_p[-1] == '.') { | |
478 | /* | |
479 | * Take host name | |
480 | */ | |
481 | todo = E_Host; | |
482 | --len; | |
e1a31032 KM |
483 | } else { |
484 | /* | |
485 | * Take the whole lot | |
486 | */ | |
487 | todo = E_All; | |
488 | } | |
489 | /* | |
490 | * Truncate if too long. Since it won't | |
491 | * match anyway it doesn't matter that | |
492 | * it has been cut short. | |
493 | */ | |
494 | if (len > NLEN) | |
495 | len = NLEN; | |
496 | /* | |
497 | * Put the string into another buffer so | |
498 | * we can do comparisons. | |
499 | */ | |
500 | strncpy(nbuf, cp, len); | |
501 | nbuf[len] = '\0'; | |
502 | /* | |
503 | * Advance cp | |
504 | */ | |
505 | cp = br_p + 1; | |
506 | /* | |
507 | * Search the option array | |
508 | */ | |
509 | for (op = opt_fields; op->name; op++) { | |
510 | /* | |
511 | * Check for match | |
512 | */ | |
513 | if (len == op->nlen && STREQ(op->name, nbuf)) { | |
514 | char xbuf[NLEN+3]; | |
515 | char *val; | |
516 | /* | |
517 | * Found expansion. Copy | |
518 | * the correct value field. | |
519 | */ | |
520 | if (!(!op->sel_p == !sel_p)) { | |
521 | /* | |
522 | * Copy the string across unexpanded | |
523 | */ | |
524 | sprintf(xbuf, "${%s%s%s}", | |
2664d859 JSP |
525 | todo == E_File ? "/" : |
526 | todo == E_Domain ? "." : "", | |
e1a31032 | 527 | nbuf, |
2664d859 JSP |
528 | todo == E_Dir ? "/" : |
529 | todo == E_Host ? "." : ""); | |
e1a31032 KM |
530 | val = xbuf; |
531 | /* | |
532 | * Make sure expansion doesn't | |
533 | * munge the value! | |
534 | */ | |
535 | todo = E_All; | |
536 | } else if (op->sel_p) { | |
537 | val = *op->sel_p; | |
538 | } else { | |
539 | val = *op->optp; | |
540 | } | |
541 | if (val) { | |
542 | /* | |
543 | * Do expansion: | |
544 | * ${/var} means take just the last part | |
545 | * ${var/} means take all but the last part | |
2664d859 JSP |
546 | * ${.var} means take all but first part |
547 | * ${var.} means take just the first part | |
e1a31032 KM |
548 | * ${var} means take the whole lot |
549 | */ | |
550 | int vlen = strlen(val); | |
551 | char *vptr = val; | |
552 | switch (todo) { | |
553 | case E_Dir: | |
554 | vptr = strrchr(val, '/'); | |
555 | if (vptr) | |
556 | vlen = vptr - val; | |
557 | vptr = val; | |
558 | break; | |
559 | case E_File: | |
560 | vptr = strrchr(val, '/'); | |
561 | if (vptr) { | |
562 | vptr++; | |
563 | vlen = strlen(vptr); | |
564 | } else | |
565 | vptr = val; | |
566 | break; | |
2664d859 JSP |
567 | case E_Domain: |
568 | vptr = strchr(val, '.'); | |
569 | if (vptr) { | |
570 | vptr++; | |
571 | vlen = strlen(vptr); | |
572 | } else { | |
573 | vptr = ""; | |
574 | vlen = 0; | |
575 | } | |
576 | break; | |
577 | case E_Host: | |
578 | vptr = strchr(val, '.'); | |
579 | if (vptr) | |
580 | vlen = vptr - val; | |
581 | vptr = val; | |
582 | break; | |
583 | case E_All: | |
584 | break; | |
e1a31032 KM |
585 | } |
586 | #ifdef DEBUG | |
587 | /*dlog("Expanding \"%s\" to \"%s\"", nbuf, val);*/ | |
588 | #endif /* DEBUG */ | |
589 | if (BUFSPACE(ep, vlen)) { | |
590 | strcpy(ep, vptr); | |
591 | ep += vlen; | |
592 | } else { | |
593 | plog(XLOG_ERROR, expand_error, *p->opt); | |
594 | goto out; | |
595 | } | |
596 | } | |
597 | /* | |
598 | * Done with this variable | |
599 | */ | |
600 | break; | |
601 | } | |
602 | } | |
603 | /* | |
604 | * Check that the search was succesful | |
605 | */ | |
606 | if (!op->name) { | |
607 | /* | |
608 | * If it wasn't then scan the | |
609 | * environment for that name | |
610 | * and use any value found | |
611 | */ | |
612 | char *env = getenv(nbuf); | |
613 | if (env) { | |
614 | int vlen = strlen(env); | |
615 | ||
616 | if (BUFSPACE(ep, vlen)) { | |
617 | strcpy(ep, env); | |
618 | ep += vlen; | |
619 | } else { | |
620 | plog(XLOG_ERROR, expand_error, *p->opt); | |
621 | goto out; | |
622 | } | |
623 | #ifdef DEBUG | |
624 | Debug(D_STR) | |
625 | plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env); | |
626 | #endif /* DEBUG */ | |
627 | } else { | |
628 | plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf); | |
629 | } | |
630 | } | |
631 | } else { | |
632 | /* | |
633 | * Error, error | |
634 | */ | |
635 | plog(XLOG_USER, "Unknown $ sequence in \"%s\"", *p->opt); | |
636 | } | |
637 | } | |
638 | ||
639 | out: | |
640 | /* | |
641 | * Handle common case - no expansion | |
642 | */ | |
643 | if (cp == *p->opt) { | |
644 | *p->opt = strdup(cp); | |
645 | } else { | |
646 | /* | |
647 | * Finish off the expansion | |
648 | */ | |
649 | if (BUFSPACE(ep, strlen(cp))) { | |
650 | strcpy(ep, cp); | |
651 | /*ep += strlen(ep);*/ | |
652 | } else { | |
653 | plog(XLOG_ERROR, expand_error, *p->opt); | |
654 | } | |
655 | ||
656 | /* | |
657 | * Save the exansion | |
658 | */ | |
659 | *p->opt = strdup(expbuf); | |
660 | } | |
661 | ||
2664d859 | 662 | normalize_slash(*p->opt); |
e1a31032 KM |
663 | |
664 | #ifdef DEBUG | |
665 | Debug(D_STR) { | |
666 | plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig); | |
667 | plog(XLOG_DEBUG, "... is \"%s\"", *p->opt); | |
668 | } | |
669 | #endif /* DEBUG */ | |
670 | } | |
671 | ||
672 | /* | |
673 | * Wrapper for expand_op | |
674 | */ | |
675 | static void expand_opts P((opt_apply*, int)); | |
676 | static void expand_opts(p, sel_p) | |
677 | opt_apply *p; | |
678 | int sel_p; | |
679 | { | |
680 | if (*p->opt) { | |
681 | expand_op(p, sel_p); | |
682 | } else if (p->val) { | |
683 | /* | |
684 | * Do double expansion, remembering | |
685 | * to free the string from the first | |
686 | * expansion... | |
687 | */ | |
688 | char *s = *p->opt = expand_key(p->val); | |
689 | expand_op(p, sel_p); | |
690 | free(s); | |
691 | } | |
692 | } | |
693 | ||
694 | /* | |
695 | * Apply a function to a list of options | |
696 | */ | |
697 | static void apply_opts(op, ppp, b) | |
698 | void (*op)(); | |
699 | opt_apply ppp[]; | |
700 | int b; | |
701 | { | |
702 | opt_apply *pp; | |
703 | for (pp = ppp; pp->opt; pp++) | |
704 | (*op)(pp, b); | |
705 | } | |
706 | ||
707 | /* | |
708 | * Free the option table | |
709 | */ | |
710 | void free_opts(fo) | |
711 | am_opts *fo; | |
712 | { | |
713 | /* | |
714 | * Copy in the structure we are playing with | |
715 | */ | |
716 | fs_static = *fo; | |
717 | ||
718 | /* | |
719 | * Free previously allocated memory | |
720 | */ | |
721 | apply_opts(free_op, to_free, FALSE); | |
722 | } | |
723 | ||
724 | /* | |
725 | * Expand lookup key | |
726 | */ | |
727 | char *expand_key(key) | |
728 | char *key; | |
729 | { | |
730 | opt_apply oa; | |
731 | ||
732 | oa.opt = &key; oa.val = 0; | |
733 | expand_opts(&oa, TRUE); | |
734 | ||
735 | return key; | |
736 | } | |
737 | ||
738 | /* | |
2664d859 JSP |
739 | * Remove trailing /'s from a string |
740 | * unless the string is a single / (Steven Glassman) | |
e1a31032 | 741 | */ |
2664d859 JSP |
742 | void deslashify P((char *s)); |
743 | void deslashify(s) | |
e1a31032 KM |
744 | char *s; |
745 | { | |
2664d859 JSP |
746 | if (s && *s) { |
747 | char *sl = s + strlen(s); | |
748 | while (*--sl == '/' && sl > s) | |
e1a31032 KM |
749 | *sl = '\0'; |
750 | } | |
751 | } | |
752 | ||
753 | int eval_fs_opts(fo, opts, g_opts, path, key, map) | |
754 | am_opts *fo; | |
755 | char *opts, *g_opts, *path, *key, *map; | |
756 | { | |
757 | int ok = TRUE; | |
758 | ||
759 | free_opts(fo); | |
760 | ||
761 | /* | |
762 | * Clear out the option table | |
763 | */ | |
764 | bzero((voidp) &fs_static, sizeof(fs_static)); | |
765 | bzero((voidp) vars, sizeof(vars)); | |
766 | bzero((voidp) fo, sizeof(*fo)); | |
767 | ||
768 | /* | |
769 | * Set key, map & path before expansion | |
770 | */ | |
771 | opt_key = key; | |
772 | opt_map = map; | |
773 | opt_path = path; | |
774 | ||
775 | /* | |
776 | * Expand global options | |
777 | */ | |
778 | fs_static.fs_glob = expand_key(g_opts); | |
779 | ||
780 | /* | |
781 | * Expand local options | |
782 | */ | |
783 | fs_static.fs_local = expand_key(opts); | |
784 | ||
785 | /* | |
786 | * Expand default (global) options | |
787 | */ | |
2664d859 | 788 | if (!eval_opts(fs_static.fs_glob, key)) |
e1a31032 KM |
789 | ok = FALSE; |
790 | ||
791 | /* | |
792 | * Expand local options | |
793 | */ | |
2664d859 | 794 | if (ok && !eval_opts(fs_static.fs_local, key)) |
e1a31032 KM |
795 | ok = FALSE; |
796 | ||
797 | /* | |
798 | * Normalise remote host name. | |
799 | * 1. Expand variables | |
800 | * 2. Normalize relative to host tables | |
801 | * 3. Strip local domains from the remote host | |
802 | * name before using it in other expansions. | |
803 | * This makes mount point names and other things | |
804 | * much shorter, while allowing cross domain | |
805 | * sharing of mount maps. | |
806 | */ | |
807 | apply_opts(expand_opts, rhost_expansion, FALSE); | |
808 | if (ok && fs_static.opt_rhost && *fs_static.opt_rhost) | |
809 | host_normalize(&fs_static.opt_rhost); | |
810 | ||
811 | /* | |
812 | * Macro expand the options. | |
813 | * Do this regardless of whether we are accepting | |
814 | * this mount - otherwise nasty things happen | |
815 | * with memory allocation. | |
816 | */ | |
817 | apply_opts(expand_opts, expansions, FALSE); | |
818 | ||
819 | /* | |
820 | * Strip trailing slashes from local pathname... | |
821 | */ | |
822 | deslashify(fs_static.opt_fs); | |
823 | ||
824 | /* | |
825 | * ok... copy the data back out. | |
826 | */ | |
827 | *fo = fs_static; | |
828 | ||
829 | /* | |
830 | * Clear defined options | |
831 | */ | |
832 | opt_key = opt_map = opt_path = nullstr; | |
833 | ||
834 | return ok; | |
835 | } |