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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 /*
  26  * @(#)files.cc 1.37 06/12/12
  27  */
  28 
  29 #pragma ident   "@(#)files.cc   1.37    06/12/12"
  30 
  31 /*
  32  *      files.c
  33  *
  34  *      Various file related routines:
  35  *              Figure out if file exists
  36  *              Wildcard resolution for directory reader
  37  *              Directory reader
  38  */
  39 
  40 
  41 /*
  42  * Included files
  43  */
  44 #if defined(SUN5_0) || defined(HP_UX)
  45 #include <dirent.h>               /* opendir() */
  46 #else
  47 #include <sys/dir.h>              /* opendir() */
  48 #endif
  49 #include <errno.h>                /* errno */
  50 #include <mk/defs.h>
  51 #include <mksh/macro.h>           /* getvar() */
  52 #include <mksh/misc.h>            /* get_prop(), append_prop() */
  53 #include <sys/stat.h>             /* lstat() */
  54 
  55 /*
  56  * Defined macros
  57  */
  58 
  59 /*
  60  * typedefs & structs
  61  */
  62 
  63 /*
  64  * Static variables
  65  */
  66 
  67 /*
  68  * File table of contents
  69  */
  70 extern  timestruc_t&        exists(register Name target);
  71 extern  void            set_target_stat(register Name target, struct stat buf);
  72 static  timestruc_t&        vpath_exists(register Name target);
  73 static  Name            enter_file_name(wchar_t *name_string, wchar_t *library);
  74 static  Boolean         star_match(register char *string, register char *pattern);
  75 static  Boolean         amatch(register wchar_t *string, register wchar_t *pattern);
  76   
  77 /*
  78  *      exists(target)
  79  *
  80  *      Figure out the timestamp for one target.
  81  *
  82  *      Return value:
  83  *                              The time the target was created
  84  *
  85  *      Parameters:
  86  *              target          The target to check
  87  *
  88  *      Global variables used:
  89  *              debug_level     Should we trace the stat call?
  90  *              recursion_level Used for tracing
  91  *              vpath_defined   Was the variable VPATH defined in environment?
  92  */
  93 timestruc_t&
  94 exists(register Name target)
  95 {
  96         struct stat             buf;
  97         register int            result;
  98 
  99         /* We cache stat information. */
 100         if (target->stat.time != file_no_time) {
 101                 return target->stat.time;
 102         }
 103 
 104         /*
 105          * If the target is a member, we have to extract the time
 106          * from the archive.
 107          */
 108         if (target->is_member &&
 109             (get_prop(target->prop, member_prop) != NULL)) {
 110                 return read_archive(target);
 111         }
 112 
 113         if (debug_level > 1) {
 114                 (void) printf(NOCATGETS("%*sstat(%s)\n"),
 115                               recursion_level,
 116                               "",
 117                               target->string_mb);
 118         }
 119 
 120         result = lstat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT);
 121         if ((result != -1) && ((buf.st_mode & S_IFMT) == S_IFLNK)) {
 122                 /*
 123                  * If the file is a symbolic link, we remember that
 124                  * and then we get the status for the refd file.
 125                  */
 126                 target->stat.is_sym_link = true;
 127                 result = stat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT);
 128         } else {
 129                 target->stat.is_sym_link = false;
 130         }
 131 
 132         if (result < 0) {
 133                 target->stat.time = file_doesnt_exist;
 134                 target->stat.stat_errno = errno;
 135                 if ((errno == ENOENT) &&
 136                     vpath_defined &&
 137 /* azv, fixing bug 1262942, VPATH works with a leaf name
 138  * but not a directory name.
 139  */
 140                     (target->string_mb[0] != (int) slash_char) ) {
 141 /* BID_1214655 */
 142 /* azv */
 143                         vpath_exists(target);
 144                         // return vpath_exists(target);
 145                 }
 146         } else {
 147                 /* Save all the information we need about the file */
 148                 target->stat.stat_errno = 0;
 149                 target->stat.is_file = true;
 150                 target->stat.mode = buf.st_mode & 0777;
 151                 target->stat.size = buf.st_size;
 152                 target->stat.is_dir =
 153                   BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR);
 154                 if (target->stat.is_dir) {
 155                         target->stat.time = file_is_dir;
 156                 } else {
 157                         /* target->stat.time = buf.st_mtime; */
 158 /* BID_1129806 */
 159 /* vis@nbsp.nsk.su */
 160 #if defined(linux)
 161                         timestruc_t ttime = { buf.st_mtime, 0 };
 162                         target->stat.time = MAX(ttime, file_min_time);
 163 #else
 164                         target->stat.time = MAX(buf.st_mtim, file_min_time);
 165 #endif
 166                 }
 167         }
 168         if ((target->colon_splits > 0) &&
 169             (get_prop(target->prop, time_prop) == NULL)) {
 170                 append_prop(target, time_prop)->body.time.time =
 171                   target->stat.time;
 172         }
 173         return target->stat.time;
 174 }
 175 
 176 /*
 177  *      set_target_stat( target, buf)
 178  *
 179  *      Called by exists() to set some stat fields in the Name structure
 180  *      to those read by the stat_vroot() call (from disk).
 181  *
 182  *      Parameters:
 183  *              target          The target whose stat field is set
 184  *              buf             stat values (on disk) of the file
 185  *                              represented by target.
 186  */
 187 void
 188 set_target_stat(register Name target, struct stat buf)
 189 {
 190         target->stat.stat_errno = 0;
 191         target->stat.is_file = true;
 192         target->stat.mode = buf.st_mode & 0777;
 193         target->stat.size = buf.st_size;
 194         target->stat.is_dir =
 195           BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR);
 196         if (target->stat.is_dir) {
 197                 target->stat.time = file_is_dir;
 198         } else {
 199                 /* target->stat.time = buf.st_mtime; */
 200 /* BID_1129806 */
 201 /* vis@nbsp.nsk.su */
 202 #if defined(linux)
 203                 timestruc_t ttime = { buf.st_mtime, 0 };
 204                 target->stat.time = ttime;
 205 #else
 206                 target->stat.time = MAX(buf.st_mtim, file_min_time);
 207 #endif
 208         }
 209 }
 210 
 211 
 212 /*
 213  *      vpath_exists(target)
 214  *
 215  *      Called if exists() discovers that there is a VPATH defined.
 216  *      This function stats the VPATH translation of the target.
 217  *
 218  *      Return value:
 219  *                              The time the target was created
 220  *
 221  *      Parameters:
 222  *              target          The target to check
 223  *
 224  *      Global variables used:
 225  *              vpath_name      The Name "VPATH", used to get macro value
 226  */
 227 static timestruc_t&
 228 vpath_exists(register Name target)
 229 {
 230         wchar_t                 *vpath;
 231         wchar_t                 file_name[MAXPATHLEN];
 232         wchar_t                 *name_p;
 233         Name                    alias;
 234 
 235         /*
 236          * To avoid recursive search through VPATH when exists(alias) is called
 237          */
 238         vpath_defined = false;
 239 
 240         Wstring wcb(getvar(vpath_name));
 241         Wstring wcb1(target);
 242 
 243         vpath = wcb.get_string();
 244 
 245         while (*vpath != (int) nul_char) {
 246                 name_p = file_name;
 247                 while ((*vpath != (int) colon_char) &&
 248                        (*vpath != (int) nul_char)) {
 249                         *name_p++ = *vpath++;
 250                 }
 251                 *name_p++ = (int) slash_char;
 252                 (void) wscpy(name_p, wcb1.get_string());
 253                 alias = GETNAME(file_name, FIND_LENGTH);
 254                 if (exists(alias) != file_doesnt_exist) {
 255                         target->stat.is_file = true;
 256                         target->stat.mode = alias->stat.mode;
 257                         target->stat.size = alias->stat.size;
 258                         target->stat.is_dir = alias->stat.is_dir;
 259                         target->stat.time = alias->stat.time;
 260                         maybe_append_prop(target, vpath_alias_prop)->
 261                                                 body.vpath_alias.alias = alias;
 262                         target->has_vpath_alias_prop = true;
 263                         vpath_defined = true;
 264                         return alias->stat.time;
 265                 }
 266                 while ((*vpath != (int) nul_char) &&
 267                        ((*vpath == (int) colon_char) || iswspace(*vpath))) {
 268                         vpath++;
 269                 }
 270         }
 271         /*
 272          * Restore vpath_defined
 273          */
 274         vpath_defined = true;
 275         return target->stat.time;
 276 }
 277 
 278 /*
 279  *      read_dir(dir, pattern, line, library)
 280  *
 281  *      Used to enter the contents of directories into makes namespace.
 282  *      Presence of a file is important when scanning for implicit rules.
 283  *      read_dir() is also used to expand wildcards in dependency lists.
 284  *
 285  *      Return value:
 286  *                              Non-0 if we found files to match the pattern
 287  *
 288  *      Parameters:
 289  *              dir             Path to the directory to read
 290  *              pattern         Pattern for that files should match or NULL
 291  *              line            When we scan using a pattern we enter files
 292  *                              we find as dependencies for this line
 293  *              library         If we scan for "lib.a(<wildcard-member>)"
 294  *
 295  *      Global variables used:
 296  *              debug_level     Should we trace the dir reading?
 297  *              dot             The Name ".", compared against
 298  *              sccs_dir_path   The path to the SCCS dir (from PROJECTDIR)
 299  *              vpath_defined   Was the variable VPATH defined in environment?
 300  *              vpath_name      The Name "VPATH", use to get macro value
 301  */
 302 int
 303 read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library)
 304 {
 305         wchar_t                 file_name[MAXPATHLEN];
 306         wchar_t                 *file_name_p = file_name;
 307         Name                    file;
 308         wchar_t                 plain_file_name[MAXPATHLEN];
 309         wchar_t                 *plain_file_name_p;
 310         Name                    plain_file;
 311         wchar_t                 tmp_wcs_buffer[MAXPATHLEN];
 312         DIR                     *dir_fd;
 313         int                     m_local_dependency=0;
 314 #if defined(SUN5_0) || defined(HP_UX)
 315 #define d_fileno d_ino
 316         register struct dirent  *dp;
 317 #else
 318         register struct direct  *dp;
 319 #endif
 320         wchar_t                 *vpath = NULL;
 321         wchar_t                 *p;
 322         int                     result = 0;
 323 
 324         if(dir->hash.length >= MAXPATHLEN) {
 325                 return 0;
 326         }
 327 
 328         Wstring wcb(dir);
 329         Wstring vps;
 330 
 331         /* A directory is only read once unless we need to expand wildcards. */
 332         if (pattern == NULL) {
 333                 if (dir->has_read_dir) {
 334                         return 0;
 335                 }
 336                 dir->has_read_dir = true;
 337         }
 338         /* Check if VPATH is active and setup list if it is. */
 339         if (vpath_defined && (dir == dot)) {
 340                 vps.init(getvar(vpath_name));
 341                 vpath = vps.get_string();
 342         }
 343 
 344         /*
 345          * Prepare the string where we build the full name of the
 346          * files in the directory.
 347          */
 348         if ((dir->hash.length > 1) || (wcb.get_string()[0] != (int) period_char)) {
 349                 (void) wscpy(file_name, wcb.get_string());
 350                 MBSTOWCS(wcs_buffer, "/");
 351                 (void) wscat(file_name, wcs_buffer);
 352                 file_name_p = file_name + wslen(file_name);
 353         }
 354 
 355         /* Open the directory. */
 356 vpath_loop:
 357         dir_fd = opendir(dir->string_mb);
 358         if (dir_fd == NULL) {
 359                 return 0;
 360         }
 361 
 362         /* Read all the directory entries. */
 363         while ((dp = readdir(dir_fd)) != NULL) {
 364                 /* We ignore "." and ".." */
 365                 if ((dp->d_fileno == 0) ||
 366                     ((dp->d_name[0] == (int) period_char) &&
 367                      ((dp->d_name[1] == 0) ||
 368                       ((dp->d_name[1] == (int) period_char) &&
 369                        (dp->d_name[2] == 0))))) {
 370                         continue;
 371                 }
 372                 /*
 373                  * Build the full name of the file using whatever
 374                  * path supplied to the function.
 375                  */
 376                 MBSTOWCS(tmp_wcs_buffer, dp->d_name);
 377                 (void) wscpy(file_name_p, tmp_wcs_buffer);
 378                 file = enter_file_name(file_name, library);
 379                 if ((pattern != NULL) && amatch(tmp_wcs_buffer, pattern)) {
 380                         /*
 381                          * If we are expanding a wildcard pattern, we
 382                          * enter the file as a dependency for the target.
 383                          */
 384                         if (debug_level > 0){
 385                                 WCSTOMBS(mbs_buffer, pattern);
 386                                 (void) printf(catgets(catd, 1, 231, "'%s: %s' due to %s expansion\n"),
 387                                               line->body.line.target->string_mb,
 388                                               file->string_mb,
 389                                               mbs_buffer);
 390                         }
 391                         enter_dependency(line, file, false);
 392                         result++;
 393                 } else {
 394                         /*
 395                          * If the file has an SCCS/s. file,
 396                          * we will detect that later on.
 397                          */
 398                         file->stat.has_sccs = NO_SCCS;
 399                 /*
 400                  * If this is an s. file, we also enter it as if it
 401                  * existed in the plain directory.
 402                  */
 403                 if ((dp->d_name[0] == 's') &&
 404                     (dp->d_name[1] == (int) period_char)) {
 405         
 406                         MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2);
 407                         plain_file_name_p = plain_file_name;
 408                         (void) wscpy(plain_file_name_p, tmp_wcs_buffer);
 409                         plain_file = GETNAME(plain_file_name, FIND_LENGTH);
 410                         plain_file->stat.is_file = true;
 411                         plain_file->stat.has_sccs = HAS_SCCS;
 412                         /*
 413                          * Enter the s. file as a dependency for the
 414                          * plain file.
 415                          */
 416                         maybe_append_prop(plain_file, sccs_prop)->
 417                           body.sccs.file = file;
 418                         MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2);
 419                         if ((pattern != NULL) &&
 420                             amatch(tmp_wcs_buffer, pattern)) {
 421                                 if (debug_level > 0) {
 422                                         WCSTOMBS(mbs_buffer, pattern);
 423                                         (void) printf(catgets(catd, 1, 232, "'%s: %s' due to %s expansion\n"),
 424                                                       line->body.line.target->
 425                                                       string_mb,
 426                                                       plain_file->string_mb,
 427                                                       mbs_buffer);
 428                                 }
 429                                 enter_dependency(line, plain_file, false);
 430                                 result++;
 431                         }
 432                 }
 433               }
 434         }
 435         (void) closedir(dir_fd);
 436         if ((vpath != NULL) && (*vpath != (int) nul_char)) {
 437                 while ((*vpath != (int) nul_char) &&
 438                        (iswspace(*vpath) || (*vpath == (int) colon_char))) {
 439                         vpath++;
 440                 }
 441                 p = vpath;
 442                 while ((*vpath != (int) colon_char) &&
 443                        (*vpath != (int) nul_char)) {
 444                         vpath++;
 445                 }
 446                 if (vpath > p) {
 447                         dir = GETNAME(p, vpath - p);
 448                         goto vpath_loop;
 449                 }
 450         }
 451 /*
 452  * look into SCCS directory only if it's not svr4. For svr4 dont do that.
 453  */
 454 
 455 /*
 456  * Now read the SCCS directory.
 457  * Files in the SCSC directory are considered to be part of the set of
 458  * files in the plain directory. They are also entered in their own right.
 459  * Prepare the string where we build the true name of the SCCS files.
 460  */
 461         (void) wsncpy(plain_file_name,
 462                       file_name,
 463                       file_name_p - file_name);
 464         plain_file_name[file_name_p - file_name] = 0;
 465         plain_file_name_p = plain_file_name + wslen(plain_file_name);
 466 
 467         if(!svr4) {
 468 
 469           if (sccs_dir_path != NULL) {
 470                 wchar_t         tmp_wchar;
 471                 wchar_t         path[MAXPATHLEN];
 472                 char            mb_path[MAXPATHLEN];
 473 
 474                 if (file_name_p - file_name > 0) {
 475                         tmp_wchar = *file_name_p;
 476                         *file_name_p = 0;
 477                         WCSTOMBS(mbs_buffer, file_name);
 478                         (void) sprintf(mb_path, NOCATGETS("%s/%s/SCCS"),
 479                                         sccs_dir_path,
 480                                         mbs_buffer);
 481                         *file_name_p = tmp_wchar;
 482                 } else {
 483                         (void) sprintf(mb_path, NOCATGETS("%s/SCCS"), sccs_dir_path);
 484                 }
 485                 MBSTOWCS(path, mb_path);
 486                 (void) wscpy(file_name, path);
 487           } else {
 488                 MBSTOWCS(wcs_buffer, NOCATGETS("SCCS"));
 489                 (void) wscpy(file_name_p, wcs_buffer);
 490           }
 491         } else {
 492                 MBSTOWCS(wcs_buffer, NOCATGETS("."));
 493                 (void) wscpy(file_name_p, wcs_buffer);
 494         }
 495         /* Internalize the constructed SCCS dir name. */
 496         (void) exists(dir = GETNAME(file_name, FIND_LENGTH));
 497         /* Just give up if the directory file doesnt exist. */
 498         if (!dir->stat.is_file) {
 499                 return result;
 500         }
 501         /* Open the directory. */
 502         dir_fd = opendir(dir->string_mb);
 503         if (dir_fd == NULL) {
 504                 return result;
 505         }
 506         MBSTOWCS(wcs_buffer, "/");
 507         (void) wscat(file_name, wcs_buffer);
 508         file_name_p = file_name + wslen(file_name);
 509 
 510         while ((dp = readdir(dir_fd)) != NULL) {
 511                 if ((dp->d_fileno == 0) ||
 512                     ((dp->d_name[0] == (int) period_char) &&
 513                      ((dp->d_name[1] == 0) ||
 514                       ((dp->d_name[1] == (int) period_char) &&
 515                        (dp->d_name[2] == 0))))) {
 516                         continue;
 517                 }
 518                 /* Construct and internalize the true name of the SCCS file. */
 519                 MBSTOWCS(wcs_buffer, dp->d_name);
 520                 (void) wscpy(file_name_p, wcs_buffer);
 521                 file = GETNAME(file_name, FIND_LENGTH);
 522                 file->stat.is_file = true;
 523                 file->stat.has_sccs = NO_SCCS;
 524                 /*
 525                  * If this is an s. file, we also enter it as if it
 526                  * existed in the plain directory.
 527                  */
 528                 if ((dp->d_name[0] == 's') &&
 529                     (dp->d_name[1] == (int) period_char)) {
 530         
 531                         MBSTOWCS(wcs_buffer, dp->d_name + 2);
 532                         (void) wscpy(plain_file_name_p, wcs_buffer);
 533                         plain_file = GETNAME(plain_file_name, FIND_LENGTH);
 534                         plain_file->stat.is_file = true;
 535                         plain_file->stat.has_sccs = HAS_SCCS;
 536                                 /* if sccs dependency is already set,skip */
 537                         if(plain_file->prop) {
 538                                 Property sprop = get_prop(plain_file->prop,sccs_prop);
 539                                 if(sprop != NULL) {
 540                                         if (sprop->body.sccs.file) {
 541                                                 goto try_pattern;
 542                                         }
 543                                 }
 544                         }
 545 
 546                         /*
 547                          * Enter the s. file as a dependency for the
 548                          * plain file.
 549                          */
 550                         maybe_append_prop(plain_file, sccs_prop)->
 551                           body.sccs.file = file;
 552 try_pattern:
 553                         MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2);
 554                         if ((pattern != NULL) &&
 555                             amatch(tmp_wcs_buffer, pattern)) {
 556                                 if (debug_level > 0) {
 557                                         WCSTOMBS(mbs_buffer, pattern);
 558                                         (void) printf(catgets(catd, 1, 233, "'%s: %s' due to %s expansion\n"),
 559                                                       line->body.line.target->
 560                                                       string_mb,
 561                                                       plain_file->string_mb,
 562                                                       mbs_buffer);
 563                                 }
 564                                 enter_dependency(line, plain_file, false);
 565                                 result++;
 566                         }
 567                 }
 568         }
 569         (void) closedir(dir_fd);
 570 
 571         return result;
 572 }
 573 
 574 /*
 575  *      enter_file_name(name_string, library)
 576  *
 577  *      Helper function for read_dir().
 578  *
 579  *      Return value:
 580  *                              The Name that was entered
 581  *
 582  *      Parameters:
 583  *              name_string     Name of the file we want to enter
 584  *              library         The library it is a member of, if any
 585  *
 586  *      Global variables used:
 587  */
 588 static Name
 589 enter_file_name(wchar_t *name_string, wchar_t *library)
 590 {
 591         wchar_t         buffer[STRING_BUFFER_LENGTH];
 592         String_rec      lib_name;
 593         Name            name;
 594         Property        prop;
 595 
 596         if (library == NULL) {
 597                 name = GETNAME(name_string, FIND_LENGTH);
 598                 name->stat.is_file = true;
 599                 return name;
 600         }
 601 
 602         INIT_STRING_FROM_STACK(lib_name, buffer);
 603         append_string(library, &lib_name, FIND_LENGTH);
 604         append_char((int) parenleft_char, &lib_name);
 605         append_string(name_string, &lib_name, FIND_LENGTH);
 606         append_char((int) parenright_char, &lib_name);
 607 
 608         name = GETNAME(lib_name.buffer.start, FIND_LENGTH);
 609         name->stat.is_file = true;
 610         name->is_member = true;
 611         prop = maybe_append_prop(name, member_prop);
 612         prop->body.member.library = GETNAME(library, FIND_LENGTH);
 613         prop->body.member.library->stat.is_file = true;
 614         prop->body.member.entry = NULL;
 615         prop->body.member.member = GETNAME(name_string, FIND_LENGTH);
 616         prop->body.member.member->stat.is_file = true;
 617         return name;
 618 }
 619 
 620 /*
 621  *      star_match(string, pattern)
 622  *
 623  *      This is a regular shell type wildcard pattern matcher
 624  *      It is used when xpanding wildcards in dependency lists
 625  *
 626  *      Return value:
 627  *                              Indication if the string matched the pattern
 628  *
 629  *      Parameters:
 630  *              string          String to match
 631  *              pattern         Pattern to match it against
 632  *
 633  *      Global variables used:
 634  */
 635 static Boolean
 636 star_match(register wchar_t *string, register wchar_t *pattern)
 637 {
 638         register int            pattern_ch;
 639 
 640         switch (*pattern) {
 641         case 0:
 642                 return succeeded;
 643         case bracketleft_char:
 644         case question_char:
 645         case asterisk_char:
 646                 while (*string) {
 647                         if (amatch(string++, pattern)) {
 648                                 return succeeded;
 649                         }
 650                 }
 651                 break;
 652         default:
 653                 pattern_ch = (int) *pattern++;
 654                 while (*string) {
 655                         if ((*string++ == pattern_ch) &&
 656                             amatch(string, pattern)) {
 657                                 return succeeded;
 658                         }
 659                 }
 660                 break;
 661         }
 662         return failed;
 663 }
 664 
 665 /*
 666  *      amatch(string, pattern)
 667  *
 668  *      Helper function for shell pattern matching
 669  *
 670  *      Return value:
 671  *                              Indication if the string matched the pattern
 672  *
 673  *      Parameters:
 674  *              string          String to match
 675  *              pattern         Pattern to match it against
 676  *
 677  *      Global variables used:
 678  */
 679 static Boolean
 680 amatch(register wchar_t *string, register wchar_t *pattern)
 681 {
 682         register long           lower_bound;
 683         register long           string_ch;
 684         register long           pattern_ch;
 685         register int            k;
 686 
 687 top:
 688         for (; 1; pattern++, string++) {
 689                 lower_bound = 017777777777;
 690                 string_ch = *string;
 691                 switch (pattern_ch = *pattern) {
 692                 case bracketleft_char:
 693                         k = 0;
 694                         while ((pattern_ch = *++pattern) != 0) {
 695                                 switch (pattern_ch) {
 696                                 case bracketright_char:
 697                                         if (!k) {
 698                                                 return failed;
 699                                         }
 700                                         string++;
 701                                         pattern++;
 702                                         goto top;
 703                                 case hyphen_char:
 704                                         k |= (lower_bound <= string_ch) &&
 705                                              (string_ch <=
 706                                               (pattern_ch = pattern[1]));
 707                                 default:
 708                                         if (string_ch ==
 709                                             (lower_bound = pattern_ch)) {
 710                                                 k++;
 711                                         }
 712                                 }
 713                         }
 714                         return failed;
 715                 case asterisk_char:
 716                         return star_match(string, ++pattern);
 717                 case 0:
 718                         return BOOLEAN(!string_ch);
 719                 case question_char:
 720                         if (string_ch == 0) {
 721                                 return failed;
 722                         }
 723                         break;
 724                 default:
 725                         if (pattern_ch != string_ch) {
 726                                 return failed;
 727                         }
 728                         break;
 729                 }
 730         }
 731         /* NOTREACHED */
 732 }
 733