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 (5273B)


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