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