jdict

command line tool for looking up terms in yomidict dictionaries
git clone anongit@rnpnr.xyz:jdict.git
Log | Files | Refs | Feed | README | LICENSE

platform_posix.c (3585B)


      1 #define _DEFAULT_SOURCE 1
      2 #include <dirent.h>
      3 #include <fcntl.h>
      4 #include <sys/mman.h>
      5 #include <sys/stat.h>
      6 #include <unistd.h>
      7 
      8 #define os_path_sep s8("/")
      9 
     10 #include "jdict.c"
     11 
     12 static void
     13 os_exit(i32 code)
     14 {
     15 	_exit(code);
     16 	unreachable();
     17 }
     18 
     19 static Arena
     20 os_new_arena(size cap)
     21 {
     22 	Arena a;
     23 
     24 	size pagesize = sysconf(_SC_PAGESIZE);
     25 	if (cap % pagesize != 0)
     26 		cap += pagesize - cap % pagesize;
     27 
     28 	a.beg = mmap(0, cap, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
     29 	if (a.beg == MAP_FAILED)
     30 		return (Arena){0};
     31 	a.end = a.beg + cap;
     32 #ifdef _DEBUG_ARENA
     33 	a.min_capacity_remaining = cap;
     34 #endif
     35 	return a;
     36 }
     37 
     38 static b32
     39 os_read_stdin(u8 *buf, size count)
     40 {
     41 	size rlen = read(STDIN_FILENO, buf, count);
     42 	return rlen == count;
     43 }
     44 
     45 static s8
     46 os_read_whole_file(char *file, Arena *a, u32 arena_flags)
     47 {
     48 
     49 	struct stat st;
     50 	if (stat(file, &st) < 0) {
     51 		stream_append_s8(&error_stream, s8("failed to stat: "));
     52 		stream_append_s8(&error_stream, cstr_to_s8(file));
     53 		die(&error_stream);
     54 	}
     55 
     56 	i32 fd = open(file, O_RDONLY);
     57 	if (fd < 0) {
     58 		stream_append_s8(&error_stream, s8("failed to open: "));
     59 		stream_append_s8(&error_stream, cstr_to_s8(file));
     60 		die(&error_stream);
     61 	}
     62 
     63 	s8 result = {.len = st.st_size, .s = alloc(a, u8, st.st_size, arena_flags|ARENA_NO_CLEAR)};
     64 	size rlen = read(fd, result.s, result.len);
     65 	close(fd);
     66 
     67 	if (rlen != result.len) {
     68 		stream_append_s8(&error_stream, s8("failed to read whole file: "));
     69 		stream_append_s8(&error_stream, cstr_to_s8(file));
     70 		die(&error_stream);
     71 	}
     72 
     73 	return result;
     74 }
     75 
     76 static b32
     77 os_write(iptr file, s8 raw)
     78 {
     79 	while (raw.len) {
     80 		size r = write(file, raw.s, raw.len);
     81 		if (r < 0) return 0;
     82 		raw = s8_cut_head(raw, r);
     83 	}
     84 	return 1;
     85 }
     86 
     87 static PathStream
     88 os_begin_path_stream(Stream *dir_name, Arena *a, u32 arena_flags)
     89 {
     90 	(void)a; (void)arena_flags;
     91 
     92 	stream_append_byte(dir_name, 0);
     93 	DIR *dir = opendir((char *)dir_name->data);
     94 	dir_name->widx--;
     95 	if (!dir) {
     96 		stream_append_s8(&error_stream, s8("opendir: failed to open: "));
     97 		stream_append_s8(&error_stream, (s8){.len = dir_name->widx, .s = dir_name->data});
     98 		die(&error_stream);
     99 	}
    100 	stream_append_byte(dir_name, '/');
    101 	return (PathStream){.dir_name = dir_name, .dirfd = dir};
    102 }
    103 
    104 static s8
    105 os_get_valid_file(PathStream *ps, s8 match_prefix, Arena *a, u32 arena_flags)
    106 {
    107 	s8 result = {0};
    108 	if (ps->dirfd) {
    109 		struct dirent *dent;
    110 		while ((dent = readdir(ps->dirfd)) != NULL) {
    111 			if (dent->d_type == DT_REG) {
    112 				b32 valid = 1;
    113 				/* NOTE: technically this contains extra NULs but it doesn't matter
    114 				 * for this purpose. We need NUL terminated to call read() */
    115 				s8 name = {.len = dent->d_reclen - 2 - offsetof(struct dirent, d_name),
    116 				           .s   = (u8 *)dent->d_name};
    117 				for (size i = 0; i < match_prefix.len; i++) {
    118 					if (match_prefix.s[i] != name.s[i]) {
    119 						valid = 0;
    120 						break;
    121 					}
    122 				}
    123 				if (valid) {
    124 					Stream dir_name = *ps->dir_name;
    125 					stream_append_s8(&dir_name, name);
    126 					result = os_read_whole_file((char *)dir_name.data, a,
    127 					                            arena_flags);
    128 					break;
    129 				}
    130 			}
    131 		}
    132 	}
    133 	return result;
    134 }
    135 
    136 static void
    137 os_end_path_stream(PathStream *ps)
    138 {
    139 	closedir(ps->dirfd);
    140 	ps->dirfd = 0;
    141 }
    142 
    143 i32
    144 main(i32 argc, char *argv[])
    145 {
    146 	Arena memory = os_new_arena(1024 * MEGABYTE);
    147 
    148 	error_stream.fd   = STDERR_FILENO;
    149 	error_stream.cap  = 4096;
    150 	error_stream.data = alloc(&memory, u8, error_stream.cap, ARENA_NO_CLEAR);
    151 
    152 	stdout_stream.fd   = STDOUT_FILENO;
    153 	stdout_stream.cap  = 8 * MEGABYTE;
    154 	stdout_stream.data = alloc(&memory, u8, error_stream.cap, ARENA_NO_CLEAR);
    155 
    156 	return jdict(&memory, argc, argv);
    157 }