/* -*-Mode: C++;-*- * PRCS - The Project Revision Control System * Copyright (C) 1997 Josh MacDonald * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: misc.cc 1.30.1.3.1.17.1.11.1.7.1.18.1.35 Wed, 06 Feb 2002 20:57:16 -0800 jmacd $ */ extern "C" { #include #include #include #include #include #include #include #include #include #include "maketime.h" } #include "prcs.h" #include "docs.h" #include "prcsdir.h" #include "misc.h" #include "vc.h" #include "syscmd.h" #include "populate.h" #include "repository.h" #include "system.h" #include "checkout.h" const char *cmd_root_project_name = NULL; const char *cmd_root_project_full_name; const char *cmd_root_project_file_path; const char *cmd_root_project_path; int cmd_root_project_path_len; bool cmd_prj_given_as_file = false; char **cmd_filenames_given; const char **cmd_corrected_filenames_given; int cmd_filenames_count; bool *cmd_filenames_found; char **cmd_diff_options_given; int cmd_diff_options_count; const char *cmd_version_specifier_major = NULL; const char *cmd_version_specifier_minor = NULL; int cmd_version_specifier_minor_int = -1; const char *cmd_alt_version_specifier_major = NULL; const char *cmd_alt_version_specifier_minor = NULL; const char *cmd_repository_path = NULL; int option_immediate_uncompression = 0; int option_force_resolution = 0; int option_long_format = 0; int option_really_long_format = 0; int option_report_actions = 0; int option_version_present = 0; int option_diff_keywords = 0; int option_diff_new_file = 0; int option_populate_delete = 0; int option_package_compress = 0; int option_be_silent = 0; int option_preserve_permissions = 0; int option_exclude_project_file = 0; int option_unlink = 0; int option_match_file = 0; int option_not_match_file = 0; int option_all_files = 0; int option_preorder = 0; int option_pipe = 0; int option_nokeywords = 0; int option_version_log = 0; #ifdef PRCS_DEVEL int option_n_debug = 1; int option_tune = 10; #endif int option_jobs = 1; int option_skilled_merge = 0; int option_plain_format = 0; int option_sort = 0; const char *option_match_file_pattern = NULL; const char *option_not_match_file_pattern = NULL; const char *option_sort_type = NULL; const char *option_version_log_string = NULL; const char* temp_file_1 = NULL; const char* temp_file_2 = NULL; const char* temp_file_3 = NULL; const char* temp_directory = NULL; const char* maintainer = "prcs-bugs@XCF.Berkeley.EDU"; PrVoidError bug (void) { pthrow prcserror << "Please report this to " << maintainer << dotendl << "When sending bug reports, always include:" << prcsendl << "-- a complete description of the problem encountered" << prcsendl << "-- the output of `prcs config'" << prcsendl << "-- the operating system and version" << prcsendl << "-- the architecture" << dotendl << "If possible, include:" << prcsendl << "-- the file " << squote (bug_name()) << ", if it exists" << prcsendl << "-- the working project file, if one was in use" << prcsendl << "-- the output of `ls -lR' in the current directory" << dotendl << "Disk space permitting, retain the following:" << prcsendl << "-- any relevant working project files" << prcsendl << "-- a repository package created with `prcs package PROJECT'" << dotendl << "Disk space not permitting, retain just:" << prcsendl << "-- the repository file PRCS/PROJECT/PROJECT.prj,v" << prcsendl << "-- the repository log PRCS/PROJECT/prcs_log" << prcsendl << "-- the repository data file PRCS/PROJECT/prcs_data" << prcsendl << "-- the output of `rlog' on each repository file under PRCS/PROJECT" << dotendl << "These steps will help diagnose the problem" << dotendl; } const char* bug_name (void) { const char* tmp = get_environ_var ("TMPDIR"); if (!tmp) tmp = "/tmp"; static Dstring *it; if (!it) it = new Dstring; it->assign (tmp); it->append ("/"); it->append ("prcs_bug"); return it->cast(); } bool is_linked(const char* path) { struct stat buf; if (lstat(path, &buf) < 0) { return false; } else if (S_ISLNK(buf.st_mode) || buf.st_nlink > 1) { return true; } else { return false; } } /* this unbackslashes all backslashed characters and removes excess * slashes from path */ void correct_path(const char *path, Dstring* in) { ASSERT(path != NULL, "can't be null"); while(*path) { if(*path == '\\') { path += 1; } in->append(*path); path += 1; } } /* backslashes all backslashes and quotation marks in a path so that * they can be inserted into the project file */ void protect_path(const char* path, Dstring* in) { while(*path) { switch (*path) { case '\"': case '\\': case ')': case '(': case ' ': case '\t': case '\n': case '\v': case '\r': case '\f': case ';': in->append('\\'); default: in->append(*path); } path += 1; } } void print_protected (const char* str, ostream& os) { while(*str) { char c = *str++; switch (c) { case '\"': case '\\': case ')': case '(': case ' ': case '\t': case '\n': case '\v': case '\r': case '\f': case ';': os << '\\'; default: os << c; } } } void protect_string(const char* path, Dstring* in) { while(*path) { switch (*path) { case '\"': case '\\': in->append('\\'); default: in->append(*path); } path += 1; } } bool weird_pathname(const char* N) { /* don't allow anything outside the repository */ if (strncmp("../", N, 3) == 0) return true; else if (strncmp("./", N, 2) == 0) return true; if (N[0] == '/') return true; /* don't allow any '/../'s in the path */ while ((N = strchr(N, '/')) != NULL) { if (strncmp(N, "/../", 4) == 0) return true; if (strncmp(N, "/./", 3) == 0) return true; N += 1; } if (N) { int len = strlen(N); if (len > 0 && N[len - 1] == '/') return true; } return false; } const char* strip_leading_path(const char* P) { char* s = strrchr(P, '/'); if ( s == '\0' ) return P; else return s+1; } bool fs_is_symlink (const char* P) { struct stat buf; if (lstat(P, &buf) < 0) return false; if (S_ISLNK(buf.st_mode)) return true; return false; } bool fs_is_directory_not_link(const char* P) { struct stat buf; if(stat(P, &buf) < 0) return false; if(!S_ISDIR(buf.st_mode) || S_ISLNK(buf.st_mode)) return false; return true; } bool fs_is_directory(const char* P) { struct stat buf; if(stat(P, &buf) < 0) return false; if(!S_ISDIR(buf.st_mode)) return false; return true; } bool fs_file_readable(const char* P) { return access(P, F_OK | R_OK) >= 0; } bool fs_file_wrandex(const char* P) { return access(P, F_OK | W_OK | X_OK) >= 0; } bool fs_file_writeable(const char* P) { return access(P, F_OK | W_OK ) >= 0; } bool fs_file_exists(const char* P) { return access(P, F_OK) >= 0; } bool fs_file_executable(const char* P) { return access(P, F_OK | X_OK) >= 0; } bool fs_file_rwx(const char* P) { return access(P, F_OK | X_OK | W_OK | R_OK) >= 0; } PrVoidError directory_recurse(const char* base, const void *data, PrVoidError (*func)(const char* name, const void* data)) /* This is used for iterating through the repository. * It doesn't follow symlinks, as a result. */ { Dir current_dir(base); foreach (ent_ptr, current_dir, Dir::FullDirIterator) { const char *ent = *ent_ptr; if (fs_is_directory_not_link(ent)) Return_if_fail(directory_recurse(ent, data, func)); else Return_if_fail(func(ent, data)); } if (! current_dir.OK() ) { pthrow prcserror << "Error reading directory " << squote (base) << perror; } return NoError; } PrVoidError directory_recurse_dirs(const char* base, const void *data, PrVoidError (*func)(const char* name, const void* data)) /* This is used for iterating through the repository. * It doesn't follow symlinks, as a result. */ { Dir current_dir(base); Return_if_fail ((*func) (base, data)); foreach (ent_ptr, current_dir, Dir::FullDirIterator) { const char *ent = *ent_ptr; if (fs_is_directory_not_link(ent)) { Return_if_fail ((*func) (ent, data)); Return_if_fail(directory_recurse(ent, data, func)); } } if (! current_dir.OK() ) { pthrow prcserror << "Error reading directory " << squote (base) << perror; } return NoError; } NprVoidError fs_nuke_file(const char* file) { bool ret = true; if(fs_is_directory(file)) { char old_dir[MAXPATHLEN]; const char* old_dir_name; If_fail(old_dir_name << name_in_cwd("")) ret = false; strncpy (old_dir, old_dir_name, MAXPATHLEN-1); If_fail(change_cwd(file)) ret = false; if (ret) { { Dir current_dir("."); foreach(ent_ptr, current_dir, Dir::DirIterator) { If_fail(fs_nuke_file(*ent_ptr)) { ret = false; } } Return_if_fail(change_cwd(old_dir)); ret &= current_dir.OK(); } ret &= rmdir(file) >= 0; } } else { ret &= unlink(file) >= 0; } if(ret) return NoError; else return FatalError; } mode_t get_umask() { mode_t mask = umask(0); umask(mask); return mask; } const char* get_login() { static char buf[32]; /* L_cuserid */ static bool do_once = false; if(do_once) return buf; struct passwd *user = getpwuid(get_user_id()); if(!user) strcpy(buf, "unknown"); else strcpy(buf, user->pw_name); do_once = true; return buf; } uid_t get_user_id() { static bool do_once = false; static uid_t uid; if (do_once) return uid; do_once = true; uid = getuid(); return uid; } /* * get_utc_time -- * * returns a time formatted like so: Sun, 24 Mar 1996 23:20:44 -0800 * which is UTC in that the offset from gmt is included, but the hour * minute and date are in local time. careful--the result is over * written by the next call. */ const char* get_utc_time() { static char buf[64]; static bool do_once = false; if(do_once) return buf; strcpy(buf, time_t_to_rfc822(get_utc_time_t())); do_once = true; return buf; } time_t get_utc_time_t() { static time_t t = 0; if(t) return t; time(&t); return t; } /* * timestr_to_time_t -- * * this calls the somewhat excessive RCS time conversion routine that * converts a standard RCS time string such as 1996/01/23 07:36:14 * into a time_t. I could write a less bulky conversion routine that * didn't try to convert 10 different formats. */ time_t timestr_to_time_t(const char* rcstime) { return str2time(rcstime, 0, 0); } /* * From RFC#822, Aug 13, 1982 * * 5.1. SYNTAX * * date-time = [ day "," ] date time ; dd mm yy * ; hh:mm:ss zzz * day = "Mon" / "Tue" / "Wed" / "Thu" * / "Fri" / "Sat" / "Sun" * date = 1*2DIGIT month 2*4DIGIT ; correction in RFC#1123 * ; day month year * ; e.g. 20 Jun 82 * month = "Jan" / "Feb" / "Mar" / "Apr" * / "May" / "Jun" / "Jul" / "Aug" * / "Sep" / "Oct" / "Nov" / "Dec" * time = hour zone ; ANSI and Military * hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] * ; 00:00:00 - 23:59:59 * zone = "UT" / "GMT" ; Universal Time * ; North American : UT * / "EST" / "EDT" ; Eastern: - 5/ - 4 * / "CST" / "CDT" ; Central: - 6/ - 5 * / "MST" / "MDT" ; Mountain: - 7/ - 6 * / "PST" / "PDT" ; Pacific: - 8/ - 7 * / 1ALPHA ; Military: Z = UT; * ; A:-1; (J not used) * ; M:-12; N:+1; Y:+12 * / ( ("+" / "-") 4DIGIT ) ; Local differential * ; hours+min. (HHMM) */ const char* time_t_to_rfc822(time_t t) { static char timebuf[64]; static const char day[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char mon[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; struct tm lt = *localtime(&t); int utc_offset = difftm(<, gmtime(&t)); char sign = utc_offset < 0 ? '-' : '+'; int minutes = abs(utc_offset) / 60; int hours = minutes / 60; sprintf(timebuf, "%s, %02d %s %d %02d:%02d:%02d %c%02d%02d", day[lt.tm_wday], lt.tm_mday, mon[lt.tm_mon], lt.tm_year + 1900, lt.tm_hour, lt.tm_min, lt.tm_sec, sign, hours, minutes % 60); return timebuf; } /* * get_host_name -- * * This used to be here because I couldn't find a portable method * of finding a hostname, but now I did. This is used by the * locking mechanism to alert users what machine is holding a lock. */ const char* get_host_name() { static char buf[256]; struct utsname utsbuf; if(uname(&utsbuf) < 0) strcpy(buf, "** uname() failed -- unknown host **"); else strcpy(buf, utsbuf.nodename); return buf; } /* * name_in_cwd -- * * I don't like SunOS's getcwd(), it freaked me out when I was * looking at a profiler output. It calls popen on 'pwd' and fgets * on the output! * * returns NULL on failure. */ static bool changed = true; PrConstCharPtrError name_in_cwd(const char* name) { static char buf[MAXPATHLEN]; static int buflen; if(changed) { if( #if defined(sun) && !defined(__SVR4) getwd(buf) #else getcwd(buf, MAXPATHLEN) #endif == NULL) { pthrow prcserror << "Getcwd failed" << perror; } else changed = false; buflen = strlen(buf); } if(buflen + strlen(name) + 1 > MAXPATHLEN) pthrow prcserror << "Failed building full pathname to " << squote(name) << ", name too long" << dotendl; buf[buflen] = '/'; strcpy(buf + buflen + 1, name); return buf; } /* * change_cwd -- * * Change the current working directory and tell name_in_cwd its different * now. */ NprVoidError change_cwd(const char* path) { changed = true; if(chdir(path) == 0) return NoError; else return NonFatalError; } /* * guess_prj_name -- * * returns NULL if 0 or more than 1 .prj file are in the current directory. * otherwise returns a heap-allocated Dstring containing the name of the * file, after truncating the ".prj" */ PrDstringPtrError guess_prj_name(const char* dir) { bool found = false; Dstring buf; Dstring *ret; Dir current_dir(dir); foreach(ent_ptr, current_dir, Dir::DirIterator) { const char* ent = *ent_ptr; int len = strlen(ent); if (len > 4 && strcmp(".prj", ent + len - 4) == 0) { if(found) { return (Dstring*)0; } else { buf.assign(ent); found = true; } } } if (! current_dir.OK() ) { pthrow prcserror << "Error reading current directory" << perror; } if(found) { ret = new Dstring(buf); ret->truncate(ret->length() - 4); return ret; } else { return (Dstring*)0; } } PrConstCharPtrError read_sym_link(const char* name) { static char buf[MAXPATHLEN]; int ret; if((ret = readlink(name, buf, MAXPATHLEN)) < 0) { pthrow prcserror << "Error reading symlink " << squote(name) << perror; } else { buf[ret] = '\0'; /* readlink() doesn't zero terminate */ } if (ret == 0) pthrow prcserror << "Symlink " << squote(name) << " may not be null" << dotendl; return buf; } PrConstCharPtrError show_file_info(const char* file) { static Dstring* lsout; ArgList *args; FILE* out; if(lsout == NULL) lsout = new Dstring(); Return_if_fail(args << ls_command.new_arg_list()); args->append("-ldgF"); args->append(file); Return_if_fail(ls_command.open(true, false)); out = ls_command.standard_out(); lsout->truncate(0); If_fail(read_string(out, lsout)) pthrow prcserror << "Read failure from ls output" << perror; Return_if_fail_if_ne(ls_command.close(), 0) pthrow prcserror << ls_command.path() << " exited abnormally" << dotendl; return lsout->cast(); } PrConstCharPtrError absolute_path(const char* name) { static Dstring *buf = NULL; if(!buf) buf = new Dstring; if(strcmp(name, "-") == 0) { return name; } else if(name[0] == '/') { return name; } else { const char* cwd; Return_if_fail(cwd << name_in_cwd("")); buf->assign(cwd); buf->append(name); return buf->cast(); } } bool pathname_equal(const char* const& a, const char* const& b) { const char *c(a), *d(b); for(;;) { while(c != a && *c == '/' && *(c - 1) == '/') { c += 1; } while(d != b && *d == '/' && *(d - 1) == '/') { d += 1; } if(*c != *d) return false; if(*c == '\0') { return true; } c += 1; d += 1; } } int pathname_hash(const char* const& s, int M) { const char *p; unsigned int h(0), g; for(p = s; *p != '\0'; p += 1) { if(p != s && *p == '/' && *(p-1) == '/') continue; h = ( h << 4 ) + *p; if ( ( g = h & 0xf0000000 ) ) { h = h ^ (g >> 24); h = h ^ g; } } return h % M; } char get_user_char() /* 0 is error */ { int ans, c; If_fail(ans << Err_fgetc(stdin)) return 0; if(ans == EOF) return 0; else if(ans == '\n') return '\n'; while(true) { If_fail(c << Err_fgetc(stdin)) return 0; if (c == EOF) return 0; else if (c == '\n') break; } return (char)ans; } void make_temp_file_same_dir (Dstring* temp_name) { temp_name->append(".prcs_tmp_"); int len = temp_name->length(); int tries = 0; do { temp_name->truncate(len); temp_name->append_int(tries++); } while (fs_file_exists(*temp_name)); } const char* make_temp_file(const char* extension) { static int counter = 0; ASSERT(temp_directory, "set the temp dir"); Dstring *t = new Dstring(temp_directory); t->sprintfa("/prcs_temp.%d%s", counter, extension); counter += 1; return *t; } const char* format_type(FileType t, bool cap) { switch (t) { case SymLink: return cap ? "Symlink" : "symlink"; case Directory: return cap ? "Directory" : "directory"; case RealFile: return cap ? "File" : "file"; } return "*** Illegal Type ***"; } PrVoidError fs_write_filename(FILE *in, const char* filename) { FILE* out; If_fail(out << Err_fopen(filename, "w")) pthrow prcserror << "Failed opening file " << squote(filename) << " for writing" << perror; Return_if_fail(write_file(in, out)); If_fail(Err_fclose(out)) pthrow prcserror << "Failed writing to file " << squote(filename) << perror; return NoError; } PrVoidError fs_move_filename(const char* infile, const char* outfile) { if (rename(infile, outfile) >= 0) return NoError; if (errno != EXDEV) pthrow prcserror << "Rename failed on " << squote(infile) << " to " << squote(outfile) << perror; return fs_copy_filename(infile, outfile); } PrVoidError fs_copy_filename(const char* infile, const char* outfile) { FILE *in, *out; If_fail(in << Err_fopen(infile, "r")) pthrow prcserror << "Failed opening file " << squote(infile) << " for reading" << perror; If_fail(out << Err_fopen(outfile, "w")) pthrow prcserror << "Failed opening file " << squote(outfile) << " for writing" << perror; Return_if_fail(write_file(in, out)); fclose(in); If_fail(Err_fclose(out)) pthrow prcserror << "Failed writing file " << squote(outfile) << perror; return NoError; } static char *Buffer1 = NULL, *Buffer2 = NULL; static int Buffer1Len = 0, Buffer2Len = 0; static int Buffer1Alloc = 0, Buffer2Alloc = 0; static void opt_buffer(int toread, int* buflen, int* bufalloc, char** buf) { struct stat statbuf; *buflen = 1 << 10; /* default */ if(toread >= 0 && fstat(toread, &statbuf) >= 0) *buflen = statbuf.st_blksize; if(*bufalloc < *buflen) { if(*bufalloc == 0) { *buf = NEWVEC(char, *buflen); *bufalloc = *buflen; } else { *buf = (char*)EXPANDVEC(*buf, *bufalloc, *buflen - *bufalloc); } } } #define opt_buffer_1(fd) opt_buffer(fd, &Buffer1Len, &Buffer1Alloc, &Buffer1) #define opt_buffer_2(fd) opt_buffer(fd, &Buffer2Len, &Buffer2Alloc, &Buffer2) PrVoidError write_file(FILE* in, FILE* out) { int c; opt_buffer_1(-1); for (;;) { If_fail (c << Err_fread(Buffer1, Buffer1Len, in)) pthrow prcserror << "Read failure" << perror; if (c == 0) break; If_fail (Err_fwrite(Buffer1, c, out)) pthrow prcserror << "Write failure" << perror; } return NoError; } bool empty_file(FILE* empty) { int c; opt_buffer_1(-1); do { If_fail (c << Err_fread(Buffer1, Buffer1Len, empty)) c = -1; } while (c > 0); return true; } PrVoidError fs_zero_file(const char* name) { /* use open so that if FILE doesn't exist create it. */ If_fail(Err_open(name, O_TRUNC|O_CREAT, 0644)) pthrow prcserror << "Failed truncating file "<< squote(name) << perror; return NoError; } NprVoidError write_fds(int infd, int outfd) { int c; opt_buffer_1(infd); do { If_fail(c << Err_read(infd, Buffer1, Buffer1Len)) return ReadFailure; If_fail(Err_write(outfd, Buffer1, c)) return WriteFailure; } while (c); return NoError; } bool fs_same_file_owner(const char* file) { struct stat buf; if(stat(file, &buf) < 0) return false; return get_user_id() == buf.st_uid; } NprBoolError cmp_fds(int one, int two) { int nRead1 = 1, nRead2 = 1, len; opt_buffer_1(one); opt_buffer_2(two); len = Buffer1Len < Buffer2Len ? Buffer1Len : Buffer2Len; while(true) { If_fail(nRead1 << Err_read(one, Buffer1, len)) return ReadFailure; If_fail(nRead2 << Err_read(two, Buffer2, len)) return ReadFailure; if (nRead1 != nRead2) return true; if (nRead1 == 0) return false; if(memcmp(Buffer1, Buffer2, nRead1) != 0) return 1; } return nRead1 != nRead2; } NprVoidError read_string(FILE* f, Dstring* s) { while(true) { int c; Return_if_fail(c << Err_fgetc(f)); if (c == EOF) return ReadFailure; if (c == '\n') break; s->append((char)c); } return NoError; } NprCFilePtrError Err_fopen(const char* file, const char* type) { FILE* f = fopen(file, type); if(f) return f; else return NonFatalError; } NprVoidError Err_fclose(FILE* f) { if(fclose(f) < 0) return NonFatalError; else return NoError; } NprVoidError Err_unlink(const char* a) { if(unlink(a) < 0 && errno != ENOENT) return NonFatalError; else return NoError; } NprVoidError Err_rename(const char* a, const char* b) { if(rename(a, b) < 0) return NonFatalError; else return NoError; } NprIntError Err_fread(void* buf, size_t nbytes, FILE* f) { int c; DEBUG ("Err_fread (" << nbytes << ")"); do { errno = 0; clearerr(f); c = fread (buf, 1, nbytes, f); DEBUG (c << " = fread (" << nbytes << ")"); if (errno) DEBUG ("errno = " << errno); if (ferror(f)) DEBUG ("ferror(f) = " << ferror (f)); } while (c <= 0 && ferror(f) && errno == EINTR); if (c <= 0 && ferror (f)) return ReadFailure; else return c; } NprVoidError Err_fwrite(const void* buf, size_t nbytes, FILE* out) { int c; DEBUG ("Err_fwrite (" << nbytes << ")"); while (nbytes > 0) { do { errno = 0; clearerr(out); c = fwrite (buf, 1, nbytes, out); DEBUG (c << " = fwrite (" << nbytes << ")"); if (errno) DEBUG ("errno = " << errno); if (ferror(out)) DEBUG ("ferror(out) = " << ferror (out)); } while (c <= 0 && ferror(out) && errno == EINTR); if (c <= 0 && ferror(out)) return WriteFailure; nbytes -= c; buf = ((const char*)buf) + c; } return NoError; } NprIntError Err_read(int fd, void* buf, size_t nbytes) { int c; do { c = read(fd, buf, nbytes); } while (c < 0 && errno == EINTR); if (c < 0) return ReadFailure; else return c; } NprVoidError Err_read_expect(int fd, void* buf, size_t nbytes) { int c; while(nbytes > 0) { Return_if_fail(c << Err_read(fd, buf, nbytes)); nbytes -= c; } return NoError; } NprVoidError Err_write(int fd, const void* buf, size_t nbytes) { int c; while (nbytes > 0) { do { c = write(fd, buf, nbytes); } while (c < 0 && errno == EINTR); if (c < 0) return WriteFailure; nbytes -= c; buf = ((const char*)buf) + c; } return NoError; } NprIntError Err_open(const char* a, int flags, mode_t mode) { int fd; fd = open(a, flags, mode); if(fd < 0) return NonFatalError; else return fd; } NprVoidError Err_close(int fd) { if(close(fd) < 0) return NonFatalError; else return NoError; } NprVoidError Err_mkdir(const char* name, mode_t mode) { ASSERT(name[strlen(name) - 1] != '/', "This fails on some operating systems, " "notably old versions of Linux"); if(mkdir(name, mode) < 0) return NonFatalError; else return NoError; } NprVoidError Err_chmod(const char* name, mode_t mode) { if(chmod(name, mode) < 0) return NonFatalError; else return NoError; } NprVoidError Err_chown(const char* name, uid_t uid, gid_t gid) { if(chown(name, uid, gid) < 0) return NonFatalError; else return NoError; } NprVoidError Err_utime(const char* name, const struct utimbuf *ut) { if(utime(name, ut) < 0) return NonFatalError; else return NoError; } NprVoidError Err_fstat(int fd, struct stat* buf) { if(fstat(fd, buf) < 0) return NonFatalError; else return NoError; } NprVoidError Err_stat(const char* file, struct stat* buf) { if(stat(file, buf) < 0) return NonFatalError; else return NoError; } NprVoidError Err_symlink(const char* a, const char* b) { if(symlink(a, b) < 0) return NonFatalError; else return NoError; } NprIntError Err_waitpid(int pid, pid_t* pid_ret, bool nohang) { int status; pid_t ret; while (true) { while ((ret = waitpid(pid, &status, (nohang ? WNOHANG : 0))) < 0 && errno == EINTR) { } if (WIFSTOPPED(status)) kill(SIGCONT, pid); else break; } if (pid_ret) (*pid_ret) = ret; if (ret < 0) return FatalError; else return status; } NprIntError Err_waitpid_nostart(int pid, pid_t* pid_ret, bool nohang) { int status; pid_t ret; while (true) { while ((ret = waitpid(pid, &status, (nohang ? WNOHANG : 0))) < 0 && errno == EINTR) { } if (WIFSTOPPED(status)) sleep(1); else break; } if (pid_ret) (*pid_ret) = ret; if (ret < 0) return FatalError; else return status; } NprIntError Err_fgetc(FILE* f) { int c; while ((c = fgetc(f)) == EOF && errno == EINTR) { } if (c == EOF && ferror(f)) return ReadFailure; else return c; } NprVoidError Err_creat(const char* name, mode_t mode) { int s = open (name, O_CREAT | O_TRUNC, mode); if (s < 0) return FatalError; return NoError; } char* p_strdup(const char* name) { char *s = NEWVEC(char, strlen(name)+1); strcpy(s, name); return s; } PrConstCharPtrError find_real_filename(const char* filename) { static char buf[MAXPATHLEN]; struct stat sbuf; if (lstat(filename, &sbuf) < 0) pthrow prcserror << "Lstat failed on " << squote(filename) << perror; if (S_ISLNK(sbuf.st_mode)) { const char* sym; Return_if_fail(sym << read_sym_link(filename)); if (sym[0] == '/') { return find_real_filename(sym); } else { Dstring directory; dirname(filename, &directory); directory.append('/'); directory.append(sym); return find_real_filename(directory); } } else { strncpy(buf, filename, MAXPATHLEN-1); buf[MAXPATHLEN-1] = 0; return buf; } } void dirname(const char* p, Dstring* s) { const char* last_slash = strrchr(p, '/'); if (!last_slash) s->assign("./"); else s->append(p, last_slash - p); } PrVoidError WriteableFile::copy(FILE* copy_me) { opt_buffer_1(-1); int c; for (;;) { If_fail (c << Err_fread(Buffer1, Buffer1Len, copy_me)) pthrow prcserror << "Read failure" << perror; if (c == 0) break; os.write (Buffer1, c); if (os.bad()) pthrow prcserror << "Error writing " << squote(temp_name) << perror; } return NoError; } PrVoidError WriteableFile::copy(const char* copy_me) { FILE* it; If_fail(it << Err_fopen(copy_me, "r")) pthrow prcserror << "Open failed on " << squote(copy_me) << perror; PrVoidError ret = copy(it); fclose(it); return ret; } const char* major_version_of(const char* name) { static Dstring* result = NULL; if (!result) result = new Dstring; const char* last_period = strrchr(name, '.'); if (!last_period) return NULL; result->assign (name); result->truncate (last_period - name); return result->cast(); } const char* minor_version_of(const char* name) { static Dstring* result = NULL; if (!result) result = new Dstring; const char* last_period = strrchr(name, '.'); if (!last_period) return NULL; result->assign(last_period + 1); return result->cast(); } const char* get_environ_var (const char* var) { static KeywordTable env_table; static bool once = false; const char* env; if (!once) { once = true; for (int i = 0; i < env_names_count; i += 1) { env = getenv (env_names[i].var); if (!env) env = env_names[i].defval; if (env && !env[0]) env = NULL; env_table.insert (env_names[i].var, env); } } const char** lu = env_table.lookup (var); if (lu) return *lu; env = getenv (var); if (env && *env) { env_table.insert (var, env); return env; } return NULL; } Dir::Dir(const char* path) { finished = false; thisdir = opendir(path); full_name.assign(path); full_name_len = full_name.length(); Dir_next(); } Dir::~Dir() { Dir_close(); } bool Dir::Dir_open(const char* path) { Dir_close(); thisdir = opendir(path); full_name.assign(path); full_name_len = full_name.length(); Dir_next(); return OK(); } bool Dir::OK() const { return thisdir != NULL; } const char* Dir::Dir_entry() const { return entry->d_name; } const char* Dir::Dir_full_entry() { if(!finished) { full_name.truncate(full_name_len); full_name.append('/'); full_name.append(entry->d_name); } return full_name; } bool Dir::Dir_next() { const char* name; if(thisdir == NULL) { finished = true; return false; } if(finished) return false; if((entry = readdir(thisdir)) == NULL) { finished = true; return false; } name = Dir_entry(); if(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) return Dir_next(); return true; } bool Dir::Dir_finished() const { return finished; } void Dir::Dir_close() { if(thisdir) { closedir(thisdir); thisdir = NULL; } } Dir::DirIterator::DirIterator(Dir& d0) { dp = &d0; } const char* Dir::DirIterator::operator*() const { return dp->Dir_entry(); } void Dir::DirIterator::next() { dp->Dir_next(); } bool Dir::DirIterator::finished() const { return dp->Dir_finished(); } Dir::FullDirIterator::FullDirIterator(Dir& d0) { dp = &d0; } const char* Dir::FullDirIterator::operator*() const { return dp->Dir_full_entry(); } void Dir::FullDirIterator::next() { dp->Dir_next(); } bool Dir::FullDirIterator::finished() const { return dp->Dir_finished(); }