Skip to content

Commit

Permalink
Further improve cocmd interpreter
Browse files Browse the repository at this point in the history
  • Loading branch information
jart committed Oct 12, 2022
1 parent 0cee831 commit 0f89140
Show file tree
Hide file tree
Showing 14 changed files with 278 additions and 181 deletions.
5 changes: 3 additions & 2 deletions libc/calls/calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,9 @@ int linkat(int, const char *, int, const char *, int);
int madvise(void *, uint64_t, int);
int memfd_create(const char *, unsigned int);
int mincore(void *, size_t, unsigned char *);
int mkdir(const char *, uint32_t);
int mkdirat(int, const char *, uint32_t);
int mkdir(const char *, unsigned);
int mkdirat(int, const char *, unsigned);
int makedirs(const char *, unsigned);
int mkfifo(const char *, uint32_t);
int mkfifoat(int, const char *, uint32_t);
int mknod(const char *, uint32_t, uint64_t);
Expand Down
2 changes: 1 addition & 1 deletion libc/calls/clock_nanosleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@
* @return 0 on success, or errno on error
* @raise EINTR when a signal got delivered while we were waiting
* @raise ENOTSUP if `clock` is known but we can't use it here
* @raise EFAULT if `req` or null or bad memory was passed
* @raise EINVAL if `clock` is unknown to current platform
* @raise EINVAL if `flags` has an unrecognized value
* @raise EINVAL if `req->tv_nsec ∉ [0,1000000000)`
* @raise EFAULT if bad memory was passed
* @raise ENOSYS on bare metal
* @returnserrno
* @norestart
Expand Down
101 changes: 59 additions & 42 deletions libc/stdio/makedirs.c → libc/calls/makedirs.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,58 +17,75 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/mem/mem.h"
#include "libc/str/path.h"
#include "libc/str/str.h"

static char *DirName(const char *path) {
char *dirp;
if (!(path = strdup(path))) return 0;
dirp = strdup(dirname(path));
free(path);
return dirp;
}

static int MakeDirs(const char *path, unsigned mode, int e) {
int rc;
char *dir;
if (!mkdir(path, mode) || errno == EEXIST) {
errno = e;
return 0;
}
if (errno != ENOENT) {
return -1;
}
if (!(dir = DirName(path))) {
return -1;
}
if (strcmp(dir, path)) {
rc = MakeDirs(dir, mode, e);
} else {
rc = -1;
}
free(dir);
if (rc == -1) return -1;
errno = e;
if (!mkdir(path, mode) || errno == EEXIST) {
errno = e;
return 0;
} else {
return -1;
}
}
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"

/**
* Recursively creates directory a.k.a. folder.
* Creates directory and parent components.
*
* This function won't fail if the directory already exists.
* This function is similar to mkdir() except it iteratively creates
* parent directories and it won't fail if the directory already exists.
*
* @param path is a UTF-8 string, preferably relative w/ forward slashes
* @param mode can be, for example, 0755
* @return 0 on success or -1 w/ errno
* @raise EEXIST if named file already exists as non-directory
* @raise ENOTDIR if directory component in `path` existed as non-directory
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
* @raise EROFS if parent directory is on read-only filesystem
* @raise ENOSPC if file system or parent directory is full
* @raise EACCES if write permission was denied on parent directory
* @raise EACCES if search permission was denied on component in `path`
* @raise ENOENT if `path` is an empty string
* @raise ELOOP if loop was detected resolving components of `path`
* @asyncsignalsafe
* @threadsafe
*/
int makedirs(const char *path, unsigned mode) {
return MakeDirs(path, mode, errno);
int c, e, i, n;
struct stat st;
char buf[PATH_MAX];

e = errno;
n = strlen(path);
if (n >= PATH_MAX) return enametoolong();
memcpy(buf, path, n + 1);
i = n;

// descend
while (i) {
if (!mkdir(buf, mode)) break;
if (errno == EEXIST) {
if (i == n) goto CheckTop;
break;
}
if (errno != ENOENT) return -1;
while (i && _isdirsep(buf[i - 1])) buf[--i] = 0;
while (i && !_isdirsep(buf[i - 1])) buf[--i] = 0;
}

// ascend
for (;;) {
if (mkdir(buf, mode)) {
if (errno != EEXIST) return -1;
if (i == n) goto CheckTop;
}
if (i == n) break;
while (i < n && !_isdirsep((c = path[i]))) buf[i++] = c;
while (i < n && _isdirsep((c = path[i]))) buf[i++] = c;
}

Finish:
errno = e;
return 0;

CheckTop:
if (stat(path, &st)) return -1;
if (S_ISDIR(st.st_mode)) goto Finish;
return eexist();
}
21 changes: 14 additions & 7 deletions libc/calls/mkdir.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"

/**
* Creates directory a.k.a. folder.
Expand All @@ -36,9 +31,21 @@
* @param path is a UTF-8 string, preferably relative w/ forward slashes
* @param mode can be, for example, 0755
* @return 0 on success or -1 w/ errno
* @error ENAMETOOLONG if >246 characters on NT
* @raise EEXIST if named file already exists
* @raise ENOTDIR if directory component in `path` existed as non-directory
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
* @raise EROFS if parent directory is on read-only filesystem
* @raise ENOSPC if file system or parent directory is full
* @raise EACCES if write permission was denied on parent directory
* @raise EACCES if search permission was denied on component in `path`
* @raise ENOENT if a component within `path` didn't exist
* @raise ENOENT if `path` is an empty string
* @raise ELOOP if loop was detected resolving components of `path`
* @see makedirs() which is higher-level
* @see mkdirat() for modern call
* @asyncsignalsafe
* @see makedirs()
* @threadsafe
*/
int mkdir(const char *path, unsigned mode) {
return mkdirat(AT_FDCWD, path, mode);
Expand Down
19 changes: 15 additions & 4 deletions libc/calls/mkdirat.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,23 @@
/**
* Creates directory a.k.a. folder.
*
* @param dirfd is normally AT_FDCWD but if it's an open directory and
* @param dirfd is normally `AT_FDCWD` but if it's an open directory and
* path is relative, then path becomes relative to dirfd
* @param path is a UTF-8 string, preferably relative w/ forward slashes
* @param mode can be, for example, 0755
* @return 0 on success or -1 w/ errno
* @error EEXIST, ENOTDIR, ENAMETOOLONG, EACCES, ENOENT
* @param mode is permissions bits, which is usually 0755
* @return 0 on success, or -1 w/ errno
* @raise EEXIST if named file already exists
* @raise EBADF if `path` is relative and `dirfd` isn't `AT_FDCWD` or valid
* @raise ENOTDIR if directory component in `path` existed as non-directory
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
* @raise EROFS if parent directory is on read-only filesystem
* @raise ENOSPC if file system or parent directory is full
* @raise EACCES if write permission was denied on parent directory
* @raise EACCES if search permission was denied on component in `path`
* @raise ENOENT if a component within `path` didn't exist
* @raise ENOENT if `path` is an empty string
* @raise ELOOP if loop was detected resolving components of `path`
* @asyncsignalsafe
* @see makedirs()
*/
Expand Down
Loading

0 comments on commit 0f89140

Please sign in to comment.