Unified · Split
| @@ -0,0 +1,534 @@ | ||
| 1 | +implement Bufio; | |
| 2 | + | |
| 3 | +include "sys.m"; | |
| 4 | + sys: Sys; | |
| 5 | + | |
| 6 | +include "bufio.m"; | |
| 7 | + | |
| 8 | +UTFself: con 16r80; # ascii and UTF sequences are the same (<) | |
| 9 | +Maxrune: con 8; # could probably be Sys->UTFmax | |
| 10 | +Bufsize: con Sys->ATOMICIO; | |
| 11 | + | |
| 12 | +Filler: adt | |
| 13 | +{ | |
| 14 | + iobuf: ref Iobuf; | |
| 15 | + fill: BufioFill; | |
| 16 | + next: cyclic ref Filler; | |
| 17 | +}; | |
| 18 | + | |
| 19 | +fillers: ref Filler; | |
| 20 | + | |
| 21 | +create(filename: string, mode, perm: int): ref Iobuf | |
| 22 | +{ | |
| 23 | + if (sys == nil) | |
| 24 | + sys = load Sys Sys->PATH; | |
| 25 | + if ((fd := sys->create(filename, mode, perm)) == nil) | |
| 26 | + return nil; | |
| 27 | + return ref Iobuf(fd, array[Bufsize+Maxrune] of byte, 0, 0, 0, big 0, big 0, mode, mode); | |
| 28 | +} | |
| 29 | + | |
| 30 | +open(filename: string, mode: int): ref Iobuf | |
| 31 | +{ | |
| 32 | + if (sys == nil) | |
| 33 | + sys = load Sys Sys->PATH; | |
| 34 | + if ((fd := sys->open(filename, mode)) == nil) | |
| 35 | + return nil; | |
| 36 | + return ref Iobuf(fd, array[Bufsize+Maxrune] of byte, 0, 0, 0, big 0, big 0, mode, mode); | |
| 37 | +} | |
| 38 | + | |
| 39 | +fopen(fd: ref Sys->FD, mode: int): ref Iobuf | |
| 40 | +{ | |
| 41 | + if (sys == nil) | |
| 42 | + sys = load Sys Sys->PATH; | |
| 43 | + if ((filpos := sys->seek(fd, big 0, 1)) < big 0) | |
| 44 | + filpos = big 0; | |
| 45 | + return ref Iobuf(fd, array[Bufsize+Maxrune] of byte, 0, 0, 0, filpos, filpos, mode, mode); | |
| 46 | +} | |
| 47 | + | |
| 48 | +sopen(input: string): ref Iobuf | |
| 49 | +{ | |
| 50 | + return aopen(array of byte input); | |
| 51 | +} | |
| 52 | + | |
| 53 | +aopen(b: array of byte): ref Iobuf | |
| 54 | +{ | |
| 55 | + if (sys == nil) | |
| 56 | + sys = load Sys Sys->PATH; | |
| 57 | + return ref Iobuf(nil, b, 0, len b, 0, big 0, big 0, OREAD, OREAD); | |
| 58 | +} | |
| 59 | + | |
| 60 | +readchunk(b: ref Iobuf): int | |
| 61 | +{ | |
| 62 | + if (b.fd == nil){ | |
| 63 | + if ((f := filler(b)) != nil){ | |
| 64 | + if ((n := f.fill->fill(b)) == EOF) | |
| 65 | + nofill(b); | |
| 66 | + return n; | |
| 67 | + } | |
| 68 | + return EOF; | |
| 69 | + } | |
| 70 | + if (b.filpos != b.bufpos + big b.size) { | |
| 71 | + s := b.bufpos + big b.size; | |
| 72 | + if (sys->seek(b.fd, s, 0) != s) | |
| 73 | + return ERROR; | |
| 74 | + b.filpos = s; | |
| 75 | + } | |
| 76 | + i := len b.buffer - b.size - 1; | |
| 77 | + if(i > Bufsize) | |
| 78 | + i = Bufsize; | |
| 79 | + if ((i = sys->read(b.fd, b.buffer[b.size:], i)) <= 0) { | |
| 80 | + if(i < 0) | |
| 81 | + return ERROR; | |
| 82 | + return EOF; | |
| 83 | + } | |
| 84 | + b.size += i; | |
| 85 | + b.filpos += big i; | |
| 86 | + return i; | |
| 87 | +} | |
| 88 | + | |
| 89 | +writechunk(b: ref Iobuf): int | |
| 90 | +{ | |
| 91 | + err := (b.fd == nil); | |
| 92 | + if (b.filpos != b.bufpos) { | |
| 93 | + if (sys->seek(b.fd, b.bufpos, 0) != b.bufpos) | |
| 94 | + err = 1; | |
| 95 | + b.filpos = b.bufpos; | |
| 96 | + } | |
| 97 | + if ((size := b.size) > Bufsize) | |
| 98 | + size = Bufsize; | |
| 99 | + if (sys->write(b.fd, b.buffer, size) != size) | |
| 100 | + err = 1; | |
| 101 | + b.filpos += big size; | |
| 102 | + b.size -= size; | |
| 103 | + if (b.size) { | |
| 104 | + b.dirty = 1; | |
| 105 | + b.buffer[0:] = b.buffer[Bufsize:Bufsize+b.size]; | |
| 106 | + } else | |
| 107 | + b.dirty = 0; | |
| 108 | + b.bufpos += big size; | |
| 109 | + b.index -= size; | |
| 110 | + if(err) | |
| 111 | + return ERROR; | |
| 112 | + return size; | |
| 113 | +} | |
| 114 | + | |
| 115 | +Iobuf.close(b: self ref Iobuf) | |
| 116 | +{ | |
| 117 | + if (b.fd == nil) { | |
| 118 | + nofill(b); | |
| 119 | + return; | |
| 120 | + } | |
| 121 | + if (b.dirty) | |
| 122 | + b.flush(); | |
| 123 | + b.fd = nil; | |
| 124 | + b.buffer = nil; | |
| 125 | +} | |
| 126 | + | |
| 127 | +Iobuf.flush(b: self ref Iobuf): int | |
| 128 | +{ | |
| 129 | + if (b.fd == nil) | |
| 130 | + return ERROR; | |
| 131 | + if (b.lastop == OREAD){ | |
| 132 | + b.bufpos = b.filpos; | |
| 133 | + b.size = 0; | |
| 134 | + return 0; | |
| 135 | + } | |
| 136 | + while (b.dirty) { | |
| 137 | + if (writechunk(b) < 0) | |
| 138 | + return ERROR; | |
| 139 | + if (b.index < 0) { | |
| 140 | + b.bufpos += big b.index; | |
| 141 | + b.index = 0; | |
| 142 | + } | |
| 143 | + } | |
| 144 | + return 0; | |
| 145 | +} | |
| 146 | + | |
| 147 | +Iobuf.seek(b: self ref Iobuf, off: big, start: int): big | |
| 148 | +{ | |
| 149 | + npos: big; | |
| 150 | + | |
| 151 | + if (b.fd == nil){ | |
| 152 | + if(filler(b) != nil) | |
| 153 | + return big ERROR; | |
| 154 | + } | |
| 155 | + case (start) { | |
| 156 | + 0 => # absolute address | |
| 157 | + npos = off; | |
| 158 | + 1 => # offset from current location | |
| 159 | + npos = b.bufpos + big b.index + off; | |
| 160 | + off = npos; | |
| 161 | + start = Sys->SEEKSTART; | |
| 162 | + 2 => # offset from EOF | |
| 163 | + npos = big -1; | |
| 164 | + * => return big ERROR; | |
| 165 | + } | |
| 166 | + if (b.bufpos <= npos && npos < b.bufpos + big b.size) { | |
| 167 | + b.index = int(npos - b.bufpos); | |
| 168 | + return npos; | |
| 169 | + } | |
| 170 | + if (b.fd == nil || b.dirty && b.flush() < 0) | |
| 171 | + return big ERROR; | |
| 172 | + b.size = 0; | |
| 173 | + b.index = 0; | |
| 174 | + if ((s := sys->seek(b.fd, off, start)) < big 0) { | |
| 175 | + b.bufpos = b.filpos; | |
| 176 | + return big ERROR; | |
| 177 | + } | |
| 178 | + b.bufpos = b.filpos = s; | |
| 179 | + return s; | |
| 180 | +} | |
| 181 | + | |
| 182 | +Iobuf.offset(b: self ref Iobuf): big | |
| 183 | +{ | |
| 184 | + return b.bufpos + big b.index; | |
| 185 | +} | |
| 186 | + | |
| 187 | +write2read(b: ref Iobuf): int | |
| 188 | +{ | |
| 189 | + while (b.dirty) | |
| 190 | + if (b.flush() < 0) | |
| 191 | + return ERROR; | |
| 192 | + b.bufpos = b.filpos; | |
| 193 | + b.size = 0; | |
| 194 | + b.lastop = OREAD; | |
| 195 | + if ((r := readchunk(b)) < 0) | |
| 196 | + return r; | |
| 197 | + if (b.index > b.size) | |
| 198 | + return EOF; | |
| 199 | + return 0; | |
| 200 | +} | |
| 201 | + | |
| 202 | +Iobuf.read(b: self ref Iobuf, buf: array of byte, n: int): int | |
| 203 | +{ | |
| 204 | + if (b.mode == OWRITE) | |
| 205 | + return ERROR; | |
| 206 | + if (b.lastop != OREAD){ | |
| 207 | + if ((r := write2read(b)) < 0) | |
| 208 | + return r; | |
| 209 | + } | |
| 210 | + k := n; | |
| 211 | + while (b.size - b.index < k) { | |
| 212 | + buf[0:] = b.buffer[b.index:b.size]; | |
| 213 | + buf = buf[b.size - b.index:]; | |
| 214 | + k -= b.size - b.index; | |
| 215 | + | |
| 216 | + b.bufpos += big b.size; | |
| 217 | + b.index = 0; | |
| 218 | + b.size = 0; | |
| 219 | + if ((r := readchunk(b)) < 0) { | |
| 220 | + if(r == EOF || n != k) | |
| 221 | + return n-k; | |
| 222 | + return ERROR; | |
| 223 | + } | |
| 224 | + } | |
| 225 | + buf[0:] = b.buffer[b.index:b.index+k]; | |
| 226 | + b.index += k; | |
| 227 | + return n; | |
| 228 | +} | |
| 229 | + | |
| 230 | +Iobuf.getb(b: self ref Iobuf): int | |
| 231 | +{ | |
| 232 | + if(b.lastop != OREAD){ | |
| 233 | + if(b.mode == OWRITE) | |
| 234 | + return ERROR; | |
| 235 | + if((r := write2read(b)) < 0) | |
| 236 | + return r; | |
| 237 | + } | |
| 238 | + if (b.index == b.size) { | |
| 239 | + b.bufpos += big b.index; | |
| 240 | + b.index = 0; | |
| 241 | + b.size = 0; | |
| 242 | + if ((r := readchunk(b)) < 0) | |
| 243 | + return r; | |
| 244 | + } | |
| 245 | + return int b.buffer[b.index++]; | |
| 246 | +} | |
| 247 | + | |
| 248 | +Iobuf.ungetb(b: self ref Iobuf): int | |
| 249 | +{ | |
| 250 | + if(b.mode == OWRITE || b.lastop != OREAD) | |
| 251 | + return ERROR; | |
| 252 | + b.index--; | |
| 253 | + return 1; | |
| 254 | +} | |
| 255 | + | |
| 256 | +Iobuf.getc(b: self ref Iobuf): int | |
| 257 | +{ | |
| 258 | + r, i, s: int; | |
| 259 | + | |
| 260 | + if(b.lastop != OREAD){ | |
| 261 | + if(b.mode == OWRITE) | |
| 262 | + return ERROR; | |
| 263 | + if((r = write2read(b)) < 0) | |
| 264 | + return r; | |
| 265 | + } | |
| 266 | + for(;;) { | |
| 267 | + if(b.index < b.size) { | |
| 268 | + r = int b.buffer[b.index]; | |
| 269 | + if(r < UTFself){ | |
| 270 | + b.index++; | |
| 271 | + return r; | |
| 272 | + } | |
| 273 | + (r, i, s) = sys->byte2char(b.buffer[0:b.size], b.index); | |
| 274 | + if (i != 0) { | |
| 275 | + b.index += i; | |
| 276 | + return r; | |
| 277 | + } | |
| 278 | + b.buffer[0:] = b.buffer[b.index:b.size]; | |
| 279 | + } | |
| 280 | + b.bufpos += big b.index; | |
| 281 | + b.size -= b.index; | |
| 282 | + b.index = 0; | |
| 283 | + if ((r = readchunk(b)) < 0) | |
| 284 | + return r; | |
| 285 | + } | |
| 286 | + # Not reached: | |
| 287 | + return -1; | |
| 288 | +} | |
| 289 | + | |
| 290 | +Iobuf.ungetc(b: self ref Iobuf): int | |
| 291 | +{ | |
| 292 | + if(b.index == 0 || b.mode == OWRITE || b.lastop != OREAD) | |
| 293 | + return ERROR; | |
| 294 | + stop := b.index - Sys->UTFmax; | |
| 295 | + if(stop < 0) | |
| 296 | + stop = 0; | |
| 297 | + buf := b.buffer[0:b.size]; | |
| 298 | + for(i := b.index-1; i >= stop; i--){ | |
| 299 | + (nil, n, s) := sys->byte2char(buf, i); | |
| 300 | + if(s && i + n == b.index){ | |
| 301 | + b.index = i; | |
| 302 | + return 1; | |
| 303 | + } | |
| 304 | + } | |
| 305 | + b.index--; | |
| 306 | + | |
| 307 | + return 1; | |
| 308 | +} | |
| 309 | + | |
| 310 | +# optimised when term < UTFself (common case) | |
| 311 | +tgets(b: ref Iobuf, t: int): string | |
| 312 | +{ | |
| 313 | + str: string; | |
| 314 | + term := byte t; | |
| 315 | + for(;;){ | |
| 316 | + start := b.index; | |
| 317 | + end := start + sys->utfbytes(b.buffer[start:], b.size-start); | |
| 318 | + buf := b.buffer; | |
| 319 | + # XXX could speed up by adding extra byte to end of buffer and | |
| 320 | + # placing a sentinel there (eliminate one test, perhaps 35% speedup). | |
| 321 | + # (but not when we've been given the buffer externally) | |
| 322 | + for(i := start; i < end; i++){ | |
| 323 | + if(buf[i] == term){ | |
| 324 | + i++; | |
| 325 | + str += string buf[start:i]; | |
| 326 | + b.index = i; | |
| 327 | + return str; | |
| 328 | + } | |
| 329 | + } | |
| 330 | + str += string buf[start:i]; | |
| 331 | + if(i < b.size) | |
| 332 | + b.buffer[0:] = buf[i:b.size]; | |
| 333 | + b.size -= i; | |
| 334 | + b.bufpos += big i; | |
| 335 | + b.index = 0; | |
| 336 | + if(readchunk(b) < 0) | |
| 337 | + break; | |
| 338 | + } | |
| 339 | + return str; | |
| 340 | +} | |
| 341 | + | |
| 342 | +Iobuf.gets(b: self ref Iobuf, term: int): string | |
| 343 | +{ | |
| 344 | + i: int; | |
| 345 | + | |
| 346 | + if(b.mode == OWRITE) | |
| 347 | + return nil; | |
| 348 | + if(b.lastop != OREAD && write2read(b) < 0) | |
| 349 | + return nil; | |
| 350 | +# if(term < UTFself) | |
| 351 | +# return tgets(b, term); | |
| 352 | + str: string; | |
| 353 | + ch := -1; | |
| 354 | + for (;;) { | |
| 355 | + start := b.index; | |
| 356 | + n := 0; | |
| 357 | + while(b.index < b.size){ | |
| 358 | + (ch, i, nil) = sys->byte2char(b.buffer[0:b.size], b.index); | |
| 359 | + if(i == 0) # too few bytes for full Rune | |
| 360 | + break; | |
| 361 | + n += i; | |
| 362 | + b.index += i; | |
| 363 | + if(ch == term) | |
| 364 | + break; | |
| 365 | + } | |
| 366 | + if(n > 0) | |
| 367 | + str += string b.buffer[start:start+n]; | |
| 368 | + if(ch == term) | |
| 369 | + return str; | |
| 370 | + b.buffer[0:] = b.buffer[b.index:b.size]; | |
| 371 | + b.bufpos += big b.index; | |
| 372 | + b.size -= b.index; | |
| 373 | + b.index = 0; | |
| 374 | + if (readchunk(b) < 0) | |
| 375 | + break; | |
| 376 | + } | |
| 377 | + return str; # nil at EOF | |
| 378 | +} | |
| 379 | + | |
| 380 | +Iobuf.gett(b: self ref Iobuf, s: string): string | |
| 381 | +{ | |
| 382 | + r := ""; | |
| 383 | + if (b.mode == OWRITE || (ch := b.getc()) < 0) | |
| 384 | + return nil; | |
| 385 | + do { | |
| 386 | + r[len r] = ch; | |
| 387 | + for (i:=0; i<len(s); i++) | |
| 388 | + if (ch == s[i]) | |
| 389 | + return r; | |
| 390 | + } while ((ch = b.getc()) >= 0); | |
| 391 | + return r; | |
| 392 | +} | |
| 393 | + | |
| 394 | +read2write(b: ref Iobuf) | |
| 395 | +{ | |
| 396 | + # last operation was a read | |
| 397 | + b.bufpos += big b.index; | |
| 398 | + b.size = 0; | |
| 399 | + b.index = 0; | |
| 400 | + b.lastop = OWRITE; | |
| 401 | +} | |
| 402 | + | |
| 403 | +Iobuf.write(b: self ref Iobuf, buf: array of byte, n: int): int | |
| 404 | +{ | |
| 405 | + if(b.lastop != OWRITE) { | |
| 406 | + if(b.mode == OREAD) | |
| 407 | + return ERROR; | |
| 408 | + read2write(b); | |
| 409 | + } | |
| 410 | + start := 0; | |
| 411 | + k := n; | |
| 412 | + while(k > 0){ | |
| 413 | + nw := Bufsize - b.index; | |
| 414 | + if(nw > k) | |
| 415 | + nw = k; | |
| 416 | + end := start + nw; | |
| 417 | + b.buffer[b.index:] = buf[start:end]; | |
| 418 | + start = end; | |
| 419 | + b.index += nw; | |
| 420 | + k -= nw; | |
| 421 | + if(b.index > b.size) | |
| 422 | + b.size = b.index; | |
| 423 | + b.dirty = 1; | |
| 424 | + if(b.size == Bufsize && writechunk(b) < 0) | |
| 425 | + return ERROR; | |
| 426 | + } | |
| 427 | + return n; | |
| 428 | +} | |
| 429 | + | |
| 430 | +Iobuf.putb(b: self ref Iobuf, c: byte): int | |
| 431 | +{ | |
| 432 | + if(b.lastop != OWRITE) { | |
| 433 | + if(b.mode == OREAD) | |
| 434 | + return ERROR; | |
| 435 | + read2write(b); | |
| 436 | + } | |
| 437 | + b.buffer[b.index++] = c; | |
| 438 | + if(b.index > b.size) | |
| 439 | + b.size = b.index; | |
| 440 | + b.dirty = 1; | |
| 441 | + if(b.size >= Bufsize) { | |
| 442 | + if (b.fd == nil) | |
| 443 | + return ERROR; | |
| 444 | + if (writechunk(b) < 0) | |
| 445 | + return ERROR; | |
| 446 | + } | |
| 447 | + return 0; | |
| 448 | +} | |
| 449 | + | |
| 450 | +Iobuf.putc(b: self ref Iobuf, c: int): int | |
| 451 | +{ | |
| 452 | + if(b.lastop != OWRITE) { | |
| 453 | + if (b.mode == OREAD) | |
| 454 | + return ERROR; | |
| 455 | + read2write(b); | |
| 456 | + } | |
| 457 | + if(c < UTFself) | |
| 458 | + b.buffer[b.index++] = byte c; | |
| 459 | + else | |
| 460 | + b.index += sys->char2byte(c, b.buffer, b.index); | |
| 461 | + if (b.index > b.size) | |
| 462 | + b.size = b.index; | |
| 463 | + b.dirty = 1; | |
| 464 | + if (b.size >= Bufsize) { | |
| 465 | + if (writechunk(b) < 0) | |
| 466 | + return ERROR; | |
| 467 | + } | |
| 468 | + return 0; | |
| 469 | +} | |
| 470 | + | |
| 471 | +Iobuf.puts(b: self ref Iobuf, s: string): int | |
| 472 | +{ | |
| 473 | + if(b.lastop != OWRITE) { | |
| 474 | + if (b.mode == OREAD) | |
| 475 | + return ERROR; | |
| 476 | + read2write(b); | |
| 477 | + } | |
| 478 | + n := len s; | |
| 479 | + if (n == 0) | |
| 480 | + return 0; | |
| 481 | + ind := b.index; | |
| 482 | + buf := b.buffer; | |
| 483 | + for(i := 0; i < n; i++){ | |
| 484 | + c := s[i]; | |
| 485 | + if(c < UTFself) | |
| 486 | + buf[ind++] = byte c; | |
| 487 | + else | |
| 488 | + ind += sys->char2byte(c, buf, ind); | |
| 489 | + if(ind >= Bufsize){ | |
| 490 | + b.index = ind; | |
| 491 | + if(ind > b.size) | |
| 492 | + b.size = ind; | |
| 493 | + b.dirty = 1; | |
| 494 | + if(writechunk(b) < 0) | |
| 495 | + return ERROR; | |
| 496 | + ind = b.index; | |
| 497 | + } | |
| 498 | + } | |
| 499 | + b.dirty = b.index != ind; | |
| 500 | + b.index = ind; | |
| 501 | + if (ind > b.size) | |
| 502 | + b.size = ind; | |
| 503 | + return n; | |
| 504 | +} | |
| 505 | + | |
| 506 | +filler(b: ref Iobuf): ref Filler | |
| 507 | +{ | |
| 508 | + for (f := fillers; f != nil; f = f.next) | |
| 509 | + if(f.iobuf == b) | |
| 510 | + return f; | |
| 511 | + return nil; | |
| 512 | +} | |
| 513 | + | |
| 514 | +Iobuf.setfill(b: self ref Iobuf, fill: BufioFill) | |
| 515 | +{ | |
| 516 | + if ((f := filler(b)) != nil) | |
| 517 | + f.fill = fill; | |
| 518 | + else | |
| 519 | + fillers = ref Filler(b, fill, fillers); | |
| 520 | +} | |
| 521 | + | |
| 522 | +nofill(b: ref Iobuf) | |
| 523 | +{ | |
| 524 | + prev: ref Filler; | |
| 525 | + for(f := fillers; f != nil; f = f.next) { | |
| 526 | + if(f.iobuf == b) { | |
| 527 | + if (prev == nil) | |
| 528 | + fillers = f.next; | |
| 529 | + else | |
| 530 | + prev.next = f.next; | |
| 531 | + } | |
| 532 | + prev = f; | |
| 533 | + } | |
| 534 | +} | |
| @@ -0,0 +1,70 @@ | ||
| 1 | +Bufio: module | |
| 2 | +{ | |
| 3 | + PATH: con "/dis/lib/bufio.dis"; | |
| 4 | + | |
| 5 | + SEEKSTART: con Sys->SEEKSTART; | |
| 6 | + SEEKRELA: con Sys->SEEKRELA; | |
| 7 | + SEEKEND: con Sys->SEEKEND; | |
| 8 | + | |
| 9 | + OREAD: con Sys->OREAD; | |
| 10 | + OWRITE: con Sys->OWRITE; | |
| 11 | + ORDWR: con Sys->ORDWR; | |
| 12 | + | |
| 13 | + EOF: con -1; | |
| 14 | + ERROR: con -2; | |
| 15 | + | |
| 16 | + Iobuf: adt { | |
| 17 | + seek: fn(b: self ref Iobuf, n: big, where: int): big; | |
| 18 | + offset: fn(b: self ref Iobuf): big; | |
| 19 | + | |
| 20 | + read: fn(b: self ref Iobuf, a: array of byte, n: int): int; | |
| 21 | + write: fn(b: self ref Iobuf, a: array of byte, n: int): int; | |
| 22 | + | |
| 23 | + getb: fn(b: self ref Iobuf): int; | |
| 24 | + getc: fn(b: self ref Iobuf): int; | |
| 25 | + gets: fn(b: self ref Iobuf, sep: int): string; | |
| 26 | + gett: fn(b: self ref Iobuf, sep: string): string; | |
| 27 | + | |
| 28 | + ungetb: fn(b: self ref Iobuf): int; | |
| 29 | + ungetc: fn(b: self ref Iobuf): int; | |
| 30 | + | |
| 31 | + putb: fn(b: self ref Iobuf, b: byte): int; | |
| 32 | + putc: fn(b: self ref Iobuf, c: int): int; | |
| 33 | + puts: fn(b: self ref Iobuf, s: string): int; | |
| 34 | + | |
| 35 | + flush: fn(b: self ref Iobuf): int; | |
| 36 | + close: fn(b: self ref Iobuf); | |
| 37 | + | |
| 38 | + setfill: fn(b: self ref Iobuf, f: BufioFill); | |
| 39 | + | |
| 40 | + # Internal variables | |
| 41 | + fd: ref Sys->FD; # the file | |
| 42 | + buffer: array of byte; # the buffer | |
| 43 | + index: int; # read/write pointer in buffer | |
| 44 | + size: int; # characters remaining/written | |
| 45 | + dirty: int; # needs flushing | |
| 46 | + bufpos: big; # position in file of buf[0] | |
| 47 | + filpos: big; # current file pointer | |
| 48 | + lastop: int; # OREAD or OWRITE | |
| 49 | + mode: int; # mode of open | |
| 50 | + }; | |
| 51 | + | |
| 52 | + open: fn(name: string, mode: int): ref Iobuf; | |
| 53 | + create: fn(name: string, mode, perm: int): ref Iobuf; | |
| 54 | + fopen: fn(fd: ref Sys->FD, mode: int): ref Iobuf; | |
| 55 | + sopen: fn(input: string): ref Iobuf; | |
| 56 | + aopen: fn(input: array of byte): ref Iobuf; | |
| 57 | +}; | |
| 58 | + | |
| 59 | +BufioFill: module | |
| 60 | +{ | |
| 61 | + fill: fn(b: ref Bufio->Iobuf): int; | |
| 62 | +}; | |
| 63 | + | |
| 64 | +ChanFill: module | |
| 65 | +{ | |
| 66 | + PATH: con "/dis/lib/chanfill.dis"; | |
| 67 | + | |
| 68 | + init: fn(data: array of byte, fid: int, wc: Sys->Rwrite, r: ref Sys->FileIO, b: Bufio): ref Bufio->Iobuf; | |
| 69 | + fill: fn(b: ref Bufio->Iobuf): int; | |
| 70 | +}; | |
| @@ -0,0 +1,427 @@ | ||
| 1 | +implement String; | |
| 2 | + | |
| 3 | +include "string.m"; | |
| 4 | + | |
| 5 | +splitl(s: string, cl: string): (string, string) | |
| 6 | +{ | |
| 7 | + n := len s; | |
| 8 | + for(j := 0; j < n; j++) { | |
| 9 | + if(in(s[j], cl)) | |
| 10 | + return (s[0:j], s[j:n]); | |
| 11 | + } | |
| 12 | + return (s,""); | |
| 13 | +} | |
| 14 | + | |
| 15 | +splitr(s: string, cl: string): (string, string) | |
| 16 | +{ | |
| 17 | + n := len s; | |
| 18 | + for(j := n-1; j >= 0; j--) { | |
| 19 | + if(in(s[j], cl)) | |
| 20 | + return (s[0:j+1], s[j+1:n]); | |
| 21 | + } | |
| 22 | + return ("",s); | |
| 23 | +} | |
| 24 | + | |
| 25 | +drop(s: string, cl: string): string | |
| 26 | +{ | |
| 27 | + n := len s; | |
| 28 | + for(j := 0; j < n; j++) { | |
| 29 | + if(!in(s[j], cl)) | |
| 30 | + return (s[j:n]); | |
| 31 | + } | |
| 32 | + return ""; | |
| 33 | +} | |
| 34 | + | |
| 35 | +take(s: string, cl: string): string | |
| 36 | +{ | |
| 37 | + n := len s; | |
| 38 | + for(j := 0; j < n; j++) { | |
| 39 | + if(!in(s[j], cl)) | |
| 40 | + return (s[0:j]); | |
| 41 | + } | |
| 42 | + return s; | |
| 43 | +} | |
| 44 | + | |
| 45 | +in(c: int, s: string): int | |
| 46 | +{ | |
| 47 | + n := len s; | |
| 48 | + if(n == 0) | |
| 49 | + return 0; | |
| 50 | + ans := 0; | |
| 51 | + negate := 0; | |
| 52 | + if(s[0] == '^') { | |
| 53 | + negate = 1; | |
| 54 | + s = s[1:]; | |
| 55 | + n--; | |
| 56 | + } | |
| 57 | + for(i := 0; i < n; i++) { | |
| 58 | + if(s[i] == '-' && i > 0 && i < n-1) { | |
| 59 | + if(c >= s[i-1] && c <= s[i+1]) { | |
| 60 | + ans = 1; | |
| 61 | + break; | |
| 62 | + } | |
| 63 | + i++; | |
| 64 | + } | |
| 65 | + else | |
| 66 | + if(c == s[i]) { | |
| 67 | + ans = 1; | |
| 68 | + break; | |
| 69 | + } | |
| 70 | + } | |
| 71 | + if(negate) | |
| 72 | + ans = !ans; | |
| 73 | + return ans; | |
| 74 | +} | |
| 75 | + | |
| 76 | +splitstrl(s: string, t: string): (string, string) | |
| 77 | +{ | |
| 78 | + n := len s; | |
| 79 | + nt := len t; | |
| 80 | + if(nt == 0) | |
| 81 | + return ("", s); | |
| 82 | + c0 := t[0]; | |
| 83 | + mainloop: | |
| 84 | + for(j := 0; j <= n-nt; j++) { | |
| 85 | + if(s[j] == c0) { | |
| 86 | + for(k := 1; k < nt; k++) | |
| 87 | + if(s[j+k] != t[k]) | |
| 88 | + continue mainloop; | |
| 89 | + return(s[0:j], s[j:n]); | |
| 90 | + } | |
| 91 | + } | |
| 92 | + return (s,""); | |
| 93 | +} | |
| 94 | + | |
| 95 | +splitstrr(s: string, t: string): (string, string) | |
| 96 | +{ | |
| 97 | + n := len s; | |
| 98 | + nt := len t; | |
| 99 | + if(nt == 0) | |
| 100 | + return (s, ""); | |
| 101 | + c0 := t[0]; | |
| 102 | + mainloop: | |
| 103 | + for(j := n-nt; j >= 0; j--) { | |
| 104 | + if(s[j] == c0) { | |
| 105 | + for(k := 1; k < nt; k++) | |
| 106 | + if(s[j+k] != t[k]) | |
| 107 | + continue mainloop; | |
| 108 | + return(s[0:j+nt], s[j+nt:n]); | |
| 109 | + } | |
| 110 | + } | |
| 111 | + return ("",s); | |
| 112 | +} | |
| 113 | + | |
| 114 | +prefix(pre: string, s: string): int | |
| 115 | +{ | |
| 116 | + if(len s < len pre) | |
| 117 | + return 0; | |
| 118 | + for(k := 0; k < len pre; k++) | |
| 119 | + if(pre[k] != s[k]) | |
| 120 | + return 0; | |
| 121 | + return 1; | |
| 122 | +} | |
| 123 | + | |
| 124 | +tolower(s: string): string | |
| 125 | +{ | |
| 126 | + for(i := 0; i < len s; i++) { | |
| 127 | + c := s[i]; | |
| 128 | + if(c >= 'A' && c <= 'Z') | |
| 129 | + s[i] += 'a' - 'A'; | |
| 130 | + } | |
| 131 | + return s; | |
| 132 | +} | |
| 133 | + | |
| 134 | +toupper(s: string): string | |
| 135 | +{ | |
| 136 | + for(i := 0; i < len s; i++) { | |
| 137 | + c := s[i]; | |
| 138 | + if(c >= 'a' && c <= 'z') | |
| 139 | + s[i] += 'A' - 'a'; | |
| 140 | + } | |
| 141 | + return s; | |
| 142 | +} | |
| 143 | + | |
| 144 | +startnum(s: string, base: int): (int, int, int) | |
| 145 | +{ | |
| 146 | + if(s == nil || base != 0 && (base < 2 || base > 36)) | |
| 147 | + return (0, 0, 0); | |
| 148 | + | |
| 149 | + # skip possible leading white space | |
| 150 | + c := ' '; | |
| 151 | + for (i := 0; i < len s; i++) { | |
| 152 | + c = s[i]; | |
| 153 | + if(c != ' ' && c != '\t' && c != '\n') | |
| 154 | + break; | |
| 155 | + } | |
| 156 | + | |
| 157 | + # optional sign | |
| 158 | + neg := 0; | |
| 159 | + if(c == '-' || c == '+') { | |
| 160 | + if(c == '-') | |
| 161 | + neg = 1; | |
| 162 | + i++; | |
| 163 | + } | |
| 164 | + | |
| 165 | + if(base == 0) { | |
| 166 | + # parse possible leading base designator | |
| 167 | + start := i; | |
| 168 | + base = -1; | |
| 169 | + for (; i < start+3 && i < len s; i++) { | |
| 170 | + c = s[i]; | |
| 171 | + if (c == 'r' && i > start) { | |
| 172 | + base = int s[start:i]; | |
| 173 | + i++; | |
| 174 | + break; | |
| 175 | + } else if (c < '0' || c > '9') | |
| 176 | + break; | |
| 177 | + } | |
| 178 | + if (base == -1) { | |
| 179 | + i = start; | |
| 180 | + base = 10; | |
| 181 | + } else if (base == 0 || base > 36) | |
| 182 | + return (0, 0, i); | |
| 183 | + } | |
| 184 | + if(i >= len s) | |
| 185 | + return (0, 0, 0); | |
| 186 | + | |
| 187 | + return (base, neg, i); | |
| 188 | +} | |
| 189 | + | |
| 190 | +tobig(s: string, base: int): (big, string) | |
| 191 | +{ | |
| 192 | + neg, i: int; | |
| 193 | + | |
| 194 | + (base, neg, i) = startnum(s, base); | |
| 195 | + if(base == 0) | |
| 196 | + return (big 0, s); | |
| 197 | + | |
| 198 | + # parse number itself. | |
| 199 | + # probably this should check for overflow, and max out, as limbo op does? | |
| 200 | + start := i; | |
| 201 | + n := big 0; | |
| 202 | + for (; i < len s; i++) { | |
| 203 | + if((d := digit(s[i], base)) < 0) | |
| 204 | + break; | |
| 205 | + n = n*big base + big d; | |
| 206 | + } | |
| 207 | + if (i == start) | |
| 208 | + return (big 0, s); | |
| 209 | + if (neg) | |
| 210 | + return (-n, s[i:]); | |
| 211 | + return (n, s[i:]); | |
| 212 | +} | |
| 213 | + | |
| 214 | +toint(s: string, base: int): (int, string) | |
| 215 | +{ | |
| 216 | + neg, i: int; | |
| 217 | + | |
| 218 | + (base, neg, i) = startnum(s, base); | |
| 219 | + if(base == 0) | |
| 220 | + return (0, s); | |
| 221 | + | |
| 222 | + # parse number itself. | |
| 223 | + # probably this should check for overflow, and max out, as limbo op does? | |
| 224 | + start := i; | |
| 225 | + n := 0; | |
| 226 | + for (; i < len s; i++){ | |
| 227 | + if((d := digit(s[i], base)) < 0) | |
| 228 | + break; | |
| 229 | + n = n*base + d; | |
| 230 | + } | |
| 231 | + if (i == start) | |
| 232 | + return (0, s); | |
| 233 | + if (neg) | |
| 234 | + return (-n, s[i:]); | |
| 235 | + return (n, s[i:]); | |
| 236 | +} | |
| 237 | + | |
| 238 | +digit(c: int, base: int): int | |
| 239 | +{ | |
| 240 | + if ('0' <= c && c <= '0' + base - 1) | |
| 241 | + return c-'0'; | |
| 242 | + else if ('a' <= c && c < 'a' + base - 10) | |
| 243 | + return (c - 'a' + 10); | |
| 244 | + else if ('A' <= c && c < 'A' + base - 10) | |
| 245 | + return (c - 'A' + 10); | |
| 246 | + else | |
| 247 | + return -1; | |
| 248 | +} | |
| 249 | + | |
| 250 | +rpow(x: real, n: int): real | |
| 251 | +{ | |
| 252 | + inv := 0; | |
| 253 | + if(n < 0){ | |
| 254 | + n = -n; | |
| 255 | + inv = 1; | |
| 256 | + } | |
| 257 | + r := 1.0; | |
| 258 | + for(;;){ | |
| 259 | + if(n&1) | |
| 260 | + r *= x; | |
| 261 | + if((n >>= 1) == 0) | |
| 262 | + break; | |
| 263 | + x *= x; | |
| 264 | + } | |
| 265 | + if(inv) | |
| 266 | + r = 1.0/r; | |
| 267 | + return r; | |
| 268 | +} | |
| 269 | + | |
| 270 | +match(p: string, s: string, i: int): int | |
| 271 | +{ | |
| 272 | + if(i+len p > len s) | |
| 273 | + return 0; | |
| 274 | + for(j := 0; j < len p; j++){ | |
| 275 | + c := s[i++]; | |
| 276 | + if(c >= 'A' && c <= 'Z') | |
| 277 | + c += 'a'-'A'; | |
| 278 | + if(p[j] != c) | |
| 279 | + return 0; | |
| 280 | + } | |
| 281 | + return 1; | |
| 282 | +} | |
| 283 | + | |
| 284 | +toreal(s: string, base: int): (real, string) | |
| 285 | +{ | |
| 286 | + neg, i: int; | |
| 287 | + | |
| 288 | + (base, neg, i) = startnum(s, base); | |
| 289 | + if(base == 0) | |
| 290 | + return (0.0, s); | |
| 291 | + | |
| 292 | + c := s[i]; | |
| 293 | + if((c == 'i' || c == 'I') && match("infinity", s, i)) | |
| 294 | + return (real s, s[i+8:]); | |
| 295 | + if((c == 'n' || c == 'N') && match("nan", s, i)) | |
| 296 | + return (real s, s[i+3:]); | |
| 297 | + | |
| 298 | + if(digit(c, base) < 0) | |
| 299 | + return (0.0, s); | |
| 300 | + | |
| 301 | + num := 0.0; | |
| 302 | + for(; i < len s && (d := digit(s[i], base)) >= 0; i++) | |
| 303 | + num = num*real base + real d; | |
| 304 | + dig := 0; # digits in fraction | |
| 305 | + if(i < len s && s[i] == '.'){ | |
| 306 | + i++; | |
| 307 | + for(; i < len s && (d = digit(s[i], base)) >= 0; i++){ | |
| 308 | + num = num*real base + real d; | |
| 309 | + dig++; | |
| 310 | + } | |
| 311 | + } | |
| 312 | + exp := 0; | |
| 313 | + eneg := 0; | |
| 314 | + if(i < len s && ((c = s[i]) == 'e' || c == 'E')){ | |
| 315 | + start := i; # might still be badly formed | |
| 316 | + i++; | |
| 317 | + if(i < len s && ((c = s[i]) == '-' || c == '+')){ | |
| 318 | + i++; | |
| 319 | + if(c == '-'){ | |
| 320 | + dig = -dig; | |
| 321 | + eneg = 1; | |
| 322 | + } | |
| 323 | + } | |
| 324 | + if(i < len s && s[i] >= '0' && s[i] <= '9'){ # exponents are always decimal | |
| 325 | + for(; i < len s && (d = digit(s[i], 10)) >= 0; i++) | |
| 326 | + exp = exp*base + d; | |
| 327 | + }else | |
| 328 | + i = start; | |
| 329 | + } | |
| 330 | + if(base == 10) | |
| 331 | + return (real s[0: i], s[i:]); # conversion can be more accurate | |
| 332 | + exp -= dig; | |
| 333 | + if(exp < 0){ | |
| 334 | + exp = -exp; | |
| 335 | + eneg = !eneg; | |
| 336 | + } | |
| 337 | + if(exp < 0 || exp > 19999) | |
| 338 | + exp = 19999; # huge but otherwise arbitrary limit | |
| 339 | + dem := rpow(real base, exp); | |
| 340 | + if(eneg) | |
| 341 | + num /= dem; | |
| 342 | + else | |
| 343 | + num *= dem; | |
| 344 | + if(neg) | |
| 345 | + return (-num,s[i:]); | |
| 346 | + return (num, s[i:]); | |
| 347 | +} | |
| 348 | + | |
| 349 | +append(s: string, l: list of string): list of string | |
| 350 | +{ | |
| 351 | + t: list of string; | |
| 352 | + | |
| 353 | + # Reverse l, prepend s, and reverse result. | |
| 354 | + while (l != nil) { | |
| 355 | + t = hd l :: t; | |
| 356 | + l = tl l; | |
| 357 | + } | |
| 358 | + t = s :: t; | |
| 359 | + do { | |
| 360 | + l = hd t :: l; | |
| 361 | + t = tl t; | |
| 362 | + } while (t != nil); | |
| 363 | + return l; | |
| 364 | +} | |
| 365 | + | |
| 366 | +quoted(argv: list of string): string | |
| 367 | +{ | |
| 368 | + return quotedc(argv, nil); | |
| 369 | +} | |
| 370 | + | |
| 371 | +quotedc(argv: list of string, cl: string): string | |
| 372 | +{ | |
| 373 | + s := ""; | |
| 374 | + while(argv != nil){ | |
| 375 | + arg := hd argv; | |
| 376 | + for(i := 0; i < len arg; i++){ | |
| 377 | + c := arg[i]; | |
| 378 | + if(c == ' ' || c == '\t' || c == '\n' || c == '\'' || in(c, cl)) | |
| 379 | + break; | |
| 380 | + } | |
| 381 | + if(i < len arg || arg == nil){ | |
| 382 | + s += "'" + arg[0:i]; | |
| 383 | + for(; i < len arg; i++){ | |
| 384 | + if (arg[i] == '\'') | |
| 385 | + s[len s] = '\''; | |
| 386 | + s[len s] = arg[i]; | |
| 387 | + } | |
| 388 | + s[len s] = '\''; | |
| 389 | + }else | |
| 390 | + s += arg; | |
| 391 | + if(tl argv != nil) | |
| 392 | + s[len s] = ' '; | |
| 393 | + argv = tl argv; | |
| 394 | + } | |
| 395 | + return s; | |
| 396 | +} | |
| 397 | + | |
| 398 | +unquoted(s: string): list of string | |
| 399 | +{ | |
| 400 | + args: list of string; | |
| 401 | + word: string; | |
| 402 | + inquote := 0; | |
| 403 | + for(j := len s; j > 0;){ | |
| 404 | + c := s[j-1]; | |
| 405 | + if(c == ' ' || c == '\t' || c == '\n'){ | |
| 406 | + j--; | |
| 407 | + continue; | |
| 408 | + } | |
| 409 | + for(i := j-1; i >= 0 && ((c = s[i]) != ' ' && c != '\t' && c != '\n' || inquote); i--){ # collect word | |
| 410 | + if(c == '\''){ | |
| 411 | + word = s[i+1:j] + word; | |
| 412 | + j = i; | |
| 413 | + if(!inquote || i == 0 || s[i-1] != '\'') | |
| 414 | + inquote = !inquote; | |
| 415 | + else | |
| 416 | + i--; | |
| 417 | + } | |
| 418 | + } | |
| 419 | + args = (s[i+1:j]+word) :: args; | |
| 420 | + word = nil; | |
| 421 | + j = i; | |
| 422 | + } | |
| 423 | + # if quotes were unbalanced, balance them and try again. | |
| 424 | + if(inquote) | |
| 425 | + return unquoted(s + "'"); | |
| 426 | + return args; | |
| 427 | +} | |
| @@ -0,0 +1,40 @@ | ||
| 1 | +String: module | |
| 2 | +{ | |
| 3 | + PATH: con "/dis/lib/string.dis"; | |
| 4 | + | |
| 5 | + # the second arg of the following is a character class | |
| 6 | + # e.g., "a-zA-Z", or "^ \t\n" | |
| 7 | + # (ranges indicated by - except in first position; | |
| 8 | + # ^ is first position means "not in" the following class) | |
| 9 | + # splitl splits just before first char in class; (s, "") if no split | |
| 10 | + # splitr splits just after last char in class; ("", s) if no split | |
| 11 | + # drop removes maximal prefix in class | |
| 12 | + # take returns maximal prefix in class | |
| 13 | + | |
| 14 | + splitl: fn(s, cl: string): (string, string); | |
| 15 | + splitr: fn(s, cl: string): (string, string); | |
| 16 | + drop: fn(s, cl: string): string; | |
| 17 | + take: fn(s, cl: string): string; | |
| 18 | + in: fn(c: int, cl: string): int; | |
| 19 | + | |
| 20 | + # in these, the second string is a string to match, not a class | |
| 21 | + splitstrl: fn(s, t: string): (string, string); | |
| 22 | + splitstrr: fn(s, t: string): (string, string); | |
| 23 | + | |
| 24 | + # is first arg a prefix of second? | |
| 25 | + prefix: fn(pre, s: string): int; | |
| 26 | + | |
| 27 | + tolower: fn(s: string): string; | |
| 28 | + toupper: fn(s: string): string; | |
| 29 | + | |
| 30 | + # string to int returning value, remainder | |
| 31 | + toint: fn(s: string, base: int): (int, string); | |
| 32 | + tobig: fn(s: string, base: int): (big, string); | |
| 33 | + toreal: fn(s: string, base: int): (real, string); | |
| 34 | + | |
| 35 | + # append s to end of l | |
| 36 | + append: fn(s: string, l: list of string): list of string; | |
| 37 | + quoted: fn(argv: list of string): string; | |
| 38 | + quotedc: fn(argv: list of string, cl: string): string; | |
| 39 | + unquoted: fn(args: string): list of string; | |
| 40 | +}; | |