📄 utils.c
字号:
#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, bool 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, bool binary, char **opened_name){ /* unique file name, based on NAME */ char *uname = unique_name (name, false); FILE *fp; while ((fp = fopen_excl (uname, binary)) == NULL && errno == EEXIST) { xfree (uname); uname = unique_name (name, false); } if (opened_name && fp != NULL) { if (fp) *opened_name = uname; else { *opened_name = NULL; xfree (uname); } } else xfree (uname); return fp;}/* Open the file for writing, with the addition that the file is opened "exclusively". This means that, if the file already exists, this function will *fail* and errno will be set to EEXIST. If BINARY is set, the file will be opened in binary mode, equivalent to fopen's "wb". If opening the file fails for any reason, including the file having previously existed, this function returns NULL and sets errno appropriately. */ FILE *fopen_excl (const char *fname, bool binary){ int fd;#ifdef O_EXCL int flags = O_WRONLY | O_CREAT | O_EXCL;# ifdef O_BINARY if (binary) flags |= O_BINARY;# endif fd = open (fname, flags, 0666); if (fd < 0) return NULL; return fdopen (fd, binary ? "wb" : "w");#else /* not O_EXCL */ /* Manually check whether the file exists. This is prone to race conditions, but systems without O_EXCL haven't deserved better. */ if (file_exists_p (fname)) { errno = EEXIST; return NULL; } return fopen (fname, binary ? "wb" : "w");#endif /* not O_EXCL */}/* 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 i, ret, quit = 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. */char *file_merge (const char *base, const char *file){ char *result; const char *cut = (const char *)strrchr (base, '/'); if (!cut) return xstrdup (file); result = xmalloc (cut - base + 1 + strlen (file) + 1); memcpy (result, base, cut - base); result[cut - base] = '/'; strcpy (result + (cut - base) + 1, file); return result;}/* Like fnmatch, but performs a case-insensitive match. */intfnmatch_nocase (const char *pattern, const char *string, int flags){#ifdef FNM_CASEFOLD /* The FNM_CASEFOLD flag started as a GNU extension, but it is now also present on *BSD platforms, and possibly elsewhere. */ return fnmatch (pattern, string, flags | FNM_CASEFOLD);#else /* Turn PATTERN and STRING to lower case and call fnmatch on them. */ char *patcopy = (char *) alloca (strlen (pattern) + 1); char *strcopy = (char *) alloca (strlen (string) + 1); char *p; for (p = patcopy; *pattern; pattern++, p++) *p = TOLOWER (*pattern); *p = '\0'; for (p = strcopy; *string; string++, p++) *p = TOLOWER (*string); *p = '\0'; return fnmatch (patcopy, strcopy, flags);#endif}static bool in_acclist (const char *const *, const char *, bool);/* Determine whether a file is acceptable to be followed, according to lists of patterns to accept/reject. */boolacceptable (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, true) && !in_acclist ((const char *const *)opt.rejects, s, true)); else return in_acclist ((const char *const *)opt.accepts, s, true); } else if (opt.rejects) return !in_acclist ((const char *const *)opt.rejects, s, true); return true;}/* Check if D2 is a subdirectory of D1. E.g. if D1 is `/something', subdir_p() will return true if and only if D2 begins with `/something/' or is exactly '/something'. */boolsubdir_p (const char *d1, const char *d2){ if (*d1 == '\0') return true; if (!opt.ignore_case) for (; *d1 && *d2 && (*d1 == *d2); ++d1, ++d2) ; else for (; *d1 && *d2 && (TOLOWER (*d1) == TOLOWER (*d2)); ++d1, ++d2) ; return *d1 == '\0' && (*d2 == '\0' || *d2 == '/');}/* Iterate through DIRLIST (which must be NULL-terminated), and return the first element that matches DIR, through wildcards or front comparison (as appropriate). */static booldir_matches_p (char **dirlist, const char *dir){ char **x; int (*matcher) (const char *, const char *, int) = opt.ignore_case ? fnmatch_nocase : fnmatch; for (x = dirlist; *x; x++) { /* Remove leading '/' */ char *p = *x + (**x == '/'); if (has_wildcards_p (p)) { if (matcher (p, dir, FNM_PATHNAME) == 0) break; } else { if (subdir_p (p, dir)) break; } } return *x ? true : false;}/* Returns whether DIRECTORY is acceptable for download, wrt the include/exclude lists. The leading `/' is ignored in paths; relative and absolute paths may be freely intermixed. */boolaccdir (const char *directory){ /* Remove starting '/'. */ if (*directory == '/') ++directory; if (opt.includes) { if (!dir_matches_p (opt.includes, directory)) return false; } if (opt.excludes) { if (dir_matches_p (opt.excludes, directory)) return false; } return true;}/* Return true if STRING ends with TAIL. For instance: match_tail ("abc", "bc", false) -> 1 match_tail ("abc", "ab", false) -> 0 match_tail ("abc", "abc", false) -> 1 If FOLD_CASE is true, the comparison will be case-insensitive. */boolmatch_tail (const char *string, const char *tail, bool fold_case){ int i, j; /* We want this to be fast, so we code two loops, one with case-folding, one without. */ if (!fold_case) { 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 true; else return false;}/* 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 false, don't do backward comparison -- just compare them normally. */static boolin_acclist (const char *const *accepts, const char *s, bool backward){ for (; *accepts; accepts++) { if (has_wildcards_p (*accepts)) { int res = opt.ignore_case ? fnmatch_nocase (*accepts, s, 0) : fnmatch (*accepts, s, 0); /* fnmatch returns 0 if the pattern *does* match the string. */ if (res == 0) return true; } else { if (backward) { if (match_tail (s, *accepts, opt.ignore_case)) return true; } else { int cmp = opt.ignore_case ? strcasecmp (s, *accepts) : strcmp (s, *accepts); if (cmp == 0) return true; } } } return false;}/* 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 true if S contains globbing wildcards (`*', `?', `[' or `]'). */boolhas_wildcards_p (const char *s){ for (; *s; s++) if (*s == '*' || *s == '?' || *s == '[' || *s == ']') return true; return false;}/* Return true 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! */boolhas_html_suffix_p (const char *fname){ char *suf; if ((suf = suffix (fname)) == NULL) return false; if (!strcasecmp (suf, "html")) return true; if (!strcasecmp (suf, "htm")) return true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -