4 * Copyright (c) 1997-2009 Erez Zadok
5 * Copyright (c) 1989 Jan-Simon Pendry
6 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1989 The Regents of the University of California.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * File: am-utils/amd/opts.c
48 #endif /* HAVE_CONFIG_H */
55 #define NLEN 16 /* Length of longest option name (conservative) */
56 #define S(x) (x) , (sizeof(x)-1)
58 * The BUFSPACE macros checks that there is enough space
59 * left in the expansion buffer. If there isn't then we
60 * give up completely. This is done to avoid crashing the
61 * automounter itself (which would be a bad thing to do).
63 #define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
68 typedef int (*IntFuncPtr
) (char *);
69 typedef struct opt_apply opt_apply
;
70 enum vs_opt
{ SelEQ
, SelNE
, VarAss
};
76 char *name
; /* Name of the option */
77 int nlen
; /* Length of option name */
78 char **optp
; /* Pointer to option value string */
79 char **sel_p
; /* Pointer to selector value string */
80 int (*fxn_p
)(char *); /* Pointer to boolean function */
81 int case_insensitive
; /* How to do selector comparisons */
97 static int f_in_network(char *);
98 static int f_xhost(char *);
99 static int f_netgrp(char *);
100 static int f_netgrpd(char *);
101 static int f_exists(char *);
102 static int f_false(char *);
103 static int f_true(char *);
104 static inline char *expand_options(char *key
);
109 static char NullStr
[] = "<NULL>";
110 static char nullstr
[] = "";
111 static char *opt_dkey
= NullStr
;
112 static char *opt_host
= nullstr
; /* XXX: was the global hostname */
113 static char *opt_hostd
= hostd
;
114 static char *opt_key
= nullstr
;
115 static char *opt_keyd
= nullstr
;
116 static char *opt_map
= nullstr
;
117 static char *opt_path
= nullstr
;
118 char uid_str
[SIZEOF_UID_STR
], gid_str
[SIZEOF_GID_STR
];
119 char *opt_uid
= uid_str
;
120 char *opt_gid
= gid_str
;
121 static char *vars
[8];
122 static char *literal_dollar
= "$"; /* ${dollar}: a literal '$' in maps */
127 static struct am_opts fs_static
; /* copy of the options to play with */
131 * Options in some order corresponding to frequency of use so that
132 * first-match algorithm is sped up.
134 static struct opt opt_fields
[] = {
136 Option str. Selector str. boolean fxn. case sensitive */
138 &fs_static
.opt_opts
, 0, 0, FALSE
},
140 0, &opt_host
, 0, TRUE
},
142 0, &opt_hostd
, 0, TRUE
},
144 &fs_static
.opt_type
, 0, 0, FALSE
},
146 &fs_static
.opt_rhost
, 0, 0, TRUE
},
148 &fs_static
.opt_rfs
, 0, 0, FALSE
},
150 &fs_static
.opt_fs
, 0, 0, FALSE
},
152 0, &opt_key
, 0, FALSE
},
154 0, &opt_map
, 0, FALSE
},
156 &fs_static
.opt_sublink
, 0, 0, FALSE
},
158 0, &gopt
.arch
, 0, TRUE
},
160 &fs_static
.opt_dev
, 0, 0, FALSE
},
162 &fs_static
.opt_pref
, 0, 0, FALSE
},
164 0, &opt_path
, 0, FALSE
},
166 0, &gopt
.auto_dir
, 0, FALSE
},
168 &fs_static
.opt_delay
, 0, 0, FALSE
},
170 0, &hostdomain
, 0, TRUE
},
172 0, &gopt
.karch
, 0, TRUE
},
174 0, &gopt
.cluster
, 0, TRUE
},
176 0, 0, f_in_network
, TRUE
},
178 0, 0, f_in_network
, TRUE
},
180 0, 0, f_in_network
, TRUE
},
182 0, &endian
, 0, TRUE
},
184 0, &gopt
.op_sys
, 0, TRUE
},
186 0, &gopt
.op_sys_ver
, 0, TRUE
},
188 0, &gopt
.op_sys_full
, 0, TRUE
},
190 0, &gopt
.op_sys_vendor
, 0, TRUE
},
192 &fs_static
.opt_remopts
, 0, 0, FALSE
},
194 &fs_static
.opt_mount
, 0, 0, FALSE
},
196 &fs_static
.opt_unmount
, 0, 0, FALSE
},
198 &fs_static
.opt_umount
, 0, 0, FALSE
},
200 &fs_static
.opt_cache
, 0, 0, FALSE
},
202 &fs_static
.opt_user
, 0, 0, FALSE
},
204 &fs_static
.opt_group
, 0, 0, FALSE
},
206 0, &opt_dkey
, 0, FALSE
},
208 0, &opt_keyd
, 0, FALSE
},
210 &fs_static
.opt_maptype
, 0, 0, FALSE
},
212 &fs_static
.opt_cachedir
, 0, 0, FALSE
},
214 &fs_static
.opt_addopts
, 0, 0, FALSE
},
216 0, &opt_uid
, 0, FALSE
},
218 0, &opt_gid
, 0, FALSE
},
220 &fs_static
.opt_mount_type
, 0, 0, FALSE
},
222 &literal_dollar
, 0, 0, FALSE
},
224 &vars
[0], 0, 0, FALSE
},
226 &vars
[1], 0, 0, FALSE
},
228 &vars
[2], 0, 0, FALSE
},
230 &vars
[3], 0, 0, FALSE
},
232 &vars
[4], 0, 0, FALSE
},
234 &vars
[5], 0, 0, FALSE
},
236 &vars
[6], 0, 0, FALSE
},
238 &vars
[7], 0, 0, FALSE
},
239 { 0, 0, 0, 0, 0, FALSE
},
242 static struct functable functable
[] = {
243 { "in_network", f_in_network
},
244 { "xhost", f_xhost
},
245 { "netgrp", f_netgrp
},
246 { "netgrpd", f_netgrpd
},
247 { "exists", f_exists
},
248 { "false", f_false
},
254 * Specially expand the remote host name first
256 static opt_apply rhost_expansion
[] =
258 {&fs_static
.opt_rhost
, "${host}"},
263 * List of options which need to be expanded
264 * Note that the order here _may_ be important.
266 static opt_apply expansions
[] =
268 {&fs_static
.opt_sublink
, 0},
269 {&fs_static
.opt_rfs
, "${path}"},
270 {&fs_static
.opt_fs
, "${autodir}/${rhost}${rfs}"},
271 {&fs_static
.opt_opts
, "rw"},
272 {&fs_static
.opt_remopts
, "${opts}"},
273 {&fs_static
.opt_mount
, 0},
274 {&fs_static
.opt_unmount
, 0},
275 {&fs_static
.opt_umount
, 0},
276 {&fs_static
.opt_cachedir
, 0},
277 {&fs_static
.opt_addopts
, 0},
282 * List of options which need to be free'ed before re-use
284 static opt_apply to_free
[] =
286 {&fs_static
.fs_glob
, 0},
287 {&fs_static
.fs_local
, 0},
288 {&fs_static
.fs_mtab
, 0},
289 {&fs_static
.opt_sublink
, 0},
290 {&fs_static
.opt_rfs
, 0},
291 {&fs_static
.opt_fs
, 0},
292 {&fs_static
.opt_rhost
, 0},
293 {&fs_static
.opt_opts
, 0},
294 {&fs_static
.opt_remopts
, 0},
295 {&fs_static
.opt_mount
, 0},
296 {&fs_static
.opt_unmount
, 0},
297 {&fs_static
.opt_umount
, 0},
298 {&fs_static
.opt_cachedir
, 0},
299 {&fs_static
.opt_addopts
, 0},
313 * expand backslash escape sequences
314 * (escaped slash is handled separately in normalize_slash)
321 if ((*p
)[1] == '\0') {
322 plog(XLOG_USER
, "Empty backslash escape");
330 c
= '\007'; /* Bell */
333 c
= '\010'; /* Backspace */
336 c
= '\011'; /* Horizontal Tab */
339 c
= '\012'; /* New Line */
342 c
= '\013'; /* Vertical Tab */
345 c
= '\014'; /* Form Feed */
348 c
= '\015'; /* Carriage Return */
351 c
= '\033'; /* Escape */
364 for (cnt
= 0, val
= 0; cnt
< 3; cnt
++) {
366 if (ch
< '0' || ch
> '7') {
370 val
= (val
<< 3) | (ch
- '0');
373 if ((val
& 0xffffff00) != 0)
375 "Too large character constant %u\n",
394 * Skip to next option in the string
404 while (*cp
&& *cp
!= ';') {
409 for (cp
++; *cp
&& *cp
!= '"'; cp
++)
411 *dp
++ = backslash(&cp
);
422 * Skip past any remaining ';'s
428 * If we have a zero length string
429 * and there are more fields, then
430 * parse the next one. This allows
431 * sequences of empty fields.
444 * These routines add a new style of selector; function-style boolean
445 * operators. To add new ones, just define functions as in true, false,
446 * exists (below) and add them to the functable, above.
448 * Usage example: Some people have X11R5 local, some go to a server. I do
451 * * exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \
452 * -type:=nfs;rfs=/usr/pkg/${key} \
456 * -Rens Troost <rens@imsi.com>
459 functable_lookup(char *key
)
461 struct functable
*fp
;
463 for (fp
= functable
; fp
->name
; fp
++)
464 if (FSTREQ(fp
->name
, key
))
466 return (IntFuncPtr
) NULL
;
471 * Fill in the global structure fs_static by
472 * cracking the string opts. opts may be
473 * scribbled on at will. Does NOT evaluate options.
474 * Returns 0 on error, 1 if no syntax errors were discovered.
477 split_opts(char *opts
, char *mapkey
)
483 * For each user-specified option
485 for (f
= opt(&o
); *f
; f
= opt(&o
)) {
487 char *eq
= strchr(f
, '=');
493 if (*(eq
-1) == '!' ||
495 eq
[1] == '!') { /* != or == or =! */
496 continue; /* we don't care about selectors */
499 if (*(eq
-1) == ':') { /* := */
502 /* old style assignment */
508 * For each recognized option
510 for (op
= opt_fields
; op
->name
; op
++) {
512 * Check whether they match
514 if (FSTREQ(op
->name
, f
)) {
516 plog(XLOG_USER
, "key %s: Can't assign to a selector (%s)",
520 *op
->optp
= opt
; /* actual assignment into fs_static */
521 break; /* break out of for loop */
522 } /* end of "if (FSTREQ(op->name, f))" statement */
523 } /* end of "for (op = opt_fields..." statement */
526 plog(XLOG_USER
, "key %s: Unrecognized key/option \"%s\"", mapkey
, f
);
534 * Just evaluate selectors, which were split by split_opts.
535 * Returns 0 on error or no match, 1 if matched.
538 eval_selectors(char *opts
, char *mapkey
)
544 o
= old_o
= strdup(opts
);
547 * For each user-specified option
549 for (f
= opt(&o
); *f
; f
= opt(&o
)) {
552 char *eq
= strchr(f
, '=');
560 * No value, is it a function call?
562 arg
= strchr(f
, '(');
564 if (!arg
|| arg
[1] == '\0' || arg
== f
) {
568 plog(XLOG_USER
, "key %s: No value component in \"%s\"", mapkey
, f
);
572 /* null-terminate the argument */
574 fx
= strchr(arg
, ')');
575 if (!arg
|| fx
== arg
) {
576 plog(XLOG_USER
, "key %s: Malformed function in \"%s\"", mapkey
, f
);
588 * look up f in functable and pass it arg.
589 * func must return 0 on failure, and 1 on success.
591 if ((func
= functable_lookup(f
))) {
594 /* this allocates memory, don't forget to free */
595 arg
= expand_options(arg
);
606 plog(XLOG_USER
, "key %s: unknown function \"%s\"", mapkey
, f
);
610 if (eq
[1] == '\0' || eq
== f
) {
611 /* misformed selector */
612 plog(XLOG_USER
, "key %s: Bad selector \"%s\"", mapkey
, f
);
618 * Check what type of operation is happening
623 if (*(eq
-1) == '!') { /* != */
627 } else if (*(eq
-1) == ':') { /* := */
629 } else if (eq
[1] == '=') { /* == */
633 } else if (eq
[1] == '!') { /* =! */
638 /* old style assignment */
643 * For each recognized option
645 for (op
= opt_fields
; op
->name
; op
++) {
647 * Check whether they match
649 if (FSTREQ(op
->name
, f
)) {
650 opt
= expand_options(opt
);
652 if (op
->sel_p
!= NULL
) {
654 if (op
->case_insensitive
) {
655 selok
= STRCEQ(*op
->sel_p
, opt
);
657 selok
= STREQ(*op
->sel_p
, opt
);
662 plog(XLOG_MAP
, "key %s: map selector %s (=%s) did not %smatch %s",
666 vs_opt
== SelNE
? "mis" : "",
673 /* check if to apply a function */
677 funok
= op
->fxn_p(opt
);
681 plog(XLOG_MAP
, "key %s: map function %s did not %smatch %s",
684 vs_opt
== SelNE
? "mis" : "",
691 break; /* break out of for loop */
696 plog(XLOG_USER
, "key %s: Unrecognized key/option \"%s\"", mapkey
, f
);
709 * Skip to next option in the string, but don't scribble over the string.
710 * However, *p gets repointed to the start of the next string past ';'.
713 opt_no_scribble(char **p
)
720 while (*cp
&& *cp
!= ';') {
726 while (*cp
&& *cp
!= '\"')
736 * Skip past any remaining ';'s
742 * If we have a zero length string
743 * and there are more fields, then
744 * parse the next one. This allows
745 * sequences of empty fields.
756 * Strip any selectors from a string. Selectors are all assumed to be
757 * first in the string. This is used for the new /defaults method which will
758 * use selectors as well.
761 strip_selectors(char *opts
, char *mapkey
)
764 * Fill in the global structure fs_static by
765 * cracking the string opts. opts may be
766 * scribbled on at will.
773 * Scan options. Note that the opt() function scribbles on the opt string.
775 while (*(f
= opt_no_scribble(&o
))) {
776 enum vs_opt vs_opt
= VarAss
;
777 char *eq
= strchr(f
, '=');
779 if (!eq
|| eq
[1] == '\0' || eq
== f
) {
781 * No option or assignment? Return as is.
783 plog(XLOG_USER
, "key %s: No option or assignment in \"%s\"", mapkey
, f
);
787 * Check what type of operation is happening
792 if (*(eq
-1) == '!') { /* != */
794 } else if (*(eq
-1) == ':') { /* := */
796 } else if (eq
[1] == '=') { /* == */
798 } else if (eq
[1] == '!') { /* =! */
804 /* Skip this selector, maybe there's another one following it */
805 plog(XLOG_USER
, "skipping selector to \"%s\"", o
);
806 /* store previous match. it may have been the first assignment */
811 /* found the first assignment, return the string starting with it */
812 dlog("found first assignment past selectors \"%s\"", o
);
817 /* return the same string by default. should not happen. */
822 /*****************************************************************************
823 *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true): ***
824 *****************************************************************************/
826 /* test if arg is any of this host's network names or numbers */
828 f_in_network(char *arg
)
835 status
= is_network_member(arg
);
836 dlog("%s is %son a local network", arg
, (status
? "" : "not "));
842 * Test if arg is any of this host's names or aliases (CNAMES).
843 * Note: this function compares against the fully expanded host name (hostd).
844 * XXX: maybe we also need to compare against the stripped host name?
855 /* simple test: does it match main host name? */
856 if (STREQ(arg
, opt_hostd
))
859 /* now find all of the names of "arg" and compare against opt_hostd */
860 hp
= gethostbyname(arg
);
862 #ifdef HAVE_HSTRERROR
863 plog(XLOG_ERROR
, "gethostbyname xhost(%s): %s", arg
, hstrerror(h_errno
));
864 #else /* not HAVE_HSTRERROR */
865 plog(XLOG_ERROR
, "gethostbyname xhost(%s): h_errno %d", arg
, h_errno
);
866 #endif /* not HAVE_HSTRERROR */
869 /* check primary name */
871 dlog("xhost: compare %s==%s", hp
->h_name
, opt_hostd
);
872 if (STREQ(hp
->h_name
, opt_hostd
)) {
873 plog(XLOG_INFO
, "xhost(%s): matched h_name %s", arg
, hp
->h_name
);
877 /* check all aliases, if any */
878 if (hp
->h_aliases
== NULL
) {
879 dlog("gethostbyname(%s) has no aliases", arg
);
884 dlog("xhost: compare alias %s==%s", *cp
, opt_hostd
);
885 if (STREQ(*cp
, opt_hostd
)) {
886 plog(XLOG_INFO
, "xhost(%s): matched alias %s", arg
, *cp
);
891 /* nothing matched */
896 /* test if this host (short hostname form) is in netgroup (arg) */
903 if ((ptr
= strchr(arg
, ',')) != NULL
) {
909 status
= innetgr(arg
, nhost
, NULL
, NULL
);
910 dlog("netgrp = %s status = %d host = %s", arg
, status
, nhost
);
917 /* test if this host (fully-qualified name) is in netgroup (arg) */
924 if ((ptr
= strchr(arg
, ',')) != NULL
) {
930 status
= innetgr(arg
, nhost
, NULL
, NULL
);
931 dlog("netgrp = %s status = %d hostd = %s", arg
, status
, nhost
);
938 /* test if file (arg) exists via lstat */
944 if (lstat(arg
, &buf
) < 0)
971 free_op(opt_apply
*p
, int b
)
980 * Normalize slashes in the string.
983 normalize_slash(char *p
)
987 if (!(gopt
.flags
& CFM_NORMALIZE_SLASHES
))
990 f0
= f
= strchr(p
, '/');
994 /* assert(*f == '/'); */
995 if (f
== f0
&& f
[0] == '/' && f
[1] == '/') {
996 /* copy double slash iff first */
1000 /* copy a single / across */
1004 /* assert(f[-1] == '/'); */
1005 /* skip past more /'s */
1009 /* assert(*f != '/'); */
1010 /* keep copying up to next / */
1011 while (*f
&& *f
!= '/') {
1012 /* support escaped slashes '\/' */
1013 if (f
[0] == '\\' && f
[1] == '/')
1014 f
++; /* skip backslash */
1018 /* assert(*f == 0 || *f == '/'); */
1021 *t
= '\0'; /* derived from fix by Steven Glassman */
1027 * Macro-expand an option. Note that this does not
1028 * handle recursive expansions. They will go badly wrong.
1029 * If sel_p is true then old expand selectors, otherwise
1030 * don't expand selectors.
1033 expand_op(char *opt
, int sel_p
)
1035 #define EXPAND_ERROR "No space to expand \"%s\""
1036 char expbuf
[MAXPATHLEN
+ 1];
1037 char nbuf
[NLEN
+ 1];
1042 char *cp_orig
= opt
;
1044 while ((dp
= strchr(cp
, '$'))) {
1047 * First copy up to the $
1053 if (BUFSPACE(ep
, len
)) {
1055 * We use strncpy (not xstrlcpy) because 'ep' relies on its
1056 * semantics. BUFSPACE guarantees that ep can hold len.
1058 strncpy(ep
, cp
, len
);
1061 plog(XLOG_ERROR
, EXPAND_ERROR
, opt
);
1070 if (BUFSPACE(ep
, 1)) {
1073 plog(XLOG_ERROR
, EXPAND_ERROR
, opt
);
1076 } else if (ch
== '{') {
1079 E_All
, E_Dir
, E_File
, E_Domain
, E_Host
1082 * Find closing brace
1084 char *br_p
= strchr(cp
, '}');
1094 plog(XLOG_USER
, "No closing '}' in \"%s\"", opt
);
1100 * Figure out which part of the variable to grab.
1104 * Just take the last component
1109 } else if (*(br_p
-1) == '/') {
1111 * Take all but the last component
1115 } else if (*cp
== '.') {
1122 } else if (*(br_p
-1) == '.') {
1130 * Take the whole lot
1136 * Truncate if too long. Since it won't
1137 * match anyway it doesn't matter that
1138 * it has been cut short.
1144 * Put the string into another buffer so
1145 * we can do comparisons.
1147 * We use strncpy here (not xstrlcpy) because the dest is meant
1148 * to be truncated and we don't want to log it as an error. The
1149 * use of the BUFSPACE macro above guarantees the safe use of
1150 * strncpy with nbuf.
1152 strncpy(nbuf
, cp
, len
);
1161 * Search the option array
1163 for (op
= opt_fields
; op
->name
; op
++) {
1167 if (len
== op
->nlen
&& STREQ(op
->name
, nbuf
)) {
1168 char xbuf
[NLEN
+ 3];
1171 * Found expansion. Copy
1172 * the correct value field.
1174 if (!(!op
->sel_p
== !sel_p
)) {
1176 * Copy the string across unexpanded
1178 xsnprintf(xbuf
, sizeof(xbuf
), "${%s%s%s}",
1179 todo
== E_File
? "/" :
1180 todo
== E_Domain
? "." : "",
1182 todo
== E_Dir
? "/" :
1183 todo
== E_Host
? "." : "");
1186 * Make sure expansion doesn't
1190 } else if (op
->sel_p
) {
1199 * ${/var} means take just the last part
1200 * ${var/} means take all but the last part
1201 * ${.var} means take all but first part
1202 * ${var.} means take just the first part
1203 * ${var} means take the whole lot
1205 int vlen
= strlen(val
);
1209 vptr
= strrchr(val
, '/');
1215 vptr
= strrchr(val
, '/');
1218 vlen
= strlen(vptr
);
1223 vptr
= strchr(val
, '.');
1226 vlen
= strlen(vptr
);
1233 vptr
= strchr(val
, '.');
1242 if (BUFSPACE(ep
, vlen
+1)) {
1244 * Don't call xstrlcpy() to truncate a string here. It causes
1245 * spurious xstrlcpy() syslog() errors. Use memcpy() and
1246 * explicitly terminate the string.
1248 memcpy(ep
, vptr
, vlen
+1);
1252 plog(XLOG_ERROR
, EXPAND_ERROR
, opt
);
1257 * Done with this variable
1264 * Check that the search was successful
1268 * If it wasn't then scan the
1269 * environment for that name
1270 * and use any value found
1272 char *env
= getenv(nbuf
);
1275 int vlen
= strlen(env
);
1277 if (BUFSPACE(ep
, vlen
+1)) {
1278 xstrlcpy(ep
, env
, vlen
+1);
1281 plog(XLOG_ERROR
, EXPAND_ERROR
, opt
);
1284 if (amuDebug(D_STR
))
1285 plog(XLOG_DEBUG
, "Environment gave \"%s\" -> \"%s\"", nbuf
, env
);
1287 plog(XLOG_USER
, "Unknown sequence \"${%s}\"", nbuf
);
1294 plog(XLOG_USER
, "Unknown $ sequence in \"%s\"", opt
);
1300 * Handle common case - no expansion
1306 * Finish off the expansion
1308 int vlen
= strlen(cp
);
1309 if (BUFSPACE(ep
, vlen
+1)) {
1310 xstrlcpy(ep
, cp
, vlen
+1);
1313 plog(XLOG_ERROR
, EXPAND_ERROR
, opt
);
1317 * Save the expansion
1319 opt
= strdup(expbuf
);
1322 normalize_slash(opt
);
1324 if (amuDebug(D_STR
)) {
1325 plog(XLOG_DEBUG
, "Expansion of \"%s\"...", cp_orig
);
1326 plog(XLOG_DEBUG
, "......... is \"%s\"", opt
);
1333 * Wrapper for expand_op
1336 expand_opts(opt_apply
*p
, int sel_p
)
1339 *p
->opt
= expand_op(*p
->opt
, sel_p
);
1340 } else if (p
->val
) {
1342 * Do double expansion, remembering
1343 * to free the string from the first
1346 char *s
= expand_op(p
->val
, TRUE
);
1347 *p
->opt
= expand_op(s
, sel_p
);
1354 * Apply a function to a list of options
1357 apply_opts(void (*op
) (opt_apply
*, int), opt_apply ppp
[], int b
)
1361 for (pp
= ppp
; pp
->opt
; pp
++)
1367 * Free the option table
1370 free_opts(am_opts
*fo
)
1373 * Copy in the structure we are playing with
1378 * Free previously allocated memory
1380 apply_opts(free_op
, to_free
, FALSE
);
1385 * Expand selectors (variables that cannot be assigned to or overridden)
1388 expand_selectors(char *key
)
1390 return expand_op(key
, TRUE
);
1395 * Expand options (i.e. non-selectors, see above for definition)
1397 static inline char *
1398 expand_options(char *key
)
1400 return expand_op(key
, FALSE
);
1405 * Remove trailing /'s from a string
1406 * unless the string is a single / (Steven Glassman)
1407 * or unless it is two slashes // (Kevin D. Bond)
1408 * or unless amd.conf says not to touch slashes.
1413 if (!(gopt
.flags
& CFM_NORMALIZE_SLASHES
))
1417 char *sl
= s
+ strlen(s
);
1419 while (*--sl
== '/' && sl
> s
)
1426 eval_fs_opts(am_opts
*fo
, char *opts
, char *g_opts
, char *path
, char *key
, char *map
)
1433 * Clear out the option table
1435 memset((voidp
) &fs_static
, 0, sizeof(fs_static
));
1436 memset((voidp
) vars
, 0, sizeof(vars
));
1437 memset((voidp
) fo
, 0, sizeof(*fo
));
1440 opt_host
= (char *) am_get_hostname();
1443 * Set key, map & path before expansion
1449 opt_dkey
= strchr(key
, '.');
1454 opt_keyd
= strnsave(key
, opt_dkey
- key
);
1456 if (*opt_dkey
== '\0') /* check for 'host.' */
1461 * Expand global options
1463 fs_static
.fs_glob
= expand_selectors(g_opts
);
1466 * Expand local options
1468 fs_static
.fs_local
= expand_selectors(opts
);
1470 /* break global options into fs_static fields */
1471 if ((ok
= split_opts(fs_static
.fs_glob
, key
))) {
1472 dlog("global split_opts ok");
1474 * evaluate local selectors
1476 if ((ok
= eval_selectors(fs_static
.fs_local
, key
))) {
1477 dlog("local eval_selectors ok");
1478 /* if the local selectors matched, then do the local overrides */
1479 ok
= split_opts(fs_static
.fs_local
, key
);
1481 dlog("local split_opts ok");
1486 * Normalize remote host name.
1487 * 1. Expand variables
1488 * 2. Normalize relative to host tables
1489 * 3. Strip local domains from the remote host
1490 * name before using it in other expansions.
1491 * This makes mount point names and other things
1492 * much shorter, while allowing cross domain
1493 * sharing of mount maps.
1495 apply_opts(expand_opts
, rhost_expansion
, FALSE
);
1496 if (ok
&& fs_static
.opt_rhost
&& *fs_static
.opt_rhost
)
1497 host_normalize(&fs_static
.opt_rhost
);
1500 * Macro expand the options.
1501 * Do this regardless of whether we are accepting
1502 * this mount - otherwise nasty things happen
1503 * with memory allocation.
1505 apply_opts(expand_opts
, expansions
, FALSE
);
1508 * Strip trailing slashes from local pathname...
1510 deslashify(fs_static
.opt_fs
);
1513 * ok... copy the data back out.
1518 * Clear defined options
1520 if (opt_keyd
!= key
&& opt_keyd
!= nullstr
)
1524 opt_key
= opt_map
= opt_path
= nullstr
;