2 * Copyright (c) 1997-2006 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.
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
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
40 * File: am-utils/amd/opts.c
46 #endif /* HAVE_CONFIG_H */
53 #define NLEN 16 /* Length of longest option name (conservative) */
54 #define S(x) (x) , (sizeof(x)-1)
56 * The BUFSPACE macros checks that there is enough space
57 * left in the expansion buffer. If there isn't then we
58 * give up completely. This is done to avoid crashing the
59 * automounter itself (which would be a bad thing to do).
61 #define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
66 typedef int (*IntFuncPtr) (char *);
67 typedef struct opt_apply opt_apply;
68 enum vs_opt { SelEQ, SelNE, VarAss };
74 char *name; /* Name of the option */
75 int nlen; /* Length of option name */
76 char **optp; /* Pointer to option value string */
77 char **sel_p; /* Pointer to selector value string */
78 int (*fxn_p)(char *); /* Pointer to boolean function */
79 int case_insensitive; /* How to do selector comparisons */
95 static int f_in_network(char *);
96 static int f_xhost(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 *);
102 static inline char *expand_options(char *key);
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 char uid_str[SIZEOF_UID_STR], gid_str[SIZEOF_GID_STR];
117 char *opt_uid = uid_str;
118 char *opt_gid = gid_str;
119 static char *vars[8];
120 static char *literal_dollar = "$"; /* ${dollar}: a literal '$' in maps */
125 static struct am_opts fs_static; /* copy of the options to play with */
129 * Options in some order corresponding to frequency of use so that
130 * first-match algorithm is sped up.
132 static struct opt opt_fields[] = {
134 Option str. Selector str. boolean fxn. case sensitive */
136 &fs_static.opt_opts, 0, 0, FALSE },
138 0, &opt_host, 0, TRUE },
140 0, &opt_hostd, 0, TRUE },
142 &fs_static.opt_type, 0, 0, FALSE },
144 &fs_static.opt_rhost, 0, 0, TRUE },
146 &fs_static.opt_rfs, 0, 0, FALSE },
148 &fs_static.opt_fs, 0, 0, FALSE },
150 0, &opt_key, 0, FALSE },
152 0, &opt_map, 0, FALSE },
154 &fs_static.opt_sublink, 0, 0, FALSE },
156 0, &gopt.arch, 0, TRUE },
158 &fs_static.opt_dev, 0, 0, FALSE },
160 &fs_static.opt_pref, 0, 0, FALSE },
162 0, &opt_path, 0, FALSE },
164 0, &gopt.auto_dir, 0, FALSE },
166 &fs_static.opt_delay, 0, 0, FALSE },
168 0, &hostdomain, 0, TRUE },
170 0, &gopt.karch, 0, TRUE },
172 0, &gopt.cluster, 0, TRUE },
174 0, 0, f_in_network, TRUE },
176 0, 0, f_in_network, TRUE },
178 0, 0, f_in_network, TRUE },
180 0, &endian, 0, TRUE },
182 0, &gopt.op_sys, 0, TRUE },
184 0, &gopt.op_sys_ver, 0, TRUE },
186 0, &gopt.op_sys_full, 0, TRUE },
188 0, &gopt.op_sys_vendor, 0, TRUE },
190 &fs_static.opt_remopts, 0, 0, FALSE },
192 &fs_static.opt_mount, 0, 0, FALSE },
194 &fs_static.opt_unmount, 0, 0, FALSE },
196 &fs_static.opt_umount, 0, 0, FALSE },
198 &fs_static.opt_cache, 0, 0, FALSE },
200 &fs_static.opt_user, 0, 0, FALSE },
202 &fs_static.opt_group, 0, 0, FALSE },
204 0, &opt_dkey, 0, FALSE },
206 0, &opt_keyd, 0, FALSE },
208 &fs_static.opt_maptype, 0, 0, FALSE },
210 &fs_static.opt_cachedir, 0, 0, FALSE },
212 &fs_static.opt_addopts, 0, 0, FALSE },
214 0, &opt_uid, 0, FALSE },
216 0, &opt_gid, 0, FALSE },
218 &fs_static.opt_mount_type, 0, 0, FALSE },
220 &literal_dollar, 0, 0, FALSE },
222 &vars[0], 0, 0, FALSE },
224 &vars[1], 0, 0, FALSE },
226 &vars[2], 0, 0, FALSE },
228 &vars[3], 0, 0, FALSE },
230 &vars[4], 0, 0, FALSE },
232 &vars[5], 0, 0, FALSE },
234 &vars[6], 0, 0, FALSE },
236 &vars[7], 0, 0, FALSE },
237 { 0, 0, 0, 0, 0, FALSE },
240 static struct functable functable[] = {
241 { "in_network", f_in_network },
242 { "xhost", f_xhost },
243 { "netgrp", f_netgrp },
244 { "netgrpd", f_netgrpd },
245 { "exists", f_exists },
246 { "false", f_false },
252 * Specially expand the remote host name first
254 static opt_apply rhost_expansion[] =
256 {&fs_static.opt_rhost, "${host}"},
261 * List of options which need to be expanded
262 * Note that the order here _may_ be important.
264 static opt_apply expansions[] =
266 {&fs_static.opt_sublink, 0},
267 {&fs_static.opt_rfs, "${path}"},
268 {&fs_static.opt_fs, "${autodir}/${rhost}${rfs}"},
269 {&fs_static.opt_opts, "rw"},
270 {&fs_static.opt_remopts, "${opts}"},
271 {&fs_static.opt_mount, 0},
272 {&fs_static.opt_unmount, 0},
273 {&fs_static.opt_umount, 0},
274 {&fs_static.opt_cachedir, 0},
275 {&fs_static.opt_addopts, 0},
280 * List of options which need to be free'ed before re-use
282 static opt_apply to_free[] =
284 {&fs_static.fs_glob, 0},
285 {&fs_static.fs_local, 0},
286 {&fs_static.fs_mtab, 0},
287 {&fs_static.opt_sublink, 0},
288 {&fs_static.opt_rfs, 0},
289 {&fs_static.opt_fs, 0},
290 {&fs_static.opt_rhost, 0},
291 {&fs_static.opt_opts, 0},
292 {&fs_static.opt_remopts, 0},
293 {&fs_static.opt_mount, 0},
294 {&fs_static.opt_unmount, 0},
295 {&fs_static.opt_umount, 0},
296 {&fs_static.opt_cachedir, 0},
297 {&fs_static.opt_addopts, 0},
311 * expand backslash escape sequences
312 * (escaped slash is handled separately in normalize_slash)
319 if ((*p)[1] == '\0') {
320 plog(XLOG_USER, "Empty backslash escape");
328 c = '\007'; /* Bell */
331 c = '\010'; /* Backspace */
334 c = '\011'; /* Horizontal Tab */
337 c = '\012'; /* New Line */
340 c = '\013'; /* Vertical Tab */
343 c = '\014'; /* Form Feed */
346 c = '\015'; /* Carriage Return */
349 c = '\033'; /* Escape */
362 for (cnt = 0, val = 0; cnt < 3; cnt++) {
364 if (ch < '0' || ch > '7') {
368 val = (val << 3) | (ch - '0');
371 if ((val & 0xffffff00) != 0)
373 "Too large character constant %u\n",
392 * Skip to next option in the string
402 while (*cp && *cp != ';') {
407 for (cp++; *cp && *cp != '"'; cp++)
409 *dp++ = backslash(&cp);
420 * Skip past any remaining ';'s
426 * If we have a zero length string
427 * and there are more fields, then
428 * parse the next one. This allows
429 * sequences of empty fields.
442 * These routines add a new style of selector; function-style boolean
443 * operators. To add new ones, just define functions as in true, false,
444 * exists (below) and add them to the functable, above.
446 * Usage example: Some people have X11R5 local, some go to a server. I do
449 * * exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \
450 * -type:=nfs;rfs=/usr/pkg/${key} \
454 * -Rens Troost <rens@imsi.com>
457 functable_lookup(char *key)
459 struct functable *fp;
461 for (fp = functable; fp->name; fp++)
462 if (FSTREQ(fp->name, key))
464 return (IntFuncPtr) NULL;
469 * Fill in the global structure fs_static by
470 * cracking the string opts. opts may be
471 * scribbled on at will. Does NOT evaluate options.
472 * Returns 0 on error, 1 if no syntax errors were discovered.
475 split_opts(char *opts, char *mapkey)
481 * For each user-specified option
483 for (f = opt(&o); *f; f = opt(&o)) {
485 char *eq = strchr(f, '=');
491 if (*(eq-1) == '!' ||
493 eq[1] == '!') { /* != or == or =! */
494 continue; /* we don't care about selectors */
497 if (*(eq-1) == ':') { /* := */
500 /* old style assignment */
506 * For each recognized option
508 for (op = opt_fields; op->name; op++) {
510 * Check whether they match
512 if (FSTREQ(op->name, f)) {
514 plog(XLOG_USER, "key %s: Can't assign to a selector (%s)",
518 *op->optp = opt; /* actual assignment into fs_static */
519 break; /* break out of for loop */
520 } /* end of "if (FSTREQ(op->name, f))" statement */
521 } /* end of "for (op = opt_fields..." statement */
524 plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
532 * Just evaluate selectors, which were split by split_opts.
533 * Returns 0 on error or no match, 1 if matched.
536 eval_selectors(char *opts, char *mapkey)
542 o = old_o = strdup(opts);
545 * For each user-specified option
547 for (f = opt(&o); *f; f = opt(&o)) {
550 char *eq = strchr(f, '=');
558 * No value, is it a function call?
560 arg = strchr(f, '(');
562 if (!arg || arg[1] == '\0' || arg == f) {
566 plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f);
570 /* null-terminate the argument */
572 fx = strchr(arg, ')');
573 if (!arg || fx == arg) {
574 plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f);
586 * look up f in functable and pass it arg.
587 * func must return 0 on failure, and 1 on success.
589 if ((func = functable_lookup(f))) {
592 /* this allocates memory, don't forget to free */
593 arg = expand_options(arg);
604 plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f);
608 if (eq[1] == '\0' || eq == f) {
609 /* misformed selector */
610 plog(XLOG_USER, "key %s: Bad selector \"%s\"", mapkey, f);
616 * Check what type of operation is happening
621 if (*(eq-1) == '!') { /* != */
625 } else if (*(eq-1) == ':') { /* := */
627 } else if (eq[1] == '=') { /* == */
631 } else if (eq[1] == '!') { /* =! */
636 /* old style assignment */
641 * For each recognized option
643 for (op = opt_fields; op->name; op++) {
645 * Check whether they match
647 if (FSTREQ(op->name, f)) {
648 opt = expand_options(opt);
650 if (op->sel_p != NULL) {
652 if (op->case_insensitive) {
653 selok = STRCEQ(*op->sel_p, opt);
655 selok = STREQ(*op->sel_p, opt);
660 plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s",
664 vs_opt == SelNE ? "mis" : "",
671 /* check if to apply a function */
675 funok = op->fxn_p(opt);
679 plog(XLOG_MAP, "key %s: map function %s did not %smatch %s",
682 vs_opt == SelNE ? "mis" : "",
689 break; /* break out of for loop */
694 plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
707 * Skip to next option in the string, but don't scribble over the string.
708 * However, *p gets repointed to the start of the next string past ';'.
711 opt_no_scribble(char **p)
718 while (*cp && *cp != ';') {
724 while (*cp && *cp != '\"')
734 * Skip past any remaining ';'s
740 * If we have a zero length string
741 * and there are more fields, then
742 * parse the next one. This allows
743 * sequences of empty fields.
754 * Strip any selectors from a string. Selectors are all assumed to be
755 * first in the string. This is used for the new /defaults method which will
756 * use selectors as well.
759 strip_selectors(char *opts, char *mapkey)
762 * Fill in the global structure fs_static by
763 * cracking the string opts. opts may be
764 * scribbled on at will.
771 * Scan options. Note that the opt() function scribbles on the opt string.
773 while (*(f = opt_no_scribble(&o))) {
774 enum vs_opt vs_opt = VarAss;
775 char *eq = strchr(f, '=');
777 if (!eq || eq[1] == '\0' || eq == f) {
779 * No option or assignment? Return as is.
781 plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f);
785 * Check what type of operation is happening
790 if (*(eq-1) == '!') { /* != */
792 } else if (*(eq-1) == ':') { /* := */
794 } else if (eq[1] == '=') { /* == */
796 } else if (eq[1] == '!') { /* =! */
802 /* Skip this selector, maybe there's another one following it */
803 plog(XLOG_USER, "skipping selector to \"%s\"", o);
804 /* store previous match. it may have been the first assignment */
809 /* found the first assignment, return the string starting with it */
810 dlog("found first assignment past selectors \"%s\"", o);
815 /* return the same string by default. should not happen. */
820 /*****************************************************************************
821 *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true): ***
822 *****************************************************************************/
824 /* test if arg is any of this host's network names or numbers */
826 f_in_network(char *arg)
833 status = is_network_member(arg);
834 dlog("%s is %son a local network", arg, (status ? "" : "not "));
840 * Test if arg is any of this host's names or aliases (CNAMES).
841 * Note: this function compares against the fully expanded host name (hostd).
842 * XXX: maybe we also need to compare against the stripped host name?
853 /* simple test: does it match main host name? */
854 if (STREQ(arg, opt_hostd))
857 /* now find all of the names of "arg" and compare against opt_hostd */
858 hp = gethostbyname(arg);
860 #ifdef HAVE_HSTRERROR
861 plog(XLOG_ERROR, "gethostbyname xhost(%s): %s", arg, hstrerror(h_errno));
862 #else /* not HAVE_HSTRERROR */
863 plog(XLOG_ERROR, "gethostbyname xhost(%s): h_errno %d", arg, h_errno);
864 #endif /* not HAVE_HSTRERROR */
867 /* check primary name */
869 dlog("xhost: compare %s==%s", hp->h_name, opt_hostd);
870 if (STREQ(hp->h_name, opt_hostd)) {
871 plog(XLOG_INFO, "xhost(%s): matched h_name %s", arg, hp->h_name);
875 /* check all aliases, if any */
876 if (hp->h_aliases == NULL) {
877 dlog("gethostbyname(%s) has no aliases", arg);
882 dlog("xhost: compare alias %s==%s", *cp, opt_hostd);
883 if (STREQ(*cp, opt_hostd)) {
884 plog(XLOG_INFO, "xhost(%s): matched alias %s", arg, *cp);
889 /* nothing matched */
894 /* test if this host (short hostname form) is in netgroup (arg) */
901 if ((ptr = strchr(arg, ',')) != NULL) {
907 status = innetgr(arg, nhost, NULL, NULL);
908 dlog("netgrp = %s status = %d host = %s", arg, status, nhost);
915 /* test if this host (fully-qualified name) is in netgroup (arg) */
922 if ((ptr = strchr(arg, ',')) != NULL) {
928 status = innetgr(arg, nhost, NULL, NULL);
929 dlog("netgrp = %s status = %d hostd = %s", arg, status, nhost);
936 /* test if file (arg) exists via lstat */
942 if (lstat(arg, &buf) < 0)
969 free_op(opt_apply *p, int b)
978 * Normalize slashes in the string.
981 normalize_slash(char *p)
985 if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
988 f0 = f = strchr(p, '/');
992 /* assert(*f == '/'); */
993 if (f == f0 && f[0] == '/' && f[1] == '/') {
994 /* copy double slash iff first */
998 /* copy a single / across */
1002 /* assert(f[-1] == '/'); */
1003 /* skip past more /'s */
1007 /* assert(*f != '/'); */
1008 /* keep copying up to next / */
1009 while (*f && *f != '/') {
1010 /* support escaped slashes '\/' */
1011 if (f[0] == '\\' && f[1] == '/')
1012 f++; /* skip backslash */
1016 /* assert(*f == 0 || *f == '/'); */
1019 *t = 0; /* derived from fix by Steven Glassman */
1025 * Macro-expand an option. Note that this does not
1026 * handle recursive expansions. They will go badly wrong.
1027 * If sel_p is true then old expand selectors, otherwise
1028 * don't expand selectors.
1031 expand_op(char *opt, int sel_p)
1033 #define EXPAND_ERROR "No space to expand \"%s\""
1034 char expbuf[MAXPATHLEN + 1];
1035 char nbuf[NLEN + 1];
1040 char *cp_orig = opt;
1042 while ((dp = strchr(cp, '$'))) {
1045 * First copy up to the $
1051 if (BUFSPACE(ep, len)) {
1053 * We use strncpy (not xstrlcpy) because 'ep' relies on its
1054 * semantics. BUFSPACE guarantees that ep can hold len.
1056 strncpy(ep, cp, len);
1059 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1068 if (BUFSPACE(ep, 1)) {
1071 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1074 } else if (ch == '{') {
1077 E_All, E_Dir, E_File, E_Domain, E_Host
1080 * Find closing brace
1082 char *br_p = strchr(cp, '}');
1092 plog(XLOG_USER, "No closing '}' in \"%s\"", opt);
1098 * Figure out which part of the variable to grab.
1102 * Just take the last component
1107 } else if (*(br_p-1) == '/') {
1109 * Take all but the last component
1113 } else if (*cp == '.') {
1120 } else if (*(br_p-1) == '.') {
1128 * Take the whole lot
1134 * Truncate if too long. Since it won't
1135 * match anyway it doesn't matter that
1136 * it has been cut short.
1142 * Put the string into another buffer so
1143 * we can do comparisons.
1145 * We use strncpy here (not xstrlcpy) because the dest is meant
1146 * to be truncated and we don't want to log it as an error. The
1147 * use of the BUFSPACE macro above guarantees the safe use of
1148 * strncpy with nbuf.
1150 strncpy(nbuf, cp, len);
1159 * Search the option array
1161 for (op = opt_fields; op->name; op++) {
1165 if (len == op->nlen && STREQ(op->name, nbuf)) {
1166 char xbuf[NLEN + 3];
1169 * Found expansion. Copy
1170 * the correct value field.
1172 if (!(!op->sel_p == !sel_p)) {
1174 * Copy the string across unexpanded
1176 xsnprintf(xbuf, sizeof(xbuf), "${%s%s%s}",
1177 todo == E_File ? "/" :
1178 todo == E_Domain ? "." : "",
1180 todo == E_Dir ? "/" :
1181 todo == E_Host ? "." : "");
1184 * Make sure expansion doesn't
1188 } else if (op->sel_p) {
1197 * ${/var} means take just the last part
1198 * ${var/} means take all but the last part
1199 * ${.var} means take all but first part
1200 * ${var.} means take just the first part
1201 * ${var} means take the whole lot
1203 int vlen = strlen(val);
1207 vptr = strrchr(val, '/');
1213 vptr = strrchr(val, '/');
1216 vlen = strlen(vptr);
1221 vptr = strchr(val, '.');
1224 vlen = strlen(vptr);
1231 vptr = strchr(val, '.');
1240 if (BUFSPACE(ep, vlen+1)) {
1241 xstrlcpy(ep, vptr, vlen+1);
1244 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1249 * Done with this variable
1256 * Check that the search was successful
1260 * If it wasn't then scan the
1261 * environment for that name
1262 * and use any value found
1264 char *env = getenv(nbuf);
1267 int vlen = strlen(env);
1269 if (BUFSPACE(ep, vlen+1)) {
1270 xstrlcpy(ep, env, vlen+1);
1273 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1276 if (amuDebug(D_STR))
1277 plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
1279 plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
1286 plog(XLOG_USER, "Unknown $ sequence in \"%s\"", opt);
1292 * Handle common case - no expansion
1298 * Finish off the expansion
1300 int vlen = strlen(cp);
1301 if (BUFSPACE(ep, vlen+1)) {
1302 xstrlcpy(ep, cp, vlen+1);
1305 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1309 * Save the expansion
1311 opt = strdup(expbuf);
1314 normalize_slash(opt);
1316 if (amuDebug(D_STR)) {
1317 plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig);
1318 plog(XLOG_DEBUG, "......... is \"%s\"", opt);
1325 * Wrapper for expand_op
1328 expand_opts(opt_apply *p, int sel_p)
1331 *p->opt = expand_op(*p->opt, sel_p);
1332 } else if (p->val) {
1334 * Do double expansion, remembering
1335 * to free the string from the first
1338 char *s = expand_op(p->val, TRUE);
1339 *p->opt = expand_op(s, sel_p);
1346 * Apply a function to a list of options
1349 apply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b)
1353 for (pp = ppp; pp->opt; pp++)
1359 * Free the option table
1362 free_opts(am_opts *fo)
1365 * Copy in the structure we are playing with
1370 * Free previously allocated memory
1372 apply_opts(free_op, to_free, FALSE);
1377 * Expand selectors (variables that cannot be assigned to or overridden)
1380 expand_selectors(char *key)
1382 return expand_op(key, TRUE);
1387 * Expand options (i.e. non-selectors, see above for definition)
1389 static inline char *
1390 expand_options(char *key)
1392 return expand_op(key, FALSE);
1397 * Remove trailing /'s from a string
1398 * unless the string is a single / (Steven Glassman)
1399 * or unless it is two slashes // (Kevin D. Bond)
1400 * or unless amd.conf says not to touch slashes.
1405 if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
1409 char *sl = s + strlen(s);
1411 while (*--sl == '/' && sl > s)
1418 eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map)
1425 * Clear out the option table
1427 memset((voidp) &fs_static, 0, sizeof(fs_static));
1428 memset((voidp) vars, 0, sizeof(vars));
1429 memset((voidp) fo, 0, sizeof(*fo));
1432 opt_host = (char *) am_get_hostname();
1435 * Set key, map & path before expansion
1441 opt_dkey = strchr(key, '.');
1446 opt_keyd = strnsave(key, opt_dkey - key);
1448 if (*opt_dkey == '\0') /* check for 'host.' */
1453 * Expand global options
1455 fs_static.fs_glob = expand_selectors(g_opts);
1458 * Expand local options
1460 fs_static.fs_local = expand_selectors(opts);
1462 /* break global options into fs_static fields */
1463 if ((ok = split_opts(fs_static.fs_glob, key))) {
1464 dlog("global split_opts ok");
1466 * evaluate local selectors
1468 if ((ok = eval_selectors(fs_static.fs_local, key))) {
1469 dlog("local eval_selectors ok");
1470 /* if the local selectors matched, then do the local overrides */
1471 ok = split_opts(fs_static.fs_local, key);
1473 dlog("local split_opts ok");
1478 * Normalize remote host name.
1479 * 1. Expand variables
1480 * 2. Normalize relative to host tables
1481 * 3. Strip local domains from the remote host
1482 * name before using it in other expansions.
1483 * This makes mount point names and other things
1484 * much shorter, while allowing cross domain
1485 * sharing of mount maps.
1487 apply_opts(expand_opts, rhost_expansion, FALSE);
1488 if (ok && fs_static.opt_rhost && *fs_static.opt_rhost)
1489 host_normalize(&fs_static.opt_rhost);
1492 * Macro expand the options.
1493 * Do this regardless of whether we are accepting
1494 * this mount - otherwise nasty things happen
1495 * with memory allocation.
1497 apply_opts(expand_opts, expansions, FALSE);
1500 * Strip trailing slashes from local pathname...
1502 deslashify(fs_static.opt_fs);
1505 * ok... copy the data back out.
1510 * Clear defined options
1512 if (opt_keyd != key && opt_keyd != nullstr)
1516 opt_key = opt_map = opt_path = nullstr;