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_linux.c (5191B)


      1 /* See LICENSE for license details. */
      2 #define os_path_sep s8("/")
      3 
      4 #define NULL ((void *)0)
      5 
      6 #include "jdict.c"
      7 
      8 #define PROT_READ     0x01
      9 #define PROT_WRITE    0x02
     10 #define MAP_PRIVATE   0x02
     11 #define MAP_ANONYMOUS 0x20
     12 
     13 #define O_RDONLY      0x00
     14 #define O_DIRECTORY   0x10000
     15 
     16 #define DT_REGULAR_FILE 8
     17 
     18 static i64 syscall1(i64, i64);
     19 static i64 syscall2(i64, i64, i64);
     20 static i64 syscall3(i64, i64, i64, i64);
     21 static i64 syscall6(i64, i64, i64, i64, i64, i64, i64);
     22 
     23 typedef struct {
     24 	u8   buf[2048];
     25 	iptr fd;
     26 	i32  buf_pos;
     27 	i32  buf_end;
     28 } LinuxDirectoryStream;
     29 
     30 /* NOTE: necessary garbage required by GCC/CLANG even when -nostdlib is used */
     31 __attribute((section(".text.memset")))
     32 void *memset(void *d, int c, usize n)
     33 {
     34 	return mem_clear(d, c, n);
     35 }
     36 
     37 static void
     38 os_exit(i32 code)
     39 {
     40 	syscall1(SYS_exit, code);
     41 	__builtin_unreachable();
     42 }
     43 
     44 static b32
     45 os_write(iptr fd, s8 raw)
     46 {
     47 	while (raw.len) {
     48 		size r = syscall3(SYS_write, fd, (i64)raw.s, raw.len);
     49 		if (r < 0) return 0;
     50 		raw = s8_cut_head(raw, r);
     51 	}
     52 	return 1;
     53 }
     54 
     55 static b32
     56 os_read_stdin(u8 *buf, size count)
     57 {
     58 	size rlen = syscall3(SYS_read, 0, (iptr)buf, count);
     59 	return rlen == count;
     60 }
     61 
     62 static s8
     63 os_read_whole_file(char *file, Arena *a, u32 arena_flags)
     64 {
     65 	__attribute((aligned(16))) u8 stat_buf[STAT_BUF_SIZE];
     66 	u64 status = syscall2(SYS_stat, (iptr)file, (iptr)stat_buf);
     67 	if (status > -4096UL) {
     68 		stream_append_s8(&error_stream, s8("failed to stat: "));
     69 		stream_append_s8(&error_stream, cstr_to_s8(file));
     70 		die(&error_stream);
     71 	}
     72 
     73 	u64 fd = syscall3(SYS_open, (iptr)file, O_RDONLY, 0);
     74 	if (fd > -4096UL) {
     75 		stream_append_s8(&error_stream, s8("failed to open: "));
     76 		stream_append_s8(&error_stream, cstr_to_s8(file));
     77 		die(&error_stream);
     78 	}
     79 
     80 	i64 *file_size = (i64 *)(stat_buf + STAT_SIZE_OFF);
     81 	s8 result = {.len = *file_size, .s = alloc(a, u8, *file_size, arena_flags|ARENA_NO_CLEAR)};
     82 	size rlen = syscall3(SYS_read, fd, (iptr)result.s, result.len);
     83 	syscall1(SYS_close, fd);
     84 
     85 	if (rlen != result.len) {
     86 		stream_append_s8(&error_stream, s8("failed to read whole file: "));
     87 		stream_append_s8(&error_stream, cstr_to_s8(file));
     88 		die(&error_stream);
     89 	}
     90 
     91 	return result;
     92 }
     93 
     94 static Arena
     95 os_new_arena(size cap)
     96 {
     97 	Arena a = {0};
     98 
     99 	size pagesize = PAGESIZE;
    100 	if (cap % pagesize != 0)
    101 		cap += pagesize - cap % pagesize;
    102 
    103 	u64 p = syscall6(SYS_mmap, 0, cap, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
    104 	if (p > -4096UL) {
    105 		return (Arena){0};
    106 	} else {
    107 		a.beg = (u8 *)p;
    108 	}
    109 	a.end = a.beg + cap;
    110 #ifdef _DEBUG_ARENA
    111 	a.min_capacity_remaining = cap;
    112 #endif
    113 	return a;
    114 }
    115 
    116 static PathStream
    117 os_begin_path_stream(Stream *dir_name, Arena *a, u32 arena_flags)
    118 {
    119 	stream_append_byte(dir_name, 0);
    120 	u64 fd = syscall3(SYS_open, (iptr)dir_name->data, O_DIRECTORY|O_RDONLY, 0);
    121 	dir_name->widx--;
    122 
    123 	if (fd > -4096UL) {
    124 		stream_append_s8(&error_stream, s8("os_begin_path_stream: failed to open: "));
    125 		stream_append_s8(&error_stream, (s8){.len = dir_name->widx, .s = dir_name->data});
    126 		die(&error_stream);
    127 	}
    128 
    129 	stream_append_byte(dir_name, '/');
    130 
    131 	LinuxDirectoryStream *lds = alloc(a, LinuxDirectoryStream, 1, arena_flags);
    132 	lds->fd = fd;
    133 	return (PathStream){.dir_name = dir_name, .dirfd = lds};
    134 }
    135 
    136 static void
    137 os_end_path_stream(PathStream *ps)
    138 {
    139 	LinuxDirectoryStream *lds = ps->dirfd;
    140 	syscall1(SYS_close, (iptr)lds->fd);
    141 	ps->dirfd = 0;
    142 }
    143 
    144 static s8
    145 os_get_valid_file(PathStream *ps, s8 match_prefix, Arena *a, u32 arena_flags)
    146 {
    147 	s8 result = {0};
    148 	LinuxDirectoryStream *lds = ps->dirfd;
    149 	if (lds) {
    150 		for (;;) {
    151 			if (lds->buf_pos >= lds->buf_end) {
    152 				u64 ret = syscall3(SYS_getdents64, lds->fd, (iptr)lds->buf,
    153 				                   sizeof(lds->buf));
    154 				if (ret > -4096UL) {
    155 					stream_append_s8(&error_stream, s8("os_get_valid_file: SYS_getdents"));
    156 					die(&error_stream);
    157 				}
    158 				if (ret == 0)
    159 					break;
    160 				lds->buf_end = ret;
    161 				lds->buf_pos = 0;
    162 			}
    163 			u16  record_len = *(u16 *)(lds->buf + lds->buf_pos + DIRENT_RECLEN_OFF);
    164 			u8   type       = lds->buf[lds->buf_pos + DIRENT_TYPE_OFF];
    165 			/* NOTE: technically this contains extra NULs but it doesn't matter
    166 			 * for this purpose. We need NUL terminated to call SYS_read */
    167 			s8   name       = {.len = record_len - 2 - DIRENT_NAME_OFF,
    168 			                   .s = lds->buf + lds->buf_pos + DIRENT_NAME_OFF};
    169 			lds->buf_pos += record_len;
    170 			if (type == DT_REGULAR_FILE) {
    171 				b32 valid = 1;
    172 				for (size i = 0; i < match_prefix.len; i++) {
    173 					if (match_prefix.s[i] != name.s[i]) {
    174 						valid = 0;
    175 						break;
    176 					}
    177 				}
    178 				if (valid) {
    179 					Stream dir_name = *ps->dir_name;
    180 					stream_append_s8(&dir_name, name);
    181 					result = os_read_whole_file((char *)dir_name.data, a,
    182 					                            arena_flags);
    183 					break;
    184 				}
    185 			}
    186 		}
    187 	}
    188 	return result;
    189 }
    190 
    191 void
    192 linux_main(i32 argc, char *argv[], char *envp[])
    193 {
    194 	(void)envp;
    195 
    196 	Arena memory = os_new_arena(1024 * MEGABYTE);
    197 
    198 	error_stream.fd   = 2;
    199 	error_stream.cap  = 4096;
    200 	error_stream.data = alloc(&memory, u8, error_stream.cap, ARENA_NO_CLEAR);
    201 
    202 	stdout_stream.fd   = 1;
    203 	stdout_stream.cap  = 8 * MEGABYTE;
    204 	stdout_stream.data = alloc(&memory, u8, error_stream.cap, ARENA_NO_CLEAR);
    205 
    206 	i32 result = jdict(&memory, argc, argv);
    207 
    208 	os_exit(result);
    209 }