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