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 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <locale.h>
  30 #include <sys/types.h>
  31 #include <sys/param.h>
  32 #include <stdio.h>
  33 #include <string.h>
  34 #include <errno.h>
  35 #include <search.h>
  36 
  37 #include <dirent.h>
  38 #include <fnmatch.h>
  39 #include <sys/stat.h>
  40 
  41 #include "rules.h"
  42 
  43 extern char *mstrdup(const char *);
  44 
  45 /*
  46  * The do_base_dir() function is called when a BASE command is encountered
  47  * in the input directives or end-of-file is reach on the input directive
  48  * file. This function causes the commands associated with the previous
  49  * BASE command to be executed. for example,
  50  *
  51  *      BASE a/b/c
  52  *      LIST ...
  53  *      IGNORE ...
  54  *      BASE d/e/f (this command will cause do_base_dir() to be called for
  55  *                 base directory a/b/c)
  56  *
  57  * Input arguments:
  58  *      dirpath - the BASE directory being operated on
  59  *      incld_lst - A list of strings to be matched obtained from the
  60  *                  LIST commands associated with the BASE directory.
  61  *      excld_lst - A list of strings to be matched obtained from the
  62  *                  IGNORE commands associated with the BASE directory.
  63  *      func    - A function to be called for each matched file. The
  64  *                functions allow files to be  packed, unpacked,
  65  *                examined and filenames printed.
  66  */
  67 
  68 void
  69 do_base_dir(char *dirpath, struct item *incld_lst, struct item *excld_lst,
  70     int (*func)(char *, char *, DIR *, int))
  71 {
  72         struct item *iitem;
  73         int err;
  74         int files_processed = 0;
  75         struct item symlk_hd, *symlk, *symlk_sv;
  76         struct stat64 statbuf;
  77         char linkbuf[MAXPATHLEN];
  78         int sz;
  79         char *s;
  80         char *mk_base_dir(char *, char *);
  81 
  82 #ifdef DEBUG
  83         prtitem("Global IGNOREs", &gign_hd);
  84         prtitem("LIST cmds", &list_hd);
  85         prtitem("Local IGNOREs", &lign_hd);
  86 #endif /* DEBUG */
  87 
  88 
  89         symlk = &symlk_hd;
  90         symlk_hd.i_next = (struct item *)0;
  91 
  92         iitem  = incld_lst->i_next;
  93         if (iitem == (struct item *)0)
  94                 return;
  95         while (iitem != (struct item *)0) {
  96 #ifdef DEBUG
  97                 printf("do_base_dir: iitem->i_str = %s  iitem->i_flag = %x\n",
  98                     iitem->i_str, iitem->i_flag);
  99                 fflush(stdout);
 100 #endif /* DEBUG */
 101                 err = do_list_item(dirpath, iitem->i_str,
 102                     iitem->i_flag, excld_lst, 0, &symlk, func);
 103                 if (err == 0) {
 104                         fprintf(stderr,
 105                             gettext("cachefspack: basedir = %s"),
 106                             dirpath);
 107                         fprintf(stderr,
 108                             gettext("    %s - no file(s) selected\n"),
 109                             iitem->i_str);
 110                 }
 111                 iitem = iitem->i_next;
 112         };
 113         /*
 114          * Invoke 'func' for each component of the BASE
 115          * directory.
 116          */
 117         func_dir_path(dirpath, func);
 118 
 119         if (lstat64(dirpath, &statbuf) < 0) {
 120                 perror(gettext("Can't stat base directory"));
 121         } else {
 122                 if (S_ISLNK(statbuf.st_mode)) {
 123                         sz = readlink(dirpath, linkbuf, MAXPATHLEN-1);
 124                         if (sz > 0) {
 125                                 linkbuf[sz] = '\0';
 126                                 s = mk_base_dir(dirpath, linkbuf);
 127                                 if (s != (char *)0) {
 128                                         func_dir_path(s, func);
 129                                 }
 130                         }
 131                 }
 132         }
 133 
 134 #ifdef DEBUG
 135         prtitem("Symbolic Links", &symlk_hd);
 136 #endif /* DEBUG */
 137         iitem = symlk_hd.i_next;
 138         if (iitem == (struct item *)0)
 139                 return;
 140         while (iitem != (struct item *)0) {
 141 #ifdef DEBUG
 142                 printf("do_bas sl: iitem->i_str = %s  iitem->i_flag = %x\n",
 143                     iitem->i_str, iitem->i_flag);
 144                 fflush(stdout);
 145 #endif /* DEBUG */
 146                 files_processed = do_list_item(iitem->i_str, "*",
 147                     (LF_SYMLINK | LF_REGEX), excld_lst, 0, &symlk, func);
 148                 if (files_processed) {
 149                         /*
 150                          * Invoke 'func' for each component of the BASE
 151                          * directory.
 152                          */
 153                         func_dir_path(iitem->i_str, func);
 154                 }
 155                 symlk_sv = iitem;
 156                 iitem = iitem->i_next;
 157                 symlk_hd.i_next = iitem;
 158                 free(symlk_sv);
 159 #ifdef DEBUG
 160                 prtitem("Symbolic Links loop", &symlk_hd);
 161 #endif /* DEBUG */
 162         }
 163 }
 164 
 165 /*
 166  * The do_list_item() function is called for each LIST item associated with
 167  * a BASE directory. It does the work of descending directories and matching
 168  * filenames.
 169  *
 170  * Input arguments:
 171  *      dirpath - the BASE directory being operated on
 172  *      pat - The argument from the LIST command to match
 173  *      flags - Flags which affect how patterns are matched:
 174  *              LF_STRIP_DOTSLASH - means strip off "." and/or "/" at the
 175  *                                  beginning of the pattern to match.
 176  *              LF_REGEX - Means match the pattern as a regular expression.
 177  *                         Otherwise, an exact match of characters is required.
 178  *      excld_lst - A list of strings to be matched obtained from the
 179  *                  IGNORE commands associated with the BASE directory.
 180  *      func - A function to be called for each matched file. The
 181  *              functions allow files to be  packed, unpacked,
 182  *              examined and filenames printed.
 183  *
 184  * Return values:
 185  *      0 -  'func' NOT invoked for any file
 186  *      1 -  'func' invoked for at least 1 file
 187  */
 188 int
 189 do_list_item(char *dirpath, char *pat, int flags, struct item *excld_lst,
 190     DIR *pdir, struct item **symlk_lst, int (*func)(char *, char *, DIR *, int))
 191 {
 192         static char statnam[MAXPATHLEN];
 193         static int glastpos = 0;
 194         static int basedir_lastpos;
 195         static int depth = 0;
 196         static int unwind = 0;
 197         static int do_dir = 0;
 198         static int sl_cnt;
 199         static int retval;
 200         static char linkbuf[MAXPATHLEN];
 201         DIR *dir, *parent_dir;
 202         struct dirent64 *dirent;
 203         int match;
 204         int err;
 205         struct stat64 statbuf;
 206         int llastpos;
 207         struct item *eitem;
 208         int excld_flag;
 209         char *p;
 210         int diropn;
 211         int len;
 212         int sz;
 213         void process_symlk();
 214 
 215         strcpy(&statnam[glastpos], dirpath);
 216         len = strlen(statnam) - 1;
 217         if (statnam[len] != '/') {
 218                 strcat(statnam, "/");
 219         }
 220         parent_dir = pdir;
 221         llastpos = glastpos;
 222         glastpos = strlen(statnam);
 223         if (depth == 0) {
 224                 basedir_lastpos = glastpos;
 225                 sl_cnt = slash_cnt(pat);
 226                 retval = 0;
 227         }
 228         depth++;
 229 
 230         diropn = 0;
 231         dir = opendir(statnam);
 232         if (dir == NULL) {
 233                 fprintf(stderr, gettext("\ncachefspack: %s - "), statnam);
 234                 perror(gettext("Can't open directory"));
 235                 goto out;
 236         }
 237         diropn = 1;
 238 
 239         while (1) {
 240                 dirent = readdir64(dir);
 241                 if (dirent == NULL) { /* EOF */
 242                         if ((depth-1) > do_dir) {
 243                                 do_dir = depth - 1;
 244                         }
 245                         break;
 246                 }
 247                 /*
 248                  * If file is '..' skip it
 249                  */
 250                 if (strcmp(dirent->d_name, "..") == 0) {
 251                         continue;
 252                 }
 253                 /*
 254                  * Apply excludes if this is not a LISTed directory
 255                  * NOTE: names from IGNORE commands are matched against the
 256                  *       component name(a name between '/' marks), not the
 257                  *       whole pathname.
 258                  */
 259                 if (flags & LF_SYMLINK) {
 260                         match = ((depth-1) >= sl_cnt);
 261                 } else {
 262                         match = ((depth-1) > sl_cnt);
 263                 }
 264                 if (match) {
 265                         eitem = excld_lst->i_next;
 266                         excld_flag = 0;
 267                         while (eitem != (struct item *)0) {
 268                                 match = gmatch(dirent->d_name, eitem->i_str);
 269                                 if (match == 1) {
 270                                         excld_flag = 1;
 271                                         break;
 272                                 }
 273                                 eitem = eitem->i_next;
 274                         }
 275                         if (excld_flag == 1) {
 276                                 continue;
 277                         }
 278                 }
 279                 strcpy(&statnam[glastpos], dirent->d_name);
 280                 err = lstat64(statnam, &statbuf);
 281                 if (err < 0) {
 282                         fprintf(stderr,
 283                             gettext("cachefspack: %s - stat failed"),
 284                             statnam);
 285                         perror(gettext(" "));
 286                         continue;
 287                 }
 288                 p = pat;
 289                 if (flags & LF_STRIP_DOTSLASH) {
 290                         if (strncmp(p, "./", 2) == 0) {
 291                                 p += 2;
 292                         }
 293                 }
 294                 if (S_ISDIR(statbuf.st_mode)) {
 295 #ifdef DEBUG
 296                         printf("directory:  &statnam[basedir_lastpos] = %s\n",
 297                         &statnam[basedir_lastpos]);
 298                         printf("statbuf.st_mode = %o\n", statbuf.st_mode);
 299                         printf("depth = %d sl_cnt = %d\n", depth, sl_cnt);
 300                         fflush(stdout);
 301 #endif /* DEBUG */
 302                         if ((depth-1) == sl_cnt) {
 303                                 if (flags & LF_REGEX) {
 304                                         match =
 305                                             gmatch(&statnam[basedir_lastpos],
 306                                             p);
 307                                 } else {
 308                                         match =
 309                                             (strcmp(&statnam[basedir_lastpos],
 310                                             p) == 0);
 311                                 }
 312                                 if (match) {
 313                                         /*
 314                                          * Don't descend '.' directory
 315                                          * but match it
 316                                          */
 317                                         if (strcmp(dirent->d_name, ".") != 0) {
 318                                                 do_list_item(dirent->d_name,
 319                                                     "*", flags, excld_lst,
 320                                                     dir, symlk_lst, func);
 321                                         } else {
 322                                                 if ((depth-1) > do_dir) {
 323                                                         do_dir = depth - 1;
 324                                                 }
 325                                                 (void) func(statnam,
 326                                                     dirent->d_name,
 327                                                     dir, depth);
 328                                         }
 329                                         retval = 1;
 330                                         if (unwind = discont_srch(flags, p)) {
 331                                                 goto out;
 332                                         }
 333                                 }
 334                                 continue;
 335                         }
 336                         /*
 337                          * Don't descend '.' directory
 338                          */
 339                         if (strcmp(dirent->d_name, ".") != 0) {
 340                                 do_list_item(dirent->d_name, p, flags,
 341                                     excld_lst, dir, symlk_lst, func);
 342                         }
 343                         if (unwind) {
 344                                 goto out;
 345                         }
 346                         continue;
 347                 }
 348                 if (S_ISLNK(statbuf.st_mode)) {
 349                         if (flags & LF_SYMLINK)
 350                             continue;
 351 #ifdef DEBUG
 352                         printf("sym link : &statnam[basedir_lastpos] = %s\n",
 353                         &statnam[basedir_lastpos]);
 354                         printf("statbuf.st_mode = %o\n", statbuf.st_mode);
 355                         printf("statnam = %s\n", statnam);
 356 #endif /* DEBUG */
 357                         /*
 358                          * Symbolic link was explicitly specified or matches a
 359                          * regular expression in a LIST item. Thus we follow
 360                          * the link. Otherwise, just call 'func' for the link
 361                          * name.
 362                          */
 363 #ifdef DEBUG
 364                         printf("depth = %d  sl_cnt = %d\n", depth, sl_cnt);
 365                         fflush(stdout);
 366 #endif /* DEBUG */
 367                         if ((depth-1) == sl_cnt) {
 368                                 if (flags & LF_REGEX) {
 369                                         match =
 370                                             gmatch(&statnam[basedir_lastpos],
 371                                             p);
 372                                 } else {
 373                                         match =
 374                                             (strcmp(&statnam[basedir_lastpos],
 375                                             p) == 0);
 376                                 }
 377 #ifdef DEBUG
 378                                 printf("match = %d\n", match);
 379                                 fflush(stdout);
 380 #endif /* DEBUG */
 381                                 if (match) {
 382                                         if ((depth-1) > do_dir) {
 383                                                 do_dir = depth - 1;
 384                                         }
 385                                         retval = 1;
 386                                         (void) func(statnam, dirent->d_name,
 387                                             dir, depth);
 388                                         sz = readlink(
 389                                             statnam, linkbuf, MAXPATHLEN-1);
 390 #ifdef DEBUG
 391                                         printf("linkbuf = %s\n", linkbuf);
 392                                         printf("sz = %d\n", sz);
 393                                         fflush(stdout);
 394 #endif /* DEBUG */
 395                                         if (sz < 0) {
 396                                                 continue;
 397                                         }
 398                                         linkbuf[sz] = '\0';
 399                                         process_symlk(linkbuf, statnam,
 400                                             glastpos, symlk_lst, func);
 401                                         if (unwind = discont_srch(flags, p)) {
 402                                                 goto out;
 403                                         }
 404                                 }
 405                         }
 406                         if ((depth-1) > sl_cnt) {
 407                                 if ((depth-1) > do_dir) {
 408                                         do_dir = depth - 1;
 409                                 }
 410                                 retval = 1;
 411                                 (void) func(statnam, dirent->d_name, dir,
 412                                     depth);
 413                                 sz = readlink(statnam, linkbuf, MAXPATHLEN-1);
 414 #ifdef DEBUG
 415                                 printf("linkbuf = %s\n", linkbuf);
 416                                 printf("sz = %d\n", sz);
 417                                 fflush(stdout);
 418 #endif /* DEBUG */
 419                                 if (sz < 0) {
 420                                         continue;
 421                                 }
 422                                 linkbuf[sz] = '\0';
 423                                 process_symlk(linkbuf, statnam, glastpos,
 424                                     symlk_lst, func);
 425                                 if (unwind = discont_srch(flags, p)) {
 426                                         goto out;
 427                                 }
 428                         }
 429                         continue;
 430                 }
 431                 /*
 432                  * File must be a regular file -
 433                  * Does it match the specified pattern?
 434                  */
 435 #ifdef DEBUG
 436                 printf("reg file : &statnam[basedir_lastpos] = %s  p = %s\n",
 437                         &statnam[basedir_lastpos], p);
 438                 printf("statbuf.st_mode = %o\n", statbuf.st_mode);
 439                 fflush(stdout);
 440 #endif /* DEBUG */
 441                 if (flags & LF_REGEX) {
 442                         match = gmatch(&statnam[basedir_lastpos], p);
 443                 } else {
 444                         match = (strcmp(&statnam[basedir_lastpos], p) == 0);
 445                 }
 446                 if (!match) {
 447                         continue;
 448                 }
 449                 if ((depth - 1) > do_dir) {
 450                         do_dir = depth - 1;
 451                 }
 452                 retval = 1;
 453                 (void) func(statnam, dirent->d_name, dir, depth);
 454                 /*
 455                  * If the file is an executable, check to see if shared
 456                  * libraries need to be packed.
 457                  */
 458                 if (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
 459                         process_executable(statnam, func);
 460                 }
 461 
 462                 if (unwind = discont_srch(flags, p)) {
 463                         goto out;
 464                 }
 465         }
 466 out:
 467         depth--;
 468         if (depth == 0) {
 469                 unwind = 0;
 470         }
 471         statnam[glastpos] = '\0';
 472         if (do_dir) {
 473                 do_dir--;
 474 #ifdef DEBUG
 475                 printf("out:  call func\n");
 476                 fflush(stdout);
 477                 printf("out: statnam = %s\n", statnam);
 478                 fflush(stdout);
 479                 printf("out: &statnam[llastpos] = %s\n", &statnam[llastpos]);
 480                 fflush(stdout);
 481 #endif /* DEBUG */
 482                 if (func(statnam, &statnam[llastpos], parent_dir, depth) < 0) {
 483                         do_dir = 0;
 484                 }
 485         }
 486         glastpos = llastpos;
 487         if (diropn)
 488             closedir(dir);
 489         return (retval);
 490 }
 491 
 492 /*
 493  * Count all the '/' characters in the string except for those
 494  * in the first character position and last character position
 495  * of the string.
 496  */
 497 int
 498 slash_cnt(char *str)
 499 {
 500         char *p = str;
 501         int len;
 502         int i;
 503         int count = 0;
 504 
 505 #ifdef DEBUG
 506         printf("slash_cnt: str = %s", str);
 507 #endif /* DEBUG */
 508         /*
 509          * NOTE //a, /a and ./a are the same
 510          */
 511         if (*p == '.')
 512             p++;
 513         while (*p == '/')
 514             p++;
 515         len = strlen(str) - 1;
 516         for (i = 0; i < len; i++) {
 517                 if (*p == '/') {
 518                         count++;
 519                         i--;
 520                         while (*p == '/') {
 521                                 p++;
 522                                 i++;
 523                         }
 524                 } else {
 525                         p++;
 526                 }
 527         }
 528 #ifdef DEBUG
 529         printf("  count = %d\n", count);
 530         fflush(stdout);
 531 #endif /* DEBUG */
 532         return (count);
 533 }
 534 
 535 /*
 536  * For each directory in the path name, call 'func'.
 537  */
 538 int
 539 func_dir_path(char *path, int (*func)(char *, char *, DIR *, int))
 540 {
 541         char *dnam;
 542         char *fnam;
 543         char *pathtmp;
 544         DIR *dir;
 545         char *get_fname(char *);
 546         char *get_dirname(char *);
 547         ENTRY hitem, *hitemp;
 548 
 549 #ifdef DEBUG
 550         printf("func_dir_path: path = %s\n", path);
 551         fflush(stdout);
 552 #endif /* DEBUG */
 553         fnam = path;
 554         dnam = path;
 555         pathtmp = mstrdup(path);
 556         while (fnam != NULL) {
 557 
 558                 fnam = get_fname(dnam);
 559                 dnam = get_dirname(dnam);
 560                 if (fnam != (char *)0) {
 561                         if (strcmp(fnam, "..") == 0) {
 562                                 free(pathtmp);
 563                                 pathtmp = mstrdup(dnam);
 564                                 continue;
 565                         }
 566                 }
 567 #ifdef DEBUG
 568                 if (fnam != (char *)0) {
 569                         printf("func_dir_path: fnam = %s\n", fnam);
 570                 }
 571                 printf("func_dir_path: dnam = %s  pathtmp = %s\n",
 572                     dnam,  pathtmp);
 573                 fflush(stdout);
 574 #endif /* DEBUG */
 575 
 576                 hitem.key = mstrdup(pathtmp);
 577                 hitem.data = 0;
 578                 hitemp = hsearch(hitem, FIND);
 579                 if (hitemp != NULL) {
 580                         /*
 581                          * If hash item data is 0, item has not been packed.
 582                          * If hash item data is 1, item has been packed.
 583                          */
 584 #ifdef DEBUG
 585                         printf("func_dir_path: key = %s hitemp->data = %x\n",
 586                             hitemp->key, hitemp->data);
 587                         fflush(stdout);
 588 #endif /* DEBUG */
 589                         if (hitemp->data == (char *)1)
 590                             break;
 591                         hitemp->data = (char *)1;
 592                 } else {
 593                         hitem.key = mstrdup(pathtmp);
 594                         hitem.data = (char *)1;
 595                         if (hsearch(hitem, ENTER) == NULL) {
 596                                 fprintf(stderr,
 597                                     gettext("cachefspack: hash table full\n"));
 598                         }
 599                 }
 600 
 601                 dir = opendir(dnam);
 602                 if (dir != NULL) {
 603                         if (func(pathtmp, fnam, dir, 0) < 0) {
 604 #ifdef DEBUG
 605                                 printf("func_dir_path: errno = %d\n", errno);
 606                                 fflush(stdout);
 607 #endif /* DEBUG */
 608                                 closedir(dir);
 609                                 return (-1);
 610                         }
 611                         closedir(dir);
 612                 } else {
 613                         printf(gettext("cachefspack:  error opening dir -"));
 614                         printf("%s\n", dnam);
 615                         fflush(stdout);
 616                 }
 617 
 618                 free(pathtmp);
 619                 pathtmp = mstrdup(dnam);
 620         }
 621         free(pathtmp);
 622         return (0);
 623 }
 624 void
 625 process_symlk(char *lkpath, char *relpath, int rel_lastpos,
 626     struct item **symlk, int (*func)(char *, char *, DIR *, int))
 627 {
 628         struct stat64 lstatbuf;
 629         char *l;
 630         struct item *add_item(struct item *, char *, int);
 631         int len;
 632 
 633         /*
 634          * if the link has a relative pathname, append the name to
 635          * current path.
 636          */
 637         if (*lkpath != '/') {
 638                 len = strlen(lkpath);
 639                 if ((len + rel_lastpos + 2) > MAXPATHLEN) {
 640                         fprintf(stderr, gettext("can't process sym link - %s"),
 641                             lkpath);
 642                         return;
 643                 }
 644                 strcpy(&relpath[rel_lastpos], lkpath);
 645                 l = relpath;
 646         } else {
 647                 l = lkpath;
 648         }
 649 #ifdef DEBUG
 650         printf("process_symlk: lkpath = %s\n", lkpath);
 651         printf("process_symlk: l = %s\n", l);
 652         printf("lstatbuf.st_mode = %o\n", lstatbuf.st_mode);
 653         fflush(stdout);
 654 #endif /* DEBUG */
 655         if (lstat64(l, &lstatbuf) < 0) {
 656                 fprintf(stderr, gettext("Can't lstat sym link - %s"), l);
 657                 perror(" ");
 658                 return;
 659         }
 660         if (S_ISDIR(lstatbuf.st_mode)) {
 661                 *symlk = add_item(*symlk, l, 0);
 662         }
 663         if (S_ISREG(lstatbuf.st_mode)) {
 664                 func_dir_path(l, func);
 665         }
 666 }
 667 
 668 int
 669 discont_srch(int flags, char *pat)
 670 {
 671         char *wild;
 672 
 673 #ifdef DEBUG
 674         printf("discont_srch: flags = %x  pat = %s\n", flags, pat);
 675         fflush(stdout);
 676 #endif /* DEBUG */
 677 
 678         /*
 679          * if patterns are NOT being matched as regular expressions
 680          * we can have at most 1 match. We got it so quit.
 681          */
 682         if ((flags & LF_REGEX) != LF_REGEX) {
 683 #ifdef DEBUG
 684                 printf("discont_srch: ! LF_REGEX\n");
 685                 fflush(stdout);
 686 #endif /* DEBUG */
 687                 return (1);
 688         }
 689         /*
 690          * if the pattern does not contain wildcard characters and
 691          * we have found a match we are done.
 692          */
 693         if (WILDCARD(wild, pat) == NULL) {
 694 #ifdef DEBUG
 695                 printf("discont_srch: wild = %x\n", wild);
 696                 fflush(stdout);
 697 #endif /* DEBUG */
 698                 return (1);
 699         }
 700         return (0);
 701 }
 702 
 703 #ifdef DEBUG
 704 prtitem(char *str, struct item *hd)
 705 {
 706         struct item *p = hd->i_next;
 707 
 708         printf("\n%s\n\n", str);
 709         while (p != (struct item *)0) {
 710                 printf("str = %s\n", p->i_str);
 711                 p = p->i_next;
 712         }
 713 }
 714 #endif /* DEBUG */