/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2019 RackTop Systems. */ /* * files.c * * Various file related routines: * Figure out if file exists * Wildcard resolution for directory reader * Directory reader */ /* * Included files */ #include /* opendir() */ #include /* errno */ #include #include /* getvar() */ #include /* get_prop(), append_prop() */ #include /* lstat() */ #include /* * Defined macros */ /* * typedefs & structs */ /* * Static variables */ /* * File table of contents */ extern timestruc_t& exists(Name target); extern void set_target_stat(Name target, struct stat buf); static timestruc_t& vpath_exists(Name target); static Name enter_file_name(wchar_t *name_string, wchar_t *library); static Boolean star_match(char *string, char *pattern); static Boolean amatch(wchar_t *string, wchar_t *pattern); /* * exists(target) * * Figure out the timestamp for one target. * * Return value: * The time the target was created * * Parameters: * target The target to check * * Global variables used: * debug_level Should we trace the stat call? * recursion_level Used for tracing * vpath_defined Was the variable VPATH defined in environment? */ timestruc_t& exists(Name target) { struct stat buf; int result; /* We cache stat information. */ if (target->stat.time != file_no_time) { return target->stat.time; } /* * If the target is a member, we have to extract the time * from the archive. */ if (target->is_member && (get_prop(target->prop, member_prop) != NULL)) { return read_archive(target); } if (debug_level > 1) { (void) printf("%*sstat(%s)\n", recursion_level, "", target->string_mb); } result = lstat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT); if ((result != -1) && ((buf.st_mode & S_IFMT) == S_IFLNK)) { /* * If the file is a symbolic link, we remember that * and then we get the status for the refd file. */ target->stat.is_sym_link = true; result = stat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT); } else { target->stat.is_sym_link = false; } if (result < 0) { target->stat.time = file_doesnt_exist; target->stat.stat_errno = errno; if ((errno == ENOENT) && vpath_defined && /* azv, fixing bug 1262942, VPATH works with a leaf name * but not a directory name. */ (target->string_mb[0] != (int) slash_char) ) { /* BID_1214655 */ /* azv */ vpath_exists(target); // return vpath_exists(target); } } else { /* Save all the information we need about the file */ target->stat.stat_errno = 0; target->stat.is_file = true; target->stat.mode = buf.st_mode & 0777; target->stat.size = buf.st_size; target->stat.is_dir = BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR); if (target->stat.is_dir) { target->stat.time = file_is_dir; } else { /* target->stat.time = buf.st_mtime; */ /* BID_1129806 */ /* vis@nbsp.nsk.su */ target->stat.time = MAX(buf.st_mtim, file_min_time); } } if ((target->colon_splits > 0) && (get_prop(target->prop, time_prop) == NULL)) { append_prop(target, time_prop)->body.time.time = target->stat.time; } return target->stat.time; } /* * set_target_stat( target, buf) * * Called by exists() to set some stat fields in the Name structure * to those read by the stat_vroot() call (from disk). * * Parameters: * target The target whose stat field is set * buf stat values (on disk) of the file * represented by target. */ void set_target_stat(Name target, struct stat buf) { target->stat.stat_errno = 0; target->stat.is_file = true; target->stat.mode = buf.st_mode & 0777; target->stat.size = buf.st_size; target->stat.is_dir = BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR); if (target->stat.is_dir) { target->stat.time = file_is_dir; } else { /* target->stat.time = buf.st_mtime; */ /* BID_1129806 */ /* vis@nbsp.nsk.su */ target->stat.time = MAX(buf.st_mtim, file_min_time); } } /* * vpath_exists(target) * * Called if exists() discovers that there is a VPATH defined. * This function stats the VPATH translation of the target. * * Return value: * The time the target was created * * Parameters: * target The target to check * * Global variables used: * vpath_name The Name "VPATH", used to get macro value */ static timestruc_t& vpath_exists(Name target) { wchar_t *vpath; wchar_t file_name[MAXPATHLEN]; wchar_t *name_p; Name alias; /* * To avoid recursive search through VPATH when exists(alias) is called */ vpath_defined = false; Wstring wcb(getvar(vpath_name)); Wstring wcb1(target); vpath = wcb.get_string(); while (*vpath != (int) nul_char) { name_p = file_name; while ((*vpath != (int) colon_char) && (*vpath != (int) nul_char)) { *name_p++ = *vpath++; } *name_p++ = (int) slash_char; (void) wcscpy(name_p, wcb1.get_string()); alias = GETNAME(file_name, FIND_LENGTH); if (exists(alias) != file_doesnt_exist) { target->stat.is_file = true; target->stat.mode = alias->stat.mode; target->stat.size = alias->stat.size; target->stat.is_dir = alias->stat.is_dir; target->stat.time = alias->stat.time; maybe_append_prop(target, vpath_alias_prop)-> body.vpath_alias.alias = alias; target->has_vpath_alias_prop = true; vpath_defined = true; return alias->stat.time; } while ((*vpath != (int) nul_char) && ((*vpath == (int) colon_char) || iswspace(*vpath))) { vpath++; } } /* * Restore vpath_defined */ vpath_defined = true; return target->stat.time; } /* * read_dir(dir, pattern, line, library) * * Used to enter the contents of directories into makes namespace. * Presence of a file is important when scanning for implicit rules. * read_dir() is also used to expand wildcards in dependency lists. * * Return value: * Non-0 if we found files to match the pattern * * Parameters: * dir Path to the directory to read * pattern Pattern for that files should match or NULL * line When we scan using a pattern we enter files * we find as dependencies for this line * library If we scan for "lib.a()" * * Global variables used: * debug_level Should we trace the dir reading? * dot The Name ".", compared against * sccs_dir_path The path to the SCCS dir (from PROJECTDIR) * vpath_defined Was the variable VPATH defined in environment? * vpath_name The Name "VPATH", use to get macro value */ int read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library) { wchar_t file_name[MAXPATHLEN]; wchar_t *file_name_p = file_name; Name file; wchar_t plain_file_name[MAXPATHLEN]; wchar_t *plain_file_name_p; Name plain_file; wchar_t tmp_wcs_buffer[MAXPATHLEN]; DIR *dir_fd; int m_local_dependency=0; #define d_fileno d_ino struct dirent *dp; wchar_t *vpath = NULL; wchar_t *p; int result = 0; if(dir->hash.length >= MAXPATHLEN) { return 0; } Wstring wcb(dir); Wstring vps; /* A directory is only read once unless we need to expand wildcards. */ if (pattern == NULL) { if (dir->has_read_dir) { return 0; } dir->has_read_dir = true; } /* Check if VPATH is active and setup list if it is. */ if (vpath_defined && (dir == dot)) { vps.init(getvar(vpath_name)); vpath = vps.get_string(); } /* * Prepare the string where we build the full name of the * files in the directory. */ if ((dir->hash.length > 1) || (wcb.get_string()[0] != (int) period_char)) { (void) wcscpy(file_name, wcb.get_string()); MBSTOWCS(wcs_buffer, "/"); (void) wcscat(file_name, wcs_buffer); file_name_p = file_name + wcslen(file_name); } /* Open the directory. */ vpath_loop: dir_fd = opendir(dir->string_mb); if (dir_fd == NULL) { return 0; } /* Read all the directory entries. */ while ((dp = readdir(dir_fd)) != NULL) { /* We ignore "." and ".." */ if ((dp->d_fileno == 0) || ((dp->d_name[0] == (int) period_char) && ((dp->d_name[1] == 0) || ((dp->d_name[1] == (int) period_char) && (dp->d_name[2] == 0))))) { continue; } /* * Build the full name of the file using whatever * path supplied to the function. */ MBSTOWCS(tmp_wcs_buffer, dp->d_name); (void) wcscpy(file_name_p, tmp_wcs_buffer); file = enter_file_name(file_name, library); if ((pattern != NULL) && amatch(tmp_wcs_buffer, pattern)) { /* * If we are expanding a wildcard pattern, we * enter the file as a dependency for the target. */ if (debug_level > 0){ WCSTOMBS(mbs_buffer, pattern); (void) printf(gettext("'%s: %s' due to %s expansion\n"), line->body.line.target->string_mb, file->string_mb, mbs_buffer); } enter_dependency(line, file, false); result++; } else { /* * If the file has an SCCS/s. file, * we will detect that later on. */ file->stat.has_sccs = NO_SCCS; /* * If this is an s. file, we also enter it as if it * existed in the plain directory. */ if ((dp->d_name[0] == 's') && (dp->d_name[1] == (int) period_char)) { MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); plain_file_name_p = plain_file_name; (void) wcscpy(plain_file_name_p, tmp_wcs_buffer); plain_file = GETNAME(plain_file_name, FIND_LENGTH); plain_file->stat.is_file = true; plain_file->stat.has_sccs = HAS_SCCS; /* * Enter the s. file as a dependency for the * plain file. */ maybe_append_prop(plain_file, sccs_prop)-> body.sccs.file = file; MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); if ((pattern != NULL) && amatch(tmp_wcs_buffer, pattern)) { if (debug_level > 0) { WCSTOMBS(mbs_buffer, pattern); (void) printf(gettext("'%s: %s' due to %s expansion\n"), line->body.line.target-> string_mb, plain_file->string_mb, mbs_buffer); } enter_dependency(line, plain_file, false); result++; } } } } (void) closedir(dir_fd); if ((vpath != NULL) && (*vpath != (int) nul_char)) { while ((*vpath != (int) nul_char) && (iswspace(*vpath) || (*vpath == (int) colon_char))) { vpath++; } p = vpath; while ((*vpath != (int) colon_char) && (*vpath != (int) nul_char)) { vpath++; } if (vpath > p) { dir = GETNAME(p, vpath - p); goto vpath_loop; } } /* * look into SCCS directory only if it's not svr4. For svr4 dont do that. */ /* * Now read the SCCS directory. * Files in the SCSC directory are considered to be part of the set of * files in the plain directory. They are also entered in their own right. * Prepare the string where we build the true name of the SCCS files. */ (void) wcsncpy(plain_file_name, file_name, file_name_p - file_name); plain_file_name[file_name_p - file_name] = 0; plain_file_name_p = plain_file_name + wcslen(plain_file_name); if(!svr4) { if (sccs_dir_path != NULL) { wchar_t tmp_wchar; wchar_t path[MAXPATHLEN]; char mb_path[MAXPATHLEN]; if (file_name_p - file_name > 0) { tmp_wchar = *file_name_p; *file_name_p = 0; WCSTOMBS(mbs_buffer, file_name); (void) sprintf(mb_path, "%s/%s/SCCS", sccs_dir_path, mbs_buffer); *file_name_p = tmp_wchar; } else { (void) sprintf(mb_path, "%s/SCCS", sccs_dir_path); } MBSTOWCS(path, mb_path); (void) wcscpy(file_name, path); } else { MBSTOWCS(wcs_buffer, "SCCS"); (void) wcscpy(file_name_p, wcs_buffer); } } else { MBSTOWCS(wcs_buffer, "."); (void) wcscpy(file_name_p, wcs_buffer); } /* Internalize the constructed SCCS dir name. */ (void) exists(dir = GETNAME(file_name, FIND_LENGTH)); /* Just give up if the directory file doesnt exist. */ if (!dir->stat.is_file) { return result; } /* Open the directory. */ dir_fd = opendir(dir->string_mb); if (dir_fd == NULL) { return result; } MBSTOWCS(wcs_buffer, "/"); (void) wcscat(file_name, wcs_buffer); file_name_p = file_name + wcslen(file_name); while ((dp = readdir(dir_fd)) != NULL) { if ((dp->d_fileno == 0) || ((dp->d_name[0] == (int) period_char) && ((dp->d_name[1] == 0) || ((dp->d_name[1] == (int) period_char) && (dp->d_name[2] == 0))))) { continue; } /* Construct and internalize the true name of the SCCS file. */ MBSTOWCS(wcs_buffer, dp->d_name); (void) wcscpy(file_name_p, wcs_buffer); file = GETNAME(file_name, FIND_LENGTH); file->stat.is_file = true; file->stat.has_sccs = NO_SCCS; /* * If this is an s. file, we also enter it as if it * existed in the plain directory. */ if ((dp->d_name[0] == 's') && (dp->d_name[1] == (int) period_char)) { MBSTOWCS(wcs_buffer, dp->d_name + 2); (void) wcscpy(plain_file_name_p, wcs_buffer); plain_file = GETNAME(plain_file_name, FIND_LENGTH); plain_file->stat.is_file = true; plain_file->stat.has_sccs = HAS_SCCS; /* if sccs dependency is already set,skip */ if(plain_file->prop) { Property sprop = get_prop(plain_file->prop,sccs_prop); if(sprop != NULL) { if (sprop->body.sccs.file) { goto try_pattern; } } } /* * Enter the s. file as a dependency for the * plain file. */ maybe_append_prop(plain_file, sccs_prop)-> body.sccs.file = file; try_pattern: MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); if ((pattern != NULL) && amatch(tmp_wcs_buffer, pattern)) { if (debug_level > 0) { WCSTOMBS(mbs_buffer, pattern); (void) printf(gettext("'%s: %s' due to %s expansion\n"), line->body.line.target-> string_mb, plain_file->string_mb, mbs_buffer); } enter_dependency(line, plain_file, false); result++; } } } (void) closedir(dir_fd); return result; } /* * enter_file_name(name_string, library) * * Helper function for read_dir(). * * Return value: * The Name that was entered * * Parameters: * name_string Name of the file we want to enter * library The library it is a member of, if any * * Global variables used: */ static Name enter_file_name(wchar_t *name_string, wchar_t *library) { wchar_t buffer[STRING_BUFFER_LENGTH]; String_rec lib_name; Name name; Property prop; if (library == NULL) { name = GETNAME(name_string, FIND_LENGTH); name->stat.is_file = true; return name; } INIT_STRING_FROM_STACK(lib_name, buffer); append_string(library, &lib_name, FIND_LENGTH); append_char((int) parenleft_char, &lib_name); append_string(name_string, &lib_name, FIND_LENGTH); append_char((int) parenright_char, &lib_name); name = GETNAME(lib_name.buffer.start, FIND_LENGTH); name->stat.is_file = true; name->is_member = true; prop = maybe_append_prop(name, member_prop); prop->body.member.library = GETNAME(library, FIND_LENGTH); prop->body.member.library->stat.is_file = true; prop->body.member.entry = NULL; prop->body.member.member = GETNAME(name_string, FIND_LENGTH); prop->body.member.member->stat.is_file = true; return name; } /* * star_match(string, pattern) * * This is a regular shell type wildcard pattern matcher * It is used when xpanding wildcards in dependency lists * * Return value: * Indication if the string matched the pattern * * Parameters: * string String to match * pattern Pattern to match it against * * Global variables used: */ static Boolean star_match(wchar_t *string, wchar_t *pattern) { int pattern_ch; switch (*pattern) { case 0: return succeeded; case bracketleft_char: case question_char: case asterisk_char: while (*string) { if (amatch(string++, pattern)) { return succeeded; } } break; default: pattern_ch = (int) *pattern++; while (*string) { if ((*string++ == pattern_ch) && amatch(string, pattern)) { return succeeded; } } break; } return failed; } /* * amatch(string, pattern) * * Helper function for shell pattern matching * * Return value: * Indication if the string matched the pattern * * Parameters: * string String to match * pattern Pattern to match it against * * Global variables used: */ static Boolean amatch(wchar_t *string, wchar_t *pattern) { long lower_bound; long string_ch; long pattern_ch; int k; top: for (; 1; pattern++, string++) { lower_bound = 017777777777; string_ch = *string; switch (pattern_ch = *pattern) { case bracketleft_char: k = 0; while ((pattern_ch = *++pattern) != 0) { switch (pattern_ch) { case bracketright_char: if (!k) { return failed; } string++; pattern++; goto top; case hyphen_char: k |= (lower_bound <= string_ch) && (string_ch <= (pattern_ch = pattern[1])); /* FALLTHROUGH */ default: if (string_ch == (lower_bound = pattern_ch)) { k++; } } } return failed; case asterisk_char: return star_match(string, ++pattern); case 0: return BOOLEAN(!string_ch); case question_char: if (string_ch == 0) { return failed; } break; default: if (pattern_ch != string_ch) { return failed; } break; } } /* NOTREACHED */ }