Fix basename() and dirname()

This commit is contained in:
Justine Tunney 2022-04-06 00:13:44 -07:00
parent 9b11206ae3
commit 04d39d47f1
41 changed files with 489 additions and 207 deletions

View file

@ -121,7 +121,7 @@ void SpellChecker(void) {
printf("word: ");
fflush(stdout);
if (getline(&line, &linesize, stdin) > 0) {
query = strtolower(chomp(line));
query = strtolower(_chomp(line));
if (critbit0_contains(&words, query)) {
printf("ok\r\n");
} else {
@ -147,7 +147,7 @@ void SpellChecker(void) {
void LoadWords(void) {
CHECK_NOTNULL((f = fopen("/zip/usr/share/dict/words", "r")));
while (getline(&line, &linesize, f) > 0) {
critbit0_insert(&words, strtolower(chomp(line)));
critbit0_insert(&words, strtolower(_chomp(line)));
}
CHECK_NE(-1, fclose(f));
}

View file

@ -1673,7 +1673,7 @@ char* GetLine(void) {
static char* line;
static size_t linesize;
if (getline(&line, &linesize, stdin) > 0) {
return chomp(line);
return _chomp(line);
} else {
return NULL;
}

View file

@ -17,15 +17,23 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/nt/enum/filetype.h"
#include "libc/nt/files.h"
#include "libc/sysv/errfuns.h"
textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) {
int64_t res;
if (!__isfdkind(fd, kFdFile)) return ebadf();
if (SetFilePointerEx(g_fds.p[fd].handle, offset, &res, whence)) {
return res;
if (__isfdkind(fd, kFdFile)) {
if (GetFileType(g_fds.p[fd].handle) != kNtFileTypePipe) {
if (SetFilePointerEx(g_fds.p[fd].handle, offset, &res, whence)) {
return res;
} else {
return __winerr();
}
} else {
return espipe();
}
} else {
return __winerr();
return ebadf();
}
}

View file

@ -53,8 +53,8 @@
static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
uint32_t flags, int32_t mode) {
uint32_t br;
int64_t handle;
uint32_t br, err;
char16_t path16[PATH_MAX];
uint32_t perm, share, disp, attr;
if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1;
@ -96,6 +96,18 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
} else {
attr = kNtFileAttributeNormal;
if (flags & _O_DIRECTORY) attr |= kNtFileFlagBackupSemantics;
if (~mode & 0200) {
attr |= kNtFileAttributeReadonly;
if (!IsTiny() && disp == kNtCreateAlways) {
// iron out esoteric unix/win32 inconsistency (golang #38225)
if ((handle = CreateFile(path16, perm, share, &kNtIsInheritable,
kNtTruncateExisting, kNtFileAttributeNormal,
0)) != -1 ||
(errno != ENOENT && errno != ENOTDIR)) {
return handle;
}
}
}
}
flags |= kNtFileFlagOverlapped;
if (~flags & _O_INDEXED) attr |= kNtFileAttributeNotContentIndexed;
@ -105,14 +117,7 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
if (flags & _O_DIRECT) attr |= kNtFileFlagNoBuffering;
if (flags & _O_NDELAY) attr |= kNtFileFlagWriteThrough;
if ((handle = CreateFile(path16, perm, share, &kNtIsInheritable, disp, attr,
0)) != -1) {
} else if (GetLastError() == kNtErrorFileExists &&
((flags & _O_CREAT) &&
(flags & _O_TRUNC))) { /* TODO(jart): What was this? */
handle = eisdir();
}
return handle;
return CreateFile(path16, perm, share, &kNtIsInheritable, disp, attr, 0);
}
static textwindows ssize_t sys_open_nt_console(int dirfd,

View file

@ -21,13 +21,13 @@
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/isslash.internal.h"
#include "libc/fmt/itoa.h"
#include "libc/nt/createfile.h"
#include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/creationdisposition.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filesharemode.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
@ -44,7 +44,7 @@ static void openanon_genpath(const char *name, struct OpenAnon *state,
if (!name) name = "openanon";
for (i = 0; p < pe; ++i) {
if (!(c = name[i])) break;
if (isslash(c)) c = '_';
if (_isdirsep(c)) c = '_';
*p++ = c;
}
*p++ = '.';

View file

@ -29,6 +29,7 @@
#include "libc/mem/alloca.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/str/tpenc.h"
#include "libc/str/utf16.h"

View file

@ -17,19 +17,42 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/conv.h"
#include "libc/fmt/isslash.internal.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
/**
* Returns pointer to last filename component in path.
* Returns pointer to last filename component in path, e.g.
*
* path dirname() basename()
*
* . . .
* .. . ..
* / / /
* usr . usr
* /usr/ / usr
* /usr/lib /usr lib
*
* Both / and \ are are considered valid component separators on all
* platforms. Trailing slashes are ignored. We don't grant special
* consideration to things like foo/., c:/, \\?\Volume, etc.
*
* @param path is NUL-terminated UTF-8 path
* @return pointer inside path or path itself
* @param path is UTF-8 and may be mutated, but not expanded in length
* @return pointer to path, or inside path, or to a special r/o string
* @see dirname()
* @see SUSv2
*/
textstartup char *basename(const char *path) {
return basename_n(path, strlen(path));
char *basename(char *path) {
size_t i;
if (path && *path) {
i = strlen(path) - 1;
for (; i && _isdirsep(path[i]); i--) {
path[i] = 0;
}
for (; i && !_isdirsep(path[i - 1]);) {
i--;
}
return path + i;
} else {
return ".";
}
}

View file

@ -1,49 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/conv.h"
#include "libc/fmt/isslash.internal.h"
/**
* Returns pointer to last filename component in path.
*
* Both / and \ are are considered valid component separators on all
* platforms. Trailing slashes are ignored. We don't grant special
* consideration to things like foo/., c:/, \\?\Volume, etc.
*
* @param path is UTF-8 path
* @param size is byte length of path
* @return pointer inside path or path itself
*/
textstartup char *basename_n(const char *path, size_t size) {
size_t i, l;
if (size) {
if (isslash(path[size - 1])) {
l = size - 1;
while (l && isslash(path[l - 1])) --l;
if (!l) return (/*unconst*/ char *)&path[size - 1];
size = l;
}
for (i = size; i > 0; --i) {
if (isslash(path[i - 1])) {
return (/*unconst*/ char *)&path[i];
}
}
}
return (/*unconst*/ char *)path;
}

View file

@ -42,13 +42,13 @@ size_t wcsxfrm(wchar_t *, const wchar_t *, size_t);
cosmopolitan § conversion » time
*/
int64_t DosDateTimeToUnix(unsigned, unsigned) dontthrow;
struct timeval WindowsTimeToTimeVal(int64_t) dontthrow;
struct timespec WindowsTimeToTimeSpec(int64_t) dontthrow;
int64_t TimeSpecToWindowsTime(struct timespec) dontthrow;
int64_t TimeValToWindowsTime(struct timeval) dontthrow;
struct timeval WindowsDurationToTimeVal(int64_t) dontthrow;
struct timespec WindowsDurationToTimeSpec(int64_t) dontthrow;
int64_t DosDateTimeToUnix(unsigned, unsigned) libcesque nosideeffect;
struct timeval WindowsTimeToTimeVal(int64_t) libcesque nosideeffect;
struct timespec WindowsTimeToTimeSpec(int64_t) libcesque nosideeffect;
int64_t TimeSpecToWindowsTime(struct timespec) libcesque nosideeffect;
int64_t TimeValToWindowsTime(struct timeval) libcesque nosideeffect;
struct timeval WindowsDurationToTimeVal(int64_t) libcesque nosideeffect;
struct timespec WindowsDurationToTimeSpec(int64_t) libcesque nosideeffect;
static inline struct NtFileTime MakeFileTime(int64_t x) {
return (struct NtFileTime){(uint32_t)x, (uint32_t)(x >> 32)};
@ -69,9 +69,7 @@ static inline int64_t ReadFileTime(struct NtFileTime t) {
*/
char *dirname(char *);
char *basename(const char *) nosideeffect;
char *basename_n(const char *, size_t) nosideeffect;
bool isabspath(const char *) paramsnonnull() nosideeffect;
char *basename(char *);
char *stripext(char *);
char *stripexts(char *);

View file

@ -17,34 +17,42 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/conv.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#define ISSLASH(c) (c == '/' || c == '\\')
#define ISDELIM(c) (ISSLASH(c) || c == '.')
/**
* Returns directory portion of path.
* Returns directory portion of path, e.g.
*
* This returns "." if path doesn't have slashes. If path is empty then
* this returns empty string.
* path dirname() basename()
*
* . . .
* .. . ..
* / / /
* usr . usr
* /usr/ / usr
* /usr/lib /usr lib
*
* @param s is mutated and must not be NULL
* @param path is UTF-8 and may be mutated, but not expanded in length
* @return pointer to path, or inside path, or to a special r/o string
* @see basename()
* @see SUSv2
*/
char *dirname(char *s) {
size_t i, n;
if (!(n = strlen(s))) return s;
while (n && ISDELIM(s[n - 1])) --n;
if (n) {
while (n && !ISSLASH(s[n - 1])) --n;
if (n) {
while (n && ISDELIM(s[n - 1])) --n;
if (!n) ++n;
} else {
s[n++] = '.';
char *dirname(char *path) {
size_t i;
if (path && *path) {
i = strlen(path) - 1;
for (; _isdirsep(path[i]); i--) {
if (!i) return "/";
}
for (; !_isdirsep(path[i]); i--) {
if (!i) return ".";
}
for (; _isdirsep(path[i]); i--) {
if (!i) return "/";
}
path[i + 1] = 0;
return path;
} else {
++n;
return ".";
}
s[n] = '\0';
return s;
}

View file

@ -1,10 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_FMT_ISSLASH_H_
#define COSMOPOLITAN_LIBC_FMT_ISSLASH_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
forceinline bool isslash(int c) {
return c == '/' || c == '\\';
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_FMT_ISSLASH_H_ */

View file

@ -36,6 +36,7 @@ kDos2Errno:
.e kNtErrorBadNetName,ENOENT
.e kNtErrorBadNetResp,ENETDOWN
.e kNtErrorBadPathname,ENOENT
.e kNtErrorFileExists,EEXIST
.e kNtErrorCannotMake,EACCES
.e kNtErrorCommitmentLimit,ENOMEM
.e kNtErrorConnectionAborted,ECONNABORTED

View file

@ -19,6 +19,7 @@
#include "libc/calls/calls.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/path.h"
/**
* Returns current working directory.

View file

@ -25,7 +25,7 @@
*
* This function is similar to getline() except it'll truncate lines
* exceeding size. The line ending marker is included and may be removed
* using chomp().
* using _chomp().
*/
char *fgets(char *s, int size, FILE *f) {
int c;

View file

@ -36,7 +36,7 @@
* @return number of bytes read >0, including delim, excluding NUL,
* or -1 w/ errno on EOF or error; see ferror() and feof()
* @note this function can't punt EINTR to caller
* @see getline(), chomp(), gettok_r()
* @see getline(), _chomp(), gettok_r()
*/
ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
char *p;

View file

@ -23,7 +23,7 @@
*
* This function delegates to getdelim(), which provides further
* documentation. Concerning lines, please note the \n or \r\n are
* included in results, and can be removed with chomp().
* included in results, and can be removed with _chomp().
*
* @param line is the caller's buffer (in/out) which is extended
* automatically. *line may be NULL but only if *n is 0;

View file

@ -24,7 +24,7 @@
* @param line is NULL-propagating
* @see getline
*/
char *chomp(char *line) {
char *_chomp(char *line) {
size_t i;
if (line) {
for (i = strlen(line); i--;) {

View file

@ -24,7 +24,16 @@
* @param line is NULL-propagating
* @see getline
*/
char16_t *chomp16(char16_t *line) {
if (line) line[strcspn16(line, u"\r\n")] = '\0';
char16_t *_chomp16(char16_t *line) {
size_t i;
if (line) {
for (i = strlen16(line); i--;) {
if (line[i] == '\r' || line[i] == '\n') {
line[i] = '\0';
} else {
break;
}
}
}
return line;
}

154
libc/str/classifypath.c Normal file
View file

@ -0,0 +1,154 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
/**
* Classifies file path name.
*
* For the purposes of this function, we always consider backslash
* interchangeable with forward slash, even though the underlying
* operating system might not. Therefore, for the sake of clarity,
* remaining documentation will only use the forward slash.
*
* This function behaves the same on all platforms. For instance, this
* function will categorize `C:/FOO.BAR` as a DOS path, even if you're
* running on UNIX rather than DOS.
*
* If you wish to check if a pathname is absolute, in a manner that's
* inclusive of DOS drive paths, DOS rooted paths, in addition to the
* New Technology UNC paths, then you may do the following:
*
* if (_classifypath(str) & _PATH_ABS) { ... }
*
* To check if path is a relative path:
*
* if (~_classifypath(str) & _PATH_ABS) { ... }
*
* Please note the above check includes rooted paths such as `\foo`
* which is considered absolute by MSDN and we consider it absolute
* although, it's technically relative to the current drive letter.
*
* Please note that `/foo/bar` is an absolute path on Windows, even
* though it's actually a rooted path that's considered relative to
* current drive by WIN32.
*
* @return integer value that's one of following:
* - `0` if non-weird relative path e.g. `c`
* - `_PATH_ABS` if absolute (or rooted dos) path e.g. `/`
* - `_PATH_DOS` if `c:`, `d:foo` i.e. drive-relative path
* - `_PATH_ABS|_PATH_DOS` if proper dos path e.g. `c:/foo`
* - `_PATH_DOS|_PATH_DEV` if dos device path e.g. `nul`, `conin$`
* - `_PATH_ABS|_PATH_WIN` if `//c`, `//?c`, etc.
* - `_PATH_ABS|_PATH_WIN|_PATH_DEV` if `//./⋯`, `//?/⋯`
* - `_PATH_ABS|_PATH_WIN|_PATH_DEV|_PATH_ROOT` if `//.` or `//?`
* - `_PATH_ABS|_PATH_NT` e.g. `\??\\` (undoc. strict backslash)
* @see "The Definitive Guide on Win32 to NT Path Conversion", James
* Forshaw, Google Project Zero Blog, 2016-02-29
* @see "Naming Files, Paths, and Namespaces", MSDN 01/04/2021
*/
int _classifypath(const char *s) {
if (s) {
switch (s[0]) {
case 0: // ""
return 0;
default:
if (!SupportsWindows()) {
return 0;
}
if (((((s[0] == 'a' || s[0] == 'A') && // aux
(s[1] == 'u' || s[1] == 'U') && //
(s[2] == 'x' || s[2] == 'X')) || //
((s[0] == 'p' || s[0] == 'P') && // prn
(s[1] == 'r' || s[1] == 'R') && //
(s[2] == 'n' || s[2] == 'N')) || //
((s[0] == 'n' || s[0] == 'N') && // nul
(s[1] == 'u' || s[1] == 'U') && //
(s[2] == 'l' || s[2] == 'L')) || //
((s[0] == 'c' || s[0] == 'C') && // con
(s[1] == 'o' || s[1] == 'O') && //
(s[2] == 'n' || s[2] == 'N'))) && //
!s[3]) ||
((((s[0] == 'l' || s[0] == 'L') && // lpt
(s[1] == 'p' || s[1] == 'P') && //
(s[2] == 't' || s[2] == 'T')) || //
((s[0] == 'c' || s[0] == 'C') && // com
(s[1] == 'o' || s[1] == 'O') && //
(s[2] == 'm' || s[2] == 'M'))) && //
('1' <= s[3] && s[3] <= '9') && //
!s[4])) {
return _PATH_DOS | _PATH_DEV;
}
switch (s[1]) {
case ':':
switch (s[2]) {
case 0: // c:
default: // c:wut⋯
return _PATH_DOS;
case '/': // c:/⋯
case '\\': // c:\⋯
return _PATH_ABS | _PATH_DOS;
}
default:
return 0;
}
case '\\':
if (SupportsWindows()) {
if (s[1] == '?' && s[2] == '?') {
if (!s[3]) {
return _PATH_ABS | _PATH_NT | _PATH_ROOT; // \??\⋯
} else if (s[3] == '\\') {
return _PATH_ABS | _PATH_NT; // \??\⋯
}
}
}
// fallthrough
case '/':
if (!SupportsWindows()) {
return _PATH_ABS;
}
switch (s[1]) {
case 0: // /
default: // /⋯
return _PATH_ABS;
case '/':
case '\\':
switch (s[2]) {
case 0: // //
default: // //⋯
return _PATH_ABS | _PATH_WIN;
case '.':
case '?':
switch (s[3]) {
case 0: // //? or //.
return _PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT;
default: // //?⋯ or //.⋯
return _PATH_ABS | _PATH_WIN;
case '/':
case '\\': // //?/⋯ or //./⋯
return _PATH_ABS | _PATH_WIN | _PATH_DEV;
}
}
}
}
} else {
return 0;
}
}

View file

@ -43,8 +43,8 @@ static textwindows bool shouldquotedos(const char16_t c) {
* Escapes command so DOS can run it.
* @see Iain Patterson's NSSM for original code in public domain
*/
textwindows bool escapedos(char16_t *buffer, unsigned buflen,
const char16_t *unquoted, unsigned len) {
textwindows bool _escapedos(char16_t *buffer, unsigned buflen,
const char16_t *unquoted, unsigned len) {
unsigned i, j, n;
if (len > buflen - 1) return false;
bool escape = false;

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
@ -16,33 +16,21 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
/**
* Returns true if pathname is absolute, e.g.
* Returns true if pathname is considered absolute.
*
* - `/home/jart/foo.txt` is absolute
* - `C:/Users/jart/foo.txt` is absolute on NT
* - `C:\Users\jart\foo.txt` is absolute on NT
* - `\??\C:\Users\jart\foo.txt` is absolute on NT
* - `\\.\C:\Users\jart\foo.txt` is absolute on NT
* - `/Users/jart/foo.txt` we consider it absolute enough on NT
* - `\Users\jart\foo.txt` we consider it absolute enough on NT
* - `C:/Users/jart/foo.txt` is absolute on Windows
* - `C:\Users\jart\foo.txt` is absolute on Windows
* - `\??\C:\Users\jart\foo.txt` is absolute on Windows
* - `\\.\C:\Users\jart\foo.txt` is absolute on Windows
* - `/Users/jart/foo.txt` is effectively absolute on Windows
* - `\Users\jart\foo.txt` is effectively absolute on Windows
*
* Please note that the recommended approach to using Cosmopolitan is to
* not use absolute paths at all. If you do use absolute paths then it's
* a good idea on Windows to stay within the C: drive. This is because
* Cosmopolitan Libc doesn't create a virtual filesystem layer and
* instead just replaces `\` characters with `/`.
*/
bool _isabspath(const char *p) {
if (*p == '/') {
return true;
}
if (IsWindows() &&
(*p == '/' || *p == '\\' || (isalpha(p[0]) && p[1] == ':'))) {
return true;
}
return false;
bool _isabspath(const char *path) {
return _classifypath(path) & _PATH_ABS;
}

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
@ -16,23 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/conv.h"
#include "libc/fmt/isslash.internal.h"
#include "libc/dce.h"
#include "libc/str/str.h"
/**
* Returns true if pathname could be absolute on any known platform.
*
* The ones we know about are System V (/foo/bar), DOS (C:\foo\bar),
* Windows NT (\\.\C:\foo\bar), Google Cloud (gs://bucket/foo/bar), etc.
* Returns true if character is directory separator slash.
*/
bool isabspath(const char *path) {
if (isslash(*path)) return true;
for (; *path; ++path) {
if (isslash(*path)) return false;
if (*path == ':') {
++path;
if (isslash(*path)) return true;
}
}
return false;
bool _isdirsep(int c) {
return c == '/' || c == '\\';
}

20
libc/str/path.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef COSMOPOLITAN_LIBC_STR_PATH_H_
#define COSMOPOLITAN_LIBC_STR_PATH_H_
#define _PATH_ABS 1
#define _PATH_DEV 2
#define _PATH_ROOT 4
#define _PATH_DOS 8
#define _PATH_WIN 16
#define _PATH_NT 32
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int _classifypath(const char *) libcesque nosideeffect;
bool _isabspath(const char *) libcesque strlenesque;
bool _isdirsep(int) libcesque pureconst;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_STR_PATH_H_ */

View file

@ -196,17 +196,16 @@ char *strerror(int) returnsnonnull dontthrow nocallback;
long a64l(const char *);
char *l64a(long);
char *strntolower(char *, size_t);
char *strtolower(char *) paramsnonnull();
char *strntoupper(char *, size_t);
char *strtoupper(char *) paramsnonnull();
char *chomp(char *);
char16_t *chomp16(char16_t *);
wchar_t *wchomp(wchar_t *);
bool _istext(const void *, size_t);
bool _isutf8(const void *, size_t);
bool _isabspath(const char *) strlenesque;
bool escapedos(char16_t *, unsigned, const char16_t *, unsigned);
char *strntolower(char *, size_t) libcesque;
char *strtolower(char *) libcesque paramsnonnull();
char *strntoupper(char *, size_t) libcesque;
char *strtoupper(char *) libcesque paramsnonnull();
char *_chomp(char *) libcesque;
char16_t *_chomp16(char16_t *) libcesque;
wchar_t *_wchomp(wchar_t *) libcesque;
bool _istext(const void *, size_t) libcesque;
bool _isutf8(const void *, size_t) libcesque;
bool _escapedos(char16_t *, unsigned, const char16_t *, unsigned) libcesque;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § strings » multibyte

View file

@ -24,7 +24,16 @@
* @param line is NULL-propagating
* @see getline
*/
wchar_t *wchomp(wchar_t *line) {
if (line) line[wcscspn(line, L"\r\n")] = '\0';
wchar_t *_wchomp(wchar_t *line) {
size_t i;
if (line) {
for (i = wcslen(line); i--;) {
if (line[i] == '\r' || line[i] == '\n') {
line[i] = '\0';
} else {
break;
}
}
}
return line;
}

View file

@ -17,11 +17,12 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/conv.h"
#include "libc/runtime/gc.internal.h"
#include "libc/x/x.h"
/**
* Returns directory portion of path.
*/
char *xdirname(const char *path) {
return dirname(xstrdup(path));
return xstrdup(dirname(gc(xstrdup(path))));
}

View file

@ -23,10 +23,10 @@
/**
* Reads line from stream.
*
* @return allocated line that needs free() and usually chomp() too,
* @return allocated line that needs free() and usually _chomp() too,
* or NULL on ferror() or feof()
* @see getdelim() for a more difficult api
* @see chomp()
* @see _chomp()
*/
char *xgetline(FILE *f) {
char *p;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/safemacros.internal.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/x/x.h"

View file

@ -25,7 +25,7 @@ void LogCertificate(const char *msg, mbedtls_x509_crt *cert) {
if (LOGGABLE(kLogDebug)) {
if ((s = malloc((n = 15000)))) {
if (mbedtls_x509_crt_info(s, n, " ", cert) > 0) {
DEBUGF("%s\n%s", msg, chomp(s));
DEBUGF("%s\n%s", msg, _chomp(s));
}
free(s);
}

View file

@ -54,9 +54,9 @@ TEST(mkdir, testPathIsDirectory_EEXIST) {
EXPECT_EQ(EEXIST, errno);
}
TEST(makedirs, testEmptyString_ENOENT) {
TEST(makedirs, testEmptyString_EEXIST) {
EXPECT_EQ(-1, makedirs("", 0755));
EXPECT_EQ(ENOENT, errno);
EXPECT_EQ(EEXIST, errno);
}
TEST(mkdirat, testRelativePath_opensRelativeToDirFd) {

View file

@ -21,25 +21,33 @@
#include "libc/mem/mem.h"
#include "libc/testlib/testlib.h"
TEST(basename, test) {
EXPECT_STREQ("", basename(""));
EXPECT_STREQ("/", basename("/"));
EXPECT_STREQ("hello", basename("hello"));
EXPECT_STREQ("there", basename("hello/there"));
EXPECT_STREQ("yo", basename("hello/there/yo"));
#define BASENAME(x) basename(gc(strdup(x)))
TEST(basename, testRegularExamples) {
EXPECT_STREQ("lib", BASENAME("/usr/lib"));
EXPECT_STREQ("lib", BASENAME("usr/lib"));
EXPECT_STREQ("usr", BASENAME("/usr/"));
EXPECT_STREQ("usr", BASENAME("usr"));
EXPECT_STREQ("/", BASENAME("/"));
EXPECT_STREQ(".", BASENAME("."));
EXPECT_STREQ("..", BASENAME(".."));
}
TEST(basename, testIrregularExamples) {
EXPECT_STREQ(".", basename(0));
EXPECT_STREQ(".", basename(""));
}
TEST(basename, testTrailingSlash_isIgnored) {
/* should be "foo" but basename() doesn't allocate memory */
EXPECT_STREQ("foo/", basename("foo/"));
EXPECT_STREQ("foo//", basename("foo//"));
EXPECT_STREQ("foo", BASENAME("foo/"));
EXPECT_STREQ("foo", BASENAME("foo//"));
}
TEST(basename, testOnlySlashes_oneSlashOnlyVasily) {
EXPECT_STREQ("/", basename("///"));
EXPECT_STREQ("/", BASENAME("///"));
}
TEST(basename, testWindows_isGrantedRespect) {
EXPECT_STREQ("there", basename("hello\\there"));
EXPECT_STREQ("yo", basename("hello\\there\\yo"));
EXPECT_STREQ("there", BASENAME("hello\\there"));
EXPECT_STREQ("yo", BASENAME("hello\\there\\yo"));
}

View file

@ -26,12 +26,11 @@
TEST(dirname, test) {
EXPECT_STREQ("/usr/lib", dirname(gc(strdup("/usr/lib/foo.bar"))));
EXPECT_STREQ("/usr", dirname(gc(strdup("/usr/lib"))));
EXPECT_STREQ("/usr", dirname(gc(strdup("/usr/lib"))));
EXPECT_STREQ("usr", dirname(gc(strdup("usr/lib"))));
EXPECT_STREQ("/", dirname(gc(strdup("/usr/"))));
EXPECT_STREQ(".", dirname(gc(strdup("usr"))));
EXPECT_STREQ("/", dirname(gc(strdup("/"))));
EXPECT_STREQ(".", dirname(gc(strdup("hello"))));
EXPECT_STREQ(".", dirname(gc(strdup("."))));
EXPECT_STREQ(".", dirname(gc(strdup(".."))));
EXPECT_STREQ("", dirname(gc(strdup(""))));
EXPECT_STREQ(".", dirname(gc(strdup(""))));
}

View file

@ -0,0 +1,116 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
TEST(isabspath, testUniversal) {
ASSERT_TRUE(_isabspath("/home/jart/foo.txt"));
}
TEST(isabspath, testDosPaths) {
ASSERT_FALSE(_isabspath("C:"));
ASSERT_FALSE(_isabspath("C:foo.txt"));
ASSERT_TRUE(_isabspath("C:/"));
ASSERT_TRUE(_isabspath("C:/Users/jart/foo.txt"));
ASSERT_TRUE(_isabspath("C:\\Users\\jart\\foo.txt"));
ASSERT_TRUE(_isabspath("\\Users\\jart\\foo.txt"));
}
TEST(isabspath, testWin32Paths) {
ASSERT_TRUE(_isabspath("\\\\?\\C:\\.."));
ASSERT_TRUE(_isabspath("\\\\.\\C:\\Users\\jart\\foo.txt"));
}
TEST(isabspath, testNtPaths) {
ASSERT_TRUE(_isabspath("\\??\\C:\\Users\\jart\\foo.txt"));
}
TEST(_classifypath, test) {
EXPECT_EQ(0, _classifypath(""));
EXPECT_EQ(0, _classifypath("xyz"));
EXPECT_EQ(_PATH_DOS | _PATH_DEV, _classifypath("CON"));
EXPECT_EQ(_PATH_DOS | _PATH_DEV, _classifypath("NUL"));
EXPECT_EQ(0, _classifypath(":"));
EXPECT_EQ(_PATH_DOS, _classifypath("::"));
EXPECT_EQ(_PATH_DOS, _classifypath(":::"));
EXPECT_EQ(_PATH_DOS, _classifypath("::::"));
EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("::\\"));
EXPECT_EQ(_PATH_ABS, _classifypath("\\"));
EXPECT_EQ(_PATH_ABS, _classifypath("\\:"));
EXPECT_EQ(_PATH_ABS, _classifypath("\\C:"));
EXPECT_EQ(_PATH_ABS, _classifypath("\\C:\\"));
EXPECT_EQ(_PATH_ABS, _classifypath("/"));
EXPECT_EQ(_PATH_ABS, _classifypath("/:"));
EXPECT_EQ(_PATH_ABS, _classifypath("/C:"));
EXPECT_EQ(_PATH_ABS, _classifypath("/C:/"));
EXPECT_EQ(0, _classifypath("C"));
EXPECT_EQ(_PATH_DOS, _classifypath("C:"));
EXPECT_EQ(_PATH_DOS, _classifypath("C:a"));
EXPECT_EQ(_PATH_DOS, _classifypath("C:a\\"));
EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:\\"));
EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:/"));
EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:\\a"));
EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:/a"));
EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:\\\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\;"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f\\b\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f\\b"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f"));
EXPECT_EQ(_PATH_ABS | _PATH_NT, _classifypath("\\??\\"));
EXPECT_EQ(_PATH_ABS | _PATH_NT, _classifypath("\\??\\UNC"));
EXPECT_EQ(_PATH_ABS | _PATH_NT, _classifypath("\\??\\UNC\\"));
EXPECT_EQ(_PATH_ABS, _classifypath("\\?"));
EXPECT_EQ(_PATH_ABS, _classifypath("\\?\\"));
EXPECT_EQ(_PATH_ABS, _classifypath("\\?\\UNC"));
EXPECT_EQ(_PATH_ABS, _classifypath("\\?\\UNC\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("\\\\?\\UNC\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT,
_classifypath("\\\\?"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\??"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\??\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\??\\C:\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT,
_classifypath("\\\\."));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("\\\\.\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("\\\\.\\C:\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\/"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("/\\"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("//"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("///"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("//;"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT,
_classifypath("//?"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT,
_classifypath("/\\?"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT,
_classifypath("\\/?"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("//??"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT,
_classifypath("//."));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT,
_classifypath("\\/."));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT,
_classifypath("/\\."));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("//./"));
EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("//./C:/"));
}

View file

@ -3063,7 +3063,7 @@ typedef struct {
typedef struct {
char *filename; /* module filename */
char *basename; /* module base name */
char *basename_; /* module base name */
JSWorkerMessagePipe *recv_pipe, *send_pipe;
} WorkerFuncArgs;
@ -3231,10 +3231,10 @@ static void *worker_func(void *opaque)
js_std_add_helpers(ctx, -1, NULL);
if (!JS_RunModule(ctx, args->basename, args->filename))
if (!JS_RunModule(ctx, args->basename_, args->filename))
js_std_dump_error(ctx);
free(args->filename);
free(args->basename);
free(args->basename_);
free(args);
js_std_loop(ctx);
@ -3315,7 +3315,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target,
goto oom_fail;
bzero(args, sizeof(*args));
args->filename = strdup(filename);
args->basename = strdup(basename);
args->basename_ = strdup(basename);
/* ports */
args->recv_pipe = js_new_message_pipe();
@ -3349,7 +3349,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target,
JS_FreeCString(ctx, filename);
if (args) {
free(args->filename);
free(args->basename);
free(args->basename_);
js_free_message_pipe(args->recv_pipe);
js_free_message_pipe(args->send_pipe);
free(args);

View file

@ -960,7 +960,7 @@ int main(int argc, char *argv[]) {
fin = fdopen(pipefds[0], "r");
t = NewTrace();
for (ev = 0, lineno = 1; !interrupted && (line = xgetline(fin)); ++lineno) {
chomp(line);
_chomp(line);
Parse(t, line, lineno);
free(line);
for (; ev < t->events.n && !t->events.p[ev].is_interrupted; ++ev) {

View file

@ -152,7 +152,7 @@ int main(int argc, char *argv[]) {
int y, x;
ShowCrashReports();
f = stdin;
while ((s = chomp(xgetline(f)))) {
while ((s = _chomp(xgetline(f)))) {
n = strwidth(s, 0);
xn = MAX(xn, n);
T = xrealloc(T, ++yn * sizeof(*T));

View file

@ -113,6 +113,7 @@
(let ((stab (copy-syntax-table)))
(with-syntax-table stab
(modify-syntax-entry ?+ " ")
(modify-syntax-entry ?* " ")
(let ((thing (thing-at-point 'symbol no-properties)))
(when thing
(intern thing))))))
@ -146,6 +147,7 @@
;; M-3 C-c C-c Compile w/ MODE=rel
;; M-4 C-c C-c Compile w/ MODE=dbg
;; M-5 C-c C-c Compile w/ MODE=""
;; M-7 C-c C-c Compile w/ MODE=tinylinux
;; M-8 C-c C-c Compile w/ llvm
;; M-9 C-c C-c Compile w/ chibicc
@ -162,6 +164,7 @@
((eq arg 3) "rel")
((eq arg 4) "dbg")
((eq arg 5) "")
((eq arg 7) "tinylinux")
((eq arg 8) "llvm")
(default default)
((cosmo-intest) "dbg")

View file

@ -38,7 +38,7 @@ int main(int argc, char *argv[]) {
ShowCrashReports();
f = fopen("/tmp/syms.txt", "r");
memset(tabs, '\t', 64);
while ((sym = chomp(xgetline(f)))) {
while ((sym = _chomp(xgetline(f)))) {
if (strlen(sym)) {
printf("imp\t");

View file

@ -120,7 +120,7 @@ int main(int argc, char *argv[]) {
}
} else {
while ((getline(&line_, &linecap_, stdin)) != -1) {
processarg(chomp(line_));
processarg(_chomp(line_));
}
free_s(&line_);
}

View file

@ -131,7 +131,7 @@ void processfile(void) {
int col, s;
size_t off, len;
while ((getline(&line_, &linecap_, fi_)) != -1) {
chomp(line_);
_chomp(line_);
len = strlen(line_);
s = concat(&pool_, line_, len + 1);
if (len < USHRT_MAX) {

View file

@ -148,7 +148,7 @@ void ProcessFile(void) {
fg1 = -1u;
glyph1 = -1u;
while ((getline(&line_, &linecap_, in_)) != -1) {
p = chomp(line_);
p = _chomp(line_);
sscanf(p, "%x, %u,%u,%u", &color, &bg, &fg, &glyph);
if (color != color1) {
if (color1 != -1u) {