158 lines
2.6 KiB
C
158 lines
2.6 KiB
C
#include "file.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
enum mode {
|
|
MODE_R = 0x01u,
|
|
MODE_W = 0x02u,
|
|
MODE_A = 0x04u,
|
|
MODE_B = 0x08u,
|
|
MODE_X = 0x10u,
|
|
MODE_PLUS = 0x20u,
|
|
};
|
|
|
|
static int flags_from_mode(
|
|
const char *mode_string,
|
|
int *out_sysflags,
|
|
enum file_flags *out_fflags)
|
|
{
|
|
enum mode mode = 0;
|
|
int sysflags = 0;
|
|
enum file_flags fflags = 0;
|
|
|
|
for (int i = 0; mode_string[i]; i++) {
|
|
switch (mode_string[i]) {
|
|
case 'r':
|
|
if (mode & (MODE_R | MODE_W | MODE_A | MODE_X)) {
|
|
return -1;
|
|
}
|
|
|
|
mode |= MODE_R;
|
|
break;
|
|
case 'w':
|
|
if (mode & (MODE_R | MODE_W | MODE_A)) {
|
|
return -1;
|
|
}
|
|
mode |= MODE_W;
|
|
break;
|
|
case 'a':
|
|
if (mode & (MODE_R | MODE_W | MODE_A | MODE_X)) {
|
|
return -1;
|
|
}
|
|
break;
|
|
case '+':
|
|
if (mode & MODE_PLUS) {
|
|
return -1;
|
|
}
|
|
mode |= MODE_PLUS;
|
|
break;
|
|
case 'b':
|
|
if (mode & MODE_B) {
|
|
return -1;
|
|
}
|
|
mode |= MODE_B;
|
|
break;
|
|
case 'x':
|
|
if (mode & (MODE_R | MODE_A | MODE_X)) {
|
|
return -1;
|
|
}
|
|
mode |= MODE_X;
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
switch ((int)mode) {
|
|
case MODE_R | MODE_B:
|
|
fflags = FILE_BIN;
|
|
case MODE_R:
|
|
sysflags = O_RDONLY;
|
|
break;
|
|
|
|
case MODE_W | MODE_B:
|
|
fflags = FILE_BIN;
|
|
case MODE_W:
|
|
sysflags = O_WRONLY | O_TRUNC | O_CREAT;
|
|
break;
|
|
|
|
case MODE_A | MODE_B:
|
|
fflags = FILE_BIN;
|
|
case MODE_A:
|
|
sysflags = O_WRONLY | O_APPEND | O_CREAT;
|
|
break;
|
|
|
|
case MODE_R | MODE_PLUS | MODE_B:
|
|
fflags = FILE_BIN;
|
|
case MODE_R | MODE_PLUS:
|
|
sysflags = O_RDWR;
|
|
break;
|
|
|
|
case MODE_W | MODE_PLUS | MODE_B:
|
|
fflags = FILE_BIN;
|
|
case MODE_W | MODE_PLUS:
|
|
sysflags = O_RDWR | O_TRUNC | O_CREAT;
|
|
break;
|
|
|
|
case MODE_W | MODE_PLUS | MODE_X | MODE_B:
|
|
fflags = FILE_BIN;
|
|
case MODE_W | MODE_PLUS | MODE_X:
|
|
sysflags = O_RDWR | O_TRUNC | O_CREAT | O_EXCL;
|
|
break;
|
|
|
|
case MODE_A | MODE_PLUS | MODE_B:
|
|
fflags = FILE_BIN;
|
|
case MODE_A | MODE_PLUS:
|
|
sysflags = O_RDWR | O_APPEND | O_CREAT;
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct __opaque_file *fopen(const char *path, const char *mode)
|
|
{
|
|
int sysflags = 0;
|
|
enum file_flags fflags = 0;
|
|
if (flags_from_mode(mode, &sysflags, &fflags) != 0) {
|
|
__set_errno(EINVAL);
|
|
return NULL;
|
|
}
|
|
|
|
struct __opaque_file *out = malloc(sizeof *out);
|
|
if (!out) {
|
|
__set_errno(ENOMEM);
|
|
return NULL;
|
|
}
|
|
|
|
memset(out, 0x0, sizeof *out);
|
|
|
|
int fd = open(path, sysflags);
|
|
if (fd < 0) {
|
|
free(out);
|
|
__set_errno(-fd);
|
|
return NULL;
|
|
}
|
|
|
|
if (fflags & FILE_BIN) {
|
|
out->f_buffer_mode = _IOFBF;
|
|
} else {
|
|
out->f_buffer_mode = _IOLBF;
|
|
}
|
|
|
|
out->f_fd = fd;
|
|
out->f_flags = fflags;
|
|
__set_errno(SUCCESS);
|
|
|
|
return out;
|
|
}
|