138494Sobrien/*
2174294Sobrien * Copyright (c) 1997-2006 Erez Zadok
338494Sobrien * Copyright (c) 1989 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1989 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
1938494Sobrien * 3. All advertising materials mentioning features or use of this software
2042629Sobrien *    must display the following acknowledgment:
2138494Sobrien *      This product includes software developed by the University of
2238494Sobrien *      California, Berkeley and its contributors.
2338494Sobrien * 4. Neither the name of the University nor the names of its contributors
2438494Sobrien *    may be used to endorse or promote products derived from this software
2538494Sobrien *    without specific prior written permission.
2638494Sobrien *
2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3038494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3738494Sobrien * SUCH DAMAGE.
3838494Sobrien *
3938494Sobrien *
40174294Sobrien * File: am-utils/amd/opts.c
4138494Sobrien *
4238494Sobrien */
4338494Sobrien
4438494Sobrien#ifdef HAVE_CONFIG_H
4538494Sobrien# include <config.h>
4638494Sobrien#endif /* HAVE_CONFIG_H */
4738494Sobrien#include <am_defs.h>
4838494Sobrien#include <amd.h>
4938494Sobrien
5038494Sobrien/*
5138494Sobrien * MACROS:
5238494Sobrien */
5338494Sobrien#define	NLEN	16	/* Length of longest option name (conservative) */
5438494Sobrien#define S(x) (x) , (sizeof(x)-1)
5538494Sobrien/*
5638494Sobrien * The BUFSPACE macros checks that there is enough space
5738494Sobrien * left in the expansion buffer.  If there isn't then we
5838494Sobrien * give up completely.  This is done to avoid crashing the
5938494Sobrien * automounter itself (which would be a bad thing to do).
6038494Sobrien */
6138494Sobrien#define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
6238494Sobrien
6338494Sobrien/*
6438494Sobrien * TYPEDEFS:
6538494Sobrien */
6638494Sobrientypedef int (*IntFuncPtr) (char *);
6738494Sobrientypedef struct opt_apply opt_apply;
6838494Sobrienenum vs_opt { SelEQ, SelNE, VarAss };
6938494Sobrien
7038494Sobrien/*
7138494Sobrien * STRUCTURES
7238494Sobrien */
7338494Sobrienstruct opt {
7438494Sobrien  char *name;			/* Name of the option */
7538494Sobrien  int nlen;			/* Length of option name */
7638494Sobrien  char **optp;			/* Pointer to option value string */
7738494Sobrien  char **sel_p;			/* Pointer to selector value string */
7838494Sobrien  int (*fxn_p)(char *);		/* Pointer to boolean function */
7938494Sobrien  int case_insensitive;		/* How to do selector comparisons */
8038494Sobrien};
8138494Sobrien
8238494Sobrienstruct opt_apply {
8338494Sobrien  char **opt;
8438494Sobrien  char *val;
8538494Sobrien};
8638494Sobrien
8738494Sobrienstruct functable {
8838494Sobrien  char *name;
8938494Sobrien  IntFuncPtr func;
9038494Sobrien};
9138494Sobrien
9238494Sobrien/*
9342629Sobrien * FORWARD DEFINITION:
9438494Sobrien */
9538494Sobrienstatic int f_in_network(char *);
96174294Sobrienstatic int f_xhost(char *);
9738494Sobrienstatic int f_netgrp(char *);
9851292Sobrienstatic int f_netgrpd(char *);
9938494Sobrienstatic int f_exists(char *);
10038494Sobrienstatic int f_false(char *);
10138494Sobrienstatic int f_true(char *);
102174294Sobrienstatic inline char *expand_options(char *key);
10338494Sobrien
10438494Sobrien/*
10538494Sobrien * STATICS:
10638494Sobrien */
10738494Sobrienstatic char NullStr[] = "<NULL>";
10838494Sobrienstatic char nullstr[] = "";
10938494Sobrienstatic char *opt_dkey = NullStr;
11042629Sobrienstatic char *opt_host = nullstr; /* XXX: was the global hostname */
11138494Sobrienstatic char *opt_hostd = hostd;
11238494Sobrienstatic char *opt_key = nullstr;
11338494Sobrienstatic char *opt_keyd = nullstr;
11438494Sobrienstatic char *opt_map = nullstr;
11538494Sobrienstatic char *opt_path = nullstr;
116174294Sobrienchar uid_str[SIZEOF_UID_STR], gid_str[SIZEOF_GID_STR];
11782794Sobrienchar *opt_uid = uid_str;
11882794Sobrienchar *opt_gid = gid_str;
11938494Sobrienstatic char *vars[8];
12082794Sobrienstatic char *literal_dollar = "$"; /* ${dollar}: a literal '$' in maps */
12138494Sobrien
12282794Sobrien/*
12382794Sobrien * GLOBALS
12482794Sobrien */
125174294Sobrienstatic struct am_opts fs_static;      /* copy of the options to play with */
12638494Sobrien
12782794Sobrien
12838494Sobrien/*
129174294Sobrien * Options in some order corresponding to frequency of use so that
13038494Sobrien * first-match algorithm is sped up.
13138494Sobrien */
13238494Sobrienstatic struct opt opt_fields[] = {
13338494Sobrien  /* Name and length.
13482794Sobrien	Option str.		Selector str.	boolean fxn.	case sensitive */
13538494Sobrien  { S("opts"),
13638494Sobrien       &fs_static.opt_opts,	0,		0, 		FALSE	},
13738494Sobrien  { S("host"),
13838494Sobrien	0,			&opt_host,	0,		TRUE	},
13938494Sobrien  { S("hostd"),
14038494Sobrien	0,			&opt_hostd,	0,		TRUE	},
14138494Sobrien  { S("type"),
14238494Sobrien	&fs_static.opt_type,	0,		0,		FALSE	},
14338494Sobrien  { S("rhost"),
14438494Sobrien	&fs_static.opt_rhost,	0,		0,		TRUE	},
14538494Sobrien  { S("rfs"),
14638494Sobrien	&fs_static.opt_rfs,	0,		0,		FALSE	},
14738494Sobrien  { S("fs"),
14838494Sobrien	&fs_static.opt_fs,	0,		0,		FALSE	},
14938494Sobrien  { S("key"),
15038494Sobrien	0,			&opt_key,	0,		FALSE	},
15138494Sobrien  { S("map"),
15238494Sobrien	0,			&opt_map,	0,		FALSE	},
15338494Sobrien  { S("sublink"),
15438494Sobrien	&fs_static.opt_sublink,	0,		0,		FALSE	},
15538494Sobrien  { S("arch"),
15638494Sobrien	0,			&gopt.arch,	0,		TRUE	},
15738494Sobrien  { S("dev"),
15838494Sobrien	&fs_static.opt_dev,	0,		0,		FALSE	},
15938494Sobrien  { S("pref"),
16038494Sobrien	&fs_static.opt_pref,	0,		0,		FALSE	},
16138494Sobrien  { S("path"),
16238494Sobrien	0,			&opt_path,	0,		FALSE	},
16338494Sobrien  { S("autodir"),
16438494Sobrien	0,			&gopt.auto_dir,	0,		FALSE	},
16538494Sobrien  { S("delay"),
16638494Sobrien	&fs_static.opt_delay,	0,		0,		FALSE	},
16738494Sobrien  { S("domain"),
16838494Sobrien	0,			&hostdomain,	0,		TRUE	},
16938494Sobrien  { S("karch"),
17038494Sobrien	0,			&gopt.karch,	0,		TRUE	},
17138494Sobrien  { S("cluster"),
17238494Sobrien	0,			&gopt.cluster,	0,		TRUE	},
17338494Sobrien  { S("wire"),
17438494Sobrien	0,			0,		f_in_network,	TRUE	},
17538494Sobrien  { S("network"),
17638494Sobrien	0,			0,		f_in_network,	TRUE	},
17738494Sobrien  { S("netnumber"),
17838494Sobrien	0,			0,		f_in_network,	TRUE	},
17938494Sobrien  { S("byte"),
18038494Sobrien	0,			&endian,	0,		TRUE	},
18138494Sobrien  { S("os"),
18238494Sobrien	0,			&gopt.op_sys,	0,		TRUE	},
18338494Sobrien  { S("osver"),
18438494Sobrien	0,			&gopt.op_sys_ver,	0,	TRUE	},
18552894Sobrien  { S("full_os"),
18652894Sobrien	0,			&gopt.op_sys_full,	0,	TRUE	},
18752894Sobrien  { S("vendor"),
18852894Sobrien	0,			&gopt.op_sys_vendor,	0,	TRUE	},
18938494Sobrien  { S("remopts"),
19038494Sobrien	&fs_static.opt_remopts,	0,		0,		FALSE	},
19138494Sobrien  { S("mount"),
19238494Sobrien	&fs_static.opt_mount,	0,		0,		FALSE	},
19338494Sobrien  { S("unmount"),
19438494Sobrien	&fs_static.opt_unmount,	0,		0,		FALSE	},
195174294Sobrien  { S("umount"),
196174294Sobrien	&fs_static.opt_umount,	0,		0,		FALSE	},
19738494Sobrien  { S("cache"),
19838494Sobrien	&fs_static.opt_cache,	0,		0,		FALSE	},
19938494Sobrien  { S("user"),
20038494Sobrien	&fs_static.opt_user,	0,		0,		FALSE	},
20138494Sobrien  { S("group"),
20238494Sobrien	&fs_static.opt_group,	0,		0,		FALSE	},
20338494Sobrien  { S(".key"),
20438494Sobrien	0,			&opt_dkey,	0,		FALSE	},
20538494Sobrien  { S("key."),
20638494Sobrien	0,			&opt_keyd,	0,		FALSE	},
20738494Sobrien  { S("maptype"),
20838494Sobrien	&fs_static.opt_maptype,	0,		0,		FALSE	},
20938494Sobrien  { S("cachedir"),
21038494Sobrien	&fs_static.opt_cachedir, 0,		0,		FALSE	},
21138494Sobrien  { S("addopts"),
21282794Sobrien	&fs_static.opt_addopts,	0,		0, 		FALSE	},
21382794Sobrien  { S("uid"),
21482794Sobrien	0,			&opt_uid,	0,		FALSE	},
21582794Sobrien  { S("gid"),
21682794Sobrien	0,			&opt_gid,	0, 		FALSE	},
217174294Sobrien  { S("mount_type"),
218174294Sobrien	&fs_static.opt_mount_type, 0,		0,		FALSE	},
21982794Sobrien  { S("dollar"),
22082794Sobrien	&literal_dollar,	0,		0,		FALSE	},
22138494Sobrien  { S("var0"),
22238494Sobrien	&vars[0],		0,		0,		FALSE	},
22338494Sobrien  { S("var1"),
22438494Sobrien	&vars[1],		0,		0,		FALSE	},
22538494Sobrien  { S("var2"),
22638494Sobrien	&vars[2],		0,		0,		FALSE	},
22738494Sobrien  { S("var3"),
22838494Sobrien	&vars[3],		0,		0,		FALSE	},
22938494Sobrien  { S("var4"),
23038494Sobrien	&vars[4],		0,		0,		FALSE	},
23138494Sobrien  { S("var5"),
23238494Sobrien	&vars[5],		0,		0,		FALSE	},
23338494Sobrien  { S("var6"),
23438494Sobrien	&vars[6],		0,		0,		FALSE	},
23538494Sobrien  { S("var7"),
23638494Sobrien	&vars[7],		0,		0,		FALSE	},
23738494Sobrien  { 0, 0, 0, 0, 0, FALSE },
23838494Sobrien};
23938494Sobrien
24038494Sobrienstatic struct functable functable[] = {
24138494Sobrien  { "in_network",	f_in_network },
242174294Sobrien  { "xhost",		f_xhost },
24338494Sobrien  { "netgrp",		f_netgrp },
24451292Sobrien  { "netgrpd",		f_netgrpd },
24538494Sobrien  { "exists",		f_exists },
24638494Sobrien  { "false",		f_false },
24738494Sobrien  { "true",		f_true },
24838494Sobrien  { 0, 0 },
24938494Sobrien};
25038494Sobrien
25138494Sobrien/*
25238494Sobrien * Specially expand the remote host name first
25338494Sobrien */
25438494Sobrienstatic opt_apply rhost_expansion[] =
25538494Sobrien{
25638494Sobrien  {&fs_static.opt_rhost, "${host}"},
25738494Sobrien  {0, 0},
25838494Sobrien};
25938494Sobrien
26038494Sobrien/*
26138494Sobrien * List of options which need to be expanded
26238494Sobrien * Note that the order here _may_ be important.
26338494Sobrien */
26438494Sobrienstatic opt_apply expansions[] =
26538494Sobrien{
26638494Sobrien  {&fs_static.opt_sublink, 0},
26738494Sobrien  {&fs_static.opt_rfs, "${path}"},
26838494Sobrien  {&fs_static.opt_fs, "${autodir}/${rhost}${rfs}"},
26938494Sobrien  {&fs_static.opt_opts, "rw"},
27038494Sobrien  {&fs_static.opt_remopts, "${opts}"},
27138494Sobrien  {&fs_static.opt_mount, 0},
27238494Sobrien  {&fs_static.opt_unmount, 0},
273174294Sobrien  {&fs_static.opt_umount, 0},
27438494Sobrien  {&fs_static.opt_cachedir, 0},
27538494Sobrien  {&fs_static.opt_addopts, 0},
27638494Sobrien  {0, 0},
27738494Sobrien};
27838494Sobrien
27938494Sobrien/*
28038494Sobrien * List of options which need to be free'ed before re-use
28138494Sobrien */
28238494Sobrienstatic opt_apply to_free[] =
28338494Sobrien{
28438494Sobrien  {&fs_static.fs_glob, 0},
28538494Sobrien  {&fs_static.fs_local, 0},
28638494Sobrien  {&fs_static.fs_mtab, 0},
28738494Sobrien  {&fs_static.opt_sublink, 0},
28838494Sobrien  {&fs_static.opt_rfs, 0},
28938494Sobrien  {&fs_static.opt_fs, 0},
29038494Sobrien  {&fs_static.opt_rhost, 0},
29138494Sobrien  {&fs_static.opt_opts, 0},
29238494Sobrien  {&fs_static.opt_remopts, 0},
29338494Sobrien  {&fs_static.opt_mount, 0},
29438494Sobrien  {&fs_static.opt_unmount, 0},
295174294Sobrien  {&fs_static.opt_umount, 0},
29638494Sobrien  {&fs_static.opt_cachedir, 0},
29738494Sobrien  {&fs_static.opt_addopts, 0},
29838494Sobrien  {&vars[0], 0},
29938494Sobrien  {&vars[1], 0},
30038494Sobrien  {&vars[2], 0},
30138494Sobrien  {&vars[3], 0},
30238494Sobrien  {&vars[4], 0},
30338494Sobrien  {&vars[5], 0},
30438494Sobrien  {&vars[6], 0},
30538494Sobrien  {&vars[7], 0},
30638494Sobrien  {0, 0},
30738494Sobrien};
30838494Sobrien
30938494Sobrien
31038494Sobrien/*
31138494Sobrien * expand backslash escape sequences
312174294Sobrien * (escaped slash is handled separately in normalize_slash)
31338494Sobrien */
31438494Sobrienstatic char
31538494Sobrienbackslash(char **p)
31638494Sobrien{
31738494Sobrien  char c;
31838494Sobrien
31938494Sobrien  if ((*p)[1] == '\0') {
32038494Sobrien    plog(XLOG_USER, "Empty backslash escape");
32138494Sobrien    return **p;
32238494Sobrien  }
32338494Sobrien
32438494Sobrien  if (**p == '\\') {
32538494Sobrien    (*p)++;
32638494Sobrien    switch (**p) {
32782794Sobrien    case 'g':
32838494Sobrien      c = '\007';		/* Bell */
32938494Sobrien      break;
33038494Sobrien    case 'b':
33138494Sobrien      c = '\010';		/* Backspace */
33238494Sobrien      break;
33338494Sobrien    case 't':
33438494Sobrien      c = '\011';		/* Horizontal Tab */
33538494Sobrien      break;
33638494Sobrien    case 'n':
33738494Sobrien      c = '\012';		/* New Line */
33838494Sobrien      break;
33938494Sobrien    case 'v':
34038494Sobrien      c = '\013';		/* Vertical Tab */
34138494Sobrien      break;
34238494Sobrien    case 'f':
34338494Sobrien      c = '\014';		/* Form Feed */
34438494Sobrien      break;
34538494Sobrien    case 'r':
34638494Sobrien      c = '\015';		/* Carriage Return */
34738494Sobrien      break;
34838494Sobrien    case 'e':
34938494Sobrien      c = '\033';		/* Escape */
35038494Sobrien      break;
35138494Sobrien    case '0':
35238494Sobrien    case '1':
35338494Sobrien    case '2':
35438494Sobrien    case '3':
35538494Sobrien    case '4':
35638494Sobrien    case '5':
35738494Sobrien    case '6':
35838494Sobrien    case '7':
35938494Sobrien      {
36038494Sobrien	int cnt, val, ch;
36138494Sobrien
36238494Sobrien	for (cnt = 0, val = 0; cnt < 3; cnt++) {
36338494Sobrien	  ch = *(*p)++;
36438494Sobrien	  if (ch < '0' || ch > '7') {
36538494Sobrien	    (*p)--;
36638494Sobrien	    break;
36738494Sobrien	  }
36838494Sobrien	  val = (val << 3) | (ch - '0');
36938494Sobrien	}
37038494Sobrien
37138494Sobrien	if ((val & 0xffffff00) != 0)
37238494Sobrien	  plog(XLOG_USER,
37338494Sobrien	       "Too large character constant %u\n",
37438494Sobrien	       val);
37538494Sobrien	c = (char) val;
37638494Sobrien	--(*p);
37738494Sobrien      }
37838494Sobrien      break;
37938494Sobrien
38038494Sobrien    default:
38138494Sobrien      c = **p;
38238494Sobrien      break;
38338494Sobrien    }
38438494Sobrien  } else
38538494Sobrien    c = **p;
38638494Sobrien
38738494Sobrien  return c;
38838494Sobrien}
38938494Sobrien
39038494Sobrien
39138494Sobrien/*
39238494Sobrien * Skip to next option in the string
39338494Sobrien */
39438494Sobrienstatic char *
39538494Sobrienopt(char **p)
39638494Sobrien{
39738494Sobrien  char *cp = *p;
39838494Sobrien  char *dp = cp;
39938494Sobrien  char *s = cp;
40038494Sobrien
40138494Sobrientop:
40238494Sobrien  while (*cp && *cp != ';') {
40338494Sobrien    if (*cp == '"') {
40438494Sobrien      /*
40538494Sobrien       * Skip past string
40638494Sobrien       */
40738494Sobrien      for (cp++; *cp && *cp != '"'; cp++)
40838494Sobrien	if (*cp == '\\')
40938494Sobrien	  *dp++ = backslash(&cp);
41038494Sobrien	else
41138494Sobrien	  *dp++ = *cp;
41238494Sobrien      if (*cp)
41338494Sobrien	cp++;
41438494Sobrien    } else {
41538494Sobrien      *dp++ = *cp++;
41638494Sobrien    }
41738494Sobrien  }
41838494Sobrien
41938494Sobrien  /*
42038494Sobrien   * Skip past any remaining ';'s
42138494Sobrien   */
42238494Sobrien  while (*cp == ';')
42338494Sobrien    cp++;
42438494Sobrien
42538494Sobrien  /*
42638494Sobrien   * If we have a zero length string
42738494Sobrien   * and there are more fields, then
42838494Sobrien   * parse the next one.  This allows
42938494Sobrien   * sequences of empty fields.
43038494Sobrien   */
43138494Sobrien  if (*cp && dp == s)
43238494Sobrien    goto top;
43338494Sobrien
43438494Sobrien  *dp = '\0';
43538494Sobrien
43638494Sobrien  *p = cp;
43738494Sobrien  return s;
43838494Sobrien}
43938494Sobrien
44038494Sobrien
44138494Sobrien/*
44238494Sobrien * These routines add a new style of selector; function-style boolean
44338494Sobrien * operators.  To add new ones, just define functions as in true, false,
44438494Sobrien * exists (below) and add them to the functable, above.
44538494Sobrien *
44638494Sobrien * Usage example: Some people have X11R5 local, some go to a server. I do
44738494Sobrien * this:
44838494Sobrien *
44938494Sobrien *    *       exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \
45038494Sobrien *            -type:=nfs;rfs=/usr/pkg/${key} \
45138494Sobrien *            rhost:=server1 \
45238494Sobrien *            rhost:=server2
45338494Sobrien *
45438494Sobrien * -Rens Troost <rens@imsi.com>
45538494Sobrien */
45638494Sobrienstatic IntFuncPtr
45738494Sobrienfunctable_lookup(char *key)
45838494Sobrien{
45938494Sobrien  struct functable *fp;
46038494Sobrien
46138494Sobrien  for (fp = functable; fp->name; fp++)
46238494Sobrien    if (FSTREQ(fp->name, key))
46338494Sobrien        return (fp->func);
46438494Sobrien  return (IntFuncPtr) NULL;
46538494Sobrien}
46638494Sobrien
46738494Sobrien
468174294Sobrien/*
469174294Sobrien * Fill in the global structure fs_static by
470174294Sobrien * cracking the string opts.  opts may be
471174294Sobrien * scribbled on at will.  Does NOT evaluate options.
472174294Sobrien * Returns 0 on error, 1 if no syntax errors were discovered.
473174294Sobrien */
47438494Sobrienstatic int
475174294Sobriensplit_opts(char *opts, char *mapkey)
47638494Sobrien{
477174294Sobrien  char *o = opts;
478174294Sobrien  char *f;
479174294Sobrien
48038494Sobrien  /*
481174294Sobrien   * For each user-specified option
48238494Sobrien   */
483174294Sobrien  for (f = opt(&o); *f; f = opt(&o)) {
484174294Sobrien    struct opt *op;
485174294Sobrien    char *eq = strchr(f, '=');
486174294Sobrien    char *opt = NULL;
487174294Sobrien
488174294Sobrien    if (!eq)
489174294Sobrien      continue;
490174294Sobrien
491174294Sobrien    if (*(eq-1) == '!' ||
492174294Sobrien	eq[1] == '=' ||
493174294Sobrien	eq[1] == '!') {	/* != or == or =! */
494174294Sobrien      continue;			/* we don't care about selectors */
495174294Sobrien    }
496174294Sobrien
497174294Sobrien    if (*(eq-1) == ':') {	/* := */
498174294Sobrien      *(eq-1) = '\0';
499174294Sobrien    } else {
500174294Sobrien      /* old style assignment */
501174294Sobrien      eq[0] = '\0';
502174294Sobrien    }
503174294Sobrien    opt = eq + 1;
504174294Sobrien
505174294Sobrien    /*
506174294Sobrien     * For each recognized option
507174294Sobrien     */
508174294Sobrien    for (op = opt_fields; op->name; op++) {
509174294Sobrien      /*
510174294Sobrien       * Check whether they match
511174294Sobrien       */
512174294Sobrien      if (FSTREQ(op->name, f)) {
513174294Sobrien	if (op->sel_p) {
514174294Sobrien	  plog(XLOG_USER, "key %s: Can't assign to a selector (%s)",
515174294Sobrien	       mapkey, op->name);
516174294Sobrien	  return 0;
517174294Sobrien	}
518174294Sobrien	*op->optp = opt;	/* actual assignment into fs_static */
519174294Sobrien	break;			/* break out of for loop */
520174294Sobrien      }	/* end of "if (FSTREQ(op->name, f))" statement  */
521174294Sobrien    } /* end of "for (op = opt_fields..." statement  */
522174294Sobrien
523174294Sobrien    if (!op->name)
524174294Sobrien      plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
525174294Sobrien  }
526174294Sobrien
527174294Sobrien  return 1;
528174294Sobrien}
529174294Sobrien
530174294Sobrien
531174294Sobrien/*
532174294Sobrien * Just evaluate selectors, which were split by split_opts.
533174294Sobrien * Returns 0 on error or no match, 1 if matched.
534174294Sobrien */
535174294Sobrienstatic int
536174294Sobrieneval_selectors(char *opts, char *mapkey)
537174294Sobrien{
538174294Sobrien  char *o, *old_o;
53938494Sobrien  char *f;
540174294Sobrien  int ret = 0;
54138494Sobrien
542174294Sobrien  o = old_o = strdup(opts);
543174294Sobrien
54438494Sobrien  /*
54538494Sobrien   * For each user-specified option
54638494Sobrien   */
547174294Sobrien  for (f = opt(&o); *f; f = opt(&o)) {
54838494Sobrien    struct opt *op;
549174294Sobrien    enum vs_opt vs_opt;
55038494Sobrien    char *eq = strchr(f, '=');
55138494Sobrien    char *fx;
55238494Sobrien    IntFuncPtr func;
55338494Sobrien    char *opt = NULL;
554174294Sobrien    char *arg;
55538494Sobrien
556174294Sobrien    if (!eq) {
55738494Sobrien      /*
55838494Sobrien       * No value, is it a function call?
55938494Sobrien       */
560174294Sobrien      arg = strchr(f, '(');
56138494Sobrien
56238494Sobrien      if (!arg || arg[1] == '\0' || arg == f) {
56338494Sobrien	/*
56438494Sobrien	 * No, just continue
56538494Sobrien	 */
56638494Sobrien	plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f);
56738494Sobrien	continue;
56838494Sobrien      }
56938494Sobrien
57038494Sobrien      /* null-terminate the argument  */
57138494Sobrien      *arg++ = '\0';
57238494Sobrien      fx = strchr(arg, ')');
57338494Sobrien      if (!arg || fx == arg) {
57438494Sobrien	plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f);
57538494Sobrien	continue;
57638494Sobrien      }
57738494Sobrien      *fx = '\0';
57838494Sobrien
579174294Sobrien      if (f[0] == '!') {
580174294Sobrien	vs_opt = SelNE;
581174294Sobrien	f++;
582174294Sobrien      } else {
583174294Sobrien	vs_opt = SelEQ;
584174294Sobrien      }
58538494Sobrien      /*
58638494Sobrien       * look up f in functable and pass it arg.
58738494Sobrien       * func must return 0 on failure, and 1 on success.
58838494Sobrien       */
58938494Sobrien      if ((func = functable_lookup(f))) {
590174294Sobrien	int funok;
591174294Sobrien
592174294Sobrien	/* this allocates memory, don't forget to free */
593174294Sobrien	arg = expand_options(arg);
594174294Sobrien	funok = func(arg);
595174294Sobrien	XFREE(arg);
596174294Sobrien
597174294Sobrien	if (vs_opt == SelNE)
598174294Sobrien	  funok = !funok;
599174294Sobrien	if (!funok)
600174294Sobrien	  goto out;
601174294Sobrien
60238494Sobrien	continue;
60338494Sobrien      } else {
60438494Sobrien	plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f);
605174294Sobrien	goto out;
60638494Sobrien      }
607174294Sobrien    } else {
608174294Sobrien      if (eq[1] == '\0' || eq == f) {
609174294Sobrien	/* misformed selector */
610174294Sobrien	plog(XLOG_USER, "key %s: Bad selector \"%s\"", mapkey, f);
611174294Sobrien	continue;
612174294Sobrien      }
61338494Sobrien    }
61438494Sobrien
61538494Sobrien    /*
61638494Sobrien     * Check what type of operation is happening
61738494Sobrien     * !=, =!  is SelNE
61838494Sobrien     * == is SelEQ
619174294Sobrien     * =, := is VarAss
62038494Sobrien     */
621174294Sobrien    if (*(eq-1) == '!') {	/* != */
62238494Sobrien      vs_opt = SelNE;
623174294Sobrien      *(eq-1) = '\0';
62438494Sobrien      opt = eq + 1;
625174294Sobrien    } else if (*(eq-1) == ':') {	/* := */
626174294Sobrien      continue;
62738494Sobrien    } else if (eq[1] == '=') {	/* == */
62838494Sobrien      vs_opt = SelEQ;
62938494Sobrien      eq[0] = '\0';
63038494Sobrien      opt = eq + 2;
63138494Sobrien    } else if (eq[1] == '!') {	/* =! */
63238494Sobrien      vs_opt = SelNE;
63338494Sobrien      eq[0] = '\0';
63438494Sobrien      opt = eq + 2;
635174294Sobrien    } else {
636174294Sobrien      /* old style assignment */
637174294Sobrien      continue;
63838494Sobrien    }
63938494Sobrien
64038494Sobrien    /*
64138494Sobrien     * For each recognized option
64238494Sobrien     */
64338494Sobrien    for (op = opt_fields; op->name; op++) {
64438494Sobrien      /*
64538494Sobrien       * Check whether they match
64638494Sobrien       */
64738494Sobrien      if (FSTREQ(op->name, f)) {
648174294Sobrien	opt = expand_options(opt);
649174294Sobrien
650174294Sobrien	if (op->sel_p != NULL) {
651174294Sobrien	  int selok;
652174294Sobrien	  if (op->case_insensitive) {
653174294Sobrien	    selok = STRCEQ(*op->sel_p, opt);
654174294Sobrien	  } else {
655174294Sobrien	    selok = STREQ(*op->sel_p, opt);
656174294Sobrien	  }
657174294Sobrien	  if (vs_opt == SelNE)
658174294Sobrien	    selok = !selok;
659174294Sobrien	  if (!selok) {
66038494Sobrien	    plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s",
66138494Sobrien		 mapkey,
66238494Sobrien		 op->name,
66338494Sobrien		 *op->sel_p,
66438494Sobrien		 vs_opt == SelNE ? "mis" : "",
66538494Sobrien		 opt);
666174294Sobrien	    XFREE(opt);
667174294Sobrien	    goto out;
66838494Sobrien	  }
669174294Sobrien	  XFREE(opt);
670174294Sobrien	}
671174294Sobrien	/* check if to apply a function */
672174294Sobrien	if (op->fxn_p) {
673174294Sobrien	  int funok;
674174294Sobrien
675174294Sobrien	  funok = op->fxn_p(opt);
676174294Sobrien	  if (vs_opt == SelNE)
677174294Sobrien	    funok = !funok;
678174294Sobrien	  if (!funok) {
67938494Sobrien	    plog(XLOG_MAP, "key %s: map function %s did not %smatch %s",
68038494Sobrien		 mapkey,
68138494Sobrien		 op->name,
68238494Sobrien		 vs_opt == SelNE ? "mis" : "",
68338494Sobrien		 opt);
684174294Sobrien	    XFREE(opt);
685174294Sobrien	    goto out;
68638494Sobrien	  }
687174294Sobrien	  XFREE(opt);
688174294Sobrien	}
68938494Sobrien	break;			/* break out of for loop */
69038494Sobrien      }
69138494Sobrien    }
69238494Sobrien
69338494Sobrien    if (!op->name)
69438494Sobrien      plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
69538494Sobrien  }
69638494Sobrien
697174294Sobrien  /* all is ok */
698174294Sobrien  ret = 1;
699174294Sobrien
700174294Sobrien out:
701174294Sobrien  free(old_o);
702174294Sobrien  return ret;
70338494Sobrien}
70438494Sobrien
70538494Sobrien
70638494Sobrien/*
70738494Sobrien * Skip to next option in the string, but don't scribble over the string.
70838494Sobrien * However, *p gets repointed to the start of the next string past ';'.
70938494Sobrien */
71038494Sobrienstatic char *
71138494Sobrienopt_no_scribble(char **p)
71238494Sobrien{
71338494Sobrien  char *cp = *p;
71438494Sobrien  char *dp = cp;
71538494Sobrien  char *s = cp;
71638494Sobrien
71738494Sobrientop:
71838494Sobrien  while (*cp && *cp != ';') {
71938494Sobrien    if (*cp == '\"') {
72038494Sobrien      /*
72138494Sobrien       * Skip past string
72238494Sobrien       */
72338494Sobrien      cp++;
72438494Sobrien      while (*cp && *cp != '\"')
72538494Sobrien	*dp++ = *cp++;
72638494Sobrien      if (*cp)
72738494Sobrien	cp++;
72838494Sobrien    } else {
72938494Sobrien      *dp++ = *cp++;
73038494Sobrien    }
73138494Sobrien  }
73238494Sobrien
73338494Sobrien  /*
73438494Sobrien   * Skip past any remaining ';'s
73538494Sobrien   */
73638494Sobrien  while (*cp == ';')
73738494Sobrien    cp++;
73838494Sobrien
73938494Sobrien  /*
74038494Sobrien   * If we have a zero length string
74138494Sobrien   * and there are more fields, then
74238494Sobrien   * parse the next one.  This allows
74338494Sobrien   * sequences of empty fields.
74438494Sobrien   */
74538494Sobrien  if (*cp && dp == s)
74638494Sobrien    goto top;
74738494Sobrien
74838494Sobrien  *p = cp;
74938494Sobrien  return s;
75038494Sobrien}
75138494Sobrien
75238494Sobrien
75338494Sobrien/*
75438494Sobrien * Strip any selectors from a string.  Selectors are all assumed to be
75538494Sobrien * first in the string.  This is used for the new /defaults method which will
75638494Sobrien * use selectors as well.
75738494Sobrien */
75838494Sobrienchar *
75938494Sobrienstrip_selectors(char *opts, char *mapkey)
76038494Sobrien{
76138494Sobrien  /*
76238494Sobrien   * Fill in the global structure fs_static by
76338494Sobrien   * cracking the string opts.  opts may be
76438494Sobrien   * scribbled on at will.
76538494Sobrien   */
76638494Sobrien  char *o = opts;
76738494Sobrien  char *oo = opts;
76838494Sobrien  char *f;
76938494Sobrien
77038494Sobrien  /*
77138494Sobrien   * Scan options.  Note that the opt() function scribbles on the opt string.
77238494Sobrien   */
77338494Sobrien  while (*(f = opt_no_scribble(&o))) {
77438494Sobrien    enum vs_opt vs_opt = VarAss;
77538494Sobrien    char *eq = strchr(f, '=');
77638494Sobrien
77738494Sobrien    if (!eq || eq[1] == '\0' || eq == f) {
77838494Sobrien      /*
77938494Sobrien       * No option or assignment?  Return as is.
78038494Sobrien       */
78138494Sobrien      plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f);
78238494Sobrien      return o;
78338494Sobrien    }
78438494Sobrien    /*
78538494Sobrien     * Check what type of operation is happening
78638494Sobrien     * !=, =!  is SelNE
78738494Sobrien     * == is SelEQ
78838494Sobrien     * := is VarAss
78938494Sobrien     */
790174294Sobrien    if (*(eq-1) == '!') {	/* != */
79138494Sobrien      vs_opt = SelNE;
792174294Sobrien    } else if (*(eq-1) == ':') {	/* := */
79338494Sobrien      vs_opt = VarAss;
79438494Sobrien    } else if (eq[1] == '=') {	/* == */
79538494Sobrien      vs_opt = SelEQ;
79638494Sobrien    } else if (eq[1] == '!') {	/* =! */
79738494Sobrien      vs_opt = SelNE;
79838494Sobrien    }
79938494Sobrien    switch (vs_opt) {
80038494Sobrien    case SelEQ:
80138494Sobrien    case SelNE:
80238494Sobrien      /* Skip this selector, maybe there's another one following it */
80338494Sobrien      plog(XLOG_USER, "skipping selector to \"%s\"", o);
80438494Sobrien      /* store previous match. it may have been the first assignment */
80538494Sobrien      oo = o;
80638494Sobrien      break;
80738494Sobrien
80838494Sobrien    case VarAss:
80938494Sobrien      /* found the first assignment, return the string starting with it */
81038494Sobrien      dlog("found first assignment past selectors \"%s\"", o);
81138494Sobrien      return oo;
81238494Sobrien    }
81338494Sobrien  }
81438494Sobrien
81538494Sobrien  /* return the same string by default. should not happen. */
81638494Sobrien  return oo;
81738494Sobrien}
81838494Sobrien
81938494Sobrien
82038494Sobrien/*****************************************************************************
82138494Sobrien *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true):                     ***
82238494Sobrien *****************************************************************************/
82338494Sobrien
82438494Sobrien/* test if arg is any of this host's network names or numbers */
82538494Sobrienstatic int
82638494Sobrienf_in_network(char *arg)
82738494Sobrien{
82838494Sobrien  int status;
82938494Sobrien
83038494Sobrien  if (!arg)
831174294Sobrien    return 0;
83238494Sobrien
83338494Sobrien  status = is_network_member(arg);
834174294Sobrien  dlog("%s is %son a local network", arg, (status ? "" : "not "));
83538494Sobrien  return status;
83638494Sobrien}
83738494Sobrien
83838494Sobrien
839174294Sobrien/*
840174294Sobrien * Test if arg is any of this host's names or aliases (CNAMES).
841174294Sobrien * Note: this function compares against the fully expanded host name (hostd).
842174294Sobrien * XXX: maybe we also need to compare against the stripped host name?
843174294Sobrien */
844174294Sobrienstatic int
845174294Sobrienf_xhost(char *arg)
846174294Sobrien{
847174294Sobrien  struct hostent *hp;
848174294Sobrien  char **cp;
849174294Sobrien
850174294Sobrien  if (!arg)
851174294Sobrien    return 0;
852174294Sobrien
853174294Sobrien  /* simple test: does it match main host name? */
854174294Sobrien  if (STREQ(arg, opt_hostd))
855174294Sobrien    return 1;
856174294Sobrien
857174294Sobrien  /* now find all of the names of "arg" and compare against opt_hostd */
858174294Sobrien  hp = gethostbyname(arg);
859174294Sobrien  if (hp == NULL) {
860174294Sobrien#ifdef HAVE_HSTRERROR
861174294Sobrien    plog(XLOG_ERROR, "gethostbyname xhost(%s): %s", arg, hstrerror(h_errno));
862174294Sobrien#else /* not HAVE_HSTRERROR */
863174294Sobrien    plog(XLOG_ERROR, "gethostbyname xhost(%s): h_errno %d", arg, h_errno);
864174294Sobrien#endif /* not HAVE_HSTRERROR */
865174294Sobrien    return 0;
866174294Sobrien  }
867174294Sobrien  /* check primary name */
868174294Sobrien  if (hp->h_name) {
869174294Sobrien    dlog("xhost: compare %s==%s", hp->h_name, opt_hostd);
870174294Sobrien    if (STREQ(hp->h_name, opt_hostd)) {
871174294Sobrien      plog(XLOG_INFO, "xhost(%s): matched h_name %s", arg, hp->h_name);
872174294Sobrien      return 1;
873174294Sobrien    }
874174294Sobrien  }
875174294Sobrien  /* check all aliases, if any */
876174294Sobrien  if (hp->h_aliases == NULL) {
877174294Sobrien    dlog("gethostbyname(%s) has no aliases", arg);
878174294Sobrien    return 0;
879174294Sobrien  }
880174294Sobrien  cp = hp->h_aliases;
881174294Sobrien  while (*cp) {
882174294Sobrien    dlog("xhost: compare alias %s==%s", *cp, opt_hostd);
883174294Sobrien    if (STREQ(*cp, opt_hostd)) {
884174294Sobrien      plog(XLOG_INFO, "xhost(%s): matched alias %s", arg, *cp);
885174294Sobrien      return 1;
886174294Sobrien    }
887174294Sobrien    cp++;
888174294Sobrien  }
889174294Sobrien  /* nothing matched */
890174294Sobrien  return 0;
891174294Sobrien}
892174294Sobrien
893174294Sobrien
89451292Sobrien/* test if this host (short hostname form) is in netgroup (arg) */
89538494Sobrienstatic int
89638494Sobrienf_netgrp(char *arg)
89738494Sobrien{
89838494Sobrien  int status;
899174294Sobrien  char *ptr, *nhost;
90038494Sobrien
901174294Sobrien  if ((ptr = strchr(arg, ',')) != NULL) {
902174294Sobrien    *ptr = '\0';
903174294Sobrien    nhost = ptr + 1;
904174294Sobrien  } else {
905174294Sobrien    nhost = opt_host;
906174294Sobrien  }
907174294Sobrien  status = innetgr(arg, nhost, NULL, NULL);
908174294Sobrien  dlog("netgrp = %s status = %d host = %s", arg, status, nhost);
909174294Sobrien  if (ptr)
910174294Sobrien    *ptr = ',';
91138494Sobrien  return status;
91238494Sobrien}
91338494Sobrien
91438494Sobrien
91551292Sobrien/* test if this host (fully-qualified name) is in netgroup (arg) */
91651292Sobrienstatic int
91751292Sobrienf_netgrpd(char *arg)
91851292Sobrien{
91951292Sobrien  int status;
920174294Sobrien  char *ptr, *nhost;
92151292Sobrien
922174294Sobrien  if ((ptr = strchr(arg, ',')) != NULL) {
923174294Sobrien    *ptr = '\0';
924174294Sobrien    nhost = ptr + 1;
925174294Sobrien  } else {
926174294Sobrien    nhost = opt_hostd;
927174294Sobrien  }
928174294Sobrien  status = innetgr(arg, nhost, NULL, NULL);
929174294Sobrien  dlog("netgrp = %s status = %d hostd = %s", arg, status, nhost);
930174294Sobrien  if (ptr)
931174294Sobrien    *ptr = ',';
93251292Sobrien  return status;
93351292Sobrien}
93451292Sobrien
93551292Sobrien
93638494Sobrien/* test if file (arg) exists via lstat */
93738494Sobrienstatic int
93838494Sobrienf_exists(char *arg)
93938494Sobrien{
94038494Sobrien  struct stat buf;
94138494Sobrien
94238494Sobrien  if (lstat(arg, &buf) < 0)
94338494Sobrien    return (0);
94438494Sobrien  else
94538494Sobrien    return (1);
94638494Sobrien}
94738494Sobrien
94838494Sobrien
94938494Sobrien/* always false */
95038494Sobrienstatic int
95138494Sobrienf_false(char *arg)
95238494Sobrien{
95338494Sobrien  return (0);
95438494Sobrien}
95538494Sobrien
95638494Sobrien
95738494Sobrien/* always true */
95838494Sobrienstatic int
95938494Sobrienf_true(char *arg)
96038494Sobrien{
96138494Sobrien  return (1);
96238494Sobrien}
96338494Sobrien
96438494Sobrien
96538494Sobrien/*
96638494Sobrien * Free an option
96738494Sobrien */
96838494Sobrienstatic void
96938494Sobrienfree_op(opt_apply *p, int b)
97038494Sobrien{
97138494Sobrien  if (*p->opt) {
97238494Sobrien    XFREE(*p->opt);
97338494Sobrien  }
97438494Sobrien}
97538494Sobrien
97638494Sobrien
97738494Sobrien/*
97838494Sobrien * Normalize slashes in the string.
97938494Sobrien */
98038494Sobrienvoid
98138494Sobriennormalize_slash(char *p)
98238494Sobrien{
983174294Sobrien  char *f, *f0;
98438494Sobrien
985174294Sobrien  if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
986174294Sobrien    return;
987174294Sobrien
988174294Sobrien  f0 = f = strchr(p, '/');
98938494Sobrien  if (f) {
99038494Sobrien    char *t = f;
99138494Sobrien    do {
99238494Sobrien      /* assert(*f == '/'); */
99338494Sobrien      if (f == f0 && f[0] == '/' && f[1] == '/') {
99438494Sobrien	/* copy double slash iff first */
99538494Sobrien	*t++ = *f++;
99638494Sobrien	*t++ = *f++;
99738494Sobrien      } else {
99838494Sobrien	/* copy a single / across */
99938494Sobrien	*t++ = *f++;
100038494Sobrien      }
100138494Sobrien
100238494Sobrien      /* assert(f[-1] == '/'); */
100338494Sobrien      /* skip past more /'s */
100438494Sobrien      while (*f == '/')
100538494Sobrien	f++;
100638494Sobrien
100738494Sobrien      /* assert(*f != '/'); */
100838494Sobrien      /* keep copying up to next / */
100938494Sobrien      while (*f && *f != '/') {
1010174294Sobrien	/* support escaped slashes '\/' */
1011174294Sobrien	if (f[0] == '\\' && f[1] == '/')
1012174294Sobrien	  f++;			/* skip backslash */
101338494Sobrien	*t++ = *f++;
101438494Sobrien      }
101538494Sobrien
101638494Sobrien      /* assert(*f == 0 || *f == '/'); */
101738494Sobrien
101838494Sobrien    } while (*f);
101938494Sobrien    *t = 0;			/* derived from fix by Steven Glassman */
102038494Sobrien  }
102138494Sobrien}
102238494Sobrien
102338494Sobrien
102438494Sobrien/*
102538494Sobrien * Macro-expand an option.  Note that this does not
102638494Sobrien * handle recursive expansions.  They will go badly wrong.
1027174294Sobrien * If sel_p is true then old expand selectors, otherwise
102838494Sobrien * don't expand selectors.
102938494Sobrien */
1030174294Sobrienstatic char *
1031174294Sobrienexpand_op(char *opt, int sel_p)
103238494Sobrien{
1033174294Sobrien#define EXPAND_ERROR "No space to expand \"%s\""
103438494Sobrien  char expbuf[MAXPATHLEN + 1];
103538494Sobrien  char nbuf[NLEN + 1];
103638494Sobrien  char *ep = expbuf;
1037174294Sobrien  char *cp = opt;
103838494Sobrien  char *dp;
103938494Sobrien  struct opt *op;
1040174294Sobrien  char *cp_orig = opt;
104138494Sobrien
104238494Sobrien  while ((dp = strchr(cp, '$'))) {
104338494Sobrien    char ch;
104438494Sobrien    /*
104538494Sobrien     * First copy up to the $
104638494Sobrien     */
104738494Sobrien    {
104838494Sobrien      int len = dp - cp;
104938494Sobrien
1050174294Sobrien      if (len > 0) {
1051174294Sobrien	if (BUFSPACE(ep, len)) {
1052174294Sobrien	  /*
1053174294Sobrien	   * We use strncpy (not xstrlcpy) because 'ep' relies on its
1054174294Sobrien	   * semantics.  BUFSPACE guarantees that ep can hold len.
1055174294Sobrien	   */
1056174294Sobrien	  strncpy(ep, cp, len);
1057174294Sobrien	  ep += len;
1058174294Sobrien	} else {
1059174294Sobrien	  plog(XLOG_ERROR, EXPAND_ERROR, opt);
1060174294Sobrien	  goto out;
1061174294Sobrien	}
106238494Sobrien      }
106338494Sobrien    }
106438494Sobrien
106538494Sobrien    cp = dp + 1;
106638494Sobrien    ch = *cp++;
106738494Sobrien    if (ch == '$') {
106838494Sobrien      if (BUFSPACE(ep, 1)) {
106938494Sobrien	*ep++ = '$';
107038494Sobrien      } else {
1071174294Sobrien	plog(XLOG_ERROR, EXPAND_ERROR, opt);
107238494Sobrien	goto out;
107338494Sobrien      }
107438494Sobrien    } else if (ch == '{') {
107538494Sobrien      /* Expansion... */
107638494Sobrien      enum {
107738494Sobrien	E_All, E_Dir, E_File, E_Domain, E_Host
107838494Sobrien      } todo;
107938494Sobrien      /*
108038494Sobrien       * Find closing brace
108138494Sobrien       */
108238494Sobrien      char *br_p = strchr(cp, '}');
108338494Sobrien      int len;
108438494Sobrien
108538494Sobrien      /*
108638494Sobrien       * Check we found it
108738494Sobrien       */
108838494Sobrien      if (!br_p) {
108938494Sobrien	/*
109038494Sobrien	 * Just give up
109138494Sobrien	 */
1092174294Sobrien	plog(XLOG_USER, "No closing '}' in \"%s\"", opt);
109338494Sobrien	goto out;
109438494Sobrien      }
109538494Sobrien      len = br_p - cp;
109638494Sobrien
109738494Sobrien      /*
109838494Sobrien       * Figure out which part of the variable to grab.
109938494Sobrien       */
110038494Sobrien      if (*cp == '/') {
110138494Sobrien	/*
110238494Sobrien	 * Just take the last component
110338494Sobrien	 */
110438494Sobrien	todo = E_File;
110538494Sobrien	cp++;
110638494Sobrien	--len;
1107174294Sobrien      } else if (*(br_p-1) == '/') {
110838494Sobrien	/*
110938494Sobrien	 * Take all but the last component
111038494Sobrien	 */
111138494Sobrien	todo = E_Dir;
111238494Sobrien	--len;
111338494Sobrien      } else if (*cp == '.') {
111438494Sobrien	/*
111538494Sobrien	 * Take domain name
111638494Sobrien	 */
111738494Sobrien	todo = E_Domain;
111838494Sobrien	cp++;
111938494Sobrien	--len;
1120174294Sobrien      } else if (*(br_p-1) == '.') {
112138494Sobrien	/*
112238494Sobrien	 * Take host name
112338494Sobrien	 */
112438494Sobrien	todo = E_Host;
112538494Sobrien	--len;
112638494Sobrien      } else {
112738494Sobrien	/*
112838494Sobrien	 * Take the whole lot
112938494Sobrien	 */
113038494Sobrien	todo = E_All;
113138494Sobrien      }
113238494Sobrien
113338494Sobrien      /*
113438494Sobrien       * Truncate if too long.  Since it won't
113538494Sobrien       * match anyway it doesn't matter that
113638494Sobrien       * it has been cut short.
113738494Sobrien       */
113838494Sobrien      if (len > NLEN)
113938494Sobrien	len = NLEN;
114038494Sobrien
114138494Sobrien      /*
114238494Sobrien       * Put the string into another buffer so
114338494Sobrien       * we can do comparisons.
1144174294Sobrien       *
1145174294Sobrien       * We use strncpy here (not xstrlcpy) because the dest is meant
1146174294Sobrien       * to be truncated and we don't want to log it as an error.  The
1147174294Sobrien       * use of the BUFSPACE macro above guarantees the safe use of
1148174294Sobrien       * strncpy with nbuf.
114938494Sobrien       */
115038494Sobrien      strncpy(nbuf, cp, len);
115138494Sobrien      nbuf[len] = '\0';
115238494Sobrien
115338494Sobrien      /*
115438494Sobrien       * Advance cp
115538494Sobrien       */
115638494Sobrien      cp = br_p + 1;
115738494Sobrien
115838494Sobrien      /*
115938494Sobrien       * Search the option array
116038494Sobrien       */
116138494Sobrien      for (op = opt_fields; op->name; op++) {
116238494Sobrien	/*
116338494Sobrien	 * Check for match
116438494Sobrien	 */
116538494Sobrien	if (len == op->nlen && STREQ(op->name, nbuf)) {
116638494Sobrien	  char xbuf[NLEN + 3];
116738494Sobrien	  char *val;
116838494Sobrien	  /*
116938494Sobrien	   * Found expansion.  Copy
117038494Sobrien	   * the correct value field.
117138494Sobrien	   */
117238494Sobrien	  if (!(!op->sel_p == !sel_p)) {
117338494Sobrien	    /*
117438494Sobrien	     * Copy the string across unexpanded
117538494Sobrien	     */
1176174294Sobrien	    xsnprintf(xbuf, sizeof(xbuf), "${%s%s%s}",
1177174294Sobrien		      todo == E_File ? "/" :
1178174294Sobrien		      todo == E_Domain ? "." : "",
1179174294Sobrien		      nbuf,
1180174294Sobrien		      todo == E_Dir ? "/" :
1181174294Sobrien		      todo == E_Host ? "." : "");
118238494Sobrien	    val = xbuf;
118338494Sobrien	    /*
118438494Sobrien	     * Make sure expansion doesn't
118538494Sobrien	     * munge the value!
118638494Sobrien	     */
118738494Sobrien	    todo = E_All;
118838494Sobrien	  } else if (op->sel_p) {
118938494Sobrien	    val = *op->sel_p;
119038494Sobrien	  } else {
119138494Sobrien	    val = *op->optp;
119238494Sobrien	  }
119338494Sobrien
119438494Sobrien	  if (val) {
119538494Sobrien	    /*
119638494Sobrien	     * Do expansion:
119738494Sobrien	     * ${/var} means take just the last part
119838494Sobrien	     * ${var/} means take all but the last part
119938494Sobrien	     * ${.var} means take all but first part
120038494Sobrien	     * ${var.} means take just the first part
120138494Sobrien	     * ${var} means take the whole lot
120238494Sobrien	     */
120338494Sobrien	    int vlen = strlen(val);
120438494Sobrien	    char *vptr = val;
120538494Sobrien	    switch (todo) {
120638494Sobrien	    case E_Dir:
120738494Sobrien	      vptr = strrchr(val, '/');
120838494Sobrien	      if (vptr)
120938494Sobrien		vlen = vptr - val;
121038494Sobrien	      vptr = val;
121138494Sobrien	      break;
121238494Sobrien	    case E_File:
121338494Sobrien	      vptr = strrchr(val, '/');
121438494Sobrien	      if (vptr) {
121538494Sobrien		vptr++;
121638494Sobrien		vlen = strlen(vptr);
121738494Sobrien	      } else
121838494Sobrien		vptr = val;
121938494Sobrien	      break;
122038494Sobrien	    case E_Domain:
122138494Sobrien	      vptr = strchr(val, '.');
122238494Sobrien	      if (vptr) {
122338494Sobrien		vptr++;
122438494Sobrien		vlen = strlen(vptr);
122538494Sobrien	      } else {
122638494Sobrien		vptr = "";
122738494Sobrien		vlen = 0;
122838494Sobrien	      }
122938494Sobrien	      break;
123038494Sobrien	    case E_Host:
123138494Sobrien	      vptr = strchr(val, '.');
123238494Sobrien	      if (vptr)
123338494Sobrien		vlen = vptr - val;
123438494Sobrien	      vptr = val;
123538494Sobrien	      break;
123638494Sobrien	    case E_All:
123738494Sobrien	      break;
123838494Sobrien	    }
123938494Sobrien
1240174294Sobrien	    if (BUFSPACE(ep, vlen+1)) {
1241174294Sobrien	      xstrlcpy(ep, vptr, vlen+1);
124238494Sobrien	      ep += vlen;
124338494Sobrien	    } else {
1244174294Sobrien	      plog(XLOG_ERROR, EXPAND_ERROR, opt);
124538494Sobrien	      goto out;
124638494Sobrien	    }
124738494Sobrien	  }
124838494Sobrien	  /*
124938494Sobrien	   * Done with this variable
125038494Sobrien	   */
125138494Sobrien	  break;
125238494Sobrien	}
125338494Sobrien      }
125438494Sobrien
125538494Sobrien      /*
125642629Sobrien       * Check that the search was successful
125738494Sobrien       */
125838494Sobrien      if (!op->name) {
125938494Sobrien	/*
126038494Sobrien	 * If it wasn't then scan the
126138494Sobrien	 * environment for that name
126238494Sobrien	 * and use any value found
126338494Sobrien	 */
126438494Sobrien	char *env = getenv(nbuf);
126538494Sobrien
126638494Sobrien	if (env) {
126738494Sobrien	  int vlen = strlen(env);
126838494Sobrien
1269174294Sobrien	  if (BUFSPACE(ep, vlen+1)) {
1270174294Sobrien	    xstrlcpy(ep, env, vlen+1);
127138494Sobrien	    ep += vlen;
127238494Sobrien	  } else {
1273174294Sobrien	    plog(XLOG_ERROR, EXPAND_ERROR, opt);
127438494Sobrien	    goto out;
127538494Sobrien	  }
1276174294Sobrien	  if (amuDebug(D_STR))
127738494Sobrien	    plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
127838494Sobrien	} else {
127938494Sobrien	  plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
128038494Sobrien	}
128138494Sobrien      }
128238494Sobrien    } else {
128338494Sobrien      /*
128438494Sobrien       * Error, error
128538494Sobrien       */
1286174294Sobrien      plog(XLOG_USER, "Unknown $ sequence in \"%s\"", opt);
128738494Sobrien    }
128838494Sobrien  }
128938494Sobrien
129038494Sobrienout:
129138494Sobrien  /*
129238494Sobrien   * Handle common case - no expansion
129338494Sobrien   */
1294174294Sobrien  if (cp == opt) {
1295174294Sobrien    opt = strdup(cp);
129638494Sobrien  } else {
129738494Sobrien    /*
129838494Sobrien     * Finish off the expansion
129938494Sobrien     */
1300174294Sobrien    int vlen = strlen(cp);
1301174294Sobrien    if (BUFSPACE(ep, vlen+1)) {
1302174294Sobrien      xstrlcpy(ep, cp, vlen+1);
1303174294Sobrien      /* ep += vlen; */
130438494Sobrien    } else {
1305174294Sobrien      plog(XLOG_ERROR, EXPAND_ERROR, opt);
130638494Sobrien    }
130738494Sobrien
130838494Sobrien    /*
130942629Sobrien     * Save the expansion
131038494Sobrien     */
1311174294Sobrien    opt = strdup(expbuf);
131238494Sobrien  }
131338494Sobrien
1314174294Sobrien  normalize_slash(opt);
131538494Sobrien
1316174294Sobrien  if (amuDebug(D_STR)) {
131738494Sobrien    plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig);
1318174294Sobrien    plog(XLOG_DEBUG, "......... is \"%s\"", opt);
131938494Sobrien  }
1320174294Sobrien  return opt;
132138494Sobrien}
132238494Sobrien
132338494Sobrien
132438494Sobrien/*
132538494Sobrien * Wrapper for expand_op
132638494Sobrien */
132738494Sobrienstatic void
132838494Sobrienexpand_opts(opt_apply *p, int sel_p)
132938494Sobrien{
133038494Sobrien  if (*p->opt) {
1331174294Sobrien    *p->opt = expand_op(*p->opt, sel_p);
133238494Sobrien  } else if (p->val) {
133338494Sobrien    /*
133438494Sobrien     * Do double expansion, remembering
133538494Sobrien     * to free the string from the first
133638494Sobrien     * expansion...
133738494Sobrien     */
1338174294Sobrien    char *s = expand_op(p->val, TRUE);
1339174294Sobrien    *p->opt = expand_op(s, sel_p);
134038494Sobrien    XFREE(s);
134138494Sobrien  }
134238494Sobrien}
134338494Sobrien
134438494Sobrien
134538494Sobrien/*
134638494Sobrien * Apply a function to a list of options
134738494Sobrien */
134838494Sobrienstatic void
134938494Sobrienapply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b)
135038494Sobrien{
135138494Sobrien  opt_apply *pp;
135238494Sobrien
135338494Sobrien  for (pp = ppp; pp->opt; pp++)
135438494Sobrien    (*op) (pp, b);
135538494Sobrien}
135638494Sobrien
135738494Sobrien
135838494Sobrien/*
135938494Sobrien * Free the option table
136038494Sobrien */
136138494Sobrienvoid
136238494Sobrienfree_opts(am_opts *fo)
136338494Sobrien{
136438494Sobrien  /*
136538494Sobrien   * Copy in the structure we are playing with
136638494Sobrien   */
136738494Sobrien  fs_static = *fo;
136838494Sobrien
136938494Sobrien  /*
137038494Sobrien   * Free previously allocated memory
137138494Sobrien   */
137238494Sobrien  apply_opts(free_op, to_free, FALSE);
137338494Sobrien}
137438494Sobrien
137538494Sobrien
137638494Sobrien/*
1377174294Sobrien * Expand selectors (variables that cannot be assigned to or overridden)
137838494Sobrien */
137938494Sobrienchar *
1380174294Sobrienexpand_selectors(char *key)
138138494Sobrien{
1382174294Sobrien  return expand_op(key, TRUE);
1383174294Sobrien}
138438494Sobrien
138538494Sobrien
1386174294Sobrien/*
1387174294Sobrien * Expand options (i.e. non-selectors, see above for definition)
1388174294Sobrien */
1389174294Sobrienstatic inline char *
1390174294Sobrienexpand_options(char *key)
1391174294Sobrien{
1392174294Sobrien  return expand_op(key, FALSE);
139338494Sobrien}
139438494Sobrien
139538494Sobrien
139638494Sobrien/*
139738494Sobrien * Remove trailing /'s from a string
139838494Sobrien * unless the string is a single / (Steven Glassman)
139938494Sobrien * or unless it is two slashes // (Kevin D. Bond)
1400174294Sobrien * or unless amd.conf says not to touch slashes.
140138494Sobrien */
140238494Sobrienvoid
140338494Sobriendeslashify(char *s)
140438494Sobrien{
1405174294Sobrien  if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
1406174294Sobrien    return;
1407174294Sobrien
140838494Sobrien  if (s && *s) {
140938494Sobrien    char *sl = s + strlen(s);
141038494Sobrien
141138494Sobrien    while (*--sl == '/' && sl > s)
141238494Sobrien      *sl = '\0';
141338494Sobrien  }
141438494Sobrien}
141538494Sobrien
141638494Sobrien
141738494Sobrienint
141838494Sobrieneval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map)
141938494Sobrien{
142038494Sobrien  int ok = TRUE;
142138494Sobrien
142238494Sobrien  free_opts(fo);
142338494Sobrien
142438494Sobrien  /*
142538494Sobrien   * Clear out the option table
142638494Sobrien   */
142738494Sobrien  memset((voidp) &fs_static, 0, sizeof(fs_static));
142838494Sobrien  memset((voidp) vars, 0, sizeof(vars));
142938494Sobrien  memset((voidp) fo, 0, sizeof(*fo));
143038494Sobrien
143142629Sobrien  /* set hostname */
143242629Sobrien  opt_host = (char *) am_get_hostname();
143342629Sobrien
143438494Sobrien  /*
143538494Sobrien   * Set key, map & path before expansion
143638494Sobrien   */
143738494Sobrien  opt_key = key;
143838494Sobrien  opt_map = map;
143938494Sobrien  opt_path = path;
144038494Sobrien
144138494Sobrien  opt_dkey = strchr(key, '.');
144238494Sobrien  if (!opt_dkey) {
144338494Sobrien    opt_dkey = NullStr;
144438494Sobrien    opt_keyd = key;
144538494Sobrien  } else {
144638494Sobrien    opt_keyd = strnsave(key, opt_dkey - key);
144738494Sobrien    opt_dkey++;
144838494Sobrien    if (*opt_dkey == '\0')	/* check for 'host.' */
144938494Sobrien      opt_dkey = NullStr;
145038494Sobrien  }
145138494Sobrien
145238494Sobrien  /*
145338494Sobrien   * Expand global options
145438494Sobrien   */
1455174294Sobrien  fs_static.fs_glob = expand_selectors(g_opts);
145638494Sobrien
145738494Sobrien  /*
145838494Sobrien   * Expand local options
145938494Sobrien   */
1460174294Sobrien  fs_static.fs_local = expand_selectors(opts);
146138494Sobrien
1462174294Sobrien  /* break global options into fs_static fields */
1463174294Sobrien  if ((ok = split_opts(fs_static.fs_glob, key))) {
1464174294Sobrien    dlog("global split_opts ok");
1465174294Sobrien    /*
1466174294Sobrien     * evaluate local selectors
1467174294Sobrien     */
1468174294Sobrien    if ((ok = eval_selectors(fs_static.fs_local, key))) {
1469174294Sobrien      dlog("local eval_selectors ok");
1470174294Sobrien      /* if the local selectors matched, then do the local overrides */
1471174294Sobrien      ok = split_opts(fs_static.fs_local, key);
1472174294Sobrien      if (ok)
1473174294Sobrien	dlog("local split_opts ok");
1474174294Sobrien    }
1475174294Sobrien  }
147638494Sobrien
147738494Sobrien  /*
147842629Sobrien   * Normalize remote host name.
147938494Sobrien   * 1.  Expand variables
148038494Sobrien   * 2.  Normalize relative to host tables
148138494Sobrien   * 3.  Strip local domains from the remote host
148238494Sobrien   *     name before using it in other expansions.
148338494Sobrien   *     This makes mount point names and other things
148438494Sobrien   *     much shorter, while allowing cross domain
148538494Sobrien   *     sharing of mount maps.
148638494Sobrien   */
148738494Sobrien  apply_opts(expand_opts, rhost_expansion, FALSE);
148838494Sobrien  if (ok && fs_static.opt_rhost && *fs_static.opt_rhost)
148938494Sobrien    host_normalize(&fs_static.opt_rhost);
149038494Sobrien
149138494Sobrien  /*
149238494Sobrien   * Macro expand the options.
149338494Sobrien   * Do this regardless of whether we are accepting
149438494Sobrien   * this mount - otherwise nasty things happen
149538494Sobrien   * with memory allocation.
149638494Sobrien   */
149738494Sobrien  apply_opts(expand_opts, expansions, FALSE);
149838494Sobrien
149938494Sobrien  /*
150038494Sobrien   * Strip trailing slashes from local pathname...
150138494Sobrien   */
150238494Sobrien  deslashify(fs_static.opt_fs);
150338494Sobrien
150438494Sobrien  /*
150538494Sobrien   * ok... copy the data back out.
150638494Sobrien   */
150738494Sobrien  *fo = fs_static;
150838494Sobrien
150938494Sobrien  /*
151038494Sobrien   * Clear defined options
151138494Sobrien   */
151238494Sobrien  if (opt_keyd != key && opt_keyd != nullstr)
151338494Sobrien    XFREE(opt_keyd);
151438494Sobrien  opt_keyd = nullstr;
151538494Sobrien  opt_dkey = NullStr;
151638494Sobrien  opt_key = opt_map = opt_path = nullstr;
151738494Sobrien
151838494Sobrien  return ok;
151938494Sobrien}
1520