2 * Copyright (c) 1997-2014 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * File: am-utils/amd/opts.c
42 #endif /* HAVE_CONFIG_H */
49 #define NLEN 16 /* Length of longest option name (conservative) */
50 #define S(x) (x) , (sizeof(x)-1)
52 * The BUFSPACE macros checks that there is enough space
53 * left in the expansion buffer. If there isn't then we
54 * give up completely. This is done to avoid crashing the
55 * automounter itself (which would be a bad thing to do).
57 #define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
62 typedef int (*IntFuncPtr) (char *);
63 typedef struct opt_apply opt_apply;
64 enum vs_opt { SelEQ, SelNE, VarAss };
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 int (*fxn_p)(char *); /* Pointer to boolean function */
75 int case_insensitive; /* How to do selector comparisons */
91 static int f_in_network(char *);
92 static int f_xhost(char *);
93 static int f_netgrp(char *);
94 static int f_netgrpd(char *);
95 static int f_exists(char *);
96 static int f_false(char *);
97 static int f_true(char *);
98 static inline char *expand_options(char *key);
103 static char NullStr[] = "<NULL>";
104 static char nullstr[] = "";
105 static char *opt_dkey = NullStr;
106 static char *opt_host = nullstr; /* XXX: was the global hostname */
107 static char *opt_hostd = hostd;
108 static char *opt_key = nullstr;
109 static char *opt_keyd = nullstr;
110 static char *opt_map = nullstr;
111 static char *opt_path = nullstr;
112 char uid_str[SIZEOF_UID_STR], gid_str[SIZEOF_GID_STR];
113 char *opt_uid = uid_str;
114 char *opt_gid = gid_str;
115 static char *vars[8];
116 static char *literal_dollar = "$"; /* ${dollar}: a literal '$' in maps */
121 static struct am_opts fs_static; /* copy of the options to play with */
125 * Options in some order corresponding to frequency of use so that
126 * first-match algorithm is sped up.
128 static struct opt opt_fields[] = {
130 Option str. Selector str. boolean fxn. case sensitive */
132 &fs_static.opt_opts, 0, 0, FALSE },
134 0, &opt_host, 0, TRUE },
136 0, &opt_hostd, 0, TRUE },
138 &fs_static.opt_type, 0, 0, FALSE },
140 &fs_static.opt_rhost, 0, 0, TRUE },
142 &fs_static.opt_rfs, 0, 0, FALSE },
144 &fs_static.opt_fs, 0, 0, FALSE },
146 0, &opt_key, 0, FALSE },
148 0, &opt_map, 0, FALSE },
150 &fs_static.opt_sublink, 0, 0, FALSE },
152 0, &gopt.arch, 0, TRUE },
154 &fs_static.opt_dev, 0, 0, FALSE },
156 &fs_static.opt_pref, 0, 0, FALSE },
158 0, &opt_path, 0, FALSE },
160 0, &gopt.auto_dir, 0, FALSE },
162 &fs_static.opt_delay, 0, 0, FALSE },
164 0, &hostdomain, 0, TRUE },
166 0, &gopt.karch, 0, TRUE },
168 0, &gopt.cluster, 0, TRUE },
170 0, 0, f_in_network, TRUE },
172 0, 0, f_in_network, TRUE },
174 0, 0, f_in_network, TRUE },
176 0, &endian, 0, TRUE },
178 0, &gopt.op_sys, 0, TRUE },
180 0, &gopt.op_sys_ver, 0, TRUE },
182 0, &gopt.op_sys_full, 0, TRUE },
184 0, &gopt.op_sys_vendor, 0, TRUE },
186 &fs_static.opt_remopts, 0, 0, FALSE },
188 &fs_static.opt_mount, 0, 0, FALSE },
190 &fs_static.opt_unmount, 0, 0, FALSE },
192 &fs_static.opt_umount, 0, 0, FALSE },
194 &fs_static.opt_cache, 0, 0, FALSE },
196 &fs_static.opt_user, 0, 0, FALSE },
198 &fs_static.opt_group, 0, 0, FALSE },
200 0, &opt_dkey, 0, FALSE },
202 0, &opt_keyd, 0, FALSE },
204 &fs_static.opt_maptype, 0, 0, FALSE },
206 &fs_static.opt_cachedir, 0, 0, FALSE },
208 &fs_static.opt_addopts, 0, 0, FALSE },
210 0, &opt_uid, 0, FALSE },
212 0, &opt_gid, 0, FALSE },
214 &fs_static.opt_mount_type, 0, 0, FALSE },
216 &literal_dollar, 0, 0, FALSE },
218 &vars[0], 0, 0, FALSE },
220 &vars[1], 0, 0, FALSE },
222 &vars[2], 0, 0, FALSE },
224 &vars[3], 0, 0, FALSE },
226 &vars[4], 0, 0, FALSE },
228 &vars[5], 0, 0, FALSE },
230 &vars[6], 0, 0, FALSE },
232 &vars[7], 0, 0, FALSE },
233 { 0, 0, 0, 0, 0, FALSE },
236 static struct functable functable[] = {
237 { "in_network", f_in_network },
238 { "xhost", f_xhost },
239 { "netgrp", f_netgrp },
240 { "netgrpd", f_netgrpd },
241 { "exists", f_exists },
242 { "false", f_false },
248 * Specially expand the remote host name first
250 static opt_apply rhost_expansion[] =
252 {&fs_static.opt_rhost, "${host}"},
257 * List of options which need to be expanded
258 * Note that the order here _may_ be important.
260 static opt_apply expansions[] =
262 {&fs_static.opt_sublink, 0},
263 {&fs_static.opt_rfs, "${path}"},
264 {&fs_static.opt_fs, "${autodir}/${rhost}${rfs}"},
265 {&fs_static.opt_opts, "rw"},
266 {&fs_static.opt_remopts, "${opts}"},
267 {&fs_static.opt_mount, 0},
268 {&fs_static.opt_unmount, 0},
269 {&fs_static.opt_umount, 0},
270 {&fs_static.opt_cachedir, 0},
271 {&fs_static.opt_addopts, 0},
276 * List of options which need to be free'ed before re-use
278 static opt_apply to_free[] =
280 {&fs_static.fs_glob, 0},
281 {&fs_static.fs_local, 0},
282 {&fs_static.fs_mtab, 0},
283 {&fs_static.opt_sublink, 0},
284 {&fs_static.opt_rfs, 0},
285 {&fs_static.opt_fs, 0},
286 {&fs_static.opt_rhost, 0},
287 {&fs_static.opt_opts, 0},
288 {&fs_static.opt_remopts, 0},
289 {&fs_static.opt_mount, 0},
290 {&fs_static.opt_unmount, 0},
291 {&fs_static.opt_umount, 0},
292 {&fs_static.opt_cachedir, 0},
293 {&fs_static.opt_addopts, 0},
307 * expand backslash escape sequences
308 * (escaped slash is handled separately in normalize_slash)
315 if ((*p)[1] == '\0') {
316 plog(XLOG_USER, "Empty backslash escape");
324 c = '\007'; /* Bell */
327 c = '\010'; /* Backspace */
330 c = '\011'; /* Horizontal Tab */
333 c = '\012'; /* New Line */
336 c = '\013'; /* Vertical Tab */
339 c = '\014'; /* Form Feed */
342 c = '\015'; /* Carriage Return */
345 c = '\033'; /* Escape */
358 for (cnt = 0, val = 0; cnt < 3; cnt++) {
360 if (ch < '0' || ch > '7') {
364 val = (val << 3) | (ch - '0');
367 if ((val & 0xffffff00) != 0)
369 "Too large character constant %u\n",
388 * Skip to next option in the string
398 while (*cp && *cp != ';') {
403 for (cp++; *cp && *cp != '"'; cp++)
405 *dp++ = backslash(&cp);
416 * Skip past any remaining ';'s
422 * If we have a zero length string
423 * and there are more fields, then
424 * parse the next one. This allows
425 * sequences of empty fields.
438 * These routines add a new style of selector; function-style boolean
439 * operators. To add new ones, just define functions as in true, false,
440 * exists (below) and add them to the functable, above.
442 * Usage example: Some people have X11R5 local, some go to a server. I do
445 * * exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \
446 * -type:=nfs;rfs=/usr/pkg/${key} \
450 * -Rens Troost <rens@imsi.com>
453 functable_lookup(char *key)
455 struct functable *fp;
457 for (fp = functable; fp->name; fp++)
458 if (FSTREQ(fp->name, key))
460 return (IntFuncPtr) NULL;
465 * Fill in the global structure fs_static by
466 * cracking the string opts. opts may be
467 * scribbled on at will. Does NOT evaluate options.
468 * Returns 0 on error, 1 if no syntax errors were discovered.
471 split_opts(char *opts, char *mapkey)
477 * For each user-specified option
479 for (f = opt(&o); *f; f = opt(&o)) {
481 char *eq = strchr(f, '=');
487 if (*(eq-1) == '!' ||
489 eq[1] == '!') { /* != or == or =! */
490 continue; /* we don't care about selectors */
493 if (*(eq-1) == ':') { /* := */
496 /* old style assignment */
502 * For each recognized option
504 for (op = opt_fields; op->name; op++) {
506 * Check whether they match
508 if (FSTREQ(op->name, f)) {
510 plog(XLOG_USER, "key %s: Can't assign to a selector (%s)",
514 *op->optp = opt; /* actual assignment into fs_static */
515 break; /* break out of for loop */
516 } /* end of "if (FSTREQ(op->name, f))" statement */
517 } /* end of "for (op = opt_fields..." statement */
520 plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
528 * Just evaluate selectors, which were split by split_opts.
529 * Returns 0 on error or no match, 1 if matched.
532 eval_selectors(char *opts, char *mapkey)
538 o = old_o = xstrdup(opts);
541 * For each user-specified option
543 for (f = opt(&o); *f; f = opt(&o)) {
546 char *eq = strchr(f, '=');
554 * No value, is it a function call?
556 arg = strchr(f, '(');
558 if (!arg || arg[1] == '\0' || arg == f) {
562 plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f);
566 /* null-terminate the argument */
568 fx = strchr(arg, ')');
569 if (fx == NULL || fx == arg) {
570 plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f);
582 * look up f in functable and pass it arg.
583 * func must return 0 on failure, and 1 on success.
585 if ((func = functable_lookup(f))) {
588 /* this allocates memory, don't forget to free */
589 arg = expand_options(arg);
600 plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f);
604 if (eq[1] == '\0' || eq == f) {
606 /* We allow empty assignments */
607 plog(XLOG_USER, "key %s: Bad selector \"%s\"", mapkey, f);
614 * Check what type of operation is happening
619 if (*(eq-1) == '!') { /* != */
623 } else if (*(eq-1) == ':') { /* := */
625 } else if (eq[1] == '=') { /* == */
629 } else if (eq[1] == '!') { /* =! */
634 /* old style assignment */
639 * For each recognized option
641 for (op = opt_fields; op->name; op++) {
643 * Check whether they match
645 if (FSTREQ(op->name, f)) {
646 opt = expand_options(opt);
648 if (op->sel_p != NULL) {
650 if (op->case_insensitive) {
651 selok = STRCEQ(*op->sel_p, opt);
653 selok = STREQ(*op->sel_p, opt);
658 plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s",
662 vs_opt == SelNE ? "mis" : "",
669 /* check if to apply a function */
673 funok = op->fxn_p(opt);
677 plog(XLOG_MAP, "key %s: map function %s did not %smatch %s",
680 vs_opt == SelNE ? "mis" : "",
687 break; /* break out of for loop */
692 plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
705 * Skip to next option in the string, but don't scribble over the string.
706 * However, *p gets repointed to the start of the next string past ';'.
709 opt_no_scribble(char **p)
716 while (*cp && *cp != ';') {
722 while (*cp && *cp != '\"')
732 * Skip past any remaining ';'s
738 * If we have a zero length string
739 * and there are more fields, then
740 * parse the next one. This allows
741 * sequences of empty fields.
752 * Strip any selectors from a string. Selectors are all assumed to be
753 * first in the string. This is used for the new /defaults method which will
754 * use selectors as well.
757 strip_selectors(char *opts, char *mapkey)
760 * Fill in the global structure fs_static by
761 * cracking the string opts. opts may be
762 * scribbled on at will.
769 * Scan options. Note that the opt() function scribbles on the opt string.
771 while (*(f = opt_no_scribble(&o))) {
772 enum vs_opt vs_opt = VarAss;
773 char *eq = strchr(f, '=');
775 if (!eq || eq[1] == '\0' || eq == f) {
777 * No option or assignment? Return as is.
779 plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f);
783 * Check what type of operation is happening
788 if (*(eq-1) == '!') { /* != */
790 } else if (*(eq-1) == ':') { /* := */
792 } else if (eq[1] == '=') { /* == */
794 } else if (eq[1] == '!') { /* =! */
800 /* Skip this selector, maybe there's another one following it */
801 plog(XLOG_USER, "skipping selector to \"%s\"", o);
802 /* store previous match. it may have been the first assignment */
807 /* found the first assignment, return the string starting with it */
808 dlog("found first assignment past selectors \"%s\"", o);
813 /* return the same string by default. should not happen. */
818 /*****************************************************************************
819 *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true): ***
820 *****************************************************************************/
822 /* test if arg is any of this host's network names or numbers */
824 f_in_network(char *arg)
831 status = is_network_member(arg);
832 dlog("%s is %son a local network", arg, (status ? "" : "not "));
838 * Test if arg is any of this host's names or aliases (CNAMES).
839 * Note: this function compares against the fully expanded host name (hostd).
840 * XXX: maybe we also need to compare against the stripped host name?
851 /* simple test: does it match main host name? */
852 if (STREQ(arg, opt_hostd))
855 /* now find all of the names of "arg" and compare against opt_hostd */
856 hp = gethostbyname(arg);
858 #ifdef HAVE_HSTRERROR
859 plog(XLOG_ERROR, "gethostbyname xhost(%s): %s", arg, hstrerror(h_errno));
860 #else /* not HAVE_HSTRERROR */
861 plog(XLOG_ERROR, "gethostbyname xhost(%s): h_errno %d", arg, h_errno);
862 #endif /* not HAVE_HSTRERROR */
865 /* check primary name */
867 dlog("xhost: compare %s==%s", hp->h_name, opt_hostd);
868 if (STREQ(hp->h_name, opt_hostd)) {
869 plog(XLOG_INFO, "xhost(%s): matched h_name %s", arg, hp->h_name);
873 /* check all aliases, if any */
874 if (hp->h_aliases == NULL) {
875 dlog("gethostbyname(%s) has no aliases", arg);
880 dlog("xhost: compare alias %s==%s", *cp, opt_hostd);
881 if (STREQ(*cp, opt_hostd)) {
882 plog(XLOG_INFO, "xhost(%s): matched alias %s", arg, *cp);
887 /* nothing matched */
892 /* test if this host (short hostname form) is in netgroup (arg) */
899 if ((ptr = strchr(arg, ',')) != NULL) {
905 status = innetgr(arg, nhost, NULL, NULL);
906 dlog("netgrp = %s status = %d host = %s", arg, status, nhost);
913 /* test if this host (fully-qualified name) is in netgroup (arg) */
920 if ((ptr = strchr(arg, ',')) != NULL) {
926 status = innetgr(arg, nhost, NULL, NULL);
927 dlog("netgrp = %s status = %d hostd = %s", arg, status, nhost);
934 /* test if file (arg) exists via lstat */
940 if (lstat(arg, &buf) < 0)
967 free_op(opt_apply *p, int b)
974 * Normalize slashes in the string.
977 normalize_slash(char *p)
981 if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
984 f0 = f = strchr(p, '/');
988 /* assert(*f == '/'); */
989 if (f == f0 && f[0] == '/' && f[1] == '/') {
990 /* copy double slash iff first */
994 /* copy a single / across */
998 /* assert(f[-1] == '/'); */
999 /* skip past more /'s */
1003 /* assert(*f != '/'); */
1004 /* keep copying up to next / */
1005 while (*f && *f != '/') {
1006 /* support escaped slashes '\/' */
1007 if (f[0] == '\\' && f[1] == '/')
1008 f++; /* skip backslash */
1012 /* assert(*f == 0 || *f == '/'); */
1015 *t = '\0'; /* derived from fix by Steven Glassman */
1021 * Macro-expand an option. Note that this does not
1022 * handle recursive expansions. They will go badly wrong.
1023 * If sel_p is true then old expand selectors, otherwise
1024 * don't expand selectors.
1027 expand_op(char *opt, int sel_p)
1029 #define EXPAND_ERROR "No space to expand \"%s\""
1030 char expbuf[MAXPATHLEN + 1];
1031 char nbuf[NLEN + 1];
1036 char *cp_orig = opt;
1038 while ((dp = strchr(cp, '$'))) {
1041 * First copy up to the $
1047 if (BUFSPACE(ep, len)) {
1049 * We use strncpy (not xstrlcpy) because 'ep' relies on its
1050 * semantics. BUFSPACE guarantees that ep can hold len.
1052 strncpy(ep, cp, len);
1055 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1064 if (BUFSPACE(ep, 1)) {
1067 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1070 } else if (ch == '{') {
1073 E_All, E_Dir, E_File, E_Domain, E_Host
1076 * Find closing brace
1078 char *br_p = strchr(cp, '}');
1088 plog(XLOG_USER, "No closing '}' in \"%s\"", opt);
1094 * Figure out which part of the variable to grab.
1098 * Just take the last component
1103 } else if (*(br_p-1) == '/') {
1105 * Take all but the last component
1109 } else if (*cp == '.') {
1116 } else if (*(br_p-1) == '.') {
1124 * Take the whole lot
1130 * Truncate if too long. Since it won't
1131 * match anyway it doesn't matter that
1132 * it has been cut short.
1138 * Put the string into another buffer so
1139 * we can do comparisons.
1141 * We use strncpy here (not xstrlcpy) because the dest is meant
1142 * to be truncated and we don't want to log it as an error. The
1143 * use of the BUFSPACE macro above guarantees the safe use of
1144 * strncpy with nbuf.
1146 strncpy(nbuf, cp, len);
1155 * Search the option array
1157 for (op = opt_fields; op->name; op++) {
1161 if (len == op->nlen && STREQ(op->name, nbuf)) {
1162 char xbuf[NLEN + 3];
1165 * Found expansion. Copy
1166 * the correct value field.
1168 if (!(!op->sel_p == !sel_p)) {
1170 * Copy the string across unexpanded
1172 xsnprintf(xbuf, sizeof(xbuf), "${%s%s%s}",
1173 todo == E_File ? "/" :
1174 todo == E_Domain ? "." : "",
1176 todo == E_Dir ? "/" :
1177 todo == E_Host ? "." : "");
1180 * Make sure expansion doesn't
1184 } else if (op->sel_p) {
1193 * ${/var} means take just the last part
1194 * ${var/} means take all but the last part
1195 * ${.var} means take all but first part
1196 * ${var.} means take just the first part
1197 * ${var} means take the whole lot
1199 int vlen = strlen(val);
1203 vptr = strrchr(val, '/');
1209 vptr = strrchr(val, '/');
1212 vlen = strlen(vptr);
1217 vptr = strchr(val, '.');
1220 vlen = strlen(vptr);
1227 vptr = strchr(val, '.');
1236 if (BUFSPACE(ep, vlen+1)) {
1238 * Don't call xstrlcpy() to truncate a string here. It causes
1239 * spurious xstrlcpy() syslog() errors. Use memcpy() and
1240 * explicitly terminate the string.
1242 memcpy(ep, vptr, vlen+1);
1246 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1251 * Done with this variable
1258 * Check that the search was successful
1262 * If it wasn't then scan the
1263 * environment for that name
1264 * and use any value found
1266 char *env = getenv(nbuf);
1269 int vlen = strlen(env);
1271 if (BUFSPACE(ep, vlen+1)) {
1272 xstrlcpy(ep, env, vlen+1);
1275 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1278 if (amuDebug(D_STR))
1279 plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
1281 plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
1288 plog(XLOG_USER, "Unknown $ sequence in \"%s\"", opt);
1294 * Handle common case - no expansion
1300 * Finish off the expansion
1302 int vlen = strlen(cp);
1303 if (BUFSPACE(ep, vlen+1)) {
1304 xstrlcpy(ep, cp, vlen+1);
1307 plog(XLOG_ERROR, EXPAND_ERROR, opt);
1311 * Save the expansion
1313 opt = xstrdup(expbuf);
1316 normalize_slash(opt);
1318 if (amuDebug(D_STR)) {
1319 plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig);
1320 plog(XLOG_DEBUG, "......... is \"%s\"", opt);
1327 * Wrapper for expand_op
1330 expand_opts(opt_apply *p, int sel_p)
1333 *p->opt = expand_op(*p->opt, sel_p);
1334 } else if (p->val) {
1336 * Do double expansion, remembering
1337 * to free the string from the first
1340 char *s = expand_op(p->val, TRUE);
1341 *p->opt = expand_op(s, sel_p);
1348 * Apply a function to a list of options
1351 apply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b)
1355 for (pp = ppp; pp->opt; pp++)
1361 * Free the option table
1364 free_opts(am_opts *fo)
1367 * Copy in the structure we are playing with
1372 * Free previously allocated memory
1374 apply_opts(free_op, to_free, FALSE);
1378 copy_opts(am_opts *old)
1381 newopts = CALLOC(struct am_opts);
1383 #define _AM_OPT_COPY(field) do { \
1385 newopts->field = xstrdup(old->field); \
1388 _AM_OPT_COPY(fs_glob);
1389 _AM_OPT_COPY(fs_local);
1390 _AM_OPT_COPY(fs_mtab);
1391 _AM_OPT_COPY(opt_dev);
1392 _AM_OPT_COPY(opt_delay);
1393 _AM_OPT_COPY(opt_dir);
1394 _AM_OPT_COPY(opt_fs);
1395 _AM_OPT_COPY(opt_group);
1396 _AM_OPT_COPY(opt_mount);
1397 _AM_OPT_COPY(opt_opts);
1398 _AM_OPT_COPY(opt_remopts);
1399 _AM_OPT_COPY(opt_pref);
1400 _AM_OPT_COPY(opt_cache);
1401 _AM_OPT_COPY(opt_rfs);
1402 _AM_OPT_COPY(opt_rhost);
1403 _AM_OPT_COPY(opt_sublink);
1404 _AM_OPT_COPY(opt_type);
1405 _AM_OPT_COPY(opt_mount_type);
1406 _AM_OPT_COPY(opt_unmount);
1407 _AM_OPT_COPY(opt_umount);
1408 _AM_OPT_COPY(opt_user);
1409 _AM_OPT_COPY(opt_maptype);
1410 _AM_OPT_COPY(opt_cachedir);
1411 _AM_OPT_COPY(opt_addopts);
1418 * Expand selectors (variables that cannot be assigned to or overridden)
1421 expand_selectors(char *key)
1423 return expand_op(key, TRUE);
1428 * Expand options (i.e. non-selectors, see above for definition)
1430 static inline char *
1431 expand_options(char *key)
1433 return expand_op(key, FALSE);
1438 * Remove trailing /'s from a string
1439 * unless the string is a single / (Steven Glassman)
1440 * or unless it is two slashes // (Kevin D. Bond)
1441 * or unless amd.conf says not to touch slashes.
1446 if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
1450 char *sl = s + strlen(s);
1452 while (*--sl == '/' && sl > s)
1459 eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map)
1466 * Clear out the option table
1468 memset((voidp) &fs_static, 0, sizeof(fs_static));
1469 memset((voidp) vars, 0, sizeof(vars));
1470 memset((voidp) fo, 0, sizeof(*fo));
1473 opt_host = (char *) am_get_hostname();
1476 * Set key, map & path before expansion
1482 opt_dkey = strchr(key, '.');
1487 opt_keyd = strnsave(key, opt_dkey - key);
1489 if (*opt_dkey == '\0') /* check for 'host.' */
1494 * Expand global options
1496 fs_static.fs_glob = expand_selectors(g_opts);
1499 * Expand local options
1501 fs_static.fs_local = expand_selectors(opts);
1503 /* break global options into fs_static fields */
1504 if ((ok = split_opts(fs_static.fs_glob, key))) {
1505 dlog("global split_opts ok");
1507 * evaluate local selectors
1509 if ((ok = eval_selectors(fs_static.fs_local, key))) {
1510 dlog("local eval_selectors ok");
1511 /* if the local selectors matched, then do the local overrides */
1512 ok = split_opts(fs_static.fs_local, key);
1514 dlog("local split_opts ok");
1519 * Normalize remote host name.
1520 * 1. Expand variables
1521 * 2. Normalize relative to host tables
1522 * 3. Strip local domains from the remote host
1523 * name before using it in other expansions.
1524 * This makes mount point names and other things
1525 * much shorter, while allowing cross domain
1526 * sharing of mount maps.
1528 apply_opts(expand_opts, rhost_expansion, FALSE);
1529 if (ok && fs_static.opt_rhost && *fs_static.opt_rhost)
1530 host_normalize(&fs_static.opt_rhost);
1533 * Macro expand the options.
1534 * Do this regardless of whether we are accepting
1535 * this mount - otherwise nasty things happen
1536 * with memory allocation.
1538 apply_opts(expand_opts, expansions, FALSE);
1541 * Strip trailing slashes from local pathname...
1543 deslashify(fs_static.opt_fs);
1546 * ok... copy the data back out.
1551 * Clear defined options
1553 if (opt_keyd != key && opt_keyd != nullstr)
1557 opt_key = opt_map = opt_path = nullstr;