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