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 | }; | ||