1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright (c) 1996, by Sun Microsystems, Inc.
  24  * All Rights Reserved.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <locale.h>
  30 #include <sys/types.h>
  31 #include <stdio.h>
  32 #include <sys/param.h>
  33 #include <string.h>
  34 #include <unistd.h>
  35 #include <errno.h>
  36 #include <fcntl.h>
  37 #include <sys/stat.h>
  38 #include <stdlib.h>
  39 
  40 #include "rules.h"
  41 
  42 char * lex(FILE *);
  43 
  44 extern char *mstrdup(const char *);
  45 extern void *mmalloc(size_t size);
  46 
  47 void
  48 read_rules(FILE *file, int (*rulefunc)())
  49 {
  50         char *s;
  51         int base_active = 0;
  52         int list_ent_cnt = 0;
  53         int gign_ent_cnt = 0;
  54         int lign_ent_cnt = 0;
  55         struct item *add_item();
  56         struct item *fitem, *sitem;
  57         char version[20];
  58 
  59         last_gign = &gign_hd;
  60         gign_hd.i_next = (struct item *)0;
  61         gign_hd.i_str = (char *)0;
  62         list_hd.i_next = (struct item *)0;
  63         list_hd.i_str = (char *)0;
  64         while (s = lex(file)) {
  65                 if (s == (char *)0)
  66                         break;
  67                 if (*s == '#')
  68                         continue;
  69                 if (*s == '*')
  70                         continue;
  71                 if (strcmp(s, BASE) == 0) {
  72 #ifdef DEBUG
  73                         printf("BASE base_active = %d\n", base_active);
  74 #endif /* DEBUG */
  75                         if (base_active) {
  76                                 /*
  77                                  * Tack local IGNORE strings to end of globals
  78                                  */
  79                                 if (lign_hd.i_next != (struct item *)0) {
  80                                         last_gign->i_next = &lign_hd;
  81                                 }
  82                                 /*
  83                                  * Process directives for previous BASE command
  84                                  * if there was one. Also free up LIST items
  85                                  * and local IGNORE items.
  86                                  */
  87                                 do_base_dir(basedir, &list_hd, &gign_hd,
  88                                     rulefunc);
  89                                 /*
  90                                  * Free up space from LIST item list
  91                                  */
  92                                 fitem  = list_hd.i_next;
  93                                 if (fitem != (struct item *)0) {
  94                                         while (fitem != (struct item *)0) {
  95                                                 free(fitem->i_str);
  96                                                 sitem = fitem->i_next;
  97                                                 free(fitem);
  98                                                 fitem = sitem;
  99                                         }
 100                                 }
 101                                 /*
 102                                  * Free up space from local IGNORE item list
 103                                  */
 104                                 fitem  = lign_hd.i_next;
 105                                 if (fitem != (struct item *)0) {
 106                                         while (fitem != (struct item *)0) {
 107                                                 free(fitem->i_str);
 108                                                 sitem = fitem->i_next;
 109                                                 free(fitem);
 110                                                 fitem = sitem;
 111                                         }
 112                                 }
 113                                 last_gign->i_next = (struct item *)0;
 114                         }
 115                         base_active = 1;
 116                         /*
 117                          * Reset LIST item list and local IGNORE item
 118                          * list to be empty.
 119                          */
 120                         last_list = &list_hd;
 121                         list_hd.i_next = (struct item *)0;
 122                         list_hd.i_str = (char *)0;
 123                         last_lign = &lign_hd;
 124                         lign_hd.i_next = (struct item *)0;
 125                         lign_hd.i_str = (char *)0;
 126                         /*
 127                          * Get BASE directory specified
 128                          */
 129                         s = lex(0);
 130                         if (s == (char *)0) {
 131                                 fprintf(stderr, gettext("cachefspack: "));
 132                                 fprintf(stderr, gettext(
 133                                     "illegal BASE command\n"));
 134                                 return;
 135                         }
 136 
 137                         if (*s == '$') {
 138                                 /*
 139                                  * String starts with a '$', it must be an
 140                                  * environment variable
 141                                  */
 142                                 s = getenv(&s[1]);
 143                                 if (s == (char *)NULL) {
 144                                         fprintf(stderr,
 145                                             gettext("cachefspack: "));
 146                                         fprintf(stderr,
 147                                             gettext("Can't find "
 148                                             "environment variable\n"));
 149                                         exit(1);
 150                                 }
 151                         }
 152                         basedir = mstrdup(s);
 153 #ifdef DEBUG
 154                         printf("basedir = %s\n", basedir);
 155 #endif /* DEBUG */
 156                         continue;
 157                 }
 158                 if (strcmp(s, IGNORE) == 0) {
 159 #ifdef DEBUG
 160                         printf("IGNORE - base_active = %d\n", base_active);
 161 #endif /* DEBUG */
 162                         if (base_active) {
 163                                 /*
 164                                  * Local IGNORE rule
 165                                  */
 166                                 while ((s = lex(0))
 167                                     != 0) {
 168                                         last_lign = add_item(last_lign, s,
 169                                             def_lign_flags);
 170                                 }
 171                         } else {
 172                                 /*
 173                                  * Global IGNORE rule
 174                                  */
 175                                 while ((s = lex(0)) != 0) {
 176                                         last_gign = add_item(last_gign, s,
 177                                             def_gign_flags);
 178                                 }
 179                         }
 180                         continue;
 181                 }
 182                 if (strcmp(s, LIST) == 0) {
 183 #ifdef DEBUG
 184                         printf("LIST\n");
 185 #endif /* DEBUG */
 186                         if (!base_active) {
 187                                 fprintf(stderr,
 188                                     gettext(
 189                                     "cachefspack: skipping LIST command - "));
 190                                 fprintf(stderr,
 191                                     gettext(" no active base\n"));
 192                                 continue;
 193                         }
 194                         while ((s = lex(0)) != 0) {
 195                                 last_list = add_item(last_list, s,
 196                                     def_list_flags);
 197                         }
 198                         continue;
 199                 }
 200                 if (strcmp(s, VERSION) == 0) {
 201                         sprintf(version, "%d.%d", VERMAJOR, VERMINOR);
 202                         s = lex(0);
 203                         if (s == (char *)0) {
 204                                 fprintf(stderr, gettext("cachefspack: "));
 205                                 fprintf(stderr, gettext("missing version\n"));
 206                                 fprintf(stderr, gettext("cachefspack: "));
 207                                 fprintf(stderr, gettext(
 208                                     "version = %d.%d\n"), VERMAJOR, VERMINOR);
 209                                 exit(1);
 210                         }
 211                         if (strcmp(version, s) != 0) {
 212                                 fprintf(stderr, gettext(
 213                                     "cachefspack: "));
 214                                 fprintf(stderr, gettext(
 215                                     "WARNING - version of packing rules "));
 216                                 fprintf(stderr, gettext(
 217                                     "does not match cachefspack version\n"));
 218                                 fprintf(stderr, gettext(
 219                                     "version = %d.%d\n"), VERMAJOR, VERMINOR);
 220                         }
 221                 }
 222         }
 223         /*
 224          * Tack local IGNORE strings to end of globals
 225          */
 226         if (lign_hd.i_next != (struct item *)0) {
 227                 last_gign->i_next = &lign_hd;
 228         }
 229         do_base_dir(basedir, &list_hd, &gign_hd, rulefunc);
 230 }
 231 
 232 struct item *
 233 add_item(struct item *last_item, char *str, int flags)
 234 {
 235         struct item * add_cmd_items();
 236 
 237         if (*str == CMDCHAR) {
 238                 last_item = add_cmd_items(last_item, &str[1], bang_list_flags);
 239         } else {
 240                 last_item->i_next = (struct item *)mmalloc(
 241                     sizeof (struct item));
 242                 last_item = last_item->i_next;
 243                 last_item->i_str = mstrdup(str);
 244                 last_item->i_flag = flags;
 245                 last_item->i_next = (struct item *)0;
 246         }
 247         return (last_item);
 248 }
 249 
 250 struct item *
 251 add_cmd_items(struct item *last_item, char *str, int flags)
 252 {
 253         FILE *fd;
 254         char inbuf[MAX_RULE_SZ];
 255         char *olddir = NULL;
 256         char *s;
 257         void getcmd(char *, char *);
 258 
 259         if ((basedir != NULL) && (basedir[0] != '\0')) {
 260                 olddir = getcwd(NULL, MAXPATHLEN + 1);
 261                 if (olddir == NULL) {
 262                         fprintf(stderr, gettext("cannot malloc buffer\n"));
 263                         exit(1);
 264                 }
 265 
 266                 if (chdir(basedir) != 0) {
 267                         fprintf(stderr, gettext("cannot chdir to %s: %s\n"),
 268                             basedir, strerror(errno));
 269                         exit(1);
 270                 }
 271         }
 272 
 273         getcmd(str, inbuf);
 274         fd = popen(inbuf, "r");
 275         if (fd == NULL) {
 276                 fprintf(stderr, gettext("cachefspack: LIST can't execute - "));
 277                 fprintf(stderr, "%s\n", inbuf);
 278                 exit(1);
 279         }
 280 
 281         while (s = lex(fd)) {
 282                 last_item = add_item(last_item, s, flags);
 283                 while (s = lex(0)) {
 284                         last_item = add_item(last_item, s, flags);
 285                 }
 286         }
 287         if (pclose(fd) < 0) {
 288                 fprintf(stderr, gettext("cachefspack: can't close pipe\n"));
 289         }
 290 
 291         if (olddir != NULL) {
 292                 if (chdir(olddir) != 0) {
 293                         fprintf(stderr, gettext("cannot return to %s: %s\n"),
 294                             olddir, strerror(errno));
 295                         exit(1);
 296                 }
 297                 free(olddir);
 298         }
 299 
 300         return (last_item);
 301 }
 302 
 303 void
 304 getcmd(char *str, char *buf)
 305 {
 306         char *s;
 307 
 308         strcpy(buf, str);
 309         strcat(buf, " ");
 310         while (s = lex(0)) {
 311                 strcat(buf, s);
 312                 strcat(buf, " ");
 313         }
 314 #ifdef DEBUG
 315         printf("getcmd: cmd = %s\n", buf);
 316 #endif /* DEBUG */
 317 }
 318 
 319 /*
 320  * routine:
 321  *      lex
 322  *
 323  * purpose:
 324  *      my own version of strtok that handles quoting and escaping
 325  *
 326  * parameters:
 327  *      string to be lexed (or 0 for same string)
 328  *
 329  * returns:
 330  *      pointer to next token
 331  *
 332  * notes:
 333  *      this routine makes no changes to the string it is passed,
 334  *      copying tokens into a static buffer.
 335  */
 336 char *
 337 lex(FILE *fd)
 338 {       char c, delim;
 339         char *p;
 340         const char *s;
 341         static const char *savep = 0;
 342         static char namebuf[MAX_RULE_SZ];
 343         static char inbuf[MAX_RULE_SZ];
 344         int len, space_left;
 345         char *err;
 346 
 347         /*
 348          * if the file descriptor is non-zero read a new command. Otherwise
 349          * get fields from current line.
 350          */
 351         if (fd != 0) {
 352                 len = 0;
 353                 space_left = sizeof (inbuf);
 354                 while ((err = fgets(&inbuf[len], space_left, fd)) != NULL) {
 355                         len = strlen(inbuf);
 356                         if (len == 1) {
 357                                 /*
 358                                  * must be a blank line starting command.
 359                                  * If a blank line occurs after the start of
 360                                  * a command, blanks will be included in the
 361                                  * command.
 362                                  */
 363                                 len = 0;
 364                                 continue;
 365                         }
 366                         len -= 2;
 367                         space_left -= len;
 368                         s = (char *)((int)inbuf + len);
 369                         /*
 370                          * Continuation character
 371                          */
 372                         if (strcmp(s, "\\\n") == 0) {
 373                                 continue;
 374                         }
 375                         break;
 376                 }
 377                 if (err == NULL) {
 378                         return (err);
 379                 }
 380                 s = inbuf;
 381         } else {
 382                 if (savep == 0)
 383                         return (0);
 384                 s = savep;
 385         }
 386         savep = 0;
 387 
 388         /* skip over leading white space        */
 389         while (isspace(*s))
 390                 s++;
 391         if (*s == 0) {
 392                 return (0);
 393         }
 394 
 395         /* see if this is a quoted string       */
 396         c = *s;
 397         if (c == '\'' || c == '"') {
 398                 delim = c;
 399                 s++;
 400         } else
 401                 delim = 0;
 402 
 403         /* copy the token into the buffer       */
 404         for (p = namebuf; (c = *s) != 0; s++) {
 405                 if ((p - namebuf) >= sizeof (namebuf)) {
 406                         savep = 0;
 407                         return (0);
 408                 }
 409                 /* literal escape               */
 410                 if (c == '\\') {
 411                         s++;
 412                         *p++ = *s;
 413                         continue;
 414                 }
 415 
 416                 /* closing delimiter            */
 417                 if (c == delim) {
 418                         s++;
 419                         break;
 420                 }
 421 
 422                 /* delimiting white space       */
 423                 if (delim == 0 && isspace(c))
 424                         break;
 425 
 426                 /* ordinary characters          */
 427                 *p++ = *s;
 428         }
 429 
 430 
 431         /* remember where we left off           */
 432         savep = *s ? s : 0;
 433 
 434         /* null terminate and return the buffer */
 435         *p = 0;
 436         return (namebuf);
 437 }
 438 
 439 char *
 440 mk_base_dir(char *path, char *linkpath)
 441 {
 442         static char pathb[MAXPATHLEN];
 443         char *dnam;
 444         char *get_dirname(char *);
 445         int len;
 446 
 447         /*
 448          * absolute path name
 449          */
 450         if (*linkpath == '/') {
 451                 strcpy(pathb, linkpath);
 452         } else {
 453                 /*
 454                  * relative path
 455                  */
 456                 dnam = get_dirname(path);
 457                 if (dnam == (char *)0) {
 458                         return ((char *) 0);
 459                 }
 460                 strcpy(pathb, dnam);
 461                 len = strlen(pathb);
 462                 if (len == 0)
 463                         return (pathb);
 464                 if (pathb[len-1] != '/')
 465                     strcat(pathb, "/");
 466                 if (strncmp(linkpath, "../", 3) == 0) {
 467                         /*
 468                          * path is relative to directory containing sym link
 469                          * remove "../" from beginning of linkpath
 470                          */
 471                         strcat(pathb, &linkpath[3]);
 472                 } else {
 473                         /*
 474                          * path is relative to directory containing sym link
 475                          */
 476                         strcat(pathb, linkpath);
 477                 }
 478         }
 479         return (pathb);
 480 }