kport.c (5951B)
1 /* 2 ** kport.c 3 ** Kernel Ports 4 ** See Copyright Notice in klisp.h 5 */ 6 7 #include <stdio.h> 8 #include <assert.h> 9 #include <string.h> 10 11 #include "kport.h" 12 #include "kobject.h" 13 #include "kstate.h" 14 #include "kmem.h" 15 #include "kerror.h" 16 #include "kstring.h" 17 #include "kbytevector.h" 18 #include "kgc.h" 19 #include "kpair.h" 20 21 bool kportp(TValue o) 22 { 23 return ttisport(o); 24 } 25 26 bool kinput_portp(TValue o) 27 { 28 return ttisport(o) && kport_is_input(o); 29 } 30 31 bool koutput_portp(TValue o) 32 { 33 return ttisport(o) && kport_is_output(o); 34 } 35 36 bool kbinary_portp(TValue o) 37 { 38 return ttisport(o) && kport_is_binary(o); 39 } 40 41 bool ktextual_portp(TValue o) 42 { 43 return ttisport(o) && kport_is_textual(o); 44 } 45 46 bool kfile_portp(TValue o) 47 { 48 return ttisfport(o); 49 } 50 51 bool kstring_portp(TValue o) 52 { 53 return ttismport(o) && kport_is_textual(o); 54 } 55 56 bool kbytevector_portp(TValue o) 57 { 58 return ttismport(o) && kport_is_binary(o); 59 } 60 61 bool kport_openp(TValue o) 62 { 63 klisp_assert(ttisport(o)); 64 return kport_is_open(o); 65 } 66 67 bool kport_closedp(TValue o) 68 { 69 klisp_assert(ttisport(o)); 70 return kport_is_closed(o); 71 } 72 73 /* XXX: per the c spec, this truncates the file if it exists! */ 74 /* Ask John: what would be best? Probably should also include delete, 75 file-exists? and a mechanism to truncate or append to a file, or 76 throw error if it exists. 77 Should use open, but it is non standard (fcntl.h, POSIX only) */ 78 79 /* GC: Assumes filename is rooted */ 80 TValue kmake_fport(klisp_State *K, TValue filename, bool writep, bool binaryp) 81 { 82 /* for now always use text mode */ 83 char *mode; 84 if (binaryp) 85 mode = writep? "wb": "rb"; 86 else 87 mode = writep? "w": "r"; 88 89 FILE *f = fopen(kstring_buf(filename), mode); 90 if (f == NULL) { 91 TValue mode_str = kstring_new_b(K, mode); 92 krooted_tvs_push(K, mode_str); 93 klispE_throw_errno_with_irritants(K, "fopen", 2, filename, mode_str); 94 return KINERT; 95 } else { 96 return kmake_std_fport(K, filename, writep, binaryp, f); 97 } 98 } 99 100 /* this is for creating ports for stdin/stdout/stderr & 101 also a helper for the above */ 102 103 /* GC: Assumes filename, name & si are rooted */ 104 TValue kmake_std_fport(klisp_State *K, TValue filename, bool writep, 105 bool binaryp, FILE *file) 106 { 107 FPort *new_port = klispM_new(K, FPort); 108 109 /* header + gc_fields */ 110 klispC_link(K, (GCObject *) new_port, K_TFPORT, 111 K_FLAG_CAN_HAVE_NAME | 112 (writep? K_FLAG_OUTPUT_PORT : K_FLAG_INPUT_PORT) | 113 (binaryp? K_FLAG_BINARY_PORT : 0)); 114 115 /* port specific fields */ 116 new_port->filename = filename; 117 new_port->file = file; 118 TValue tv_port = gc2fport(new_port); 119 /* line is 1-based and col is 0-based */ 120 kport_line(tv_port) = 1; 121 kport_col(tv_port) = 0; 122 123 return tv_port; 124 } 125 126 TValue kmake_mport(klisp_State *K, TValue buffer, bool writep, bool binaryp) 127 { 128 klisp_assert(!writep || ttisinert(buffer)); 129 klisp_assert(writep || (ttisbytevector(buffer) && binaryp) || 130 (ttisstring(buffer) && !binaryp)); 131 132 if (writep) { 133 buffer = binaryp? kbytevector_new_s(K, MINBYTEVECTORPORTBUFFER) : 134 kstring_new_s(K, MINSTRINGPORTBUFFER); 135 } 136 137 krooted_tvs_push(K, buffer); 138 139 MPort *new_port = klispM_new(K, MPort); 140 141 /* header + gc_fields */ 142 klispC_link(K, (GCObject *) new_port, K_TMPORT, 143 K_FLAG_CAN_HAVE_NAME | 144 (writep? K_FLAG_OUTPUT_PORT : K_FLAG_INPUT_PORT) | 145 (binaryp? K_FLAG_BINARY_PORT : 0)); 146 147 /* port specific fields */ 148 TValue tv_port = gc2mport(new_port); 149 kport_filename(tv_port) = G(K)->empty_string; /* XXX for now no filename */ 150 /* line is 1-based and col is 0-based */ 151 kport_line(tv_port) = 1; 152 kport_col(tv_port) = 0; 153 kmport_buf(tv_port) = buffer; 154 kmport_off(tv_port) = 0; /* no bytes read/written */ 155 krooted_tvs_pop(K); 156 return tv_port; 157 } 158 159 /* if the port is already closed do nothing */ 160 /* This is also called from GC, so it shouldn't throw any error */ 161 void kclose_port(klisp_State *K, TValue port) 162 { 163 assert(ttisport(port)); 164 165 if (!kport_is_closed(port)) { 166 if (ttisfport(port)) { 167 FILE *f = tv2fport(port)->file; 168 if (f != stdin && f != stderr && f != stdout) 169 fclose(f); /* it isn't necessary to check the close ret val */ 170 } 171 kport_set_closed(port); 172 } 173 174 return; 175 } 176 177 void kport_reset_source_info(TValue port) 178 { 179 /* line is 1-based and col is 0-based */ 180 kport_line(port) = 1; 181 kport_col(port) = 0; 182 } 183 184 void kport_update_source_info(TValue port, int32_t line, int32_t col) 185 { 186 kport_line(port) = line; 187 kport_col(port) = col; 188 } 189 190 /* Always grow by doubling the size (until min_size is reached) */ 191 /* GC: port should be rooted */ 192 void kmport_resize_buffer(klisp_State *K, TValue port, size_t min_size) 193 { 194 klisp_assert(ttismport(port)); 195 klisp_assert(kport_is_output(port)); 196 197 uint32_t old_size = (kport_is_binary(port))? 198 kbytevector_size(kmport_buf(port)) : 199 kstring_size(kmport_buf(port)); 200 uint64_t new_size = old_size; 201 202 while (new_size < min_size) { 203 new_size *= 2; 204 if (new_size > SIZE_MAX) 205 klispM_toobig(K); 206 } 207 208 if (new_size == old_size) 209 return; 210 211 if (kport_is_binary(port)) { 212 TValue new_bb = kbytevector_new_s(K, new_size); 213 uint32_t off = kmport_off(port); 214 if (off != 0) { 215 memcpy(kbytevector_buf(new_bb), 216 kbytevector_buf(kmport_buf(port)), 217 off); 218 } 219 kmport_buf(port) = new_bb; 220 } else { 221 TValue new_str = kstring_new_s(K, new_size); 222 uint32_t off = kmport_off(port); 223 if (off != 0) { 224 memcpy(kstring_buf(new_str), 225 kstring_buf(kmport_buf(port)), 226 off); 227 } 228 kmport_buf(port) = new_str; 229 } 230 }