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