Home | History | Annotate | Line # | Download | only in amd
      1 /*	$NetBSD: opts.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997-2014 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.
      8  * All rights reserved.
      9  *
     10  * This code is derived from software contributed to Berkeley by
     11  * Jan-Simon Pendry at Imperial College, London.
     12  *
     13  * Redistribution and use in source and binary forms, with or without
     14  * modification, are permitted provided that the following conditions
     15  * are met:
     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. Neither the name of the University nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  *
     37  *
     38  * File: am-utils/amd/opts.c
     39  *
     40  */
     41 
     42 #ifdef HAVE_CONFIG_H
     43 # include <config.h>
     44 #endif /* HAVE_CONFIG_H */
     45 #include <am_defs.h>
     46 #include <amd.h>
     47 
     48 /*
     49  * MACROS:
     50  */
     51 #define	NLEN	16	/* Length of longest option name (conservative) */
     52 #define S(x) (x) , (sizeof(x)-1)
     53 /*
     54  * The BUFSPACE macros checks that there is enough space
     55  * left in the expansion buffer.  If there isn't then we
     56  * give up completely.  This is done to avoid crashing the
     57  * automounter itself (which would be a bad thing to do).
     58  */
     59 #define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
     60 
     61 /*
     62  * TYPEDEFS:
     63  */
     64 typedef int (*IntFuncPtr) (char *);
     65 typedef struct opt_apply opt_apply;
     66 enum vs_opt { SelEQ, SelNE, VarAss };
     67 
     68 /*
     69  * STRUCTURES
     70  */
     71 struct opt {
     72   char *name;			/* Name of the option */
     73   int nlen;			/* Length of option name */
     74   char **optp;			/* Pointer to option value string */
     75   char **sel_p;			/* Pointer to selector value string */
     76   int (*fxn_p)(char *);		/* Pointer to boolean function */
     77   int case_insensitive;		/* How to do selector comparisons */
     78 };
     79 
     80 struct opt_apply {
     81   char **opt;
     82   char *val;
     83 };
     84 
     85 struct functable {
     86   char *name;
     87   IntFuncPtr func;
     88 };
     89 
     90 /*
     91  * FORWARD DEFINITION:
     92  */
     93 static int f_in_network(char *);
     94 static int f_xhost(char *);
     95 static int f_netgrp(char *);
     96 static int f_netgrpd(char *);
     97 static int f_exists(char *);
     98 static int f_false(char *);
     99 static int f_true(char *);
    100 static inline char *expand_options(char *key);
    101 
    102 /*
    103  * STATICS:
    104  */
    105 static char NullStr[] = "<NULL>";
    106 static char nullstr[] = "";
    107 static char *opt_dkey = NullStr;
    108 static char *opt_host = nullstr; /* XXX: was the global hostname */
    109 static char *opt_hostd = hostd;
    110 static char *opt_key = nullstr;
    111 static char *opt_keyd = nullstr;
    112 static char *opt_map = nullstr;
    113 static char *opt_path = nullstr;
    114 char uid_str[SIZEOF_UID_STR], gid_str[SIZEOF_GID_STR];
    115 char *opt_uid = uid_str;
    116 char *opt_gid = gid_str;
    117 static char *vars[8];
    118 static char *literal_dollar = "$"; /* ${dollar}: a literal '$' in maps */
    119 
    120 /*
    121  * GLOBALS
    122  */
    123 static struct am_opts fs_static;      /* copy of the options to play with */
    124 
    125 
    126 /*
    127  * Options in some order corresponding to frequency of use so that
    128  * first-match algorithm is sped up.
    129  */
    130 static struct opt opt_fields[] = {
    131   /* Name and length.
    132 	Option str.		Selector str.	boolean fxn.	case sensitive */
    133   { S("opts"),
    134        &fs_static.opt_opts,	0,		0, 		FALSE	},
    135   { S("host"),
    136 	0,			&opt_host,	0,		TRUE	},
    137   { S("hostd"),
    138 	0,			&opt_hostd,	0,		TRUE	},
    139   { S("type"),
    140 	&fs_static.opt_type,	0,		0,		FALSE	},
    141   { S("rhost"),
    142 	&fs_static.opt_rhost,	0,		0,		TRUE	},
    143   { S("rfs"),
    144 	&fs_static.opt_rfs,	0,		0,		FALSE	},
    145   { S("fs"),
    146 	&fs_static.opt_fs,	0,		0,		FALSE	},
    147   { S("key"),
    148 	0,			&opt_key,	0,		FALSE	},
    149   { S("map"),
    150 	0,			&opt_map,	0,		FALSE	},
    151   { S("sublink"),
    152 	&fs_static.opt_sublink,	0,		0,		FALSE	},
    153   { S("arch"),
    154 	0,			&gopt.arch,	0,		TRUE	},
    155   { S("dev"),
    156 	&fs_static.opt_dev,	0,		0,		FALSE	},
    157   { S("pref"),
    158 	&fs_static.opt_pref,	0,		0,		FALSE	},
    159   { S("path"),
    160 	0,			&opt_path,	0,		FALSE	},
    161   { S("autodir"),
    162 	0,			&gopt.auto_dir,	0,		FALSE	},
    163   { S("delay"),
    164 	&fs_static.opt_delay,	0,		0,		FALSE	},
    165   { S("domain"),
    166 	0,			&hostdomain,	0,		TRUE	},
    167   { S("karch"),
    168 	0,			&gopt.karch,	0,		TRUE	},
    169   { S("cluster"),
    170 	0,			&gopt.cluster,	0,		TRUE	},
    171   { S("wire"),
    172 	0,			0,		f_in_network,	TRUE	},
    173   { S("network"),
    174 	0,			0,		f_in_network,	TRUE	},
    175   { S("netnumber"),
    176 	0,			0,		f_in_network,	TRUE	},
    177   { S("byte"),
    178 	0,			&endian,	0,		TRUE	},
    179   { S("os"),
    180 	0,			&gopt.op_sys,	0,		TRUE	},
    181   { S("osver"),
    182 	0,			&gopt.op_sys_ver,	0,	TRUE	},
    183   { S("full_os"),
    184 	0,			&gopt.op_sys_full,	0,	TRUE	},
    185   { S("vendor"),
    186 	0,			&gopt.op_sys_vendor,	0,	TRUE	},
    187   { S("remopts"),
    188 	&fs_static.opt_remopts,	0,		0,		FALSE	},
    189   { S("mount"),
    190 	&fs_static.opt_mount,	0,		0,		FALSE	},
    191   { S("unmount"),
    192 	&fs_static.opt_unmount,	0,		0,		FALSE	},
    193   { S("umount"),
    194 	&fs_static.opt_umount,	0,		0,		FALSE	},
    195   { S("cache"),
    196 	&fs_static.opt_cache,	0,		0,		FALSE	},
    197   { S("user"),
    198 	&fs_static.opt_user,	0,		0,		FALSE	},
    199   { S("group"),
    200 	&fs_static.opt_group,	0,		0,		FALSE	},
    201   { S(".key"),
    202 	0,			&opt_dkey,	0,		FALSE	},
    203   { S("key."),
    204 	0,			&opt_keyd,	0,		FALSE	},
    205   { S("maptype"),
    206 	&fs_static.opt_maptype,	0,		0,		FALSE	},
    207   { S("cachedir"),
    208 	&fs_static.opt_cachedir, 0,		0,		FALSE	},
    209   { S("addopts"),
    210 	&fs_static.opt_addopts,	0,		0, 		FALSE	},
    211   { S("uid"),
    212 	0,			&opt_uid,	0,		FALSE	},
    213   { S("gid"),
    214 	0,			&opt_gid,	0, 		FALSE	},
    215   { S("mount_type"),
    216 	&fs_static.opt_mount_type, 0,		0,		FALSE	},
    217   { S("dollar"),
    218 	&literal_dollar,	0,		0,		FALSE	},
    219   { S("var0"),
    220 	&vars[0],		0,		0,		FALSE	},
    221   { S("var1"),
    222 	&vars[1],		0,		0,		FALSE	},
    223   { S("var2"),
    224 	&vars[2],		0,		0,		FALSE	},
    225   { S("var3"),
    226 	&vars[3],		0,		0,		FALSE	},
    227   { S("var4"),
    228 	&vars[4],		0,		0,		FALSE	},
    229   { S("var5"),
    230 	&vars[5],		0,		0,		FALSE	},
    231   { S("var6"),
    232 	&vars[6],		0,		0,		FALSE	},
    233   { S("var7"),
    234 	&vars[7],		0,		0,		FALSE	},
    235   { 0, 0, 0, 0, 0, FALSE },
    236 };
    237 
    238 static struct functable functable[] = {
    239   { "in_network",	f_in_network },
    240   { "xhost",		f_xhost },
    241   { "netgrp",		f_netgrp },
    242   { "netgrpd",		f_netgrpd },
    243   { "exists",		f_exists },
    244   { "false",		f_false },
    245   { "true",		f_true },
    246   { 0, 0 },
    247 };
    248 
    249 /*
    250  * Specially expand the remote host name first
    251  */
    252 static opt_apply rhost_expansion[] =
    253 {
    254   {&fs_static.opt_rhost, "${host}"},
    255   {0, 0},
    256 };
    257 
    258 /*
    259  * List of options which need to be expanded
    260  * Note that the order here _may_ be important.
    261  */
    262 static opt_apply expansions[] =
    263 {
    264   {&fs_static.opt_sublink, 0},
    265   {&fs_static.opt_rfs, "${path}"},
    266   {&fs_static.opt_fs, "${autodir}/${rhost}${rfs}"},
    267   {&fs_static.opt_opts, "rw"},
    268   {&fs_static.opt_remopts, "${opts}"},
    269   {&fs_static.opt_mount, 0},
    270   {&fs_static.opt_unmount, 0},
    271   {&fs_static.opt_umount, 0},
    272   {&fs_static.opt_cachedir, 0},
    273   {&fs_static.opt_addopts, 0},
    274   {0, 0},
    275 };
    276 
    277 /*
    278  * List of options which need to be free'ed before re-use
    279  */
    280 static opt_apply to_free[] =
    281 {
    282   {&fs_static.fs_glob, 0},
    283   {&fs_static.fs_local, 0},
    284   {&fs_static.fs_mtab, 0},
    285   {&fs_static.opt_sublink, 0},
    286   {&fs_static.opt_rfs, 0},
    287   {&fs_static.opt_fs, 0},
    288   {&fs_static.opt_rhost, 0},
    289   {&fs_static.opt_opts, 0},
    290   {&fs_static.opt_remopts, 0},
    291   {&fs_static.opt_mount, 0},
    292   {&fs_static.opt_unmount, 0},
    293   {&fs_static.opt_umount, 0},
    294   {&fs_static.opt_cachedir, 0},
    295   {&fs_static.opt_addopts, 0},
    296   {&vars[0], 0},
    297   {&vars[1], 0},
    298   {&vars[2], 0},
    299   {&vars[3], 0},
    300   {&vars[4], 0},
    301   {&vars[5], 0},
    302   {&vars[6], 0},
    303   {&vars[7], 0},
    304   {0, 0},
    305 };
    306 
    307 
    308 /*
    309  * expand backslash escape sequences
    310  * (escaped slash is handled separately in normalize_slash)
    311  */
    312 static char
    313 backslash(char **p)
    314 {
    315   char c;
    316 
    317   if ((*p)[1] == '\0') {
    318     plog(XLOG_USER, "Empty backslash escape");
    319     return **p;
    320   }
    321 
    322   if (**p == '\\') {
    323     (*p)++;
    324     switch (**p) {
    325     case 'g':
    326       c = '\007';		/* Bell */
    327       break;
    328     case 'b':
    329       c = '\010';		/* Backspace */
    330       break;
    331     case 't':
    332       c = '\011';		/* Horizontal Tab */
    333       break;
    334     case 'n':
    335       c = '\012';		/* New Line */
    336       break;
    337     case 'v':
    338       c = '\013';		/* Vertical Tab */
    339       break;
    340     case 'f':
    341       c = '\014';		/* Form Feed */
    342       break;
    343     case 'r':
    344       c = '\015';		/* Carriage Return */
    345       break;
    346     case 'e':
    347       c = '\033';		/* Escape */
    348       break;
    349     case '0':
    350     case '1':
    351     case '2':
    352     case '3':
    353     case '4':
    354     case '5':
    355     case '6':
    356     case '7':
    357       {
    358 	int cnt, val, ch;
    359 
    360 	for (cnt = 0, val = 0; cnt < 3; cnt++) {
    361 	  ch = *(*p)++;
    362 	  if (ch < '0' || ch > '7') {
    363 	    (*p)--;
    364 	    break;
    365 	  }
    366 	  val = (val << 3) | (ch - '0');
    367 	}
    368 
    369 	if ((val & 0xffffff00) != 0)
    370 	  plog(XLOG_USER,
    371 	       "Too large character constant %u\n",
    372 	       val);
    373 	c = (char) val;
    374 	--(*p);
    375       }
    376       break;
    377 
    378     default:
    379       c = **p;
    380       break;
    381     }
    382   } else
    383     c = **p;
    384 
    385   return c;
    386 }
    387 
    388 
    389 /*
    390  * Skip to next option in the string
    391  */
    392 static char *
    393 opt(char **p)
    394 {
    395   char *cp = *p;
    396   char *dp = cp;
    397   char *s = cp;
    398 
    399 top:
    400   while (*cp && *cp != ';') {
    401     if (*cp == '"') {
    402       /*
    403        * Skip past string
    404        */
    405       for (cp++; *cp && *cp != '"'; cp++)
    406 	if (*cp == '\\')
    407 	  *dp++ = backslash(&cp);
    408 	else
    409 	  *dp++ = *cp;
    410       if (*cp)
    411 	cp++;
    412     } else {
    413       *dp++ = *cp++;
    414     }
    415   }
    416 
    417   /*
    418    * Skip past any remaining ';'s
    419    */
    420   while (*cp == ';')
    421     cp++;
    422 
    423   /*
    424    * If we have a zero length string
    425    * and there are more fields, then
    426    * parse the next one.  This allows
    427    * sequences of empty fields.
    428    */
    429   if (*cp && dp == s)
    430     goto top;
    431 
    432   *dp = '\0';
    433 
    434   *p = cp;
    435   return s;
    436 }
    437 
    438 
    439 /*
    440  * These routines add a new style of selector; function-style boolean
    441  * operators.  To add new ones, just define functions as in true, false,
    442  * exists (below) and add them to the functable, above.
    443  *
    444  * Usage example: Some people have X11R5 local, some go to a server. I do
    445  * this:
    446  *
    447  *    *       exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \
    448  *            -type:=nfs;rfs=/usr/pkg/${key} \
    449  *            rhost:=server1 \
    450  *            rhost:=server2
    451  *
    452  * -Rens Troost <rens (at) imsi.com>
    453  */
    454 static IntFuncPtr
    455 functable_lookup(char *key)
    456 {
    457   struct functable *fp;
    458 
    459   for (fp = functable; fp->name; fp++)
    460     if (FSTREQ(fp->name, key))
    461         return (fp->func);
    462   return (IntFuncPtr) NULL;
    463 }
    464 
    465 
    466 /*
    467  * Fill in the global structure fs_static by
    468  * cracking the string opts.  opts may be
    469  * scribbled on at will.  Does NOT evaluate options.
    470  * Returns 0 on error, 1 if no syntax errors were discovered.
    471  */
    472 static int
    473 split_opts(char *opts, char *mapkey)
    474 {
    475   char *o = opts;
    476   char *f;
    477 
    478   /*
    479    * For each user-specified option
    480    */
    481   for (f = opt(&o); *f; f = opt(&o)) {
    482     struct opt *op;
    483     char *eq = strchr(f, '=');
    484     char *opt = NULL;
    485 
    486     if (!eq)
    487       continue;
    488 
    489     if (*(eq-1) == '!' ||
    490 	eq[1] == '=' ||
    491 	eq[1] == '!') {	/* != or == or =! */
    492       continue;			/* we don't care about selectors */
    493     }
    494 
    495     if (*(eq-1) == ':') {	/* := */
    496       *(eq-1) = '\0';
    497     } else {
    498       /* old style assignment */
    499       eq[0] = '\0';
    500     }
    501     opt = eq + 1;
    502 
    503     /*
    504      * For each recognized option
    505      */
    506     for (op = opt_fields; op->name; op++) {
    507       /*
    508        * Check whether they match
    509        */
    510       if (FSTREQ(op->name, f)) {
    511 	if (op->sel_p) {
    512 	  plog(XLOG_USER, "key %s: Can't assign to a selector (%s)",
    513 	       mapkey, op->name);
    514 	  return 0;
    515 	}
    516 	*op->optp = opt;	/* actual assignment into fs_static */
    517 	break;			/* break out of for loop */
    518       }	/* end of "if (FSTREQ(op->name, f))" statement  */
    519     } /* end of "for (op = opt_fields..." statement  */
    520 
    521     if (!op->name)
    522       plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
    523   }
    524 
    525   return 1;
    526 }
    527 
    528 
    529 /*
    530  * Just evaluate selectors, which were split by split_opts.
    531  * Returns 0 on error or no match, 1 if matched.
    532  */
    533 static int
    534 eval_selectors(char *opts, char *mapkey)
    535 {
    536   char *o, *old_o;
    537   char *f;
    538   int ret = 0;
    539 
    540   o = old_o = xstrdup(opts);
    541 
    542   /*
    543    * For each user-specified option
    544    */
    545   for (f = opt(&o); *f; f = opt(&o)) {
    546     struct opt *op;
    547     enum vs_opt vs_opt;
    548     char *eq = strchr(f, '=');
    549     char *fx;
    550     IntFuncPtr func;
    551     char *opt = NULL;
    552     char *arg;
    553 
    554     if (!eq) {
    555       /*
    556        * No value, is it a function call?
    557        */
    558       arg = strchr(f, '(');
    559 
    560       if (!arg || arg[1] == '\0' || arg == f) {
    561 	/*
    562 	 * No, just continue
    563 	 */
    564 	plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f);
    565 	continue;
    566       }
    567 
    568       /* null-terminate the argument  */
    569       *arg++ = '\0';
    570       fx = strchr(arg, ')');
    571       if (fx == NULL || fx == arg) {
    572 	plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f);
    573 	continue;
    574       }
    575       *fx = '\0';
    576 
    577       if (f[0] == '!') {
    578 	vs_opt = SelNE;
    579 	f++;
    580       } else {
    581 	vs_opt = SelEQ;
    582       }
    583       /*
    584        * look up f in functable and pass it arg.
    585        * func must return 0 on failure, and 1 on success.
    586        */
    587       if ((func = functable_lookup(f))) {
    588 	int funok;
    589 
    590 	/* this allocates memory, don't forget to free */
    591 	arg = expand_options(arg);
    592 	funok = func(arg);
    593 	XFREE(arg);
    594 
    595 	if (vs_opt == SelNE)
    596 	  funok = !funok;
    597 	if (!funok)
    598 	  goto out;
    599 
    600 	continue;
    601       } else {
    602 	plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f);
    603 	goto out;
    604       }
    605     } else {
    606       if (eq[1] == '\0' || eq == f) {
    607 #ifdef notdef
    608 	/* We allow empty assignments */
    609 	plog(XLOG_USER, "key %s: Bad selector \"%s\"", mapkey, f);
    610 #endif
    611 	continue;
    612       }
    613     }
    614 
    615     /*
    616      * Check what type of operation is happening
    617      * !=, =!  is SelNE
    618      * == is SelEQ
    619      * =, := is VarAss
    620      */
    621     if (*(eq-1) == '!') {	/* != */
    622       vs_opt = SelNE;
    623       *(eq-1) = '\0';
    624       opt = eq + 1;
    625     } else if (*(eq-1) == ':') {	/* := */
    626       continue;
    627     } else if (eq[1] == '=') {	/* == */
    628       vs_opt = SelEQ;
    629       eq[0] = '\0';
    630       opt = eq + 2;
    631     } else if (eq[1] == '!') {	/* =! */
    632       vs_opt = SelNE;
    633       eq[0] = '\0';
    634       opt = eq + 2;
    635     } else {
    636       /* old style assignment */
    637       continue;
    638     }
    639 
    640     /*
    641      * For each recognized option
    642      */
    643     for (op = opt_fields; op->name; op++) {
    644       /*
    645        * Check whether they match
    646        */
    647       if (FSTREQ(op->name, f)) {
    648 	opt = expand_options(opt);
    649 
    650 	if (op->sel_p != NULL) {
    651 	  int selok;
    652 	  if (op->case_insensitive) {
    653 	    selok = STRCEQ(*op->sel_p, opt);
    654 	  } else {
    655 	    selok = STREQ(*op->sel_p, opt);
    656 	  }
    657 	  if (vs_opt == SelNE)
    658 	    selok = !selok;
    659 	  if (!selok) {
    660 	    plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s",
    661 		 mapkey,
    662 		 op->name,
    663 		 *op->sel_p,
    664 		 vs_opt == SelNE ? "mis" : "",
    665 		 opt);
    666 	    XFREE(opt);
    667 	    goto out;
    668 	  }
    669 	  XFREE(opt);
    670 	}
    671 	/* check if to apply a function */
    672 	if (op->fxn_p) {
    673 	  int funok;
    674 
    675 	  funok = op->fxn_p(opt);
    676 	  if (vs_opt == SelNE)
    677 	    funok = !funok;
    678 	  if (!funok) {
    679 	    plog(XLOG_MAP, "key %s: map function %s did not %smatch %s",
    680 		 mapkey,
    681 		 op->name,
    682 		 vs_opt == SelNE ? "mis" : "",
    683 		 opt);
    684 	    XFREE(opt);
    685 	    goto out;
    686 	  }
    687 	  XFREE(opt);
    688 	}
    689 	break;			/* break out of for loop */
    690       }
    691     }
    692 
    693     if (!op->name)
    694       plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
    695   }
    696 
    697   /* all is ok */
    698   ret = 1;
    699 
    700  out:
    701   free(old_o);
    702   return ret;
    703 }
    704 
    705 
    706 /*
    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 ';'.
    709  */
    710 static char *
    711 opt_no_scribble(char **p)
    712 {
    713   char *cp = *p;
    714   char *dp = cp;
    715   char *s = cp;
    716 
    717 top:
    718   while (*cp && *cp != ';') {
    719     if (*cp == '\"') {
    720       /*
    721        * Skip past string
    722        */
    723       cp++;
    724       while (*cp && *cp != '\"')
    725 	*dp++ = *cp++;
    726       if (*cp)
    727 	cp++;
    728     } else {
    729       *dp++ = *cp++;
    730     }
    731   }
    732 
    733   /*
    734    * Skip past any remaining ';'s
    735    */
    736   while (*cp == ';')
    737     cp++;
    738 
    739   /*
    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.
    744    */
    745   if (*cp && dp == s)
    746     goto top;
    747 
    748   *p = cp;
    749   return s;
    750 }
    751 
    752 
    753 /*
    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.
    757  */
    758 char *
    759 strip_selectors(char *opts, char *mapkey)
    760 {
    761   /*
    762    * Fill in the global structure fs_static by
    763    * cracking the string opts.  opts may be
    764    * scribbled on at will.
    765    */
    766   char *o = opts;
    767   char *oo = opts;
    768   char *f;
    769 
    770   /*
    771    * Scan options.  Note that the opt() function scribbles on the opt string.
    772    */
    773   while (*(f = opt_no_scribble(&o))) {
    774     enum vs_opt vs_opt = VarAss;
    775     char *eq = strchr(f, '=');
    776 
    777     if (!eq || eq[1] == '\0' || eq == f) {
    778       /*
    779        * No option or assignment?  Return as is.
    780        */
    781       plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f);
    782       return o;
    783     }
    784     /*
    785      * Check what type of operation is happening
    786      * !=, =!  is SelNE
    787      * == is SelEQ
    788      * := is VarAss
    789      */
    790     if (*(eq-1) == '!') {	/* != */
    791       vs_opt = SelNE;
    792     } else if (*(eq-1) == ':') {	/* := */
    793       vs_opt = VarAss;
    794     } else if (eq[1] == '=') {	/* == */
    795       vs_opt = SelEQ;
    796     } else if (eq[1] == '!') {	/* =! */
    797       vs_opt = SelNE;
    798     }
    799     switch (vs_opt) {
    800     case SelEQ:
    801     case SelNE:
    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 */
    805       oo = o;
    806       break;
    807 
    808     case VarAss:
    809       /* found the first assignment, return the string starting with it */
    810       dlog("found first assignment past selectors \"%s\"", o);
    811       return oo;
    812     }
    813   }
    814 
    815   /* return the same string by default. should not happen. */
    816   return oo;
    817 }
    818 
    819 
    820 /*****************************************************************************
    821  *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true):                     ***
    822  *****************************************************************************/
    823 
    824 /* test if arg is any of this host's network names or numbers */
    825 static int
    826 f_in_network(char *arg)
    827 {
    828   int status;
    829 
    830   if (!arg)
    831     return 0;
    832 
    833   status = is_network_member(arg);
    834   dlog("%s is %son a local network", arg, (status ? "" : "not "));
    835   return status;
    836 }
    837 
    838 
    839 /*
    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?
    843  */
    844 static int
    845 f_xhost(char *arg)
    846 {
    847   struct hostent *hp;
    848   char **cp;
    849 
    850   if (!arg)
    851     return 0;
    852 
    853   /* simple test: does it match main host name? */
    854   if (STREQ(arg, opt_hostd))
    855     return 1;
    856 
    857   /* now find all of the names of "arg" and compare against opt_hostd */
    858   hp = gethostbyname(arg);
    859   if (hp == NULL) {
    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 */
    865     return 0;
    866   }
    867   /* check primary name */
    868   if (hp->h_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);
    872       return 1;
    873     }
    874   }
    875   /* check all aliases, if any */
    876   if (hp->h_aliases == NULL) {
    877     dlog("gethostbyname(%s) has no aliases", arg);
    878     return 0;
    879   }
    880   cp = hp->h_aliases;
    881   while (*cp) {
    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);
    885       return 1;
    886     }
    887     cp++;
    888   }
    889   /* nothing matched */
    890   return 0;
    891 }
    892 
    893 
    894 /* test if this host (short hostname form) is in netgroup (arg) */
    895 static int
    896 f_netgrp(char *arg)
    897 {
    898   int status;
    899   char *ptr, *nhost;
    900 
    901   if ((ptr = strchr(arg, ',')) != NULL) {
    902     *ptr = '\0';
    903     nhost = ptr + 1;
    904   } else {
    905     nhost = opt_host;
    906   }
    907   status = innetgr(arg, nhost, NULL, NULL);
    908   dlog("netgrp = %s status = %d host = %s", arg, status, nhost);
    909   if (ptr)
    910     *ptr = ',';
    911   return status;
    912 }
    913 
    914 
    915 /* test if this host (fully-qualified name) is in netgroup (arg) */
    916 static int
    917 f_netgrpd(char *arg)
    918 {
    919   int status;
    920   char *ptr, *nhost;
    921 
    922   if ((ptr = strchr(arg, ',')) != NULL) {
    923     *ptr = '\0';
    924     nhost = ptr + 1;
    925   } else {
    926     nhost = opt_hostd;
    927   }
    928   status = innetgr(arg, nhost, NULL, NULL);
    929   dlog("netgrp = %s status = %d hostd = %s", arg, status, nhost);
    930   if (ptr)
    931     *ptr = ',';
    932   return status;
    933 }
    934 
    935 
    936 /* test if file (arg) exists via lstat */
    937 static int
    938 f_exists(char *arg)
    939 {
    940   struct stat buf;
    941 
    942   if (lstat(arg, &buf) < 0)
    943     return (0);
    944   else
    945     return (1);
    946 }
    947 
    948 
    949 /* always false */
    950 static int
    951 f_false(char *arg)
    952 {
    953   return (0);
    954 }
    955 
    956 
    957 /* always true */
    958 static int
    959 f_true(char *arg)
    960 {
    961   return (1);
    962 }
    963 
    964 
    965 /*
    966  * Free an option
    967  */
    968 static void
    969 free_op(opt_apply *p, int b)
    970 {
    971   XFREE(*p->opt);
    972 }
    973 
    974 
    975 /*
    976  * Normalize slashes in the string.
    977  */
    978 void
    979 normalize_slash(char *p)
    980 {
    981   char *f, *f0;
    982 
    983   if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
    984     return;
    985 
    986   f0 = f = strchr(p, '/');
    987   if (f) {
    988     char *t = f;
    989     do {
    990       /* assert(*f == '/'); */
    991       if (f == f0 && f[0] == '/' && f[1] == '/') {
    992 	/* copy double slash iff first */
    993 	*t++ = *f++;
    994 	*t++ = *f++;
    995       } else {
    996 	/* copy a single / across */
    997 	*t++ = *f++;
    998       }
    999 
   1000       /* assert(f[-1] == '/'); */
   1001       /* skip past more /'s */
   1002       while (*f == '/')
   1003 	f++;
   1004 
   1005       /* assert(*f != '/'); */
   1006       /* keep copying up to next / */
   1007       while (*f && *f != '/') {
   1008 	/* support escaped slashes '\/' */
   1009 	if (f[0] == '\\' && f[1] == '/')
   1010 	  f++;			/* skip backslash */
   1011 	*t++ = *f++;
   1012       }
   1013 
   1014       /* assert(*f == 0 || *f == '/'); */
   1015 
   1016     } while (*f);
   1017     *t = '\0';			/* derived from fix by Steven Glassman */
   1018   }
   1019 }
   1020 
   1021 
   1022 /*
   1023  * Macro-expand an option.  Note that this does not
   1024  * handle recursive expansions.  They will go badly wrong.
   1025  * If sel_p is true then old expand selectors, otherwise
   1026  * don't expand selectors.
   1027  */
   1028 static char *
   1029 expand_op(char *opt, int sel_p)
   1030 {
   1031 #define EXPAND_ERROR "No space to expand \"%s\""
   1032   char expbuf[MAXPATHLEN + 1];
   1033   char nbuf[NLEN + 1];
   1034   char *ep = expbuf;
   1035   char *cp = opt;
   1036   char *dp;
   1037   struct opt *op;
   1038   char *cp_orig = opt;
   1039 
   1040   while ((dp = strchr(cp, '$'))) {
   1041     char ch;
   1042     /*
   1043      * First copy up to the $
   1044      */
   1045     {
   1046       int len = dp - cp;
   1047 
   1048       if (len > 0) {
   1049 	if (BUFSPACE(ep, len)) {
   1050 	  /*
   1051 	   * We use strncpy (not xstrlcpy) because 'ep' relies on its
   1052 	   * semantics.  BUFSPACE guarantees that ep can hold len.
   1053 	   */
   1054 	  strncpy(ep, cp, len);
   1055 	  ep += len;
   1056 	} else {
   1057 	  plog(XLOG_ERROR, EXPAND_ERROR, opt);
   1058 	  goto out;
   1059 	}
   1060       }
   1061     }
   1062 
   1063     cp = dp + 1;
   1064     ch = *cp++;
   1065     if (ch == '$') {
   1066       if (BUFSPACE(ep, 1)) {
   1067 	*ep++ = '$';
   1068       } else {
   1069 	plog(XLOG_ERROR, EXPAND_ERROR, opt);
   1070 	goto out;
   1071       }
   1072     } else if (ch == '{') {
   1073       /* Expansion... */
   1074       enum {
   1075 	E_All, E_Dir, E_File, E_Domain, E_Host
   1076       } todo;
   1077       /*
   1078        * Find closing brace
   1079        */
   1080       char *br_p = strchr(cp, '}');
   1081       int len;
   1082 
   1083       /*
   1084        * Check we found it
   1085        */
   1086       if (!br_p) {
   1087 	/*
   1088 	 * Just give up
   1089 	 */
   1090 	plog(XLOG_USER, "No closing '}' in \"%s\"", opt);
   1091 	goto out;
   1092       }
   1093       len = br_p - cp;
   1094 
   1095       /*
   1096        * Figure out which part of the variable to grab.
   1097        */
   1098       if (*cp == '/') {
   1099 	/*
   1100 	 * Just take the last component
   1101 	 */
   1102 	todo = E_File;
   1103 	cp++;
   1104 	--len;
   1105       } else if (*(br_p-1) == '/') {
   1106 	/*
   1107 	 * Take all but the last component
   1108 	 */
   1109 	todo = E_Dir;
   1110 	--len;
   1111       } else if (*cp == '.') {
   1112 	/*
   1113 	 * Take domain name
   1114 	 */
   1115 	todo = E_Domain;
   1116 	cp++;
   1117 	--len;
   1118       } else if (*(br_p-1) == '.') {
   1119 	/*
   1120 	 * Take host name
   1121 	 */
   1122 	todo = E_Host;
   1123 	--len;
   1124       } else {
   1125 	/*
   1126 	 * Take the whole lot
   1127 	 */
   1128 	todo = E_All;
   1129       }
   1130 
   1131       /*
   1132        * Truncate if too long.  Since it won't
   1133        * match anyway it doesn't matter that
   1134        * it has been cut short.
   1135        */
   1136       if (len > NLEN)
   1137 	len = NLEN;
   1138 
   1139       /*
   1140        * Put the string into another buffer so
   1141        * we can do comparisons.
   1142        *
   1143        * We use strncpy here (not xstrlcpy) because the dest is meant
   1144        * to be truncated and we don't want to log it as an error.  The
   1145        * use of the BUFSPACE macro above guarantees the safe use of
   1146        * strncpy with nbuf.
   1147        */
   1148       strncpy(nbuf, cp, len);
   1149       nbuf[len] = '\0';
   1150 
   1151       /*
   1152        * Advance cp
   1153        */
   1154       cp = br_p + 1;
   1155 
   1156       /*
   1157        * Search the option array
   1158        */
   1159       for (op = opt_fields; op->name; op++) {
   1160 	/*
   1161 	 * Check for match
   1162 	 */
   1163 	if (len == op->nlen && STREQ(op->name, nbuf)) {
   1164 	  char xbuf[NLEN + 3];
   1165 	  char *val;
   1166 	  /*
   1167 	   * Found expansion.  Copy
   1168 	   * the correct value field.
   1169 	   */
   1170 	  if (!(!op->sel_p == !sel_p)) {
   1171 	    /*
   1172 	     * Copy the string across unexpanded
   1173 	     */
   1174 	    xsnprintf(xbuf, sizeof(xbuf), "${%s%s%s}",
   1175 		      todo == E_File ? "/" :
   1176 		      todo == E_Domain ? "." : "",
   1177 		      nbuf,
   1178 		      todo == E_Dir ? "/" :
   1179 		      todo == E_Host ? "." : "");
   1180 	    val = xbuf;
   1181 	    /*
   1182 	     * Make sure expansion doesn't
   1183 	     * munge the value!
   1184 	     */
   1185 	    todo = E_All;
   1186 	  } else if (op->sel_p) {
   1187 	    val = *op->sel_p;
   1188 	  } else {
   1189 	    val = *op->optp;
   1190 	  }
   1191 
   1192 	  if (val) {
   1193 	    /*
   1194 	     * Do expansion:
   1195 	     * ${/var} means take just the last part
   1196 	     * ${var/} means take all but the last part
   1197 	     * ${.var} means take all but first part
   1198 	     * ${var.} means take just the first part
   1199 	     * ${var} means take the whole lot
   1200 	     */
   1201 	    int vlen = strlen(val);
   1202 	    char *vptr = val;
   1203 	    switch (todo) {
   1204 	    case E_Dir:
   1205 	      vptr = strrchr(val, '/');
   1206 	      if (vptr)
   1207 		vlen = vptr - val;
   1208 	      vptr = val;
   1209 	      break;
   1210 	    case E_File:
   1211 	      vptr = strrchr(val, '/');
   1212 	      if (vptr) {
   1213 		vptr++;
   1214 		vlen = strlen(vptr);
   1215 	      } else
   1216 		vptr = val;
   1217 	      break;
   1218 	    case E_Domain:
   1219 	      vptr = strchr(val, '.');
   1220 	      if (vptr) {
   1221 		vptr++;
   1222 		vlen = strlen(vptr);
   1223 	      } else {
   1224 		vptr = "";
   1225 		vlen = 0;
   1226 	      }
   1227 	      break;
   1228 	    case E_Host:
   1229 	      vptr = strchr(val, '.');
   1230 	      if (vptr)
   1231 		vlen = vptr - val;
   1232 	      vptr = val;
   1233 	      break;
   1234 	    case E_All:
   1235 	      break;
   1236 	    }
   1237 
   1238 	    if (BUFSPACE(ep, vlen+1)) {
   1239 	      /*
   1240 	       * Don't call xstrlcpy() to truncate a string here.  It causes
   1241 	       * spurious xstrlcpy() syslog() errors.  Use memcpy() and
   1242 	       * explicitly terminate the string.
   1243 	       */
   1244 	      memcpy(ep, vptr, vlen+1);
   1245 	      ep += vlen;
   1246 	      *ep = '\0';
   1247 	    } else {
   1248 	      plog(XLOG_ERROR, EXPAND_ERROR, opt);
   1249 	      goto out;
   1250 	    }
   1251 	  }
   1252 	  /*
   1253 	   * Done with this variable
   1254 	   */
   1255 	  break;
   1256 	}
   1257       }
   1258 
   1259       /*
   1260        * Check that the search was successful
   1261        */
   1262       if (!op->name) {
   1263 	/*
   1264 	 * If it wasn't then scan the
   1265 	 * environment for that name
   1266 	 * and use any value found
   1267 	 */
   1268 	char *env = getenv(nbuf);
   1269 
   1270 	if (env) {
   1271 	  int vlen = strlen(env);
   1272 
   1273 	  if (BUFSPACE(ep, vlen+1)) {
   1274 	    xstrlcpy(ep, env, vlen+1);
   1275 	    ep += vlen;
   1276 	  } else {
   1277 	    plog(XLOG_ERROR, EXPAND_ERROR, opt);
   1278 	    goto out;
   1279 	  }
   1280 	  if (amuDebug(D_STR))
   1281 	    plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
   1282 	} else {
   1283 	  plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
   1284 	}
   1285       }
   1286     } else {
   1287       /*
   1288        * Error, error
   1289        */
   1290       plog(XLOG_USER, "Unknown $ sequence in \"%s\"", opt);
   1291     }
   1292   }
   1293 
   1294 out:
   1295   /*
   1296    * Handle common case - no expansion
   1297    */
   1298   if (cp == opt) {
   1299     opt = xstrdup(cp);
   1300   } else {
   1301     /*
   1302      * Finish off the expansion
   1303      */
   1304     int vlen = strlen(cp);
   1305     if (BUFSPACE(ep, vlen+1)) {
   1306       xstrlcpy(ep, cp, vlen+1);
   1307       /* ep += vlen; */
   1308     } else {
   1309       plog(XLOG_ERROR, EXPAND_ERROR, opt);
   1310     }
   1311 
   1312     /*
   1313      * Save the expansion
   1314      */
   1315     opt = xstrdup(expbuf);
   1316   }
   1317 
   1318   normalize_slash(opt);
   1319 
   1320   if (amuDebug(D_STR)) {
   1321     plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig);
   1322     plog(XLOG_DEBUG, "......... is \"%s\"", opt);
   1323   }
   1324   return opt;
   1325 }
   1326 
   1327 
   1328 /*
   1329  * Wrapper for expand_op
   1330  */
   1331 static void
   1332 expand_opts(opt_apply *p, int sel_p)
   1333 {
   1334   if (*p->opt) {
   1335     *p->opt = expand_op(*p->opt, sel_p);
   1336   } else if (p->val) {
   1337     /*
   1338      * Do double expansion, remembering
   1339      * to free the string from the first
   1340      * expansion...
   1341      */
   1342     char *s = expand_op(p->val, TRUE);
   1343     *p->opt = expand_op(s, sel_p);
   1344     XFREE(s);
   1345   }
   1346 }
   1347 
   1348 
   1349 /*
   1350  * Apply a function to a list of options
   1351  */
   1352 static void
   1353 apply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b)
   1354 {
   1355   opt_apply *pp;
   1356 
   1357   for (pp = ppp; pp->opt; pp++)
   1358     (*op) (pp, b);
   1359 }
   1360 
   1361 
   1362 /*
   1363  * Free the option table
   1364  */
   1365 void
   1366 free_opts(am_opts *fo)
   1367 {
   1368   /*
   1369    * Copy in the structure we are playing with
   1370    */
   1371   fs_static = *fo;
   1372 
   1373   /*
   1374    * Free previously allocated memory
   1375    */
   1376   apply_opts(free_op, to_free, FALSE);
   1377 }
   1378 
   1379 am_opts *
   1380 copy_opts(am_opts *old)
   1381 {
   1382   am_opts *newopts;
   1383   newopts = CALLOC(struct am_opts);
   1384 
   1385 #define _AM_OPT_COPY(field) do { \
   1386     if (old->field) \
   1387       newopts->field = xstrdup(old->field); \
   1388   } while (0)
   1389 
   1390   _AM_OPT_COPY(fs_glob);
   1391   _AM_OPT_COPY(fs_local);
   1392   _AM_OPT_COPY(fs_mtab);
   1393   _AM_OPT_COPY(opt_dev);
   1394   _AM_OPT_COPY(opt_delay);
   1395   _AM_OPT_COPY(opt_dir);
   1396   _AM_OPT_COPY(opt_fs);
   1397   _AM_OPT_COPY(opt_group);
   1398   _AM_OPT_COPY(opt_mount);
   1399   _AM_OPT_COPY(opt_opts);
   1400   _AM_OPT_COPY(opt_remopts);
   1401   _AM_OPT_COPY(opt_pref);
   1402   _AM_OPT_COPY(opt_cache);
   1403   _AM_OPT_COPY(opt_rfs);
   1404   _AM_OPT_COPY(opt_rhost);
   1405   _AM_OPT_COPY(opt_sublink);
   1406   _AM_OPT_COPY(opt_type);
   1407   _AM_OPT_COPY(opt_mount_type);
   1408   _AM_OPT_COPY(opt_unmount);
   1409   _AM_OPT_COPY(opt_umount);
   1410   _AM_OPT_COPY(opt_user);
   1411   _AM_OPT_COPY(opt_maptype);
   1412   _AM_OPT_COPY(opt_cachedir);
   1413   _AM_OPT_COPY(opt_addopts);
   1414 
   1415   return newopts;
   1416 }
   1417 
   1418 
   1419 /*
   1420  * Expand selectors (variables that cannot be assigned to or overridden)
   1421  */
   1422 char *
   1423 expand_selectors(char *key)
   1424 {
   1425   return expand_op(key, TRUE);
   1426 }
   1427 
   1428 
   1429 /*
   1430  * Expand options (i.e. non-selectors, see above for definition)
   1431  */
   1432 static inline char *
   1433 expand_options(char *key)
   1434 {
   1435   return expand_op(key, FALSE);
   1436 }
   1437 
   1438 
   1439 /*
   1440  * Remove trailing /'s from a string
   1441  * unless the string is a single / (Steven Glassman)
   1442  * or unless it is two slashes // (Kevin D. Bond)
   1443  * or unless amd.conf says not to touch slashes.
   1444  */
   1445 void
   1446 deslashify(char *s)
   1447 {
   1448   if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
   1449     return;
   1450 
   1451   if (s && *s) {
   1452     char *sl = s + strlen(s);
   1453 
   1454     while (*--sl == '/' && sl > s)
   1455       *sl = '\0';
   1456   }
   1457 }
   1458 
   1459 
   1460 int
   1461 eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map)
   1462 {
   1463   int ok = TRUE;
   1464 
   1465   free_opts(fo);
   1466 
   1467   /*
   1468    * Clear out the option table
   1469    */
   1470   memset((voidp) &fs_static, 0, sizeof(fs_static));
   1471   memset((voidp) vars, 0, sizeof(vars));
   1472   memset((voidp) fo, 0, sizeof(*fo));
   1473 
   1474   /* set hostname */
   1475   opt_host = (char *) am_get_hostname();
   1476 
   1477   /*
   1478    * Set key, map & path before expansion
   1479    */
   1480   opt_key = key;
   1481   opt_map = map;
   1482   opt_path = path;
   1483 
   1484   opt_dkey = strchr(key, '.');
   1485   if (!opt_dkey) {
   1486     opt_dkey = NullStr;
   1487     opt_keyd = key;
   1488   } else {
   1489     opt_keyd = strnsave(key, opt_dkey - key);
   1490     opt_dkey++;
   1491     if (*opt_dkey == '\0')	/* check for 'host.' */
   1492       opt_dkey = NullStr;
   1493   }
   1494 
   1495   /*
   1496    * Expand global options
   1497    */
   1498   fs_static.fs_glob = expand_selectors(g_opts);
   1499 
   1500   /*
   1501    * Expand local options
   1502    */
   1503   fs_static.fs_local = expand_selectors(opts);
   1504 
   1505   /* break global options into fs_static fields */
   1506   if ((ok = split_opts(fs_static.fs_glob, key))) {
   1507     dlog("global split_opts ok");
   1508     /*
   1509      * evaluate local selectors
   1510      */
   1511     if ((ok = eval_selectors(fs_static.fs_local, key))) {
   1512       dlog("local eval_selectors ok");
   1513       /* if the local selectors matched, then do the local overrides */
   1514       ok = split_opts(fs_static.fs_local, key);
   1515       if (ok)
   1516 	dlog("local split_opts ok");
   1517     }
   1518   }
   1519 
   1520   /*
   1521    * Normalize remote host name.
   1522    * 1.  Expand variables
   1523    * 2.  Normalize relative to host tables
   1524    * 3.  Strip local domains from the remote host
   1525    *     name before using it in other expansions.
   1526    *     This makes mount point names and other things
   1527    *     much shorter, while allowing cross domain
   1528    *     sharing of mount maps.
   1529    */
   1530   apply_opts(expand_opts, rhost_expansion, FALSE);
   1531   if (ok && fs_static.opt_rhost && *fs_static.opt_rhost)
   1532     host_normalize(&fs_static.opt_rhost);
   1533 
   1534   /*
   1535    * Macro expand the options.
   1536    * Do this regardless of whether we are accepting
   1537    * this mount - otherwise nasty things happen
   1538    * with memory allocation.
   1539    */
   1540   apply_opts(expand_opts, expansions, FALSE);
   1541 
   1542   /*
   1543    * Strip trailing slashes from local pathname...
   1544    */
   1545   deslashify(fs_static.opt_fs);
   1546 
   1547   /*
   1548    * ok... copy the data back out.
   1549    */
   1550   *fo = fs_static;
   1551 
   1552   /*
   1553    * Clear defined options
   1554    */
   1555   if (opt_keyd != key && opt_keyd != nullstr)
   1556     XFREE(opt_keyd);
   1557   opt_keyd = nullstr;
   1558   opt_dkey = NullStr;
   1559   opt_key = opt_map = opt_path = nullstr;
   1560 
   1561   return ok;
   1562 }
   1563