MFC r1.27:
[dragonfly.git] / contrib / amd / amd / opts.c
blobd564e69144320b8b27d043abc4e2ef234b25cd46
1 /*
2 * Copyright (c) 1997-1999 Erez Zadok
3 * Copyright (c) 1989 Jan-Simon Pendry
4 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1989 The Regents of the University of California.
6 * All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgment:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 * %W% (Berkeley) %G%
41 * $Id: opts.c,v 1.6 1999/09/30 21:01:32 ezk Exp $
45 #ifdef HAVE_CONFIG_H
46 # include <config.h>
47 #endif /* HAVE_CONFIG_H */
48 #include <am_defs.h>
49 #include <amd.h>
52 * MACROS:
54 #define NLEN 16 /* Length of longest option name (conservative) */
55 #define S(x) (x) , (sizeof(x)-1)
57 * The BUFSPACE macros checks that there is enough space
58 * left in the expansion buffer. If there isn't then we
59 * give up completely. This is done to avoid crashing the
60 * automounter itself (which would be a bad thing to do).
62 #define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
65 * TYPEDEFS:
67 typedef int (*IntFuncPtr) (char *);
68 typedef struct opt_apply opt_apply;
69 enum vs_opt { SelEQ, SelNE, VarAss };
72 * STRUCTURES
74 struct opt {
75 char *name; /* Name of the option */
76 int nlen; /* Length of option name */
77 char **optp; /* Pointer to option value string */
78 char **sel_p; /* Pointer to selector value string */
79 int (*fxn_p)(char *); /* Pointer to boolean function */
80 int case_insensitive; /* How to do selector comparisons */
83 struct opt_apply {
84 char **opt;
85 char *val;
88 struct functable {
89 char *name;
90 IntFuncPtr func;
94 * FORWARD DEFINITION:
96 static int f_in_network(char *);
97 static int f_netgrp(char *);
98 static int f_netgrpd(char *);
99 static int f_exists(char *);
100 static int f_false(char *);
101 static int f_true(char *);
104 * STATICS:
106 static struct am_opts fs_static; /* copy of the options to play with */
107 static char NullStr[] = "<NULL>";
108 static char nullstr[] = "";
109 static char *opt_dkey = NullStr;
110 static char *opt_host = nullstr; /* XXX: was the global hostname */
111 static char *opt_hostd = hostd;
112 static char *opt_key = nullstr;
113 static char *opt_keyd = nullstr;
114 static char *opt_map = nullstr;
115 static char *opt_path = nullstr;
116 static char *vars[8];
120 * Options in something corresponding to frequency of use so that
121 * first-match algorithm is sped up.
123 static struct opt opt_fields[] = {
124 /* Name and length.
125 Option str. Selector str. boolean fxn. flags */
126 { S("opts"),
127 &fs_static.opt_opts, 0, 0, FALSE },
128 { S("host"),
129 0, &opt_host, 0, TRUE },
130 { S("hostd"),
131 0, &opt_hostd, 0, TRUE },
132 { S("type"),
133 &fs_static.opt_type, 0, 0, FALSE },
134 { S("rhost"),
135 &fs_static.opt_rhost, 0, 0, TRUE },
136 { S("rfs"),
137 &fs_static.opt_rfs, 0, 0, FALSE },
138 { S("fs"),
139 &fs_static.opt_fs, 0, 0, FALSE },
140 { S("key"),
141 0, &opt_key, 0, FALSE },
142 { S("map"),
143 0, &opt_map, 0, FALSE },
144 { S("sublink"),
145 &fs_static.opt_sublink, 0, 0, FALSE },
146 { S("arch"),
147 0, &gopt.arch, 0, TRUE },
148 { S("dev"),
149 &fs_static.opt_dev, 0, 0, FALSE },
150 { S("pref"),
151 &fs_static.opt_pref, 0, 0, FALSE },
152 { S("autopref"),
153 &fs_static.opt_autopref,0, 0, FALSE },
154 { S("path"),
155 0, &opt_path, 0, FALSE },
156 { S("autodir"),
157 0, &gopt.auto_dir, 0, FALSE },
158 { S("delay"),
159 &fs_static.opt_delay, 0, 0, FALSE },
160 { S("domain"),
161 0, &hostdomain, 0, TRUE },
162 { S("karch"),
163 0, &gopt.karch, 0, TRUE },
164 { S("cluster"),
165 0, &gopt.cluster, 0, TRUE },
166 { S("wire"),
167 0, 0, f_in_network, TRUE },
168 { S("network"),
169 0, 0, f_in_network, TRUE },
170 { S("netnumber"),
171 0, 0, f_in_network, TRUE },
172 { S("byte"),
173 0, &endian, 0, TRUE },
174 { S("os"),
175 0, &gopt.op_sys, 0, TRUE },
176 { S("osver"),
177 0, &gopt.op_sys_ver, 0, TRUE },
178 { S("full_os"),
179 0, &gopt.op_sys_full, 0, TRUE },
180 { S("vendor"),
181 0, &gopt.op_sys_vendor, 0, TRUE },
182 { S("remopts"),
183 &fs_static.opt_remopts, 0, 0, FALSE },
184 { S("mount"),
185 &fs_static.opt_mount, 0, 0, FALSE },
186 { S("unmount"),
187 &fs_static.opt_unmount, 0, 0, FALSE },
188 { S("cache"),
189 &fs_static.opt_cache, 0, 0, FALSE },
190 { S("user"),
191 &fs_static.opt_user, 0, 0, FALSE },
192 { S("group"),
193 &fs_static.opt_group, 0, 0, FALSE },
194 { S(".key"),
195 0, &opt_dkey, 0, FALSE },
196 { S("key."),
197 0, &opt_keyd, 0, FALSE },
198 { S("maptype"),
199 &fs_static.opt_maptype, 0, 0, FALSE },
200 { S("cachedir"),
201 &fs_static.opt_cachedir, 0, 0, FALSE },
202 { S("addopts"),
203 &fs_static.opt_addopts, 0, 0, FALSE },
204 { S("var0"),
205 &vars[0], 0, 0, FALSE },
206 { S("var1"),
207 &vars[1], 0, 0, FALSE },
208 { S("var2"),
209 &vars[2], 0, 0, FALSE },
210 { S("var3"),
211 &vars[3], 0, 0, FALSE },
212 { S("var4"),
213 &vars[4], 0, 0, FALSE },
214 { S("var5"),
215 &vars[5], 0, 0, FALSE },
216 { S("var6"),
217 &vars[6], 0, 0, FALSE },
218 { S("var7"),
219 &vars[7], 0, 0, FALSE },
220 { 0, 0, 0, 0, 0, FALSE },
223 static struct functable functable[] = {
224 { "in_network", f_in_network },
225 { "netgrp", f_netgrp },
226 { "netgrpd", f_netgrpd },
227 { "exists", f_exists },
228 { "false", f_false },
229 { "true", f_true },
230 { 0, 0 },
234 * Specially expand the remote host name first
236 static opt_apply rhost_expansion[] =
238 {&fs_static.opt_rhost, "${host}"},
239 {0, 0},
243 * List of options which need to be expanded
244 * Note that the order here _may_ be important.
246 static opt_apply expansions[] =
248 {&fs_static.opt_sublink, 0},
249 {&fs_static.opt_rfs, "${path}"},
250 {&fs_static.opt_fs, "${autodir}/${rhost}${rfs}"},
251 {&fs_static.opt_opts, "rw"},
252 {&fs_static.opt_remopts, "${opts}"},
253 {&fs_static.opt_mount, 0},
254 {&fs_static.opt_unmount, 0},
255 {&fs_static.opt_cachedir, 0},
256 {&fs_static.opt_addopts, 0},
257 {0, 0},
261 * List of options which need to be free'ed before re-use
263 static opt_apply to_free[] =
265 {&fs_static.fs_glob, 0},
266 {&fs_static.fs_local, 0},
267 {&fs_static.fs_mtab, 0},
268 {&fs_static.opt_sublink, 0},
269 {&fs_static.opt_rfs, 0},
270 {&fs_static.opt_fs, 0},
271 {&fs_static.opt_rhost, 0},
272 {&fs_static.opt_opts, 0},
273 {&fs_static.opt_remopts, 0},
274 {&fs_static.opt_mount, 0},
275 {&fs_static.opt_unmount, 0},
276 {&fs_static.opt_cachedir, 0},
277 {&fs_static.opt_addopts, 0},
278 {&vars[0], 0},
279 {&vars[1], 0},
280 {&vars[2], 0},
281 {&vars[3], 0},
282 {&vars[4], 0},
283 {&vars[5], 0},
284 {&vars[6], 0},
285 {&vars[7], 0},
286 {0, 0},
291 * expand backslash escape sequences
293 static char
294 backslash(char **p)
296 char c;
298 if ((*p)[1] == '\0') {
299 plog(XLOG_USER, "Empty backslash escape");
300 return **p;
303 if (**p == '\\') {
304 (*p)++;
305 switch (**p) {
306 case 'a':
307 c = '\007'; /* Bell */
308 break;
309 case 'b':
310 c = '\010'; /* Backspace */
311 break;
312 case 't':
313 c = '\011'; /* Horizontal Tab */
314 break;
315 case 'n':
316 c = '\012'; /* New Line */
317 break;
318 case 'v':
319 c = '\013'; /* Vertical Tab */
320 break;
321 case 'f':
322 c = '\014'; /* Form Feed */
323 break;
324 case 'r':
325 c = '\015'; /* Carriage Return */
326 break;
327 case 'e':
328 c = '\033'; /* Escape */
329 break;
330 case '0':
331 case '1':
332 case '2':
333 case '3':
334 case '4':
335 case '5':
336 case '6':
337 case '7':
339 int cnt, val, ch;
341 for (cnt = 0, val = 0; cnt < 3; cnt++) {
342 ch = *(*p)++;
343 if (ch < '0' || ch > '7') {
344 (*p)--;
345 break;
347 val = (val << 3) | (ch - '0');
350 if ((val & 0xffffff00) != 0)
351 plog(XLOG_USER,
352 "Too large character constant %u\n",
353 val);
354 c = (char) val;
355 --(*p);
357 break;
359 default:
360 c = **p;
361 break;
363 } else
364 c = **p;
366 return c;
371 * Skip to next option in the string
373 static char *
374 opt(char **p)
376 char *cp = *p;
377 char *dp = cp;
378 char *s = cp;
380 top:
381 while (*cp && *cp != ';') {
382 if (*cp == '"') {
384 * Skip past string
386 for (cp++; *cp && *cp != '"'; cp++)
387 if (*cp == '\\')
388 *dp++ = backslash(&cp);
389 else
390 *dp++ = *cp;
391 if (*cp)
392 cp++;
393 } else {
394 *dp++ = *cp++;
399 * Skip past any remaining ';'s
401 while (*cp == ';')
402 cp++;
405 * If we have a zero length string
406 * and there are more fields, then
407 * parse the next one. This allows
408 * sequences of empty fields.
410 if (*cp && dp == s)
411 goto top;
413 *dp = '\0';
415 *p = cp;
416 return s;
421 * These routines add a new style of selector; function-style boolean
422 * operators. To add new ones, just define functions as in true, false,
423 * exists (below) and add them to the functable, above.
425 * Usage example: Some people have X11R5 local, some go to a server. I do
426 * this:
428 * * exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \
429 * -type:=nfs;rfs=/usr/pkg/${key} \
430 * rhost:=server1 \
431 * rhost:=server2
433 * -Rens Troost <rens@imsi.com>
435 static IntFuncPtr
436 functable_lookup(char *key)
438 struct functable *fp;
440 for (fp = functable; fp->name; fp++)
441 if (FSTREQ(fp->name, key))
442 return (fp->func);
443 return (IntFuncPtr) NULL;
447 static int
448 eval_opts(char *opts, char *mapkey)
451 * Fill in the global structure fs_static by
452 * cracking the string opts. opts may be
453 * scribbled on at will.
455 char *o = opts;
456 char *f;
459 * For each user-specified option
461 while (*(f = opt(&o))) {
462 struct opt *op;
463 enum vs_opt vs_opt = VarAss;
464 char *eq = strchr(f, '=');
465 char *fx;
466 IntFuncPtr func;
467 char *opt = NULL;
469 if (!eq || eq[1] == '\0' || eq == f) {
471 * No value, is it a function call?
473 char *arg = strchr(f, '(');
475 if (!arg || arg[1] == '\0' || arg == f) {
477 * No, just continue
479 plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f);
480 continue;
483 /* null-terminate the argument */
484 *arg++ = '\0';
485 fx = strchr(arg, ')');
486 if (!arg || fx == arg) {
487 plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f);
488 continue;
490 *fx = '\0';
493 * look up f in functable and pass it arg.
494 * func must return 0 on failure, and 1 on success.
496 if ((func = functable_lookup(f))) {
497 if (!(*func) (arg)) {
498 return (0);
500 continue;
501 } else if (NSTREQ(f, "!", 1) && (func = functable_lookup(&f[1]))) {
502 /* then this is a negated prefixed function such as "!exists" */
503 plog(XLOG_INFO, "executing negated function %s", &f[1]);
504 if ((*func) (arg)) {
505 return (0);
507 continue;
508 } else {
509 plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f);
510 return (0);
516 * Check what type of operation is happening
517 * !=, =! is SelNE
518 * == is SelEQ
519 * := is VarAss
521 if (eq[-1] == '!') { /* != */
522 vs_opt = SelNE;
523 eq[-1] = '\0';
524 opt = eq + 1;
525 } else if (eq[-1] == ':') { /* := */
526 vs_opt = VarAss;
527 eq[-1] = '\0';
528 opt = eq + 1;
529 } else if (eq[1] == '=') { /* == */
530 vs_opt = SelEQ;
531 eq[0] = '\0';
532 opt = eq + 2;
533 } else if (eq[1] == '!') { /* =! */
534 vs_opt = SelNE;
535 eq[0] = '\0';
536 opt = eq + 2;
540 * For each recognized option
542 for (op = opt_fields; op->name; op++) {
544 * Check whether they match
546 if (FSTREQ(op->name, f)) {
547 int selok;
548 switch (vs_opt) {
549 case SelEQ:
550 case SelNE:
551 if ((selok = (op->sel_p != NULL))) {
552 if (op->case_insensitive) {
553 selok = (STRCEQ(*op->sel_p, opt) == (vs_opt == SelNE));
554 } else {
555 selok = (STREQ(*op->sel_p, opt) == (vs_opt == SelNE));
558 if (selok) {
559 plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s",
560 mapkey,
561 op->name,
562 *op->sel_p,
563 vs_opt == SelNE ? "mis" : "",
564 opt);
565 return 0;
567 /* check if to apply a function */
568 if (op->fxn_p &&
569 ((*op->fxn_p)(opt) == (vs_opt == SelNE))) {
570 plog(XLOG_MAP, "key %s: map function %s did not %smatch %s",
571 mapkey,
572 op->name,
573 vs_opt == SelNE ? "mis" : "",
574 opt);
575 return 0;
577 break;
579 case VarAss:
580 if (op->sel_p) {
581 plog(XLOG_USER, "key %s: Can't assign to a selector (%s)",
582 mapkey, op->name);
583 return 0;
585 *op->optp = opt;
586 break;
588 } /* end of "switch (vs_opt)" statement */
589 break; /* break out of for loop */
593 if (!op->name)
594 plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
597 return 1;
602 * Skip to next option in the string, but don't scribble over the string.
603 * However, *p gets repointed to the start of the next string past ';'.
605 static char *
606 opt_no_scribble(char **p)
608 char *cp = *p;
609 char *dp = cp;
610 char *s = cp;
612 top:
613 while (*cp && *cp != ';') {
614 if (*cp == '\"') {
616 * Skip past string
618 cp++;
619 while (*cp && *cp != '\"')
620 *dp++ = *cp++;
621 if (*cp)
622 cp++;
623 } else {
624 *dp++ = *cp++;
629 * Skip past any remaining ';'s
631 while (*cp == ';')
632 cp++;
635 * If we have a zero length string
636 * and there are more fields, then
637 * parse the next one. This allows
638 * sequences of empty fields.
640 if (*cp && dp == s)
641 goto top;
643 *p = cp;
644 return s;
649 * Strip any selectors from a string. Selectors are all assumed to be
650 * first in the string. This is used for the new /defaults method which will
651 * use selectors as well.
653 char *
654 strip_selectors(char *opts, char *mapkey)
657 * Fill in the global structure fs_static by
658 * cracking the string opts. opts may be
659 * scribbled on at will.
661 char *o = opts;
662 char *oo = opts;
663 char *f;
666 * Scan options. Note that the opt() function scribbles on the opt string.
668 while (*(f = opt_no_scribble(&o))) {
669 enum vs_opt vs_opt = VarAss;
670 char *eq = strchr(f, '=');
672 if (!eq || eq[1] == '\0' || eq == f) {
674 * No option or assignment? Return as is.
676 plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f);
677 return o;
680 * Check what type of operation is happening
681 * !=, =! is SelNE
682 * == is SelEQ
683 * := is VarAss
685 if (eq[-1] == '!') { /* != */
686 vs_opt = SelNE;
687 } else if (eq[-1] == ':') { /* := */
688 vs_opt = VarAss;
689 } else if (eq[1] == '=') { /* == */
690 vs_opt = SelEQ;
691 } else if (eq[1] == '!') { /* =! */
692 vs_opt = SelNE;
694 switch (vs_opt) {
695 case SelEQ:
696 case SelNE:
697 /* Skip this selector, maybe there's another one following it */
698 plog(XLOG_USER, "skipping selector to \"%s\"", o);
699 /* store previous match. it may have been the first assignment */
700 oo = o;
701 break;
703 case VarAss:
704 /* found the first assignment, return the string starting with it */
705 #ifdef DEBUG
706 dlog("found first assignment past selectors \"%s\"", o);
707 #endif /* DEBUG */
708 return oo;
712 /* return the same string by default. should not happen. */
713 return oo;
717 /*****************************************************************************
718 *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true): ***
719 *****************************************************************************/
721 /* test if arg is any of this host's network names or numbers */
722 static int
723 f_in_network(char *arg)
725 int status;
727 if (!arg)
728 return FALSE;
730 status = is_network_member(arg);
731 #ifdef DEBUG
732 plog(XLOG_USER, "%s is %son a local network",
733 arg, (status ? "" : "not "));
734 #endif /* DEBUG */
735 return status;
739 /* test if this host (short hostname form) is in netgroup (arg) */
740 static int
741 f_netgrp(char *arg)
743 int status;
745 status = innetgr(arg, opt_host, NULL, NULL);
746 #ifdef DEBUG
747 plog(XLOG_USER, "netgrp = %s status = %d host = %s", arg, status, opt_host);
748 #endif /* DEBUG */
749 return status;
753 /* test if this host (fully-qualified name) is in netgroup (arg) */
754 static int
755 f_netgrpd(char *arg)
757 int status;
759 status = innetgr(arg, opt_hostd, NULL, NULL);
760 #ifdef DEBUG
761 plog(XLOG_USER, "netgrp = %s status = %d hostd = %s", arg, status, opt_hostd);
762 #endif /* DEBUG */
763 return status;
767 /* test if file (arg) exists via lstat */
768 static int
769 f_exists(char *arg)
771 struct stat buf;
773 if (lstat(arg, &buf) < 0)
774 return (0);
775 else
776 return (1);
780 /* always false */
781 static int
782 f_false(char *arg)
784 return (0);
788 /* always true */
789 static int
790 f_true(char *arg)
792 return (1);
797 * Free an option
799 static void
800 free_op(opt_apply *p, int b)
802 if (*p->opt) {
803 XFREE(*p->opt);
804 *p->opt = 0;
810 * Normalize slashes in the string.
812 void
813 normalize_slash(char *p)
815 char *f = strchr(p, '/');
816 char *f0 = f;
818 if (f) {
819 char *t = f;
820 do {
821 /* assert(*f == '/'); */
822 if (f == f0 && f[0] == '/' && f[1] == '/') {
823 /* copy double slash iff first */
824 *t++ = *f++;
825 *t++ = *f++;
826 } else {
827 /* copy a single / across */
828 *t++ = *f++;
831 /* assert(f[-1] == '/'); */
832 /* skip past more /'s */
833 while (*f == '/')
834 f++;
836 /* assert(*f != '/'); */
837 /* keep copying up to next / */
838 while (*f && *f != '/') {
839 *t++ = *f++;
842 /* assert(*f == 0 || *f == '/'); */
844 } while (*f);
845 *t = 0; /* derived from fix by Steven Glassman */
851 * Macro-expand an option. Note that this does not
852 * handle recursive expansions. They will go badly wrong.
853 * If sel is true then old expand selectors, otherwise
854 * don't expand selectors.
856 static void
857 expand_op(opt_apply *p, int sel_p)
859 static char expand_error[] = "No space to expand \"%s\"";
860 char expbuf[MAXPATHLEN + 1];
861 char nbuf[NLEN + 1];
862 char *ep = expbuf;
863 char *cp = *p->opt;
864 char *dp;
865 struct opt *op;
866 #ifdef DEBUG
867 char *cp_orig = *p->opt;
868 #endif /* DEBUG */
870 while ((dp = strchr(cp, '$'))) {
871 char ch;
873 * First copy up to the $
876 int len = dp - cp;
878 if (BUFSPACE(ep, len)) {
879 strncpy(ep, cp, len);
880 ep += len;
881 } else {
882 plog(XLOG_ERROR, expand_error, *p->opt);
883 goto out;
887 cp = dp + 1;
888 ch = *cp++;
889 if (ch == '$') {
890 if (BUFSPACE(ep, 1)) {
891 *ep++ = '$';
892 } else {
893 plog(XLOG_ERROR, expand_error, *p->opt);
894 goto out;
896 } else if (ch == '{') {
897 /* Expansion... */
898 enum {
899 E_All, E_Dir, E_File, E_Domain, E_Host
900 } todo;
902 * Find closing brace
904 char *br_p = strchr(cp, '}');
905 int len;
908 * Check we found it
910 if (!br_p) {
912 * Just give up
914 plog(XLOG_USER, "No closing '}' in \"%s\"", *p->opt);
915 goto out;
917 len = br_p - cp;
920 * Figure out which part of the variable to grab.
922 if (*cp == '/') {
924 * Just take the last component
926 todo = E_File;
927 cp++;
928 --len;
929 } else if (br_p[-1] == '/') {
931 * Take all but the last component
933 todo = E_Dir;
934 --len;
935 } else if (*cp == '.') {
937 * Take domain name
939 todo = E_Domain;
940 cp++;
941 --len;
942 } else if (br_p[-1] == '.') {
944 * Take host name
946 todo = E_Host;
947 --len;
948 } else {
950 * Take the whole lot
952 todo = E_All;
956 * Truncate if too long. Since it won't
957 * match anyway it doesn't matter that
958 * it has been cut short.
960 if (len > NLEN)
961 len = NLEN;
964 * Put the string into another buffer so
965 * we can do comparisons.
967 strncpy(nbuf, cp, len);
968 nbuf[len] = '\0';
971 * Advance cp
973 cp = br_p + 1;
976 * Search the option array
978 for (op = opt_fields; op->name; op++) {
980 * Check for match
982 if (len == op->nlen && STREQ(op->name, nbuf)) {
983 char xbuf[NLEN + 3];
984 char *val;
986 * Found expansion. Copy
987 * the correct value field.
989 if (!(!op->sel_p == !sel_p)) {
991 * Copy the string across unexpanded
993 sprintf(xbuf, "${%s%s%s}",
994 todo == E_File ? "/" :
995 todo == E_Domain ? "." : "",
996 nbuf,
997 todo == E_Dir ? "/" :
998 todo == E_Host ? "." : "");
999 val = xbuf;
1001 * Make sure expansion doesn't
1002 * munge the value!
1004 todo = E_All;
1005 } else if (op->sel_p) {
1006 val = *op->sel_p;
1007 } else {
1008 val = *op->optp;
1011 if (val) {
1013 * Do expansion:
1014 * ${/var} means take just the last part
1015 * ${var/} means take all but the last part
1016 * ${.var} means take all but first part
1017 * ${var.} means take just the first part
1018 * ${var} means take the whole lot
1020 int vlen = strlen(val);
1021 char *vptr = val;
1022 switch (todo) {
1023 case E_Dir:
1024 vptr = strrchr(val, '/');
1025 if (vptr)
1026 vlen = vptr - val;
1027 vptr = val;
1028 break;
1029 case E_File:
1030 vptr = strrchr(val, '/');
1031 if (vptr) {
1032 vptr++;
1033 vlen = strlen(vptr);
1034 } else
1035 vptr = val;
1036 break;
1037 case E_Domain:
1038 vptr = strchr(val, '.');
1039 if (vptr) {
1040 vptr++;
1041 vlen = strlen(vptr);
1042 } else {
1043 vptr = "";
1044 vlen = 0;
1046 break;
1047 case E_Host:
1048 vptr = strchr(val, '.');
1049 if (vptr)
1050 vlen = vptr - val;
1051 vptr = val;
1052 break;
1053 case E_All:
1054 break;
1057 if (BUFSPACE(ep, vlen)) {
1058 strcpy(ep, vptr);
1059 ep += vlen;
1060 } else {
1061 plog(XLOG_ERROR, expand_error, *p->opt);
1062 goto out;
1066 * Done with this variable
1068 break;
1073 * Check that the search was successful
1075 if (!op->name) {
1077 * If it wasn't then scan the
1078 * environment for that name
1079 * and use any value found
1081 char *env = getenv(nbuf);
1083 if (env) {
1084 int vlen = strlen(env);
1086 if (BUFSPACE(ep, vlen)) {
1087 strcpy(ep, env);
1088 ep += vlen;
1089 } else {
1090 plog(XLOG_ERROR, expand_error, *p->opt);
1091 goto out;
1093 #ifdef DEBUG
1094 amuDebug(D_STR)
1095 plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
1096 #endif /* DEBUG */
1097 } else {
1098 plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
1101 } else {
1103 * Error, error
1105 plog(XLOG_USER, "Unknown $ sequence in \"%s\"", *p->opt);
1109 out:
1111 * Handle common case - no expansion
1113 if (cp == *p->opt) {
1114 *p->opt = strdup(cp);
1115 } else {
1117 * Finish off the expansion
1119 if (BUFSPACE(ep, strlen(cp))) {
1120 strcpy(ep, cp);
1121 /* ep += strlen(ep); */
1122 } else {
1123 plog(XLOG_ERROR, expand_error, *p->opt);
1127 * Save the expansion
1129 *p->opt = strdup(expbuf);
1132 normalize_slash(*p->opt);
1134 #ifdef DEBUG
1135 amuDebug(D_STR) {
1136 plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig);
1137 plog(XLOG_DEBUG, "... is \"%s\"", *p->opt);
1139 #endif /* DEBUG */
1144 * Wrapper for expand_op
1146 static void
1147 expand_opts(opt_apply *p, int sel_p)
1149 if (*p->opt) {
1150 expand_op(p, sel_p);
1151 } else if (p->val) {
1153 * Do double expansion, remembering
1154 * to free the string from the first
1155 * expansion...
1157 char *s = *p->opt = expand_key(p->val);
1158 expand_op(p, sel_p);
1159 XFREE(s);
1165 * Apply a function to a list of options
1167 static void
1168 apply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b)
1170 opt_apply *pp;
1172 for (pp = ppp; pp->opt; pp++)
1173 (*op) (pp, b);
1178 * Free the option table
1180 void
1181 free_opts(am_opts *fo)
1184 * Copy in the structure we are playing with
1186 fs_static = *fo;
1189 * Free previously allocated memory
1191 apply_opts(free_op, to_free, FALSE);
1196 * Expand lookup key
1198 char *
1199 expand_key(char *key)
1201 opt_apply oa;
1203 oa.opt = &key;
1204 oa.val = 0;
1205 expand_opts(&oa, TRUE);
1207 return key;
1212 * Remove trailing /'s from a string
1213 * unless the string is a single / (Steven Glassman)
1214 * or unless it is two slashes // (Kevin D. Bond)
1216 void
1217 deslashify(char *s)
1219 if (s && *s) {
1220 char *sl = s + strlen(s);
1222 while (*--sl == '/' && sl > s)
1223 *sl = '\0';
1229 eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map)
1231 int ok = TRUE;
1233 free_opts(fo);
1236 * Clear out the option table
1238 memset((voidp) &fs_static, 0, sizeof(fs_static));
1239 memset((voidp) vars, 0, sizeof(vars));
1240 memset((voidp) fo, 0, sizeof(*fo));
1242 /* set hostname */
1243 opt_host = (char *) am_get_hostname();
1246 * Set key, map & path before expansion
1248 opt_key = key;
1249 opt_map = map;
1250 opt_path = path;
1252 opt_dkey = strchr(key, '.');
1253 if (!opt_dkey) {
1254 opt_dkey = NullStr;
1255 opt_keyd = key;
1256 } else {
1257 opt_keyd = strnsave(key, opt_dkey - key);
1258 opt_dkey++;
1259 if (*opt_dkey == '\0') /* check for 'host.' */
1260 opt_dkey = NullStr;
1264 * Expand global options
1266 fs_static.fs_glob = expand_key(g_opts);
1269 * Expand local options
1271 fs_static.fs_local = expand_key(opts);
1274 * Expand default (global) options
1276 if (!eval_opts(fs_static.fs_glob, key))
1277 ok = FALSE;
1280 * Expand local options
1282 if (ok && !eval_opts(fs_static.fs_local, key))
1283 ok = FALSE;
1286 * Normalize remote host name.
1287 * 1. Expand variables
1288 * 2. Normalize relative to host tables
1289 * 3. Strip local domains from the remote host
1290 * name before using it in other expansions.
1291 * This makes mount point names and other things
1292 * much shorter, while allowing cross domain
1293 * sharing of mount maps.
1295 apply_opts(expand_opts, rhost_expansion, FALSE);
1296 if (ok && fs_static.opt_rhost && *fs_static.opt_rhost)
1297 host_normalize(&fs_static.opt_rhost);
1300 * Macro expand the options.
1301 * Do this regardless of whether we are accepting
1302 * this mount - otherwise nasty things happen
1303 * with memory allocation.
1305 apply_opts(expand_opts, expansions, FALSE);
1308 * Strip trailing slashes from local pathname...
1310 deslashify(fs_static.opt_fs);
1313 * ok... copy the data back out.
1315 *fo = fs_static;
1318 * Clear defined options
1320 if (opt_keyd != key && opt_keyd != nullstr)
1321 XFREE(opt_keyd);
1322 opt_keyd = nullstr;
1323 opt_dkey = NullStr;
1324 opt_key = opt_map = opt_path = nullstr;
1326 return ok;