📄 utils.c
字号:
/* Various utility functions. Copyright (C) 2005 Free Software Foundation, Inc.This file is part of GNU Wget.GNU Wget is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2 of the License, or(at your option) any later version.GNU Wget is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with Wget; if not, write to the Free SoftwareFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.In addition, as a special exception, the Free Software Foundationgives permission to link the code of its release of Wget with theOpenSSL project's "OpenSSL" library (or with modified versions of itthat use the same license as the "OpenSSL" library), and distributethe linked executables. You must obey the GNU General Public Licensein all respects for all of the code used other than "OpenSSL". If youmodify this file, you may extend this exception to your version of thefile, but you are not obligated to do so. If you do not wish to doso, delete this exception statement from your version. */#include <config.h>#include <stdio.h>#include <stdlib.h>#ifdef HAVE_STRING_H# include <string.h>#else /* not HAVE_STRING_H */# include <strings.h>#endif /* not HAVE_STRING_H */#include <sys/types.h>#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#ifdef HAVE_MMAP# include <sys/mman.h>#endif#ifdef HAVE_PWD_H# include <pwd.h>#endif#ifdef HAVE_LIMITS_H# include <limits.h>#endif#ifdef HAVE_UTIME_H# include <utime.h>#endif#ifdef HAVE_SYS_UTIME_H# include <sys/utime.h>#endif#include <errno.h>#ifdef NeXT# include <libc.h> /* for access() */#endif#include <fcntl.h>#include <assert.h>#ifdef WGET_USE_STDARG# include <stdarg.h>#else# include <varargs.h>#endif#ifdef HAVE_LOCALE_H# include <locale.h>#endif/* For TIOCGWINSZ and friends: */#ifdef HAVE_SYS_IOCTL_H# include <sys/ioctl.h>#endif#ifdef HAVE_TERMIOS_H# include <termios.h>#endif/* Needed for run_with_timeout. */#undef USE_SIGNAL_TIMEOUT#ifdef HAVE_SIGNAL_H# include <signal.h>#endif#ifdef HAVE_SETJMP_H# include <setjmp.h>#endif#ifndef HAVE_SIGSETJMP/* If sigsetjmp is a macro, configure won't pick it up. */# ifdef sigsetjmp# define HAVE_SIGSETJMP# endif#endif#ifdef HAVE_SIGNAL# ifdef HAVE_SIGSETJMP# define USE_SIGNAL_TIMEOUT# endif# ifdef HAVE_SIGBLOCK# define USE_SIGNAL_TIMEOUT# endif#endif#include "wget.h"#include "utils.h"#include "hash.h"#ifndef errnoextern int errno;#endif/* Utility function: like xstrdup(), but also lowercases S. */char *xstrdup_lower (const char *s){ char *copy = xstrdup (s); char *p = copy; for (; *p; p++) *p = TOLOWER (*p); return copy;}/* Copy the string formed by two pointers (one on the beginning, other on the char after the last char) to a new, malloc-ed location. 0-terminate it. */char *strdupdelim (const char *beg, const char *end){ char *res = (char *)xmalloc (end - beg + 1); memcpy (res, beg, end - beg); res[end - beg] = '\0'; return res;}/* Parse a string containing comma-separated elements, and return a vector of char pointers with the elements. Spaces following the commas are ignored. */char **sepstring (const char *s){ char **res; const char *p; int i = 0; if (!s || !*s) return NULL; res = NULL; p = s; while (*s) { if (*s == ',') { res = (char **)xrealloc (res, (i + 2) * sizeof (char *)); res[i] = strdupdelim (p, s); res[++i] = NULL; ++s; /* Skip the blanks following the ','. */ while (ISSPACE (*s)) ++s; p = s; } else ++s; } res = (char **)xrealloc (res, (i + 2) * sizeof (char *)); res[i] = strdupdelim (p, s); res[i + 1] = NULL; return res;}#ifdef WGET_USE_STDARG# define VA_START(args, arg1) va_start (args, arg1)#else# define VA_START(args, ignored) va_start (args)#endif/* Like sprintf, but allocates a string of sufficient size with malloc and returns it. GNU libc has a similar function named asprintf, which requires the pointer to the string to be passed. */char *aprintf (const char *fmt, ...){ /* This function is implemented using vsnprintf, which we provide for the systems that don't have it. Therefore, it should be 100% portable. */ int size = 32; char *str = xmalloc (size); while (1) { int n; va_list args; /* See log_vprintf_internal for explanation why it's OK to rely on the return value of vsnprintf. */ VA_START (args, fmt); n = vsnprintf (str, size, fmt, args); va_end (args); /* If the printing worked, return the string. */ if (n > -1 && n < size) return str; /* Else try again with a larger buffer. */ if (n > -1) /* C99 */ size = n + 1; /* precisely what is needed */ else size <<= 1; /* twice the old size */ str = xrealloc (str, size); }}/* Concatenate the NULL-terminated list of string arguments into freshly allocated space. */char *concat_strings (const char *str0, ...){ va_list args; int saved_lengths[5]; /* inspired by Apache's apr_pstrcat */ char *ret, *p; const char *next_str; int total_length = 0; int argcount; /* Calculate the length of and allocate the resulting string. */ argcount = 0; VA_START (args, str0); for (next_str = str0; next_str != NULL; next_str = va_arg (args, char *)) { int len = strlen (next_str); if (argcount < countof (saved_lengths)) saved_lengths[argcount++] = len; total_length += len; } va_end (args); p = ret = xmalloc (total_length + 1); /* Copy the strings into the allocated space. */ argcount = 0; VA_START (args, str0); for (next_str = str0; next_str != NULL; next_str = va_arg (args, char *)) { int len; if (argcount < countof (saved_lengths)) len = saved_lengths[argcount++]; else len = strlen (next_str); memcpy (p, next_str, len); p += len; } va_end (args); *p = '\0'; return ret;}/* Return pointer to a static char[] buffer in which zero-terminated string-representation of TM (in form hh:mm:ss) is printed. If TM is NULL, the current time will be used. */char *time_str (time_t *tm){ static char output[15]; struct tm *ptm; time_t secs = tm ? *tm : time (NULL); if (secs == -1) { /* In case of error, return the empty string. Maybe we should just abort if this happens? */ *output = '\0'; return output; } ptm = localtime (&secs); sprintf (output, "%02d:%02d:%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec); return output;}/* Like the above, but include the date: YYYY-MM-DD hh:mm:ss. */char *datetime_str (time_t *tm){ static char output[20]; /* "YYYY-MM-DD hh:mm:ss" + \0 */ struct tm *ptm; time_t secs = tm ? *tm : time (NULL); if (secs == -1) { /* In case of error, return the empty string. Maybe we should just abort if this happens? */ *output = '\0'; return output; } ptm = localtime (&secs); sprintf (output, "%04d-%02d-%02d %02d:%02d:%02d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec); return output;}/* The Windows versions of the following two functions are defined in mswindows.c. */#ifndef WINDOWSvoidfork_to_background (void){ pid_t pid; /* Whether we arrange our own version of opt.lfilename here. */ int logfile_changed = 0; if (!opt.lfilename) { /* We must create the file immediately to avoid either a race condition (which arises from using unique_name and failing to use fopen_excl) or lying to the user about the log file name (which arises from using unique_name, printing the name, and using fopen_excl later on.) */ FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, 0, &opt.lfilename); if (new_log_fp) { logfile_changed = 1; fclose (new_log_fp); } } pid = fork (); if (pid < 0) { /* parent, error */ perror ("fork"); exit (1); } else if (pid != 0) { /* parent, no error */ printf (_("Continuing in background, pid %d.\n"), (int)pid); if (logfile_changed) printf (_("Output will be written to `%s'.\n"), opt.lfilename); exit (0); /* #### should we use _exit()? */ } /* child: give up the privileges and keep running. */ setsid (); freopen ("/dev/null", "r", stdin); freopen ("/dev/null", "w", stdout); freopen ("/dev/null", "w", stderr);}#endif /* not WINDOWS *//* "Touch" FILE, i.e. make its mtime ("modified time") equal the time specified with TM. The atime ("access time") is set to the current time. */voidtouch (const char *file, time_t tm){#ifdef HAVE_STRUCT_UTIMBUF struct utimbuf times;#else struct { time_t actime; time_t modtime; } times;#endif times.modtime = tm; times.actime = time (NULL); if (utime (file, ×) == -1) logprintf (LOG_NOTQUIET, "utime(%s): %s\n", file, strerror (errno));}/* Checks if FILE is a symbolic link, and removes it if it is. Does nothing under MS-Windows. */intremove_link (const char *file){ int err = 0; struct_stat st; if (lstat (file, &st) == 0 && S_ISLNK (st.st_mode)) { DEBUGP (("Unlinking %s (symlink).\n", file)); err = unlink (file); if (err != 0) logprintf (LOG_VERBOSE, _("Failed to unlink symlink `%s': %s\n"), file, strerror (errno)); } return err;}/* Does FILENAME exist? This is quite a lousy implementation, since it supplies no error codes -- only a yes-or-no answer. Thus it will return that a file does not exist if, e.g., the directory is unreadable. I don't mind it too much currently, though. The proper way should, of course, be to have a third, error state, other than true/false, but that would introduce uncalled-for additional complexity to the callers. */intfile_exists_p (const char *filename){#ifdef HAVE_ACCESS return access (filename, F_OK) >= 0;#else struct_stat buf; return stat (filename, &buf) >= 0;#endif}/* Returns 0 if PATH is a directory, 1 otherwise (any kind of file). Returns 0 on error. */intfile_non_directory_p (const char *path){ struct_stat buf; /* Use lstat() rather than stat() so that symbolic links pointing to directories can be identified correctly. */ if (lstat (path, &buf) != 0) return 0; return S_ISDIR (buf.st_mode) ? 0 : 1;}/* Return the size of file named by FILENAME, or -1 if it cannot be opened or seeked into. */wgintfile_size (const char *filename){#if defined(HAVE_FSEEKO) && defined(HAVE_FTELLO) wgint size; /* We use fseek rather than stat to determine the file size because that way we can also verify that the file is readable without explicitly checking for permissions. Inspired by the POST patch by Arnaud Wylie. */ FILE *fp = fopen (filename, "rb"); if (!fp) return -1; fseeko (fp, 0, SEEK_END); size = ftello (fp); fclose (fp); return size;#else struct_stat st; if (stat (filename, &st) < 0) return -1; return st.st_size;#endif}/* stat file names named PREFIX.1, PREFIX.2, etc., until one that doesn't exist is found. Return a freshly allocated copy of the unused file name. */static char *unique_name_1 (const char *prefix){ int count = 1; int plen = strlen (prefix); char *template = (char *)alloca (plen + 1 + 24); char *template_tail = template + plen; memcpy (template, prefix, plen); *template_tail++ = '.'; do number_to_string (template_tail, count++); while (file_exists_p (template)); return xstrdup (template);}/* Return a unique file name, based on FILE. More precisely, if FILE doesn't exist, it is returned unmodified. If not, FILE.1 is tried, then FILE.2, etc. The first FILE.<number> file name that doesn't exist is returned. The resulting file is not created, only verified that it didn't exist at the point in time when the function was called. Therefore, where security matters, don't rely that the file created by this function exists until you open it with O_EXCL or equivalent. If ALLOW_PASSTHROUGH is 0, it always returns a freshly allocated string. Otherwise, it may return FILE if the file doesn't exist (and therefore doesn't need changing). */char *unique_name (const char *file, int allow_passthrough){ /* If the FILE itself doesn't exist, return it without modification. */ if (!file_exists_p (file)) return allow_passthrough ? (char *)file : xstrdup (file); /* Otherwise, find a numeric suffix that results in unused file name and return it. */ return unique_name_1 (file);}/* Create a file based on NAME, except without overwriting an existing file with that name. Providing O_EXCL is correctly implemented, this function does not have the race condition associated with opening the file returned by unique_name. */FILE *unique_create (const char *name, int binary, char **opened_name){ /* unique file name, based on NAME */ char *uname = unique_name (name, 0); FILE *fp; while ((fp = fopen_excl (uname, binary)) == NULL && errno == EEXIST) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -