📄 utils.c
字号:
remove_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. */longfile_size (const char *filename){ long size; /* We use fseek rather than stat to determine the file size because that way we can also verify whether the file is readable. Inspired by the POST patch by Arnaud Wylie. */ FILE *fp = fopen (filename, "rb"); if (!fp) return -1; fseek (fp, 0, SEEK_END); size = ftell (fp); fclose (fp); return size;}/* 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 something. 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 DIRECTORY. If some of the pathname components of DIRECTORY are missing, create them first. In case any mkdir() call fails, return its error status. Returns 0 on successful completion. The behaviour of this function should be identical to the behaviour of `mkdir -p' on systems where mkdir supports the `-p' option. */intmake_directory (const char *directory){ int quit = 0; int i; int ret = 0; char *dir; /* Make a copy of dir, to be able to write to it. Otherwise, the function is unsafe if called with a read-only char *argument. */ STRDUP_ALLOCA (dir, directory); /* If the first character of dir is '/', skip it (and thus enable creation of absolute-pathname directories. */ for (i = (*dir == '/'); 1; ++i) { for (; dir[i] && dir[i] != '/'; i++) ; if (!dir[i]) quit = 1; dir[i] = '\0'; /* Check whether the directory already exists. Allow creation of of intermediate directories to fail, as the initial path components are not necessarily directories! */ if (!file_exists_p (dir)) ret = mkdir (dir, 0777); else ret = 0; if (quit) break; else dir[i] = '/'; } return ret;}/* Merge BASE with FILE. BASE can be a directory or a file name, FILE should be a file name. file_merge("/foo/bar", "baz") => "/foo/baz" file_merge("/foo/bar/", "baz") => "/foo/bar/baz" file_merge("foo", "bar") => "bar" In other words, it's a simpler and gentler version of uri_merge_1. */char *file_merge (const char *base, const char *file){ char *result; const char *cut = (const char *)strrchr (base, '/'); if (!cut) return xstrdup (file); result = (char *)xmalloc (cut - base + 1 + strlen (file) + 1); memcpy (result, base, cut - base); result[cut - base] = '/'; strcpy (result + (cut - base) + 1, file); return result;}static int in_acclist PARAMS ((const char *const *, const char *, int));/* Determine whether a file is acceptable to be followed, according to lists of patterns to accept/reject. */intacceptable (const char *s){ int l = strlen (s); while (l && s[l] != '/') --l; if (s[l] == '/') s += (l + 1); if (opt.accepts) { if (opt.rejects) return (in_acclist ((const char *const *)opt.accepts, s, 1) && !in_acclist ((const char *const *)opt.rejects, s, 1)); else return in_acclist ((const char *const *)opt.accepts, s, 1); } else if (opt.rejects) return !in_acclist ((const char *const *)opt.rejects, s, 1); return 1;}/* Compare S1 and S2 frontally; S2 must begin with S1. E.g. if S1 is `/something', frontcmp() will return 1 only if S2 begins with `/something'. Otherwise, 0 is returned. */intfrontcmp (const char *s1, const char *s2){ for (; *s1 && *s2 && (*s1 == *s2); ++s1, ++s2); return !*s1;}/* Iterate through STRLIST, and return the first element that matches S, through wildcards or front comparison (as appropriate). */static char *proclist (char **strlist, const char *s, enum accd flags){ char **x; for (x = strlist; *x; x++) if (has_wildcards_p (*x)) { if (fnmatch (*x, s, FNM_PATHNAME) == 0) break; } else { char *p = *x + ((flags & ALLABS) && (**x == '/')); /* Remove '/' */ if (frontcmp (p, s)) break; } return *x;}/* Returns whether DIRECTORY is acceptable for download, wrt the include/exclude lists. If FLAGS is ALLABS, the leading `/' is ignored in paths; relative and absolute paths may be freely intermixed. */intaccdir (const char *directory, enum accd flags){ /* Remove starting '/'. */ if (flags & ALLABS && *directory == '/') ++directory; if (opt.includes) { if (!proclist (opt.includes, directory, flags)) return 0; } if (opt.excludes) { if (proclist (opt.excludes, directory, flags)) return 0; } return 1;}/* Return non-zero if STRING ends with TAIL. For instance: match_tail ("abc", "bc", 0) -> 1 match_tail ("abc", "ab", 0) -> 0 match_tail ("abc", "abc", 0) -> 1 If FOLD_CASE_P is non-zero, the comparison will be case-insensitive. */intmatch_tail (const char *string, const char *tail, int fold_case_p){ int i, j; /* We want this to be fast, so we code two loops, one with case-folding, one without. */ if (!fold_case_p) { for (i = strlen (string), j = strlen (tail); i >= 0 && j >= 0; i--, j--) if (string[i] != tail[j]) break; } else { for (i = strlen (string), j = strlen (tail); i >= 0 && j >= 0; i--, j--) if (TOLOWER (string[i]) != TOLOWER (tail[j])) break; } /* If the tail was exhausted, the match was succesful. */ if (j == -1) return 1; else return 0;}/* Checks whether string S matches each element of ACCEPTS. A list element are matched either with fnmatch() or match_tail(), according to whether the element contains wildcards or not. If the BACKWARD is 0, don't do backward comparison -- just compare them normally. */static intin_acclist (const char *const *accepts, const char *s, int backward){ for (; *accepts; accepts++) { if (has_wildcards_p (*accepts)) { /* fnmatch returns 0 if the pattern *does* match the string. */ if (fnmatch (*accepts, s, 0) == 0) return 1; } else { if (backward) { if (match_tail (s, *accepts, 0)) return 1; } else { if (!strcmp (s, *accepts)) return 1; } } } return 0;}/* Return the location of STR's suffix (file extension). Examples: suffix ("foo.bar") -> "bar" suffix ("foo.bar.baz") -> "baz" suffix ("/foo/bar") -> NULL suffix ("/foo.bar/baz") -> NULL */char *suffix (const char *str){ int i; for (i = strlen (str); i && str[i] != '/' && str[i] != '.'; i--) ; if (str[i++] == '.') return (char *)str + i; else return NULL;}/* Return non-zero if S contains globbing wildcards (`*', `?', `[' or `]'). */inthas_wildcards_p (const char *s){ for (; *s; s++) if (*s == '*' || *s == '?' || *s == '[' || *s == ']') return 1; return 0;}/* Return non-zero if FNAME ends with a typical HTML suffix. The following (case-insensitive) suffixes are presumed to be HTML files: html htm ?html (`?' matches one character) #### CAVEAT. This is not necessarily a good indication that FNAME refers to a file that contains HTML! */inthas_html_suffix_p (const char *fname){ char *suf; if ((suf = suffix (fname)) == NULL) return 0; if (!strcasecmp (suf, "html")) return 1; if (!strcasecmp (suf, "htm")) return 1; if (suf[0] && !strcasecmp (suf + 1, "html")) return 1; return 0;}/* Read a line from FP and return the pointer to freshly allocated storage. The storage space is obtained through malloc() and should be freed with free() when it is no longer needed. The length of the line is not limited, except by available memory. The newline character at the end of line is retained. The line is terminated with a zero character. After end-of-file is encountered without anything being read, NULL is returned. NULL is also returned on error. To distinguish between these two cases, use the stdio function ferror(). */char *read_whole_line (FILE *fp){ int length = 0; int bufsize = 82; char *line = (char *)xmalloc (bufsize); while (fgets (line + length, bufsize - length, fp)) { length += strlen (line + length); if (length == 0) /* Possible for example when reading from a binary file where a line begins with \0. */ continue; if (line[length - 1] == '\n') break; /* fgets() guarantees to read the whole line, or to use up the space we've given it. We can double the buffer unconditionally. */ bufsize <<= 1; line = xrealloc (line, bufsize); } if (length == 0 || ferror (fp)) { xfree (line); return NULL; } if (length + 1 < bufsize) /* Relieve the memory from our exponential greediness. We say `length + 1' because the terminating \0 is not included in LENGTH. We don't need to zero-terminate the string ourselves, though, because fgets() does that. */ line = xrealloc (line, length + 1); return line;}/* Read FILE into memory. A pointer to `struct file_memory' are returned; use struct element `content' to access file contents, and the element `length' to know the file length. `content' is *not* zero-terminated, and you should *not* read or write beyond the [0, length) range of characters. After you are done with the file contents, call read_file_free to release the memory. Depending on the operating system and the type of file that is being read, read_file() either mmap's the file into memory, or reads the file into the core using read(). If file is named "-", fileno(stdin) is used for reading instead. If you want to read from a real file named "-", use "./-" instead. */struct file_memory *read_file (const char *file){ int fd; struct file_memory *fm; long size; int inhibit_close = 0; /* Some magic in the finest tradition of Perl and its kin: if FILE is "-", just use stdin. */ if (HYPHENP (file)) { fd = fileno (stdin); inhibit_close = 1; /* Note that we don't inhibit mmap() in this case. If stdin is redirected from a regular file, mmap() will still work. */ } else fd = open (file, O_RDONLY); if (fd < 0) return NULL; fm = xmalloc (sizeof (struct file_memory));#ifdef HAVE_MMAP { struct stat buf; if (fstat (fd, &buf) < 0) goto mmap_lose; fm->length = buf.st_size; /* NOTE: As far as I know, the callers of this function never modify the file text. Relying on this would enable us to specify PROT_READ and MAP_SHARED for a marginal gain in efficiency, but at some cost to generality. */ fm->content = mmap (NULL, fm->length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fm->content == (char *)MAP_FAILED) goto mmap_lose; if (!inhibit_close) close (fd); fm->mmap_p = 1; return fm; } mmap_lose: /* The most common reason why mmap() fails is that FD does not point to a plain file. However, it's also possible that mmap() doesn't work for a particular type of file. Therefore, whenever mmap() fails, we just fall back to the regular method. */#endif /* HAVE_MMAP */ fm->length = 0; size = 512; /* number of bytes fm->contents can hold at any given time. */ fm->content = xmalloc (size); while (1) { long nread; if (fm->length > size / 2) { /* #### I'm not sure whether the whole exponential-growth thing makes sense with kernel read. On Linux at least,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -