1implement Ar; 2 3# 4# ar - portable (ascii) format version 5# 6 7include "sys.m"; 8 sys: Sys; 9 10include "draw.m"; 11 12include "bufio.m"; 13 bufio: Bufio; 14 Iobuf: import bufio; 15 16include "daytime.m"; 17 daytime: Daytime; 18 19include "string.m"; 20 str: String; 21 22Ar: module 23{ 24 init: fn(nil: ref Draw->Context, nil: list of string); 25}; 26 27ARMAG: con "!<arch>\n"; 28SARMAG: con len ARMAG; 29ARFMAG0: con byte '`'; 30ARFMAG1: con byte '\n'; 31SARNAME: con 16; # ancient limit 32 33# 34# printable archive header 35# name[SARNAME] date[12] uid[6] gid[6] mode[8] size[10] fmag[2] 36# 37Oname: con 0; 38Lname: con SARNAME; 39Odate: con Oname+Lname; 40Ldate: con 12; 41Ouid: con Odate+Ldate; 42Luid: con 6; 43Ogid: con Ouid+Luid; 44Lgid: con 6; 45Omode: con Ogid+Lgid; 46Lmode: con 8; 47Osize: con Omode+Lmode; 48Lsize: con 10; 49Ofmag: con Osize+Lsize; 50Lfmag: con 2; 51SAR_HDR: con Ofmag+Lfmag; # 60 52 53# 54# The algorithm uses up to 3 temp files. The "pivot contents" is the 55# archive contents specified by an a, b, or i option. The temp files are 56# astart - contains existing contentss up to and including the pivot contents. 57# amiddle - contains new files moved or inserted behind the pivot. 58# aend - contains the existing contentss that follow the pivot contents. 59# When all contentss have been processed, function 'install' streams the 60# temp files, in order, back into the archive. 61# 62 63Armember: adt { # one per archive contents 64 name: string; # trimmed 65 length: int; 66 date: int; 67 uid: int; 68 gid: int; 69 mode: int; 70 size: int; 71 contents: array of byte; 72 fd: ref Sys->FD; # if contents is nil and fd is not nil, fd has contents 73 next: cyclic ref Armember; 74 75 new: fn(name: string, fd: ref Sys->FD): ref Armember; 76 rdhdr: fn(b: ref Iobuf): ref Armember; 77 read: fn(m: self ref Armember, b: ref Iobuf): int; 78 wrhdr: fn(m: self ref Armember, fd: ref Sys->FD); 79 write: fn(m: self ref Armember, fd: ref Sys->FD); 80 skip: fn(m: self ref Armember, b: ref Iobuf); 81 replace: fn(m: self ref Armember, name: string, fd: ref Sys->FD); 82 copyout: fn(m: self ref Armember, b: ref Iobuf, destfd: ref Sys->FD); 83}; 84 85Arfile: adt { # one per tempfile 86 fd: ref Sys->FD; # paging file descriptor, nil if none allocated 87 88 head: ref Armember; 89 tail: ref Armember; 90 91 new: fn(): ref Arfile; 92 copy: fn(ar: self ref Arfile, b: ref Iobuf, mem: ref Armember); 93 insert: fn(ar: self ref Arfile, mem: ref Armember); 94 stream: fn(ar: self ref Arfile, fd: ref Sys->FD); 95 page: fn(ar: self ref Arfile): int; 96}; 97 98File: adt { 99 name: string; 100 trimmed: string; 101 found: int; 102}; 103 104man := "mrxtdpq"; 105opt := "uvnbailo"; 106 107aflag := 0; 108bflag := 0; 109cflag := 0; 110oflag := 0; 111uflag := 0; 112vflag := 0; 113 114pivotname: string; 115bout: ref Iobuf; 116stderr: ref Sys->FD; 117parts: array of ref Arfile; 118 119comfun: ref fn(a: string, f: array of ref File); 120 121init(nil: ref Draw->Context, args: list of string) 122{ 123 sys = load Sys Sys->PATH; 124 bufio = load Bufio Bufio->PATH; 125 daytime = load Daytime Daytime->PATH; 126 str = load String String->PATH; 127 128 stderr = sys->fildes(2); 129 bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); 130 if(len args < 3) 131 usage(); 132 args = tl args; 133 s := hd args; args = tl args; 134 for(i := 0; i < len s; i++){ 135 case s[i] { 136 'a' => aflag = 1; 137 'b' => bflag = 1; 138 'c' => cflag = 1; 139 'd' => setcom(dcmd); 140 'i' => bflag = 1; 141 'l' => ; # ignored 142 'm' => setcom(mcmd); 143 'o' => oflag = 1; 144 'p' => setcom(pcmd); 145 'q' => setcom(qcmd); 146 'r' => setcom(rcmd); 147 't' => setcom(tcmd); 148 'u' => uflag = 1; 149 'v' => vflag = 1; 150 'x' => setcom(xcmd); 151 * => 152 sys->fprint(stderr, "ar: bad option `%c'\n", s[i]); 153 usage(); 154 } 155 } 156 if(aflag && bflag){ 157 sys->fprint(stderr, "ar: only one of 'a' and 'b' can be specified\n"); 158 usage(); 159 } 160 if(aflag || bflag){ 161 pivotname = trim(hd args); args = tl args; 162 if(len args < 2) 163 usage(); 164 } 165 if(comfun == nil){ 166 if(uflag == 0){ 167 sys->fprint(stderr, "ar: one of [%s] must be specified\n", man); 168 usage(); 169 } 170 setcom(rcmd); 171 } 172 cp := hd args; args = tl args; 173 files := array[len args] of ref File; 174 for(i = 0; args != nil; args = tl args) 175 files[i++] = ref File(hd args, trim(hd args), 0); 176 comfun(cp, files); # do the command 177 allfound := 1; 178 for(i = 0; i < len files; i++) 179 if(!files[i].found){ 180 sys->fprint(stderr, "ar: %s not found\n", files[i].name); 181 allfound = 0; 182 } 183 bout.flush(); 184 if(!allfound) 185 raise "fail: file not found"; 186} 187 188# 189# select a command 190# 191setcom(fun: ref fn(s: string, f: array of ref File)) 192{ 193 if(comfun != nil){ 194 sys->fprint(stderr, "ar: only one of [%s] allowed\n", man); 195 usage(); 196 } 197 comfun = fun; 198} 199 200# 201# perform the 'r' and 'u' commands 202# 203rcmd(arname: string, files: array of ref File) 204{ 205 bar := openar(arname, Sys->ORDWR, 1); 206 parts = array[2] of {Arfile.new(), nil}; 207 ap := parts[0]; 208 if(bar != nil){ 209 while((mem := Armember.rdhdr(bar)) != nil){ 210 if(bamatch(mem.name, pivotname)) # check for pivot 211 ap = parts[1] = Arfile.new(); 212 f := match(files, mem.name); 213 if(f == nil){ 214 ap.copy(bar, mem); 215 continue; 216 } 217 f.found = 1; 218 dfd := sys->open(f.name, Sys->OREAD); 219 if(dfd == nil){ 220 if(len files > 0) 221 sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); 222 ap.copy(bar, mem); 223 continue; 224 } 225 if(uflag){ 226 (ok, d) := sys->fstat(dfd); 227 if(ok < 0 || d.mtime <= mem.date){ 228 if(ok < 0) 229 sys->fprint(stderr, "ar: cannot stat %s: %r\n", f.name); 230 ap.copy(bar, mem); 231 continue; 232 } 233 } 234 mem.skip(bar); 235 mesg('r', f.name); 236 mem.replace(f.name, dfd); 237 ap.insert(mem); 238 dfd = nil; 239 } 240 } 241 # copy in remaining files named on command line 242 for(i := 0; i < len files; i++){ 243 f := files[i]; 244 if(f.found) 245 continue; 246 f.found = 1; 247 dfd := sys->open(f.name, Sys->OREAD); 248 if(dfd != nil){ 249 mesg('a', f.name); 250 parts[0].insert(Armember.new(f.trimmed, dfd)); 251 }else 252 sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); 253 } 254 if(bar == nil && !cflag) 255 install(arname, parts, 1); # issue 'creating' msg 256 else 257 install(arname, parts, 0); 258} 259 260dcmd(arname: string, files: array of ref File) 261{ 262 if(len files == 0) 263 return; 264 changed := 0; 265 parts = array[] of {Arfile.new()}; 266 bar := openar(arname, Sys->ORDWR, 0); 267 while((mem := Armember.rdhdr(bar)) != nil){ 268 if(match(files, mem.name) != nil){ 269 mesg('d', mem.name); 270 mem.skip(bar); 271 changed = 1; 272 }else 273 parts[0].copy(bar, mem); 274 mem = nil; # conserves memory 275 } 276 if(changed) 277 install(arname, parts, 0); 278} 279 280xcmd(arname: string, files: array of ref File) 281{ 282 bar := openar(arname, Sys->OREAD, 0); 283 i := 0; 284 while((mem := Armember.rdhdr(bar)) != nil){ 285 if((f := match(files, mem.name)) != nil){ 286 f.found = 1; 287 fd := sys->create(f.name, Sys->OWRITE, mem.mode & 8r777); 288 if(fd == nil){ 289 sys->fprint(stderr, "ar: cannot create %s: %r\n", f.name); 290 mem.skip(bar); 291 }else{ 292 mesg('x', f.name); 293 mem.copyout(bar, fd); 294 if(oflag){ 295 dx := sys->nulldir; 296 dx.atime = mem.date; 297 dx.mtime = mem.date; 298 if(sys->fwstat(fd, dx) < 0) 299 sys->fprint(stderr, "ar: can't set times on %s: %r", f.name); 300 } 301 fd = nil; 302 mem = nil; 303 } 304 if(len files > 0 && ++i >= len files) 305 break; 306 }else 307 mem.skip(bar); 308 } 309} 310 311pcmd(arname: string, files: array of ref File) 312{ 313 bar := openar(arname, Sys->OREAD, 0); 314 i := 0; 315 while((mem := Armember.rdhdr(bar)) != nil){ 316 if((f := match(files, mem.name)) != nil){ 317 if(vflag) 318 sys->print("\n<%s>\n\n", f.name); 319 mem.copyout(bar, sys->fildes(1)); 320 if(len files > 0 && ++i >= len files) 321 break; 322 }else 323 mem.skip(bar); 324 mem = nil; # we no longer need the contents 325 } 326} 327 328mcmd(arname: string, files: array of ref File) 329{ 330 if(len files == 0) 331 return; 332 parts = array[3] of {Arfile.new(), Arfile.new(), nil}; 333 bar := openar(arname, Sys->ORDWR, 0); 334 ap := parts[0]; 335 while((mem := Armember.rdhdr(bar)) != nil){ 336 if(bamatch(mem.name, pivotname)) 337 ap = parts[2] = Arfile.new(); 338 if((f := match(files, mem.name)) != nil){ 339 mesg('m', f.name); 340 parts[1].copy(bar, mem); 341 }else 342 ap.copy(bar, mem); 343 } 344 if(pivotname != nil && parts[2] == nil) 345 sys->fprint(stderr, "ar: %s not found - files moved to end\n", pivotname); 346 install(arname, parts, 0); 347} 348 349tcmd(arname: string, files: array of ref File) 350{ 351 bar := openar(arname, Sys->OREAD, 0); 352 while((mem := Armember.rdhdr(bar)) != nil){ 353 if((f := match(files, mem.name)) != nil){ 354 longls := ""; 355 if(vflag) 356 longls = longtext(mem)+" "; 357 bout.puts(longls+f.trimmed+"\n"); 358 } 359 mem.skip(bar); 360 mem = nil; 361 } 362} 363 364qcmd(arname: string, files: array of ref File) 365{ 366 if(aflag || bflag){ 367 sys->fprint(stderr, "ar: abi not allowed with q\n"); 368 raise "fail:usage"; 369 } 370 fd := openrawar(arname, Sys->ORDWR, 1); 371 if(fd == nil){ 372 if(!cflag) 373 sys->fprint(stderr, "ar: creating %s\n", arname); 374 fd = arcreate(arname); 375 } 376 # leave note group behind when writing archive; i.e. sidestep interrupts 377 sys->seek(fd, big 0, 2); # append 378 for(i := 0; i < len files; i++){ 379 f := files[i]; 380 f.found = 1; 381 dfd := sys->open(f.name, Sys->OREAD); 382 if(dfd != nil){ 383 mesg('q', f.name); 384 mem := Armember.new(f.trimmed, dfd); 385 if(mem != nil){ 386 mem.write(fd); 387 mem = nil; 388 } 389 }else 390 sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); 391 } 392} 393 394# 395# open an archive and validate its header 396# 397openrawar(arname: string, mode: int, errok: int): ref Sys->FD 398{ 399 fd := sys->open(arname, mode); 400 if(fd == nil){ 401 if(!errok){ 402 sys->fprint(stderr, "ar: cannot open %s: %r\n", arname); 403 raise "fail:error"; 404 } 405 return nil; 406 } 407 mbuf := array[SARMAG] of byte; 408 if(sys->read(fd, mbuf, SARMAG) != SARMAG || string mbuf != ARMAG){ 409 sys->fprint(stderr, "ar: %s not in archive format\n", arname); 410 raise "fail:error"; 411 } 412 return fd; 413} 414 415openar(arname: string, mode: int, errok: int): ref Iobuf 416{ 417 fd := openrawar(arname, mode, errok); 418 if(fd == nil) 419 return nil; 420 bfd := bufio->fopen(fd, mode); 421 bfd.seek(big SARMAG, 0); 422 return bfd; 423} 424 425# 426# create an archive and set its header 427# 428arcreate(arname: string): ref Sys->FD 429{ 430 fd := sys->create(arname, Sys->OWRITE, 8r666); 431 if(fd == nil){ 432 sys->fprint(stderr, "ar: cannot create %s: %r\n", arname); 433 raise "fail:create"; 434 } 435 a := array of byte ARMAG; 436 mustwrite(fd, a, len a); 437 return fd; 438} 439 440# 441# error handling 442# 443wrerr() 444{ 445 sys->fprint(stderr, "ar: write error: %r\n"); 446 raise "fail:write error"; 447} 448 449rderr() 450{ 451 sys->fprint(stderr, "ar: read error: %r\n"); 452 raise "fail:read error"; 453} 454 455phaseerr(offset: big) 456{ 457 sys->fprint(stderr, "ar: phase error at offset %bd\n", offset); 458 raise "fail:phase error"; 459} 460 461usage() 462{ 463 sys->fprint(stderr, "usage: ar [%s][%s] archive files ...\n", opt, man); 464 raise "fail:usage"; 465} 466 467# 468# concatenate the several sequences of members into one archive 469# 470install(arname: string, seqs: array of ref Arfile, createflag: int) 471{ 472 # leave process group behind when copying back; i.e. sidestep interrupts 473 sys->pctl(Sys->NEWPGRP, nil); 474 475 if(createflag) 476 sys->fprint(stderr, "ar: creating %s\n", arname); 477 fd := arcreate(arname); 478 for(i := 0; i < len seqs; i++) 479 if((ap := seqs[i]) != nil) 480 ap.stream(fd); 481} 482 483# 484# return the command line File matching a given name 485# 486match(files: array of ref File, file: string): ref File 487{ 488 if(len files == 0) 489 return ref File(file, file, 0); # empty list always matches 490 for(i := 0; i < len files; i++) 491 if(!files[i].found && files[i].trimmed == file){ 492 files[i].found = 1; 493 return files[i]; 494 } 495 return nil; 496} 497 498# 499# is `file' the pivot member's name and is the archive positioned 500# at the correct point wrt after or before options? return true if so. 501# 502state := 0; 503 504bamatch(file: string, pivot: string): int 505{ 506 case state { 507 0 => # looking for position file 508 if(aflag){ 509 if(file == pivot) 510 state = 1; 511 }else if(bflag){ 512 if(file == pivot){ 513 state = 2; # found 514 return 1; 515 } 516 } 517 1 => # found - after previous file 518 state = 2; 519 return 1; 520 2 => # already found position file 521 ; 522 } 523 return 0; 524} 525 526# 527# output a message, if 'v' option was specified 528# 529mesg(c: int, file: string) 530{ 531 if(vflag) 532 bout.puts(sys->sprint("%c - %s\n", c, file)); 533} 534 535# 536# return just the file name 537# 538trim(s: string): string 539{ 540 for(j := len s; j > 0 && s[j-1] == '/';) 541 j--; 542 k := 0; 543 for(i := 0; i < j; i++) 544 if(s[i] == '/') 545 k = i+1; 546 return s[k: j]; 547} 548 549longtext(mem: ref Armember): string 550{ 551 s := modes(mem.mode); 552 s += sys->sprint(" %3d/%1d", mem.uid, mem.gid); 553 s += sys->sprint(" %7ud", mem.size); 554 t := daytime->text(daytime->local(mem.date)); 555 return s+sys->sprint(" %-12.12s %-4.4s ", t[4:], t[24:]); 556} 557 558mtab := array[] of { 559 "---", "--x", "-w-", "-wx", 560 "r--", "r-x", "rw-", "rwx" 561}; 562 563modes(mode: int): string 564{ 565 return mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7]; 566} 567 568# 569# read the header for the next archive contents 570# 571Armember.rdhdr(b: ref Iobuf): ref Armember 572{ 573 buf := array[SAR_HDR] of byte; 574 if((n := b.read(buf, len buf)) != len buf){ 575 if(n == 0) 576 return nil; 577 if(n > 0) 578 sys->werrstr("unexpected end-of-file"); 579 rderr(); 580 } 581 mem := ref Armember; 582 for(i := Oname+Lname; i > Oname; i--) 583 if(buf[i-1] != byte '/' && buf[i-1] != byte ' ') 584 break; 585 mem.name = string buf[Oname:i]; 586 mem.date = intof(buf[Odate: Odate+Ldate], 10); 587 mem.uid = intof(buf[Ouid: Ouid+Luid], 10); 588 mem.gid = intof(buf[Ogid: Ogid+Lgid], 10); 589 mem.mode = intof(buf[Omode: Omode+Lmode], 8); 590 mem.size = intof(buf[Osize: Osize+Lsize], 10); 591 if(buf[Ofmag] != ARFMAG0 || buf[Ofmag+1] != ARFMAG1) 592 phaseerr(b.offset()-big SAR_HDR); 593 return mem; 594} 595 596intof(a: array of byte, base: int): int 597{ 598 for(i := len a; i > 0; i--) 599 if(a[i-1] != byte ' '){ 600 a = a[0:i]; 601 break; 602 } 603 (n, s) := str->toint(string a, base); 604 if(s != nil){ 605 sys->fprint(stderr, "ar: invalid integer in archive member's header: %q\n", string a); 606 raise "fail:error"; 607 } 608 return n; 609} 610 611Armember.wrhdr(mem: self ref Armember, fd: ref Sys->FD) 612{ 613 b := array[SAR_HDR] of {* => byte ' '}; 614 nm := array of byte mem.name; 615 if(len nm > Lname) 616 nm = nm[0:Lname]; 617 b[Oname:] = nm; 618 b[Odate:] = sys->aprint("%-12ud", mem.date); 619 b[Ouid:] = sys->aprint("%-6d", 0); 620 b[Ogid:] = sys->aprint("%-6d", 0); 621 b[Omode:] = sys->aprint("%-8uo", mem.mode); 622 b[Osize:] = sys->aprint("%-10ud", mem.size); 623 b[Ofmag] = ARFMAG0; 624 b[Ofmag+1] = ARFMAG1; 625 mustwrite(fd, b, len b); 626} 627 628# 629# make a new member from the given file, with the file's contents 630# 631Armember.new(name: string, fd: ref Sys->FD): ref Armember 632{ 633 mem := ref Armember; 634 mem.replace(name, fd); 635 return mem; 636} 637 638# 639# replace the contents of an existing member 640# 641Armember.replace(mem: self ref Armember, name: string, fd: ref Sys->FD) 642{ 643 (ok, d) := sys->fstat(fd); 644 if(ok < 0){ 645 sys->fprint(stderr, "ar: cannot stat %s: %r\n", name); 646 raise "fail:no stat"; 647 } 648 mem.name = trim(name); 649 mem.date = d.mtime; 650 mem.uid = 0; 651 mem.gid = 0; 652 mem.mode = d.mode & 8r777; 653 mem.size = int d.length; 654 if(big mem.size != d.length){ 655 sys->fprint(stderr, "ar: file %s too big\n", name); 656 raise "fail:error"; 657 } 658 mem.fd = fd; 659 mem.contents = nil; # will be copied across from fd when needed 660} 661 662# 663# read the contents of an archive member 664# 665Armember.read(mem: self ref Armember, b: ref Iobuf): int 666{ 667 if(mem.contents != nil) 668 return len mem.contents; 669 mem.contents = buffer(mem.size + (mem.size&1)); 670 n := b.read(mem.contents, len mem.contents); 671 if(n != len mem.contents){ 672 if(n >= 0) 673 sys->werrstr("unexpected end-of-file"); 674 rderr(); 675 } 676 return n; 677} 678 679mustwrite(fd: ref Sys->FD, buf: array of byte, n: int) 680{ 681 if(sys->write(fd, buf, n) != n) 682 wrerr(); 683} 684 685# 686# write an archive member to ofd, including header 687# 688Armember.write(mem: self ref Armember, ofd: ref Sys->FD) 689{ 690 mem.wrhdr(ofd); 691 if(mem.contents != nil){ 692 mustwrite(ofd, mem.contents, len mem.contents); 693 return; 694 } 695 if(mem.fd == nil) 696 raise "ar: write nil fd"; 697 buf := array[Sys->ATOMICIO] of byte; # could be bigger 698 for(nr := mem.size; nr > 0;){ 699 n := nr; 700 if(n > len buf) 701 n = len buf; 702 n = sys->read(mem.fd, buf, n); 703 if(n <= 0){ 704 if(n == 0) 705 sys->werrstr("unexpected end-of-file"); 706 rderr(); 707 } 708 mustwrite(ofd, buf, n); 709 nr -= n; 710 } 711 if(mem.size & 1) 712 mustwrite(ofd, array[] of {byte '\n'}, 1); 713} 714 715# 716# seek past the current member's contents in b 717# 718Armember.skip(mem: self ref Armember, b: ref Iobuf) 719{ 720 b.seek(big(mem.size + (mem.size&1)), 1); 721} 722 723# 724# copy a member's contents from memory or directly from an archive to another file 725# 726Armember.copyout(mem: self ref Armember, b: ref Iobuf, ofd: ref Sys->FD) 727{ 728 if(mem.contents != nil){ 729 mustwrite(ofd, mem.contents, len mem.contents); 730 return; 731 } 732 buf := array[Sys->ATOMICIO] of byte; # could be bigger 733 for(nr := mem.size; nr > 0;){ 734 n := nr; 735 if(n > len buf) 736 n = len buf; 737 n = b.read(buf, n); 738 if(n <= 0){ 739 if(n == 0) 740 sys->werrstr("unexpected end-of-file"); 741 rderr(); 742 } 743 mustwrite(ofd, buf, n); 744 nr -= n; 745 } 746 if(mem.size & 1) 747 b.getc(); 748} 749 750# 751# Temp file I/O subsystem. We attempt to cache all three temp files in 752# core. When we run out of memory we spill to disk. 753# The I/O model assumes that temp files: 754# 1) are only written on the end 755# 2) are only read from the beginning 756# 3) are only read after all writing is complete. 757# The architecture uses one control block per temp file. Each control 758# block anchors a chain of buffers, each containing an archive contents. 759# 760Arfile.new(): ref Arfile 761{ 762 return ref Arfile; 763} 764 765# 766# copy the contents of mem at b into the temporary 767# 768Arfile.copy(ap: self ref Arfile, b: ref Iobuf, mem: ref Armember) 769{ 770 mem.read(b); 771 ap.insert(mem); 772} 773 774# 775# insert a contents buffer into the contents chain 776# 777Arfile.insert(ap: self ref Arfile, mem: ref Armember) 778{ 779 mem.next = nil; 780 if(ap.head == nil) 781 ap.head = mem; 782 else 783 ap.tail.next = mem; 784 ap.tail = mem; 785} 786 787# 788# stream the contents in a temp file to the file referenced by 'fd'. 789# 790Arfile.stream(ap: self ref Arfile, fd: ref Sys->FD) 791{ 792 if(ap.fd != nil){ # copy prefix from disk 793 buf := array[Sys->ATOMICIO] of byte; 794 sys->seek(ap.fd, big 0, 0); 795 while((n := sys->read(ap.fd, buf, len buf)) > 0) 796 mustwrite(fd, buf, n); 797 if(n < 0) 798 rderr(); 799 ap.fd = nil; 800 } 801 # dump the in-core buffers, which always follow the contents in the temp file 802 for(mem := ap.head; mem != nil; mem = mem.next) 803 mem.write(fd); 804} 805 806# 807# spill a member's contents to disk 808# 809 810totalmem := 0; 811warned := 0; 812tn := 0; 813 814Arfile.page(ap: self ref Arfile): int 815{ 816 mem := ap.head; 817 if(ap.fd == nil && !warned){ 818 pid := sys->pctl(0, nil); 819 for(i := 0;; i++){ 820 name := sys->sprint("/tmp/art%d.%d.%d", pid, tn, i); 821 ap.fd = sys->create(name, Sys->OEXCL | Sys->ORDWR | Sys->ORCLOSE, 8r600); 822 if(ap.fd != nil) 823 break; 824 if(i >= 20){ 825 warned =1; 826 sys->fprint(stderr,"ar: warning: can't create temp file %s: %r\n", name); 827 return 0; # we'll simply use the memory 828 } 829 } 830 tn++; 831 } 832 mem.write(ap.fd); 833 ap.head = mem.next; 834 if(ap.tail == mem) 835 ap.tail = mem.next; 836 totalmem -= len mem.contents; 837 return 1; 838} 839 840# 841# account for the space taken by a contents's contents, 842# pushing earlier contentss to disk to keep the space below a 843# reasonable level 844# 845 846buffer(n: int): array of byte 847{ 848Flush: 849 while(totalmem + n > 1024*1024){ 850 for(i := 0; i < len parts; i++) 851 if(parts[i] != nil && parts[i].page()) 852 continue Flush; 853 break; 854 } 855 totalmem += n; 856 return array[n] of byte; 857} 858