/
niceSOFT
/
wget2
Обзор
Документация
Войти
/
niceSOFT
/
wget2
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/utils.c
195 строк
6 KB
Tim Rühsen
* src/utils.c: Fix slash handling in paths for Win32
25 июл 2026, 00:00
Не верифицирован
25 июл 2026, 00:00
e996d5e
Код
Авторство
О чём код?
/* * Copyright (c) 2018-2026 Free Software Foundation, Inc. * * This file is part of Wget. * * Wget 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 3 of the License, or * (at your option) any later version. * * Wget 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 Wget. If not, see <https://www.gnu.org/licenses/>. * * * Utility functions */ #include <config.h> #include <sys/stat.h> #include <sys/types.h> #include <errno.h> #include <string.h> #include <glob.h> #include "filename.h" // ISSLASH #include "wget_main.h" #include "wget_utils.h" // this function should be called protected by a mutex - else race conditions will happen void mkdir_path(const char *_fname, bool is_file) { char *p1, *p2, *fname; char buf[1024]; fname = wget_strmemcpy_a(buf, sizeof(buf), _fname, strlen(_fname)); #ifdef _WIN32 for (p1 = fname + 1; *p1 && (p2 = strpbrk(p1, "/\\")); p1 = p2 + 1) { #else for (p1 = fname + 1; *p1 && (p2 = strchr(p1, '/')); p1 = p2 + 1) { #endif int rc; char sep = *p2; *p2 = 0; // replace path separator // relative paths should have been normalized earlier, // but for security reasons, don't trust myself... if (*p1 == '.' && p1[1] == '.') error_printf_exit(_("Internal error: Unexpected relative path: '%s'\n"), fname); rc = mkdir(fname, 0755); if (rc < 0 && errno != EEXIST) debug_printf("mkdir(%s)=%d errno=%d\n", fname, rc, errno); if (rc) { struct stat st; int err = errno; if (err == EEXIST && stat(fname, &st) == 0 && (st.st_mode & S_IFMT) == S_IFREG) { // we have a file in the way... move it away and retry int renamed = 0; for (int fnum = 1; fnum <= 999 && !renamed; fnum++) { char tmp[1024], *dst = tmp; if (wget_snprintf(tmp, sizeof(tmp), "%s.%d", fname, fnum) >= sizeof(tmp)) { dst = wget_aprintf("%s.%d", fname, fnum); } if (access(dst, F_OK) != 0 && rename(fname, dst) == 0) renamed = 1; if (dst != tmp) xfree(dst); } if (renamed) { rc = mkdir(fname, 0755); if (rc) { error_printf(_("Failed to make directory '%s' (errno=%d)\n"), fname, errno); *p2 = sep; // restore path separator break; } } else error_printf(_("Failed to rename '%s' (errno=%d)\n"), fname, errno); } else if (err != EEXIST) { error_printf(_("Failed to make directory '%s' (errno=%d)\n"), fname, err); *p2 = sep; // restore path separator break; } } else debug_printf("created dir %s\n", fname); *p2 = sep; // restore path separator } if (fname != buf) xfree(fname); // If the path does not represent a file, we want to also create the // directory for the last part if (!is_file) { int rc = mkdir(_fname, 0755); if (rc < 0 && errno != EEXIST) debug_printf("mkdir(%s)=%d errno=%d\n", _fname, rc, errno); } } char *shell_expand(const char *fname) { char *expanded_str = NULL; if (*fname == '~') { char *slash = strchrnul(fname, '/'); #ifdef WIN32 if (!*slash) slash = strchrnul(fname, '\\'); #endif expanded_str = wget_strnglob(fname, slash - fname, GLOB_TILDE | GLOB_ONLYDIR | GLOB_NOCHECK); } // Either the string does not start with a "~", or the glob expansion // failed. In both cases, return the original string back return expanded_str ? expanded_str : wget_strdup(fname); } /** * \param[in] fname File name to sanitize * \param[out] esc Pointer to the buffer where sanitized file name * will be stored. Should be at least * strlen(\p fname) * 3 + 1 bytes large. * \param[in] mode Mode of operation * \return Either \p fname if no escaping took place, else \p esc. * * This functions aims to be Wget1 compatible (--restrict-file-names). * See https://en.wikipedia.org/wiki/Comparison_of_file_systems * * Sanitizes file names by percent-escaping platform-specific illegal characters. */ char *wget_restrict_file_name(const char *fname, char *esc, int mode) { if (!fname || !esc || mode == WGET_RESTRICT_NAMES_NONE) return (char *) fname; const char *s; char *dst; bool escaped = false; bool lowercase = mode & WGET_RESTRICT_NAMES_LOWERCASE; bool uppercase = mode & WGET_RESTRICT_NAMES_UPPERCASE; bool mode_unix = mode & WGET_RESTRICT_NAMES_UNIX; bool windows = mode & WGET_RESTRICT_NAMES_WINDOWS; bool nocontrol = mode & WGET_RESTRICT_NAMES_NOCONTROL; bool ascii = mode & WGET_RESTRICT_NAMES_ASCII; for (dst = esc, s = fname; *s; s++) { signed char c = *s; if (lowercase && c >= 'A' && c <= 'Z') { // isupper() also returns true for chars > 0x7f, the test is not EBCDIC compatible ;-) c |= 0x20; escaped = true; } else if (uppercase && c >= 'a' && c <= 'z') { // islower() also returns true for chars > 0x7f, the test is not EBCDIC compatible ;-) c &= ~0x20; escaped = true; } if ((mode_unix && (c >= 1 && c <= 31)) || (windows && (c < 32 || strchr("\\<>:\"|?*", c))) || (!nocontrol && ((c >= 1 && c <= 31) || c == 127)) || (ascii && c <= 31)) { char nibble; *dst++ = '%'; *dst++ = (nibble = ((unsigned char) c >> 4)) >= 10 ? nibble + 'A' - 10 : nibble + '0'; *dst++ = (nibble = (c & 0xf)) >= 10 ? nibble + 'A' - 10 : nibble + '0'; escaped = true; } else { *dst++ = c; } } if (escaped) { *dst = 0; return esc; } return (char *)fname; }