BSD 4_4 release
[unix-history] / usr / src / usr.sbin / amd / amd / opts.c
CommitLineData
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 42static 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 47extern char *getenv P((const char *));
e1a31032
KM
48
49/*
50 * static copy of the options with
51 * which to play
52 */
53static struct am_opts fs_static;
54
55static char *opt_host = hostname;
56static char *opt_hostd = hostd;
57static char nullstr[] = "";
58static char *opt_key = nullstr;
59static char *opt_map = nullstr;
60static char *opt_path = nullstr;
61
62static 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)
69static 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
115typedef struct opt_apply opt_apply;
116struct opt_apply {
117 char **opt;
118 char *val;
119};
120
121/*
122 * Specially expand the remote host name first
123 */
124static 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 */
132static 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 */
147static 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 */
174static char *opt P((char**));
175static char *opt(p)
176char **p;
177{
178 char *cp = *p;
179 char *dp = cp;
180 char *s = cp;
181
182top:
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
219static int eval_opts P((char*, char*));
220static int eval_opts(opts, mapkey)
e1a31032 221char *opts;
2664d859 222char *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 */
330static void free_op P((opt_apply*, int));
331/*ARGSUSED*/
332static void free_op(p, b)
333opt_apply *p;
334int 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 */
345void normalize_slash P((char *p));
346void normalize_slash(p)
347char *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 */
388static void expand_op P((opt_apply*, int));
389static void expand_op(p, sel_p)
390opt_apply *p;
391int 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)
400static 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
639out:
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 */
675static void expand_opts P((opt_apply*, int));
676static void expand_opts(p, sel_p)
677opt_apply *p;
678int 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 */
697static void apply_opts(op, ppp, b)
698void (*op)();
699opt_apply ppp[];
700int 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 */
710void free_opts(fo)
711am_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 */
727char *expand_key(key)
728char *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
742void deslashify P((char *s));
743void deslashify(s)
e1a31032
KM
744char *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
753int eval_fs_opts(fo, opts, g_opts, path, key, map)
754am_opts *fo;
755char *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}