📄 utils.c
字号:
/* Various functions of utilitarian nature. Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001 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#include <limits.h>#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>/* 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/* This section implements several wrappers around the basic allocation routines. This is done for two reasons: first, so that the callers of these functions need not consistently check for errors. If there is not enough virtual memory for running Wget, something is seriously wrong, and Wget exits with an appropriate error message. The second reason why these are useful is that, if DEBUG_MALLOC is defined, they also provide a handy (if crude) malloc debugging interface that checks memory leaks. *//* Croak the fatal memory error and bail out with non-zero exit status. */static voidmemfatal (const char *what){ /* Make sure we don't try to store part of the log line, and thus call malloc. */ log_set_save_context (0); logprintf (LOG_ALWAYS, _("%s: %s: Not enough memory.\n"), exec_name, what); exit (1);}/* These functions end with _real because they need to be distinguished from the debugging functions, and from the macros. Explanation follows: If memory debugging is not turned on, wget.h defines these: #define xmalloc xmalloc_real #define xrealloc xrealloc_real #define xstrdup xstrdup_real #define xfree free In case of memory debugging, the definitions are a bit more complex, because we want to provide more information, *and* we want to call the debugging code. (The former is the reason why xmalloc and friends need to be macros in the first place.) Then it looks like this: #define xmalloc(a) xmalloc_debug (a, __FILE__, __LINE__) #define xfree(a) xfree_debug (a, __FILE__, __LINE__) #define xrealloc(a, b) xrealloc_debug (a, b, __FILE__, __LINE__) #define xstrdup(a) xstrdup_debug (a, __FILE__, __LINE__) Each of the *_debug function does its magic and calls the real one. */#ifdef DEBUG_MALLOC# define STATIC_IF_DEBUG static#else# define STATIC_IF_DEBUG#endifSTATIC_IF_DEBUG void *xmalloc_real (size_t size){ void *ptr = malloc (size); if (!ptr) memfatal ("malloc"); return ptr;}STATIC_IF_DEBUG void *xrealloc_real (void *ptr, size_t newsize){ void *newptr; /* Not all Un*xes have the feature of realloc() that calling it with a NULL-pointer is the same as malloc(), but it is easy to simulate. */ if (ptr) newptr = realloc (ptr, newsize); else newptr = malloc (newsize); if (!newptr) memfatal ("realloc"); return newptr;}STATIC_IF_DEBUG char *xstrdup_real (const char *s){ char *copy;#ifndef HAVE_STRDUP int l = strlen (s); copy = malloc (l + 1); if (!copy) memfatal ("strdup"); memcpy (copy, s, l + 1);#else /* HAVE_STRDUP */ copy = strdup (s); if (!copy) memfatal ("strdup");#endif /* HAVE_STRDUP */ return copy;}#ifdef DEBUG_MALLOC/* Crude home-grown routines for debugging some malloc-related problems. Featured: * Counting the number of malloc and free invocations, and reporting the "balance", i.e. how many times more malloc was called than it was the case with free. * Making malloc store its entry into a simple array and free remove stuff from that array. At the end, print the pointers which have not been freed, along with the source file and the line number. This also has the side-effect of detecting freeing memory that was never allocated. Note that this kind of memory leak checking strongly depends on every malloc() being followed by a free(), even if the program is about to finish. Wget is careful to free the data structure it allocated in init.c. */static int malloc_count, free_count;static struct { char *ptr; const char *file; int line;} malloc_debug[100000];/* Both register_ptr and unregister_ptr take O(n) operations to run, which can be a real problem. It would be nice to use a hash table for malloc_debug, but the functions in hash.c are not suitable because they can call malloc() themselves. Maybe it would work if the hash table were preallocated to a huge size, and if we set the rehash threshold to 1.0. *//* Register PTR in malloc_debug. Abort if this is not possible (presumably due to the number of current allocations exceeding the size of malloc_debug.) */static voidregister_ptr (void *ptr, const char *file, int line){ int i; for (i = 0; i < countof (malloc_debug); i++) if (malloc_debug[i].ptr == NULL) { malloc_debug[i].ptr = ptr; malloc_debug[i].file = file; malloc_debug[i].line = line; return; } abort ();}/* Unregister PTR from malloc_debug. Abort if PTR is not present in malloc_debug. (This catches calling free() with a bogus pointer.) */static voidunregister_ptr (void *ptr){ int i; for (i = 0; i < countof (malloc_debug); i++) if (malloc_debug[i].ptr == ptr) { malloc_debug[i].ptr = NULL; return; } abort ();}/* Print the malloc debug stats that can be gathered from the above information. Currently this is the count of mallocs, frees, the difference between the two, and the dump of the contents of malloc_debug. The last part are the memory leaks. */voidprint_malloc_debug_stats (void){ int i; printf ("\nMalloc: %d\nFree: %d\nBalance: %d\n\n", malloc_count, free_count, malloc_count - free_count); for (i = 0; i < countof (malloc_debug); i++) if (malloc_debug[i].ptr != NULL) printf ("0x%08ld: %s:%d\n", (long)malloc_debug[i].ptr, malloc_debug[i].file, malloc_debug[i].line);}void *xmalloc_debug (size_t size, const char *source_file, int source_line){ void *ptr = xmalloc_real (size); ++malloc_count; register_ptr (ptr, source_file, source_line); return ptr;}voidxfree_debug (void *ptr, const char *source_file, int source_line){ assert (ptr != NULL); ++free_count; unregister_ptr (ptr); free (ptr);}void *xrealloc_debug (void *ptr, size_t newsize, const char *source_file, int source_line){ void *newptr = xrealloc_real (ptr, newsize); if (!ptr) { ++malloc_count; register_ptr (newptr, source_file, source_line); } else if (newptr != ptr) { unregister_ptr (ptr); register_ptr (newptr, source_file, source_line); } return newptr;}char *xstrdup_debug (const char *s, const char *source_file, int source_line){ char *copy = xstrdup_real (s); ++malloc_count; register_ptr (copy, source_file, source_line); return copy;}#endif /* DEBUG_MALLOC *//* 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;}/* Return a count of how many times CHR occurs in STRING. */intcount_char (const char *string, char chr){ const char *p; int count = 0; for (p = string; *p; p++) if (*p == chr) ++count; return count;}/* 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;}/* 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 non-NULL, the current time-in-seconds will be stored there. (#### This is misleading: one would expect TM would be used instead of the current time in that case. This design was probably influenced by the design time(2), and should be changed at some points. No callers use non-NULL TM anyway.) */char *time_str (time_t *tm){ static char output[15]; struct tm *ptm; time_t secs = time (tm); 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 = time (tm); 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 changedp = 0; if (!opt.lfilename) { opt.lfilename = unique_name (DEFAULT_LOGFILE, 0); changedp = 1; } 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 (changedp) 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 atime and mtime equal to the time specified with TM. */voidtouch (const char *file, time_t tm){#ifdef HAVE_STRUCT_UTIMBUF struct utimbuf times; times.actime = times.modtime = tm;#else time_t times[2]; times[0] = times[1] = tm;#endif 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. */int
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -