Hex Artifact Content

Artifact 5efeea42712d2c124af84f5ab33e424694dc206b:


0000: 23 21 20 2f 75 73 72 2f 62 69 6e 2f 65 6e 76 20  #! /usr/bin/env 
0010: 74 63 6c 73 68 0a 0a 70 61 63 6b 61 67 65 20 72  tclsh..package r
0020: 65 71 75 69 72 65 20 68 74 74 70 20 32 2e 37 0a  equire http 2.7.
0030: 70 61 63 6b 61 67 65 20 72 65 71 75 69 72 65 20  package require 
0040: 73 71 6c 69 74 65 33 0a 70 61 63 6b 61 67 65 20  sqlite3.package 
0050: 72 65 71 75 69 72 65 20 73 68 61 31 0a 70 61 63  require sha1.pac
0060: 6b 61 67 65 20 72 65 71 75 69 72 65 20 61 70 70  kage require app
0070: 66 73 64 0a 70 61 63 6b 61 67 65 20 72 65 71 75  fsd.package requ
0080: 69 72 65 20 70 6c 61 74 66 6f 72 6d 0a 70 61 63  ire platform.pac
0090: 6b 61 67 65 20 72 65 71 75 69 72 65 20 70 6b 69  kage require pki
00a0: 0a 0a 23 20 46 75 6e 63 74 69 6f 6e 73 20 73 70  ..# Functions sp
00b0: 65 63 69 66 69 63 61 6c 6c 79 20 6d 65 61 6e 74  ecifically meant
00c0: 20 66 6f 72 20 75 73 65 72 73 20 74 6f 20 72 65   for users to re
00d0: 70 6c 61 63 65 20 61 73 20 61 20 70 61 72 74 20  place as a part 
00e0: 6f 66 20 63 6f 6e 66 69 67 75 72 61 74 69 6f 6e  of configuration
00f0: 0a 6e 61 6d 65 73 70 61 63 65 20 65 76 61 6c 20  .namespace eval 
0100: 3a 3a 61 70 70 66 73 3a 3a 75 73 65 72 20 7b 0a  ::appfs::user {.
0110: 09 23 20 55 73 65 72 2d 72 65 70 6c 61 63 61 62  .# User-replacab
0120: 6c 65 20 66 75 6e 63 74 69 6f 6e 20 74 6f 20 63  le function to c
0130: 6f 6e 76 65 72 74 20 61 20 68 6f 73 74 6e 61 6d  onvert a hostnam
0140: 65 2f 68 61 73 68 2f 6d 65 74 68 6f 64 20 74 6f  e/hash/method to
0150: 20 61 6e 20 55 52 4c 0a 09 70 72 6f 63 20 63 6f   an URL..proc co
0160: 6e 73 74 72 75 63 74 5f 75 72 6c 20 7b 68 6f 73  nstruct_url {hos
0170: 74 6e 61 6d 65 20 68 61 73 68 20 6d 65 74 68 6f  tname hash metho
0180: 64 7d 20 7b 0a 09 09 72 65 74 75 72 6e 20 22 68  d} {...return "h
0190: 74 74 70 3a 2f 2f 24 68 6f 73 74 6e 61 6d 65 2f  ttp://$hostname/
01a0: 61 70 70 66 73 2f 24 6d 65 74 68 6f 64 2f 24 68  appfs/$method/$h
01b0: 61 73 68 22 0a 09 7d 0a 0a 09 23 20 55 73 65 72  ash"..}...# User
01c0: 2d 72 65 70 6c 61 63 65 61 62 6c 65 20 66 75 6e  -replaceable fun
01d0: 63 74 69 6f 6e 20 67 65 74 20 74 68 65 20 68 6f  ction get the ho
01e0: 6d 65 20 64 69 72 65 63 74 6f 72 79 20 6f 66 20  me directory of 
01f0: 74 68 65 20 63 75 72 72 65 6e 74 20 75 73 65 72  the current user
0200: 0a 09 70 72 6f 63 20 67 65 74 5f 68 6f 6d 65 64  ..proc get_homed
0210: 69 72 20 7b 7d 20 7b 0a 09 09 72 65 74 75 72 6e  ir {} {...return
0220: 20 5b 3a 3a 61 70 70 66 73 64 3a 3a 67 65 74 5f   [::appfsd::get_
0230: 68 6f 6d 65 64 69 72 5d 0a 09 7d 0a 7d 0a 0a 6e  homedir]..}.}..n
0240: 61 6d 65 73 70 61 63 65 20 65 76 61 6c 20 3a 3a  amespace eval ::
0250: 61 70 70 66 73 20 7b 0a 09 76 61 72 69 61 62 6c  appfs {..variabl
0260: 65 20 63 61 63 68 65 64 69 72 20 22 2f 74 6d 70  e cachedir "/tmp
0270: 2f 61 70 70 66 73 2d 63 61 63 68 65 22 0a 09 76  /appfs-cache"..v
0280: 61 72 69 61 62 6c 65 20 74 74 6c 20 33 36 30 30  ariable ttl 3600
0290: 0a 09 76 61 72 69 61 62 6c 65 20 6e 74 74 6c 20  ..variable nttl 
02a0: 36 30 0a 0a 0a 09 70 72 6f 63 20 5f 68 61 73 68  60....proc _hash
02b0: 5f 73 65 70 20 7b 68 61 73 68 20 7b 73 65 70 73  _sep {hash {seps
02c0: 20 34 7d 7d 20 7b 0a 09 09 66 6f 72 20 7b 73 65   4}} {...for {se
02d0: 74 20 69 64 78 20 30 7d 20 7b 24 69 64 78 20 3c  t idx 0} {$idx <
02e0: 20 24 73 65 70 73 7d 20 7b 69 6e 63 72 20 69 64   $seps} {incr id
02f0: 78 7d 20 7b 0a 09 09 09 61 70 70 65 6e 64 20 72  x} {....append r
0300: 65 74 76 61 6c 20 22 5b 73 74 72 69 6e 67 20 72  etval "[string r
0310: 61 6e 67 65 20 24 68 61 73 68 20 5b 65 78 70 72  ange $hash [expr
0320: 20 7b 24 69 64 78 20 2a 20 32 7d 5d 20 5b 65 78   {$idx * 2}] [ex
0330: 70 72 20 7b 28 24 69 64 78 20 2a 20 32 29 20 2b  pr {($idx * 2) +
0340: 20 31 7d 5d 5d 2f 22 0a 09 09 7d 0a 09 09 61 70   1}]]/"...}...ap
0350: 70 65 6e 64 20 72 65 74 76 61 6c 20 22 5b 73 74  pend retval "[st
0360: 72 69 6e 67 20 72 61 6e 67 65 20 24 68 61 73 68  ring range $hash
0370: 20 5b 65 78 70 72 20 7b 24 69 64 78 20 2a 20 32   [expr {$idx * 2
0380: 7d 5d 20 65 6e 64 5d 22 0a 0a 09 09 72 65 74 75  }] end]"....retu
0390: 72 6e 20 24 72 65 74 76 61 6c 0a 09 7d 0a 0a 09  rn $retval..}...
03a0: 70 72 6f 63 20 5f 63 61 63 68 65 66 69 6c 65 20  proc _cachefile 
03b0: 7b 75 72 6c 20 6b 65 79 20 7b 6b 65 79 49 73 48  {url key {keyIsH
03c0: 61 73 68 20 31 7d 7d 20 7b 0a 09 09 73 65 74 20  ash 1}} {...set 
03d0: 66 69 6c 65 6b 65 79 20 24 6b 65 79 0a 09 09 69  filekey $key...i
03e0: 66 20 7b 24 6b 65 79 49 73 48 61 73 68 7d 20 7b  f {$keyIsHash} {
03f0: 0a 09 09 09 73 65 74 20 66 69 6c 65 6b 65 79 20  ....set filekey 
0400: 5b 5f 68 61 73 68 5f 73 65 70 20 24 66 69 6c 65  [_hash_sep $file
0410: 6b 65 79 5d 0a 09 09 7d 0a 0a 09 09 73 65 74 20  key]...}....set 
0420: 66 69 6c 65 20 5b 66 69 6c 65 20 6a 6f 69 6e 20  file [file join 
0430: 24 3a 3a 61 70 70 66 73 3a 3a 63 61 63 68 65 64  $::appfs::cached
0440: 69 72 20 24 66 69 6c 65 6b 65 79 5d 0a 0a 09 09  ir $filekey]....
0450: 66 69 6c 65 20 6d 6b 64 69 72 20 5b 66 69 6c 65  file mkdir [file
0460: 20 64 69 72 6e 61 6d 65 20 24 66 69 6c 65 5d 0a   dirname $file].
0470: 0a 09 09 69 66 20 7b 5b 66 69 6c 65 20 65 78 69  ...if {[file exi
0480: 73 74 73 20 24 66 69 6c 65 5d 7d 20 7b 0a 09 09  sts $file]} {...
0490: 09 72 65 74 75 72 6e 20 24 66 69 6c 65 0a 09 09  .return $file...
04a0: 7d 0a 0a 09 09 73 65 74 20 74 6d 70 66 69 6c 65  }....set tmpfile
04b0: 20 22 24 7b 66 69 6c 65 7d 2e 5b 65 78 70 72 20   "${file}.[expr 
04c0: 7b 72 61 6e 64 28 29 7d 5d 5b 63 6c 6f 63 6b 20  {rand()}][clock 
04d0: 63 6c 69 63 6b 73 5d 22 0a 0a 09 09 73 65 74 20  clicks]"....set 
04e0: 66 64 20 5b 6f 70 65 6e 20 24 74 6d 70 66 69 6c  fd [open $tmpfil
04f0: 65 20 22 77 22 5d 0a 09 09 66 63 6f 6e 66 69 67  e "w"]...fconfig
0500: 75 72 65 20 24 66 64 20 2d 74 72 61 6e 73 6c 61  ure $fd -transla
0510: 74 69 6f 6e 20 62 69 6e 61 72 79 0a 0a 09 09 63  tion binary....c
0520: 61 74 63 68 20 7b 0a 09 09 09 73 65 74 20 74 6f  atch {....set to
0530: 6b 65 6e 20 5b 3a 3a 68 74 74 70 3a 3a 67 65 74  ken [::http::get
0540: 75 72 6c 20 24 75 72 6c 20 2d 63 68 61 6e 6e 65  url $url -channe
0550: 6c 20 24 66 64 20 2d 62 69 6e 61 72 79 20 74 72  l $fd -binary tr
0560: 75 65 5d 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 5b  ue]...}....if {[
0570: 69 6e 66 6f 20 65 78 69 73 74 73 20 74 6f 6b 65  info exists toke
0580: 6e 5d 7d 20 7b 0a 09 09 09 73 65 74 20 6e 63 6f  n]} {....set nco
0590: 64 65 20 5b 3a 3a 68 74 74 70 3a 3a 6e 63 6f 64  de [::http::ncod
05a0: 65 20 24 74 6f 6b 65 6e 5d 0a 09 09 09 3a 3a 68  e $token]....::h
05b0: 74 74 70 3a 3a 72 65 73 65 74 20 24 74 6f 6b 65  ttp::reset $toke
05c0: 6e 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09  n...} else {....
05d0: 73 65 74 20 6e 63 6f 64 65 20 22 39 30 30 22 0a  set ncode "900".
05e0: 09 09 7d 0a 0a 09 09 63 6c 6f 73 65 20 24 66 64  ..}....close $fd
05f0: 0a 0a 09 09 69 66 20 7b 24 6b 65 79 49 73 48 61  ....if {$keyIsHa
0600: 73 68 7d 20 7b 0a 09 09 09 73 65 74 20 68 61 73  sh} {....set has
0610: 68 20 5b 73 74 72 69 6e 67 20 74 6f 6c 6f 77 65  h [string tolowe
0620: 72 20 5b 73 68 61 31 3a 3a 73 68 61 31 20 2d 68  r [sha1::sha1 -h
0630: 65 78 20 2d 66 69 6c 65 20 24 74 6d 70 66 69 6c  ex -file $tmpfil
0640: 65 5d 5d 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09  e]]...} else {..
0650: 09 09 73 65 74 20 68 61 73 68 20 24 6b 65 79 0a  ..set hash $key.
0660: 09 09 7d 0a 0a 09 09 69 66 20 7b 24 6e 63 6f 64  ..}....if {$ncod
0670: 65 20 3d 3d 20 22 32 30 30 22 20 26 26 20 24 68  e == "200" && $h
0680: 61 73 68 20 3d 3d 20 24 6b 65 79 7d 20 7b 0a 09  ash == $key} {..
0690: 09 09 66 69 6c 65 20 72 65 6e 61 6d 65 20 2d 66  ..file rename -f
06a0: 6f 72 63 65 20 2d 2d 20 24 74 6d 70 66 69 6c 65  orce -- $tmpfile
06b0: 20 24 66 69 6c 65 0a 09 09 7d 20 65 6c 73 65 20   $file...} else 
06c0: 7b 0a 09 09 09 66 69 6c 65 20 64 65 6c 65 74 65  {....file delete
06d0: 20 2d 66 6f 72 63 65 20 2d 2d 20 24 74 6d 70 66   -force -- $tmpf
06e0: 69 6c 65 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72  ile...}....retur
06f0: 6e 20 24 66 69 6c 65 0a 09 7d 0a 0a 0a 09 70 72  n $file..}....pr
0700: 6f 63 20 5f 69 73 48 61 73 68 20 7b 76 61 6c 75  oc _isHash {valu
0710: 65 7d 20 7b 0a 09 09 73 65 74 20 76 61 6c 75 65  e} {...set value
0720: 20 5b 73 74 72 69 6e 67 20 74 6f 6c 6f 77 65 72   [string tolower
0730: 20 24 76 61 6c 75 65 5d 0a 0a 09 09 69 66 20 7b   $value]....if {
0740: 5b 73 74 72 69 6e 67 20 6c 65 6e 67 74 68 20 24  [string length $
0750: 76 61 6c 75 65 5d 20 21 3d 20 34 30 7d 20 7b 0a  value] != 40} {.
0760: 09 09 09 72 65 74 75 72 6e 20 66 61 6c 73 65 0a  ...return false.
0770: 09 09 7d 0a 0a 09 09 69 66 20 7b 21 5b 72 65 67  ..}....if {![reg
0780: 65 78 70 20 7b 5e 5b 30 2d 39 61 2d 66 5d 2a 24  exp {^[0-9a-f]*$
0790: 7d 20 24 76 61 6c 75 65 5d 7d 20 7b 0a 09 09 09  } $value]} {....
07a0: 72 65 74 75 72 6e 20 66 61 6c 73 65 0a 09 09 7d  return false...}
07b0: 0a 0a 09 09 72 65 74 75 72 6e 20 74 72 75 65 0a  ....return true.
07c0: 09 7d 0a 0a 09 70 72 6f 63 20 5f 6e 6f 72 6d 61  .}...proc _norma
07d0: 6c 69 7a 65 4f 53 20 7b 6f 73 7d 20 7b 0a 09 09  lizeOS {os} {...
07e0: 73 65 74 20 6f 73 20 5b 73 74 72 69 6e 67 20 74  set os [string t
07f0: 6f 6c 6f 77 65 72 20 5b 73 74 72 69 6e 67 20 74  olower [string t
0800: 72 69 6d 20 24 6f 73 5d 5d 0a 0a 09 09 73 77 69  rim $os]]....swi
0810: 74 63 68 20 2d 2d 20 24 6f 73 20 7b 0a 09 09 09  tch -- $os {....
0820: 22 6c 69 6e 75 78 22 20 2d 20 22 66 72 65 65 62  "linux" - "freeb
0830: 73 64 22 20 2d 20 22 6f 70 65 6e 62 73 64 22 20  sd" - "openbsd" 
0840: 2d 20 22 6e 65 74 62 73 64 22 20 7b 0a 09 09 09  - "netbsd" {....
0850: 09 72 65 74 75 72 6e 20 24 6f 73 0a 09 09 09 7d  .return $os....}
0860: 0a 09 09 09 22 73 75 6e 6f 73 22 20 7b 0a 09 09  ...."sunos" {...
0870: 09 09 72 65 74 75 72 6e 20 22 73 6f 6c 61 72 69  ..return "solari
0880: 73 22 0a 09 09 09 7d 0a 09 09 09 22 6e 6f 61 72  s"....}...."noar
0890: 63 68 22 20 2d 20 22 6e 6f 6e 65 22 20 2d 20 22  ch" - "none" - "
08a0: 61 6e 79 22 20 2d 20 22 61 6c 6c 22 20 7b 0a 09  any" - "all" {..
08b0: 09 09 09 72 65 74 75 72 6e 20 22 6e 6f 61 72 63  ...return "noarc
08c0: 68 22 0a 09 09 09 7d 0a 09 09 7d 0a 0a 09 09 72  h"....}...}....r
08d0: 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f  eturn -code erro
08e0: 72 20 22 55 6e 61 62 6c 65 20 74 6f 20 6e 6f 72  r "Unable to nor
08f0: 6d 61 6c 69 7a 65 20 4f 53 3a 20 24 6f 73 22 0a  malize OS: $os".
0900: 09 7d 0a 0a 09 70 72 6f 63 20 5f 6e 6f 72 6d 61  .}...proc _norma
0910: 6c 69 7a 65 43 50 55 20 7b 63 70 75 7d 20 7b 0a  lizeCPU {cpu} {.
0920: 09 09 73 65 74 20 63 70 75 20 5b 73 74 72 69 6e  ..set cpu [strin
0930: 67 20 74 6f 6c 6f 77 65 72 20 5b 73 74 72 69 6e  g tolower [strin
0940: 67 20 74 72 69 6d 20 24 63 70 75 5d 5d 0a 0a 09  g trim $cpu]]...
0950: 09 73 77 69 74 63 68 20 2d 67 6c 6f 62 20 2d 2d  .switch -glob --
0960: 20 24 63 70 75 20 7b 0a 09 09 09 22 69 3f 38 36   $cpu {...."i?86
0970: 22 20 7b 0a 09 09 09 09 72 65 74 75 72 6e 20 22  " {.....return "
0980: 69 78 38 36 22 0a 09 09 09 7d 0a 09 09 09 22 78  ix86"....}...."x
0990: 38 36 5f 36 34 22 20 7b 0a 09 09 09 09 72 65 74  86_64" {.....ret
09a0: 75 72 6e 20 24 63 70 75 0a 09 09 09 7d 0a 09 09  urn $cpu....}...
09b0: 09 22 6e 6f 61 72 63 68 22 20 2d 20 22 6e 6f 6e  ."noarch" - "non
09c0: 65 22 20 2d 20 22 61 6e 79 22 20 2d 20 22 61 6c  e" - "any" - "al
09d0: 6c 22 20 7b 0a 09 09 09 09 72 65 74 75 72 6e 20  l" {.....return 
09e0: 22 6e 6f 61 72 63 68 22 0a 09 09 09 7d 0a 09 09  "noarch"....}...
09f0: 7d 0a 0a 09 09 72 65 74 75 72 6e 20 2d 63 6f 64  }....return -cod
0a00: 65 20 65 72 72 6f 72 20 22 55 6e 61 62 6c 65 20  e error "Unable 
0a10: 74 6f 20 6e 6f 72 6d 61 6c 69 7a 65 20 43 50 55  to normalize CPU
0a20: 3a 20 24 63 70 75 22 0a 09 7d 0a 0a 09 70 72 6f  : $cpu"..}...pro
0a30: 63 20 5f 61 73 5f 75 73 65 72 20 7b 63 6f 64 65  c _as_user {code
0a40: 7d 20 7b 0a 09 09 3a 3a 61 70 70 66 73 64 3a 3a  } {...::appfsd::
0a50: 73 69 6d 75 6c 61 74 65 5f 75 73 65 72 5f 66 73  simulate_user_fs
0a60: 5f 65 6e 74 65 72 0a 0a 09 09 73 65 74 20 72 65  _enter....set re
0a70: 74 63 6f 64 65 20 5b 63 61 74 63 68 20 5b 6c 69  tcode [catch [li
0a80: 73 74 20 75 70 6c 65 76 65 6c 20 24 63 6f 64 65  st uplevel $code
0a90: 5d 20 72 65 74 73 74 72 5d 0a 0a 09 09 3a 3a 61  ] retstr]....::a
0aa0: 70 70 66 73 64 3a 3a 73 69 6d 75 6c 61 74 65 5f  ppfsd::simulate_
0ab0: 75 73 65 72 5f 66 73 5f 6c 65 61 76 65 0a 0a 09  user_fs_leave...
0ac0: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 24 72  .return -code $r
0ad0: 65 74 63 6f 64 65 20 24 72 65 74 73 74 72 0a 09  etcode $retstr..
0ae0: 7d 0a 0a 09 70 72 6f 63 20 69 6e 69 74 20 7b 7d  }...proc init {}
0af0: 20 7b 0a 09 09 69 66 20 7b 5b 69 6e 66 6f 20 65   {...if {[info e
0b00: 78 69 73 74 73 20 3a 3a 61 70 70 66 73 3a 3a 69  xists ::appfs::i
0b10: 6e 69 74 5f 63 61 6c 6c 65 64 5d 7d 20 7b 0a 09  nit_called]} {..
0b20: 09 09 72 65 74 75 72 6e 0a 09 09 7d 0a 0a 09 09  ..return...}....
0b30: 23 20 46 6f 72 63 65 20 5b 70 61 72 72 61 79 5d  # Force [parray]
0b40: 20 74 6f 20 62 65 20 6c 6f 61 64 65 64 0a 09 09   to be loaded...
0b50: 63 61 74 63 68 20 7b 0a 09 09 09 70 61 72 72 61  catch {....parra
0b60: 79 20 64 6f 65 73 5f 6e 6f 74 5f 65 78 69 73 74  y does_not_exist
0b70: 0a 09 09 7d 0a 0a 09 09 73 65 74 20 3a 3a 61 70  ...}....set ::ap
0b80: 70 66 73 3a 3a 69 6e 69 74 5f 63 61 6c 6c 65 64  pfs::init_called
0b90: 20 31 0a 0a 09 09 23 20 4c 6f 61 64 20 63 6f 6e   1....# Load con
0ba0: 66 69 67 75 72 61 74 69 6f 6e 20 66 69 6c 65 0a  figuration file.
0bb0: 09 09 73 65 74 20 63 6f 6e 66 69 67 5f 66 69 6c  ..set config_fil
0bc0: 65 20 5b 66 69 6c 65 20 6a 6f 69 6e 20 24 3a 3a  e [file join $::
0bd0: 61 70 70 66 73 3a 3a 63 61 63 68 65 64 69 72 20  appfs::cachedir 
0be0: 63 6f 6e 66 69 67 5d 0a 09 09 69 66 20 7b 5b 66  config]...if {[f
0bf0: 69 6c 65 20 65 78 69 73 74 73 20 24 63 6f 6e 66  ile exists $conf
0c00: 69 67 5f 66 69 6c 65 5d 7d 20 7b 0a 09 09 09 73  ig_file]} {....s
0c10: 6f 75 72 63 65 20 24 63 6f 6e 66 69 67 5f 66 69  ource $config_fi
0c20: 6c 65 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 21 5b  le...}....if {![
0c30: 69 6e 66 6f 20 65 78 69 73 74 73 20 3a 3a 61 70  info exists ::ap
0c40: 70 66 73 3a 3a 64 62 5d 7d 20 7b 0a 09 09 09 66  pfs::db]} {....f
0c50: 69 6c 65 20 6d 6b 64 69 72 20 24 3a 3a 61 70 70  ile mkdir $::app
0c60: 66 73 3a 3a 63 61 63 68 65 64 69 72 0a 0a 09 09  fs::cachedir....
0c70: 09 73 71 6c 69 74 65 33 20 3a 3a 61 70 70 66 73  .sqlite3 ::appfs
0c80: 3a 3a 64 62 20 5b 66 69 6c 65 20 6a 6f 69 6e 20  ::db [file join 
0c90: 24 3a 3a 61 70 70 66 73 3a 3a 63 61 63 68 65 64  $::appfs::cached
0ca0: 69 72 20 63 61 63 68 65 2e 64 62 5d 0a 09 09 7d  ir cache.db]...}
0cb0: 0a 0a 09 09 23 20 43 72 65 61 74 65 20 74 61 62  ....# Create tab
0cc0: 6c 65 73 0a 09 09 64 62 20 65 76 61 6c 20 7b 43  les...db eval {C
0cd0: 52 45 41 54 45 20 54 41 42 4c 45 20 49 46 20 4e  REATE TABLE IF N
0ce0: 4f 54 20 45 58 49 53 54 53 20 73 69 74 65 73 28  OT EXISTS sites(
0cf0: 68 6f 73 74 6e 61 6d 65 20 50 52 49 4d 41 52 59  hostname PRIMARY
0d00: 20 4b 45 59 2c 20 6c 61 73 74 55 70 64 61 74 65   KEY, lastUpdate
0d10: 2c 20 74 74 6c 29 3b 7d 0a 09 09 64 62 20 65 76  , ttl);}...db ev
0d20: 61 6c 20 7b 43 52 45 41 54 45 20 54 41 42 4c 45  al {CREATE TABLE
0d30: 20 49 46 20 4e 4f 54 20 45 58 49 53 54 53 20 70   IF NOT EXISTS p
0d40: 61 63 6b 61 67 65 73 28 68 6f 73 74 6e 61 6d 65  ackages(hostname
0d50: 2c 20 73 68 61 31 2c 20 70 61 63 6b 61 67 65 2c  , sha1, package,
0d60: 20 76 65 72 73 69 6f 6e 2c 20 6f 73 2c 20 63 70   version, os, cp
0d70: 75 41 72 63 68 2c 20 69 73 4c 61 74 65 73 74 2c  uArch, isLatest,
0d80: 20 68 61 76 65 4d 61 6e 69 66 65 73 74 29 3b 7d   haveManifest);}
0d90: 0a 09 09 64 62 20 65 76 61 6c 20 7b 43 52 45 41  ...db eval {CREA
0da0: 54 45 20 54 41 42 4c 45 20 49 46 20 4e 4f 54 20  TE TABLE IF NOT 
0db0: 45 58 49 53 54 53 20 66 69 6c 65 73 28 70 61 63  EXISTS files(pac
0dc0: 6b 61 67 65 5f 73 68 61 31 2c 20 74 79 70 65 2c  kage_sha1, type,
0dd0: 20 74 69 6d 65 2c 20 73 6f 75 72 63 65 2c 20 73   time, source, s
0de0: 69 7a 65 2c 20 70 65 72 6d 73 2c 20 66 69 6c 65  ize, perms, file
0df0: 5f 73 68 61 31 2c 20 66 69 6c 65 5f 6e 61 6d 65  _sha1, file_name
0e00: 2c 20 66 69 6c 65 5f 64 69 72 65 63 74 6f 72 79  , file_directory
0e10: 29 3b 7d 0a 0a 09 09 23 20 43 72 65 61 74 65 20  );}....# Create 
0e20: 69 6e 64 65 78 65 73 0a 09 09 64 62 20 65 76 61  indexes...db eva
0e30: 6c 20 7b 43 52 45 41 54 45 20 49 4e 44 45 58 20  l {CREATE INDEX 
0e40: 49 46 20 4e 4f 54 20 45 58 49 53 54 53 20 73 69  IF NOT EXISTS si
0e50: 74 65 73 5f 69 6e 64 65 78 20 4f 4e 20 73 69 74  tes_index ON sit
0e60: 65 73 20 28 68 6f 73 74 6e 61 6d 65 29 3b 7d 0a  es (hostname);}.
0e70: 09 09 64 62 20 65 76 61 6c 20 7b 43 52 45 41 54  ..db eval {CREAT
0e80: 45 20 49 4e 44 45 58 20 49 46 20 4e 4f 54 20 45  E INDEX IF NOT E
0e90: 58 49 53 54 53 20 70 61 63 6b 61 67 65 73 5f 69  XISTS packages_i
0ea0: 6e 64 65 78 20 4f 4e 20 70 61 63 6b 61 67 65 73  ndex ON packages
0eb0: 20 28 68 6f 73 74 6e 61 6d 65 2c 20 73 68 61 31   (hostname, sha1
0ec0: 2c 20 70 61 63 6b 61 67 65 2c 20 76 65 72 73 69  , package, versi
0ed0: 6f 6e 2c 20 6f 73 2c 20 63 70 75 41 72 63 68 29  on, os, cpuArch)
0ee0: 3b 7d 0a 09 09 64 62 20 65 76 61 6c 20 7b 43 52  ;}...db eval {CR
0ef0: 45 41 54 45 20 49 4e 44 45 58 20 49 46 20 4e 4f  EATE INDEX IF NO
0f00: 54 20 45 58 49 53 54 53 20 66 69 6c 65 73 5f 69  T EXISTS files_i
0f10: 6e 64 65 78 20 4f 4e 20 66 69 6c 65 73 20 28 70  ndex ON files (p
0f20: 61 63 6b 61 67 65 5f 73 68 61 31 2c 20 66 69 6c  ackage_sha1, fil
0f30: 65 5f 6e 61 6d 65 2c 20 66 69 6c 65 5f 64 69 72  e_name, file_dir
0f40: 65 63 74 6f 72 79 29 3b 7d 0a 09 7d 0a 0a 09 70  ectory);}..}...p
0f50: 72 6f 63 20 64 6f 77 6e 6c 6f 61 64 20 7b 68 6f  roc download {ho
0f60: 73 74 6e 61 6d 65 20 68 61 73 68 20 7b 6d 65 74  stname hash {met
0f70: 68 6f 64 20 73 68 61 31 7d 7d 20 7b 0a 09 09 73  hod sha1}} {...s
0f80: 65 74 20 75 72 6c 20 5b 3a 3a 61 70 70 66 73 3a  et url [::appfs:
0f90: 3a 75 73 65 72 3a 3a 63 6f 6e 73 74 72 75 63 74  :user::construct
0fa0: 5f 75 72 6c 20 24 68 6f 73 74 6e 61 6d 65 20 24  _url $hostname $
0fb0: 68 61 73 68 20 24 6d 65 74 68 6f 64 5d 0a 09 09  hash $method]...
0fc0: 73 65 74 20 66 69 6c 65 20 5b 5f 63 61 63 68 65  set file [_cache
0fd0: 66 69 6c 65 20 24 75 72 6c 20 24 68 61 73 68 5d  file $url $hash]
0fe0: 0a 0a 09 09 69 66 20 7b 21 5b 66 69 6c 65 20 65  ....if {![file e
0ff0: 78 69 73 74 73 20 24 66 69 6c 65 5d 7d 20 7b 0a  xists $file]} {.
1000: 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20  ...return -code 
1010: 65 72 72 6f 72 20 22 55 6e 61 62 6c 65 20 74 6f  error "Unable to
1020: 20 66 65 74 63 68 20 28 66 69 6c 65 20 64 6f 65   fetch (file doe
1030: 73 20 6e 6f 74 20 65 78 69 73 74 3a 20 24 66 69  s not exist: $fi
1040: 6c 65 29 22 0a 09 09 7d 0a 0a 09 09 72 65 74 75  le)"...}....retu
1050: 72 6e 20 24 66 69 6c 65 0a 09 7d 0a 0a 09 70 72  rn $file..}...pr
1060: 6f 63 20 67 65 74 69 6e 64 65 78 20 7b 68 6f 73  oc getindex {hos
1070: 74 6e 61 6d 65 7d 20 7b 0a 09 09 73 65 74 20 6e  tname} {...set n
1080: 6f 77 20 5b 63 6c 6f 63 6b 20 73 65 63 6f 6e 64  ow [clock second
1090: 73 5d 0a 0a 09 09 73 65 74 20 6c 61 73 74 55 70  s]....set lastUp
10a0: 64 61 74 65 73 20 5b 64 62 20 65 76 61 6c 20 7b  dates [db eval {
10b0: 53 45 4c 45 43 54 20 6c 61 73 74 55 70 64 61 74  SELECT lastUpdat
10c0: 65 2c 20 74 74 6c 20 46 52 4f 4d 20 73 69 74 65  e, ttl FROM site
10d0: 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65  s WHERE hostname
10e0: 20 3d 20 24 68 6f 73 74 6e 61 6d 65 20 4c 49 4d   = $hostname LIM
10f0: 49 54 20 31 3b 7d 5d 0a 09 09 69 66 20 7b 5b 6c  IT 1;}]...if {[l
1100: 6c 65 6e 67 74 68 20 24 6c 61 73 74 55 70 64 61  length $lastUpda
1110: 74 65 73 5d 20 3d 3d 20 30 7d 20 7b 0a 09 09 09  tes] == 0} {....
1120: 73 65 74 20 6c 61 73 74 55 70 64 61 74 65 20 30  set lastUpdate 0
1130: 0a 09 09 09 73 65 74 20 74 74 6c 20 30 0a 09 09  ....set ttl 0...
1140: 7d 20 65 6c 73 65 20 7b 0a 09 09 09 73 65 74 20  } else {....set 
1150: 6c 61 73 74 55 70 64 61 74 65 20 5b 6c 69 6e 64  lastUpdate [lind
1160: 65 78 20 24 6c 61 73 74 55 70 64 61 74 65 73 20  ex $lastUpdates 
1170: 30 5d 0a 09 09 09 73 65 74 20 74 74 6c 20 5b 6c  0]....set ttl [l
1180: 69 6e 64 65 78 20 24 6c 61 73 74 55 70 64 61 74  index $lastUpdat
1190: 65 73 20 31 5d 0a 09 09 7d 0a 0a 09 09 69 66 20  es 1]...}....if 
11a0: 7b 24 6e 6f 77 20 3c 20 28 24 6c 61 73 74 55 70  {$now < ($lastUp
11b0: 64 61 74 65 20 2b 20 24 74 74 6c 29 7d 20 7b 0a  date + $ttl)} {.
11c0: 09 09 09 72 65 74 75 72 6e 20 43 4f 4d 50 4c 45  ...return COMPLE
11d0: 54 45 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 5b 73  TE...}....if {[s
11e0: 74 72 69 6e 67 20 6d 61 74 63 68 20 22 2a 5c 5b  tring match "*\[
11f0: 2f 7e 5c 5d 2a 22 20 24 68 6f 73 74 6e 61 6d 65  /~\]*" $hostname
1200: 5d 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d  ]} {....return -
1210: 63 6f 64 65 20 65 72 72 6f 72 20 22 49 6e 76 61  code error "Inva
1220: 6c 69 64 20 68 6f 73 74 6e 61 6d 65 22 0a 09 09  lid hostname"...
1230: 7d 0a 0a 09 09 73 65 74 20 75 72 6c 20 22 68 74  }....set url "ht
1240: 74 70 3a 2f 2f 24 68 6f 73 74 6e 61 6d 65 2f 61  tp://$hostname/a
1250: 70 70 66 73 2f 69 6e 64 65 78 22 0a 0a 09 09 63  ppfs/index"....c
1260: 61 74 63 68 20 7b 0a 09 09 09 73 65 74 20 74 6f  atch {....set to
1270: 6b 65 6e 20 5b 3a 3a 68 74 74 70 3a 3a 67 65 74  ken [::http::get
1280: 75 72 6c 20 24 75 72 6c 5d 0a 09 09 09 69 66 20  url $url]....if 
1290: 7b 5b 3a 3a 68 74 74 70 3a 3a 6e 63 6f 64 65 20  {[::http::ncode 
12a0: 24 74 6f 6b 65 6e 5d 20 3d 3d 20 22 32 30 30 22  $token] == "200"
12b0: 7d 20 7b 0a 09 09 09 09 73 65 74 20 69 6e 64 65  } {.....set inde
12c0: 78 68 61 73 68 5f 64 61 74 61 20 5b 3a 3a 68 74  xhash_data [::ht
12d0: 74 70 3a 3a 64 61 74 61 20 24 74 6f 6b 65 6e 5d  tp::data $token]
12e0: 0a 09 09 09 7d 0a 09 09 09 3a 3a 68 74 74 70 3a  ....}....::http:
12f0: 3a 72 65 73 65 74 20 24 74 6f 6b 65 6e 0a 09 09  :reset $token...
1300: 09 3a 3a 68 74 74 70 3a 3a 63 6c 65 61 6e 75 70  .::http::cleanup
1310: 20 24 74 6f 6b 65 6e 0a 09 09 7d 0a 0a 09 09 69   $token...}....i
1320: 66 20 7b 21 5b 69 6e 66 6f 20 65 78 69 73 74 73  f {![info exists
1330: 20 69 6e 64 65 78 68 61 73 68 5f 64 61 74 61 5d   indexhash_data]
1340: 7d 20 7b 0a 09 09 09 23 20 43 61 63 68 65 20 74  } {....# Cache t
1350: 68 69 73 20 72 65 73 75 6c 74 20 66 6f 72 20 36  his result for 6
1360: 30 20 73 65 63 6f 6e 64 73 0a 09 09 09 64 62 20  0 seconds....db 
1370: 65 76 61 6c 20 7b 49 4e 53 45 52 54 20 4f 52 20  eval {INSERT OR 
1380: 52 45 50 4c 41 43 45 20 49 4e 54 4f 20 73 69 74  REPLACE INTO sit
1390: 65 73 20 28 68 6f 73 74 6e 61 6d 65 2c 20 6c 61  es (hostname, la
13a0: 73 74 55 70 64 61 74 65 2c 20 74 74 6c 29 20 56  stUpdate, ttl) V
13b0: 41 4c 55 45 53 20 28 24 68 6f 73 74 6e 61 6d 65  ALUES ($hostname
13c0: 2c 20 24 6e 6f 77 2c 20 24 3a 3a 61 70 70 66 73  , $now, $::appfs
13d0: 3a 3a 6e 74 74 6c 29 3b 7d 0a 0a 09 09 09 72 65  ::nttl);}.....re
13e0: 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72  turn -code error
13f0: 20 22 55 6e 61 62 6c 65 20 74 6f 20 66 65 74 63   "Unable to fetc
1400: 68 20 24 75 72 6c 22 0a 09 09 7d 0a 0a 09 09 73  h $url"...}....s
1410: 65 74 20 69 6e 64 65 78 68 61 73 68 20 5b 6c 69  et indexhash [li
1420: 6e 64 65 78 20 5b 73 70 6c 69 74 20 24 69 6e 64  ndex [split $ind
1430: 65 78 68 61 73 68 5f 64 61 74 61 20 22 2c 22 5d  exhash_data ","]
1440: 20 30 5d 0a 0a 09 09 69 66 20 7b 21 5b 5f 69 73   0]....if {![_is
1450: 48 61 73 68 20 24 69 6e 64 65 78 68 61 73 68 5d  Hash $indexhash]
1460: 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d 63  } {....return -c
1470: 6f 64 65 20 65 72 72 6f 72 20 22 49 6e 76 61 6c  ode error "Inval
1480: 69 64 20 68 61 73 68 3a 20 24 69 6e 64 65 78 68  id hash: $indexh
1490: 61 73 68 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20  ash"...}....set 
14a0: 66 69 6c 65 20 5b 64 6f 77 6e 6c 6f 61 64 20 24  file [download $
14b0: 68 6f 73 74 6e 61 6d 65 20 24 69 6e 64 65 78 68  hostname $indexh
14c0: 61 73 68 5d 0a 09 09 73 65 74 20 66 64 20 5b 6f  ash]...set fd [o
14d0: 70 65 6e 20 24 66 69 6c 65 5d 0a 09 09 73 65 74  pen $file]...set
14e0: 20 64 61 74 61 20 5b 72 65 61 64 20 24 66 64 5d   data [read $fd]
14f0: 0a 09 09 63 6c 6f 73 65 20 24 66 64 0a 0a 09 09  ...close $fd....
1500: 73 65 74 20 63 75 72 72 5f 70 61 63 6b 61 67 65  set curr_package
1510: 73 20 5b 6c 69 73 74 5d 0a 09 09 66 6f 72 65 61  s [list]...forea
1520: 63 68 20 6c 69 6e 65 20 5b 73 70 6c 69 74 20 24  ch line [split $
1530: 64 61 74 61 20 22 5c 6e 22 5d 20 7b 0a 09 09 09  data "\n"] {....
1540: 73 65 74 20 6c 69 6e 65 20 5b 73 74 72 69 6e 67  set line [string
1550: 20 74 72 69 6d 20 24 6c 69 6e 65 5d 0a 0a 09 09   trim $line]....
1560: 09 69 66 20 7b 5b 73 74 72 69 6e 67 20 6d 61 74  .if {[string mat
1570: 63 68 20 22 2a 2f 2a 22 20 24 6c 69 6e 65 5d 7d  ch "*/*" $line]}
1580: 20 7b 0a 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a   {.....continue.
1590: 09 09 09 7d 0a 0a 09 09 09 69 66 20 7b 24 6c 69  ...}.....if {$li
15a0: 6e 65 20 3d 3d 20 22 22 7d 20 7b 0a 09 09 09 09  ne == ""} {.....
15b0: 63 6f 6e 74 69 6e 75 65 0a 09 09 09 7d 0a 0a 09  continue....}...
15c0: 09 09 73 65 74 20 77 6f 72 6b 20 5b 73 70 6c 69  ..set work [spli
15d0: 74 20 24 6c 69 6e 65 20 22 2c 22 5d 0a 0a 09 09  t $line ","]....
15e0: 09 75 6e 73 65 74 20 2d 6e 6f 63 6f 6d 70 6c 61  .unset -nocompla
15f0: 69 6e 20 70 6b 67 49 6e 66 6f 0a 09 09 09 69 66  in pkgInfo....if
1600: 20 7b 5b 63 61 74 63 68 20 7b 0a 09 09 09 09 73   {[catch {.....s
1610: 65 74 20 70 6b 67 49 6e 66 6f 28 70 61 63 6b 61  et pkgInfo(packa
1620: 67 65 29 20 20 5b 6c 69 6e 64 65 78 20 24 77 6f  ge)  [lindex $wo
1630: 72 6b 20 30 5d 0a 09 09 09 09 73 65 74 20 70 6b  rk 0].....set pk
1640: 67 49 6e 66 6f 28 76 65 72 73 69 6f 6e 29 20 20  gInfo(version)  
1650: 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 31 5d  [lindex $work 1]
1660: 0a 09 09 09 09 73 65 74 20 70 6b 67 49 6e 66 6f  .....set pkgInfo
1670: 28 6f 73 29 20 20 20 20 20 20 20 5b 5f 6e 6f 72  (os)       [_nor
1680: 6d 61 6c 69 7a 65 4f 53 20 5b 6c 69 6e 64 65 78  malizeOS [lindex
1690: 20 24 77 6f 72 6b 20 32 5d 5d 0a 09 09 09 09 73   $work 2]].....s
16a0: 65 74 20 70 6b 67 49 6e 66 6f 28 63 70 75 41 72  et pkgInfo(cpuAr
16b0: 63 68 29 20 20 5b 5f 6e 6f 72 6d 61 6c 69 7a 65  ch)  [_normalize
16c0: 43 50 55 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72  CPU [lindex $wor
16d0: 6b 20 33 5d 5d 0a 09 09 09 09 73 65 74 20 70 6b  k 3]].....set pk
16e0: 67 49 6e 66 6f 28 68 61 73 68 29 20 20 20 20 20  gInfo(hash)     
16f0: 5b 73 74 72 69 6e 67 20 74 6f 6c 6f 77 65 72 20  [string tolower 
1700: 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 34 5d  [lindex $work 4]
1710: 5d 0a 09 09 09 09 73 65 74 20 70 6b 67 49 6e 66  ].....set pkgInf
1720: 6f 28 68 61 73 68 5f 74 79 70 65 29 20 22 73 68  o(hash_type) "sh
1730: 61 31 22 0a 09 09 09 09 73 65 74 20 70 6b 67 49  a1".....set pkgI
1740: 6e 66 6f 28 69 73 4c 61 74 65 73 74 29 20 5b 65  nfo(isLatest) [e
1750: 78 70 72 20 7b 21 21 5b 6c 69 6e 64 65 78 20 24  xpr {!![lindex $
1760: 77 6f 72 6b 20 35 5d 7d 5d 0a 09 09 09 7d 5d 7d  work 5]}]....}]}
1770: 20 7b 0a 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a   {.....continue.
1780: 09 09 09 7d 0a 0a 09 09 09 69 66 20 7b 21 5b 5f  ...}.....if {![_
1790: 69 73 48 61 73 68 20 24 70 6b 67 49 6e 66 6f 28  isHash $pkgInfo(
17a0: 68 61 73 68 29 5d 7d 20 7b 0a 09 09 09 09 63 6f  hash)]} {.....co
17b0: 6e 74 69 6e 75 65 0a 09 09 09 7d 0a 0a 09 09 09  ntinue....}.....
17c0: 6c 61 70 70 65 6e 64 20 63 75 72 72 5f 70 61 63  lappend curr_pac
17d0: 6b 61 67 65 73 20 24 70 6b 67 49 6e 66 6f 28 68  kages $pkgInfo(h
17e0: 61 73 68 29 0a 0a 09 09 09 23 20 44 6f 20 6e 6f  ash).....# Do no
17f0: 74 20 64 6f 20 61 6e 79 20 61 64 64 69 74 69 6f  t do any additio
1800: 6e 61 6c 20 77 6f 72 6b 20 69 66 20 77 65 20 61  nal work if we a
1810: 6c 72 65 61 64 79 20 68 61 76 65 20 74 68 69 73  lready have this
1820: 20 70 61 63 6b 61 67 65 0a 09 09 09 73 65 74 20   package....set 
1830: 65 78 69 73 74 69 6e 67 5f 70 61 63 6b 61 67 65  existing_package
1840: 73 20 5b 64 62 20 65 76 61 6c 20 7b 53 45 4c 45  s [db eval {SELE
1850: 43 54 20 70 61 63 6b 61 67 65 20 46 52 4f 4d 20  CT package FROM 
1860: 70 61 63 6b 61 67 65 73 20 57 48 45 52 45 20 68  packages WHERE h
1870: 6f 73 74 6e 61 6d 65 20 3d 20 24 68 6f 73 74 6e  ostname = $hostn
1880: 61 6d 65 20 41 4e 44 20 73 68 61 31 20 3d 20 24  ame AND sha1 = $
1890: 70 6b 67 49 6e 66 6f 28 68 61 73 68 29 3b 7d 5d  pkgInfo(hash);}]
18a0: 0a 09 09 09 69 66 20 7b 5b 6c 73 65 61 72 63 68  ....if {[lsearch
18b0: 20 2d 65 78 61 63 74 20 24 65 78 69 73 74 69 6e   -exact $existin
18c0: 67 5f 70 61 63 6b 61 67 65 73 20 24 70 6b 67 49  g_packages $pkgI
18d0: 6e 66 6f 28 70 61 63 6b 61 67 65 29 5d 20 21 3d  nfo(package)] !=
18e0: 20 2d 31 7d 20 7b 0a 09 09 09 09 63 6f 6e 74 69   -1} {.....conti
18f0: 6e 75 65 0a 09 09 09 7d 0a 0a 09 09 09 69 66 20  nue....}.....if 
1900: 7b 24 70 6b 67 49 6e 66 6f 28 69 73 4c 61 74 65  {$pkgInfo(isLate
1910: 73 74 29 7d 20 7b 0a 09 09 09 09 64 62 20 65 76  st)} {.....db ev
1920: 61 6c 20 7b 55 50 44 41 54 45 20 70 61 63 6b 61  al {UPDATE packa
1930: 67 65 73 20 53 45 54 20 69 73 4c 61 74 65 73 74  ges SET isLatest
1940: 20 3d 20 30 20 57 48 45 52 45 20 68 6f 73 74 6e   = 0 WHERE hostn
1950: 61 6d 65 20 3d 20 24 68 6f 73 74 6e 61 6d 65 20  ame = $hostname 
1960: 41 4e 44 20 70 61 63 6b 61 67 65 20 3d 20 24 70  AND package = $p
1970: 6b 67 49 6e 66 6f 28 24 70 61 63 6b 61 67 65 29  kgInfo($package)
1980: 20 41 4e 44 20 6f 73 20 3d 20 24 70 6b 67 49 6e   AND os = $pkgIn
1990: 66 6f 28 24 70 61 63 6b 61 67 65 29 20 41 4e 44  fo($package) AND
19a0: 20 63 70 75 41 72 63 68 20 3d 20 24 70 6b 67 49   cpuArch = $pkgI
19b0: 6e 66 6f 28 63 70 75 41 72 63 68 29 3b 7d 0a 09  nfo(cpuArch);}..
19c0: 09 09 7d 0a 0a 09 09 09 64 62 20 65 76 61 6c 20  ..}.....db eval 
19d0: 7b 49 4e 53 45 52 54 20 49 4e 54 4f 20 70 61 63  {INSERT INTO pac
19e0: 6b 61 67 65 73 20 28 68 6f 73 74 6e 61 6d 65 2c  kages (hostname,
19f0: 20 73 68 61 31 2c 20 70 61 63 6b 61 67 65 2c 20   sha1, package, 
1a00: 76 65 72 73 69 6f 6e 2c 20 6f 73 2c 20 63 70 75  version, os, cpu
1a10: 41 72 63 68 2c 20 69 73 4c 61 74 65 73 74 2c 20  Arch, isLatest, 
1a20: 68 61 76 65 4d 61 6e 69 66 65 73 74 29 20 56 41  haveManifest) VA
1a30: 4c 55 45 53 20 28 24 68 6f 73 74 6e 61 6d 65 2c  LUES ($hostname,
1a40: 20 24 70 6b 67 49 6e 66 6f 28 68 61 73 68 29 2c   $pkgInfo(hash),
1a50: 20 24 70 6b 67 49 6e 66 6f 28 70 61 63 6b 61 67   $pkgInfo(packag
1a60: 65 29 2c 20 24 70 6b 67 49 6e 66 6f 28 76 65 72  e), $pkgInfo(ver
1a70: 73 69 6f 6e 29 2c 20 24 70 6b 67 49 6e 66 6f 28  sion), $pkgInfo(
1a80: 6f 73 29 2c 20 24 70 6b 67 49 6e 66 6f 28 63 70  os), $pkgInfo(cp
1a90: 75 41 72 63 68 29 2c 20 24 70 6b 67 49 6e 66 6f  uArch), $pkgInfo
1aa0: 28 69 73 4c 61 74 65 73 74 29 2c 20 30 29 3b 7d  (isLatest), 0);}
1ab0: 0a 0a 09 09 7d 0a 0a 09 09 23 20 4c 6f 6f 6b 20  ....}....# Look 
1ac0: 66 6f 72 20 70 61 63 6b 61 67 65 73 20 74 68 61  for packages tha
1ad0: 74 20 68 61 76 65 20 62 65 65 6e 20 64 65 6c 65  t have been dele
1ae0: 74 65 64 0a 09 09 73 65 74 20 66 6f 75 6e 64 5f  ted...set found_
1af0: 70 61 63 6b 61 67 65 73 20 5b 64 62 20 65 76 61  packages [db eva
1b00: 6c 20 7b 53 45 4c 45 43 54 20 73 68 61 31 20 46  l {SELECT sha1 F
1b10: 52 4f 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45  ROM packages WHE
1b20: 52 45 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 68  RE hostname = $h
1b30: 6f 73 74 6e 61 6d 65 3b 7d 5d 0a 09 09 66 6f 72  ostname;}]...for
1b40: 65 61 63 68 20 70 61 63 6b 61 67 65 20 24 66 6f  each package $fo
1b50: 75 6e 64 5f 70 61 63 6b 61 67 65 73 20 7b 0a 09  und_packages {..
1b60: 09 09 73 65 74 20 66 6f 75 6e 64 5f 70 61 63 6b  ..set found_pack
1b70: 61 67 65 73 5f 61 72 72 28 24 70 61 63 6b 61 67  ages_arr($packag
1b80: 65 29 20 31 0a 09 09 7d 0a 0a 09 09 66 6f 72 65  e) 1...}....fore
1b90: 61 63 68 20 70 61 63 6b 61 67 65 20 24 63 75 72  ach package $cur
1ba0: 72 5f 70 61 63 6b 61 67 65 73 20 7b 0a 09 09 09  r_packages {....
1bb0: 75 6e 73 65 74 20 2d 6e 6f 63 6f 6d 70 6c 61 69  unset -nocomplai
1bc0: 6e 20 66 6f 75 6e 64 5f 70 61 63 6b 61 67 65 73  n found_packages
1bd0: 5f 61 72 72 28 24 70 61 63 6b 61 67 65 29 0a 09  _arr($package)..
1be0: 09 7d 0a 0a 09 09 66 6f 72 65 61 63 68 20 70 61  .}....foreach pa
1bf0: 63 6b 61 67 65 20 5b 61 72 72 61 79 20 6e 61 6d  ckage [array nam
1c00: 65 73 20 66 6f 75 6e 64 5f 70 61 63 6b 61 67 65  es found_package
1c10: 73 5f 61 72 72 5d 20 7b 0a 09 09 09 64 62 20 65  s_arr] {....db e
1c20: 76 61 6c 20 7b 44 45 4c 45 54 45 20 46 52 4f 4d  val {DELETE FROM
1c30: 20 70 61 63 6b 61 67 65 73 20 57 48 45 52 45 20   packages WHERE 
1c40: 68 6f 73 74 6e 61 6d 65 20 3d 20 24 68 6f 73 74  hostname = $host
1c50: 6e 61 6d 65 20 41 4e 44 20 73 68 61 31 20 3d 20  name AND sha1 = 
1c60: 24 70 61 63 6b 61 67 65 3b 7d 0a 09 09 7d 0a 0a  $package;}...}..
1c70: 09 09 64 62 20 65 76 61 6c 20 7b 49 4e 53 45 52  ..db eval {INSER
1c80: 54 20 4f 52 20 52 45 50 4c 41 43 45 20 49 4e 54  T OR REPLACE INT
1c90: 4f 20 73 69 74 65 73 20 28 68 6f 73 74 6e 61 6d  O sites (hostnam
1ca0: 65 2c 20 6c 61 73 74 55 70 64 61 74 65 2c 20 74  e, lastUpdate, t
1cb0: 74 6c 29 20 56 41 4c 55 45 53 20 28 24 68 6f 73  tl) VALUES ($hos
1cc0: 74 6e 61 6d 65 2c 20 24 6e 6f 77 2c 20 24 3a 3a  tname, $now, $::
1cd0: 61 70 70 66 73 3a 3a 74 74 6c 29 3b 7d 0a 0a 09  appfs::ttl);}...
1ce0: 09 61 70 70 66 73 64 3a 3a 67 65 74 5f 70 61 74  .appfsd::get_pat
1cf0: 68 5f 69 6e 66 6f 5f 63 61 63 68 65 5f 66 6c 75  h_info_cache_flu
1d00: 73 68 0a 0a 09 09 72 65 74 75 72 6e 20 43 4f 4d  sh....return COM
1d10: 50 4c 45 54 45 0a 09 7d 0a 0a 09 70 72 6f 63 20  PLETE..}...proc 
1d20: 67 65 74 70 6b 67 6d 61 6e 69 66 65 73 74 20 7b  getpkgmanifest {
1d30: 68 6f 73 74 6e 61 6d 65 20 70 61 63 6b 61 67 65  hostname package
1d40: 5f 73 68 61 31 7d 20 7b 0a 09 09 73 65 74 20 68  _sha1} {...set h
1d50: 61 76 65 4d 61 6e 69 66 65 73 74 20 5b 64 62 20  aveManifest [db 
1d60: 6f 6e 65 63 6f 6c 75 6d 6e 20 7b 53 45 4c 45 43  onecolumn {SELEC
1d70: 54 20 68 61 76 65 4d 61 6e 69 66 65 73 74 20 46  T haveManifest F
1d80: 52 4f 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45  ROM packages WHE
1d90: 52 45 20 73 68 61 31 20 3d 20 24 70 61 63 6b 61  RE sha1 = $packa
1da0: 67 65 5f 73 68 61 31 20 4c 49 4d 49 54 20 31 3b  ge_sha1 LIMIT 1;
1db0: 7d 5d 0a 0a 09 09 69 66 20 7b 24 68 61 76 65 4d  }]....if {$haveM
1dc0: 61 6e 69 66 65 73 74 20 3d 3d 20 22 31 22 7d 20  anifest == "1"} 
1dd0: 7b 0a 09 09 09 72 65 74 75 72 6e 20 43 4f 4d 50  {....return COMP
1de0: 4c 45 54 45 0a 09 09 7d 0a 0a 09 09 69 66 20 7b  LETE...}....if {
1df0: 21 5b 5f 69 73 48 61 73 68 20 24 70 61 63 6b 61  ![_isHash $packa
1e00: 67 65 5f 73 68 61 31 5d 7d 20 7b 0a 09 09 09 72  ge_sha1]} {....r
1e10: 65 74 75 72 6e 20 46 41 49 4c 0a 09 09 7d 0a 0a  eturn FAIL...}..
1e20: 09 09 73 65 74 20 66 69 6c 65 20 5b 64 6f 77 6e  ..set file [down
1e30: 6c 6f 61 64 20 24 68 6f 73 74 6e 61 6d 65 20 24  load $hostname $
1e40: 70 61 63 6b 61 67 65 5f 73 68 61 31 5d 0a 09 09  package_sha1]...
1e50: 73 65 74 20 66 64 20 5b 6f 70 65 6e 20 24 66 69  set fd [open $fi
1e60: 6c 65 5d 0a 09 09 73 65 74 20 70 6b 67 64 61 74  le]...set pkgdat
1e70: 61 20 5b 72 65 61 64 20 24 66 64 5d 0a 09 09 63  a [read $fd]...c
1e80: 6c 6f 73 65 20 24 66 64 0a 0a 09 09 64 62 20 74  lose $fd....db t
1e90: 72 61 6e 73 61 63 74 69 6f 6e 20 7b 0a 09 09 09  ransaction {....
1ea0: 66 6f 72 65 61 63 68 20 6c 69 6e 65 20 5b 73 70  foreach line [sp
1eb0: 6c 69 74 20 24 70 6b 67 64 61 74 61 20 22 5c 6e  lit $pkgdata "\n
1ec0: 22 5d 20 7b 0a 09 09 09 09 73 65 74 20 6c 69 6e  "] {.....set lin
1ed0: 65 20 5b 73 74 72 69 6e 67 20 74 72 69 6d 20 24  e [string trim $
1ee0: 6c 69 6e 65 5d 0a 0a 09 09 09 09 69 66 20 7b 24  line]......if {$
1ef0: 6c 69 6e 65 20 3d 3d 20 22 22 7d 20 7b 0a 09 09  line == ""} {...
1f00: 09 09 09 63 6f 6e 74 69 6e 75 65 0a 09 09 09 09  ...continue.....
1f10: 7d 0a 0a 09 09 09 09 73 65 74 20 77 6f 72 6b 20  }......set work 
1f20: 5b 73 70 6c 69 74 20 24 6c 69 6e 65 20 22 2c 22  [split $line ","
1f30: 5d 0a 0a 09 09 09 09 75 6e 73 65 74 20 2d 6e 6f  ]......unset -no
1f40: 63 6f 6d 70 6c 61 69 6e 20 66 69 6c 65 49 6e 66  complain fileInf
1f50: 6f 0a 09 09 09 09 73 65 74 20 66 69 6c 65 49 6e  o.....set fileIn
1f60: 66 6f 28 74 79 70 65 29 20 5b 6c 69 6e 64 65 78  fo(type) [lindex
1f70: 20 24 77 6f 72 6b 20 30 5d 0a 09 09 09 09 73 65   $work 0].....se
1f80: 74 20 66 69 6c 65 49 6e 66 6f 28 74 69 6d 65 29  t fileInfo(time)
1f90: 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 31   [lindex $work 1
1fa0: 5d 0a 0a 09 09 09 09 73 65 74 20 77 6f 72 6b 20  ]......set work 
1fb0: 5b 6c 72 61 6e 67 65 20 24 77 6f 72 6b 20 32 20  [lrange $work 2 
1fc0: 65 6e 64 5d 0a 09 09 09 09 73 77 69 74 63 68 20  end].....switch 
1fd0: 2d 2d 20 24 66 69 6c 65 49 6e 66 6f 28 74 79 70  -- $fileInfo(typ
1fe0: 65 29 20 7b 0a 09 09 09 09 09 22 23 6d 61 6e 69  e) {......"#mani
1ff0: 66 65 73 74 6d 65 74 61 64 61 74 61 22 20 7b 0a  festmetadata" {.
2000: 09 09 09 09 09 09 75 6e 73 65 74 20 2d 6e 6f 63  ......unset -noc
2010: 6f 6d 70 6c 61 69 6e 20 66 69 6c 65 49 6e 66 6f  omplain fileInfo
2020: 0a 09 09 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a  .......continue.
2030: 09 09 09 09 09 7d 0a 09 09 09 09 09 22 66 69 6c  .....}......"fil
2040: 65 22 20 7b 0a 09 09 09 09 09 09 73 65 74 20 66  e" {.......set f
2050: 69 6c 65 49 6e 66 6f 28 73 69 7a 65 29 20 5b 6c  ileInfo(size) [l
2060: 69 6e 64 65 78 20 24 77 6f 72 6b 20 30 5d 0a 09  index $work 0]..
2070: 09 09 09 09 09 73 65 74 20 66 69 6c 65 49 6e 66  .....set fileInf
2080: 6f 28 70 65 72 6d 73 29 20 5b 6c 69 6e 64 65 78  o(perms) [lindex
2090: 20 24 77 6f 72 6b 20 31 5d 0a 09 09 09 09 09 09   $work 1].......
20a0: 73 65 74 20 66 69 6c 65 49 6e 66 6f 28 73 68 61  set fileInfo(sha
20b0: 31 29 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b  1) [lindex $work
20c0: 20 32 5d 0a 0a 09 09 09 09 09 09 73 65 74 20 77   2]........set w
20d0: 6f 72 6b 20 5b 6c 72 61 6e 67 65 20 24 77 6f 72  ork [lrange $wor
20e0: 6b 20 33 20 65 6e 64 5d 0a 09 09 09 09 09 7d 0a  k 3 end]......}.
20f0: 09 09 09 09 09 22 73 79 6d 6c 69 6e 6b 22 20 7b  ....."symlink" {
2100: 0a 09 09 09 09 09 09 73 65 74 20 66 69 6c 65 49  .......set fileI
2110: 6e 66 6f 28 73 6f 75 72 63 65 29 20 5b 6c 69 6e  nfo(source) [lin
2120: 64 65 78 20 24 77 6f 72 6b 20 30 5d 0a 09 09 09  dex $work 0]....
2130: 09 09 09 73 65 74 20 77 6f 72 6b 20 5b 6c 72 61  ...set work [lra
2140: 6e 67 65 20 24 77 6f 72 6b 20 31 20 65 6e 64 5d  nge $work 1 end]
2150: 0a 09 09 09 09 09 7d 0a 09 09 09 09 09 22 64 69  ......}......"di
2160: 72 65 63 74 6f 72 79 22 20 7b 0a 09 09 09 09 09  rectory" {......
2170: 09 23 20 4e 6f 20 65 78 74 72 61 20 64 61 74 61  .# No extra data
2180: 20 72 65 71 75 69 72 65 64 0a 09 09 09 09 09 7d   required......}
2190: 0a 09 09 09 09 09 64 65 66 61 75 6c 74 20 7b 0a  ......default {.
21a0: 09 09 09 09 09 09 23 20 48 61 6e 64 6c 65 20 75  ......# Handle u
21b0: 6e 6b 6e 6f 77 6e 20 74 79 70 65 73 0a 09 09 09  nknown types....
21c0: 09 09 09 69 66 20 7b 5b 73 74 72 69 6e 67 20 69  ...if {[string i
21d0: 6e 64 65 78 20 24 66 69 6c 65 49 6e 66 6f 28 74  ndex $fileInfo(t
21e0: 79 70 65 29 20 30 5d 20 3d 3d 20 22 23 22 7d 20  ype) 0] == "#"} 
21f0: 7b 0a 09 09 09 09 09 09 09 23 20 4d 65 74 61 64  {........# Metad
2200: 61 74 61 20 74 79 70 65 2c 20 69 67 6e 6f 72 65  ata type, ignore
2210: 0a 09 09 09 09 09 09 09 23 20 69 74 20 69 66 20  ........# it if 
2220: 77 65 20 64 6f 6e 27 74 0a 09 09 09 09 09 09 09  we don't........
2230: 23 20 75 6e 64 65 72 73 74 61 6e 64 20 74 68 69  # understand thi
2240: 73 20 74 79 70 65 0a 09 09 09 09 09 09 09 63 6f  s type........co
2250: 6e 74 69 6e 75 65 0a 09 09 09 09 09 09 7d 20 65  ntinue.......} e
2260: 6c 73 65 20 7b 0a 09 09 09 09 09 09 09 23 20 55  lse {........# U
2270: 6e 6b 6e 6f 77 6e 20 74 79 70 65 2c 0a 09 09 09  nknown type,....
2280: 09 09 09 09 23 20 67 65 6e 65 72 61 74 65 20 61  ....# generate a
2290: 6e 20 65 72 72 6f 72 0a 09 09 09 09 09 09 09 65  n error........e
22a0: 72 72 6f 72 20 22 4d 61 6e 69 66 65 73 74 20 63  rror "Manifest c
22b0: 61 6e 6e 6f 74 20 62 65 20 70 61 72 73 65 64 22  annot be parsed"
22c0: 0a 09 09 09 09 09 09 7d 0a 09 09 09 09 09 7d 0a  .......}......}.
22d0: 09 09 09 09 7d 0a 0a 09 09 09 09 73 65 74 20 66  ....}......set f
22e0: 69 6c 65 49 6e 66 6f 28 6e 61 6d 65 29 20 5b 6a  ileInfo(name) [j
22f0: 6f 69 6e 20 24 77 6f 72 6b 20 22 2c 22 5d 0a 09  oin $work ","]..
2300: 09 09 09 73 65 74 20 66 69 6c 65 49 6e 66 6f 28  ...set fileInfo(
2310: 6e 61 6d 65 29 20 5b 73 70 6c 69 74 20 5b 73 74  name) [split [st
2320: 72 69 6e 67 20 74 72 69 6d 20 24 66 69 6c 65 49  ring trim $fileI
2330: 6e 66 6f 28 6e 61 6d 65 29 20 22 2f 22 5d 20 22  nfo(name) "/"] "
2340: 2f 22 5d 0a 09 09 09 09 73 65 74 20 66 69 6c 65  /"].....set file
2350: 49 6e 66 6f 28 64 69 72 65 63 74 6f 72 79 29 20  Info(directory) 
2360: 5b 6a 6f 69 6e 20 5b 6c 72 61 6e 67 65 20 24 66  [join [lrange $f
2370: 69 6c 65 49 6e 66 6f 28 6e 61 6d 65 29 20 30 20  ileInfo(name) 0 
2380: 65 6e 64 2d 31 5d 20 22 2f 22 5d 0a 09 09 09 09  end-1] "/"].....
2390: 73 65 74 20 66 69 6c 65 49 6e 66 6f 28 6e 61 6d  set fileInfo(nam
23a0: 65 29 20 5b 6c 69 6e 64 65 78 20 24 66 69 6c 65  e) [lindex $file
23b0: 49 6e 66 6f 28 6e 61 6d 65 29 20 65 6e 64 5d 0a  Info(name) end].
23c0: 0a 09 09 09 09 64 62 20 65 76 61 6c 20 7b 49 4e  .....db eval {IN
23d0: 53 45 52 54 20 49 4e 54 4f 20 66 69 6c 65 73 20  SERT INTO files 
23e0: 28 70 61 63 6b 61 67 65 5f 73 68 61 31 2c 20 74  (package_sha1, t
23f0: 79 70 65 2c 20 74 69 6d 65 2c 20 73 6f 75 72 63  ype, time, sourc
2400: 65 2c 20 73 69 7a 65 2c 20 70 65 72 6d 73 2c 20  e, size, perms, 
2410: 66 69 6c 65 5f 73 68 61 31 2c 20 66 69 6c 65 5f  file_sha1, file_
2420: 6e 61 6d 65 2c 20 66 69 6c 65 5f 64 69 72 65 63  name, file_direc
2430: 74 6f 72 79 29 20 56 41 4c 55 45 53 20 28 24 70  tory) VALUES ($p
2440: 61 63 6b 61 67 65 5f 73 68 61 31 2c 20 24 66 69  ackage_sha1, $fi
2450: 6c 65 49 6e 66 6f 28 74 79 70 65 29 2c 20 24 66  leInfo(type), $f
2460: 69 6c 65 49 6e 66 6f 28 74 69 6d 65 29 2c 20 24  ileInfo(time), $
2470: 66 69 6c 65 49 6e 66 6f 28 73 6f 75 72 63 65 29  fileInfo(source)
2480: 2c 20 24 66 69 6c 65 49 6e 66 6f 28 73 69 7a 65  , $fileInfo(size
2490: 29 2c 20 24 66 69 6c 65 49 6e 66 6f 28 70 65 72  ), $fileInfo(per
24a0: 6d 73 29 2c 20 24 66 69 6c 65 49 6e 66 6f 28 73  ms), $fileInfo(s
24b0: 68 61 31 29 2c 20 24 66 69 6c 65 49 6e 66 6f 28  ha1), $fileInfo(
24c0: 6e 61 6d 65 29 2c 20 24 66 69 6c 65 49 6e 66 6f  name), $fileInfo
24d0: 28 64 69 72 65 63 74 6f 72 79 29 20 29 3b 7d 0a  (directory) );}.
24e0: 09 09 09 09 64 62 20 65 76 61 6c 20 7b 55 50 44  ....db eval {UPD
24f0: 41 54 45 20 70 61 63 6b 61 67 65 73 20 53 45 54  ATE packages SET
2500: 20 68 61 76 65 4d 61 6e 69 66 65 73 74 20 3d 20   haveManifest = 
2510: 31 20 57 48 45 52 45 20 73 68 61 31 20 3d 20 24  1 WHERE sha1 = $
2520: 70 61 63 6b 61 67 65 5f 73 68 61 31 3b 7d 0a 09  package_sha1;}..
2530: 09 09 7d 0a 09 09 7d 0a 0a 09 09 61 70 70 66 73  ..}...}....appfs
2540: 64 3a 3a 67 65 74 5f 70 61 74 68 5f 69 6e 66 6f  d::get_path_info
2550: 5f 63 61 63 68 65 5f 66 6c 75 73 68 0a 0a 09 09  _cache_flush....
2560: 72 65 74 75 72 6e 20 43 4f 4d 50 4c 45 54 45 0a  return COMPLETE.
2570: 09 7d 0a 0a 09 70 72 6f 63 20 5f 6c 6f 63 61 6c  .}...proc _local
2580: 70 61 74 68 20 7b 70 61 63 6b 61 67 65 20 68 6f  path {package ho
2590: 73 74 6e 61 6d 65 20 66 69 6c 65 7d 20 7b 0a 09  stname file} {..
25a0: 09 73 65 74 20 64 69 72 20 22 22 0a 09 09 63 61  .set dir ""...ca
25b0: 74 63 68 20 7b 0a 09 09 09 73 65 74 20 68 6f 6d  tch {....set hom
25c0: 65 64 69 72 20 5b 3a 3a 61 70 70 66 73 3a 3a 75  edir [::appfs::u
25d0: 73 65 72 3a 3a 67 65 74 5f 68 6f 6d 65 64 69 72  ser::get_homedir
25e0: 5d 0a 09 09 09 73 65 74 20 64 69 72 20 5b 66 69  ]....set dir [fi
25f0: 6c 65 20 6a 6f 69 6e 20 24 68 6f 6d 65 64 69 72  le join $homedir
2600: 20 2e 61 70 70 66 73 20 22 2e 2f 24 7b 70 61 63   .appfs "./${pac
2610: 6b 61 67 65 7d 40 24 7b 68 6f 73 74 6e 61 6d 65  kage}@${hostname
2620: 7d 22 20 22 2e 2f 24 7b 66 69 6c 65 7d 22 5d 0a  }" "./${file}"].
2630: 09 09 7d 0a 09 09 72 65 74 75 72 6e 20 24 64 69  ..}...return $di
2640: 72 0a 09 7d 0a 0a 09 70 72 6f 63 20 5f 77 68 69  r..}...proc _whi
2650: 74 65 6f 75 74 70 61 74 68 20 7b 70 61 63 6b 61  teoutpath {packa
2660: 67 65 20 68 6f 73 74 6e 61 6d 65 20 66 69 6c 65  ge hostname file
2670: 7d 20 7b 0a 09 09 73 65 74 20 64 69 72 20 22 22  } {...set dir ""
2680: 0a 09 09 63 61 74 63 68 20 7b 0a 09 09 09 73 65  ...catch {....se
2690: 74 20 68 6f 6d 65 64 69 72 20 5b 3a 3a 61 70 70  t homedir [::app
26a0: 66 73 3a 3a 75 73 65 72 3a 3a 67 65 74 5f 68 6f  fs::user::get_ho
26b0: 6d 65 64 69 72 5d 0a 09 09 09 73 65 74 20 64 69  medir]....set di
26c0: 72 20 5b 66 69 6c 65 20 6a 6f 69 6e 20 24 68 6f  r [file join $ho
26d0: 6d 65 64 69 72 20 2e 61 70 70 66 73 20 22 2e 2f  medir .appfs "./
26e0: 24 7b 70 61 63 6b 61 67 65 7d 40 24 7b 68 6f 73  ${package}@${hos
26f0: 74 6e 61 6d 65 7d 22 20 22 2e 41 50 50 46 53 2e  tname}" ".APPFS.
2700: 57 48 49 54 45 4f 55 54 22 20 22 2e 2f 24 7b 66  WHITEOUT" "./${f
2710: 69 6c 65 7d 2e 41 50 50 46 53 2e 57 48 49 54 45  ile}.APPFS.WHITE
2720: 4f 55 54 22 5d 0a 09 09 7d 0a 09 09 72 65 74 75  OUT"]...}...retu
2730: 72 6e 20 24 64 69 72 0a 09 7d 0a 0a 09 70 72 6f  rn $dir..}...pro
2740: 63 20 5f 70 61 72 73 65 70 61 74 68 20 7b 70 61  c _parsepath {pa
2750: 74 68 7d 20 7b 0a 09 09 73 65 74 20 70 61 74 68  th} {...set path
2760: 20 5b 73 74 72 69 6e 67 20 74 72 69 6d 20 24 70   [string trim $p
2770: 61 74 68 20 22 2f 22 5d 0a 09 09 73 65 74 20 70  ath "/"]...set p
2780: 61 74 68 20 5b 73 70 6c 69 74 20 24 70 61 74 68  ath [split $path
2790: 20 22 2f 22 5d 0a 09 09 73 65 74 20 70 61 74 68   "/"]...set path
27a0: 6c 65 6e 20 5b 6c 6c 65 6e 67 74 68 20 24 70 61  len [llength $pa
27b0: 74 68 5d 0a 0a 09 09 61 72 72 61 79 20 73 65 74  th]....array set
27c0: 20 72 65 74 76 61 6c 20 5b 6c 69 73 74 20 5f 63   retval [list _c
27d0: 68 69 6c 64 72 65 6e 20 73 69 74 65 73 20 5f 74  hildren sites _t
27e0: 79 70 65 20 74 6f 70 6c 65 76 65 6c 5d 0a 0a 09  ype toplevel]...
27f0: 09 69 66 20 7b 24 70 61 74 68 6c 65 6e 20 3e 20  .if {$pathlen > 
2800: 30 7d 20 7b 0a 09 09 09 73 65 74 20 72 65 74 76  0} {....set retv
2810: 61 6c 28 68 6f 73 74 6e 61 6d 65 29 20 5b 6c 69  al(hostname) [li
2820: 6e 64 65 78 20 24 70 61 74 68 20 30 5d 0a 09 09  ndex $path 0]...
2830: 09 73 65 74 20 72 65 74 76 61 6c 28 5f 63 68 69  .set retval(_chi
2840: 6c 64 72 65 6e 29 20 70 61 63 6b 61 67 65 73 0a  ldren) packages.
2850: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 74  ...set retval(_t
2860: 79 70 65 29 20 73 69 74 65 73 0a 0a 09 09 09 69  ype) sites.....i
2870: 66 20 7b 24 70 61 74 68 6c 65 6e 20 3e 20 31 7d  f {$pathlen > 1}
2880: 20 7b 0a 09 09 09 09 73 65 74 20 70 61 63 6b 61   {.....set packa
2890: 67 65 20 5b 6c 69 6e 64 65 78 20 24 70 61 74 68  ge [lindex $path
28a0: 20 31 5d 0a 09 09 09 09 69 66 20 7b 5b 73 74 72   1].....if {[str
28b0: 69 6e 67 20 6c 65 6e 67 74 68 20 24 70 61 63 6b  ing length $pack
28c0: 61 67 65 5d 20 3d 3d 20 22 34 30 22 20 26 26 20  age] == "40" && 
28d0: 5b 72 65 67 65 78 70 20 7b 5e 5b 61 2d 66 41 2d  [regexp {^[a-fA-
28e0: 46 30 2d 39 5d 2a 24 7d 20 24 70 61 63 6b 61 67  F0-9]*$} $packag
28f0: 65 5d 7d 20 7b 0a 09 09 09 09 09 73 65 74 20 72  e]} {......set r
2900: 65 74 76 61 6c 28 70 61 63 6b 61 67 65 5f 73 68  etval(package_sh
2910: 61 31 29 20 24 70 61 63 6b 61 67 65 0a 09 09 09  a1) $package....
2920: 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 63 68  ..set retval(_ch
2930: 69 6c 64 72 65 6e 29 20 66 69 6c 65 73 0a 09 09  ildren) files...
2940: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 74  ...set retval(_t
2950: 79 70 65 29 20 66 69 6c 65 73 0a 0a 09 09 09 09  ype) files......
2960: 09 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65 76 61  .::appfs::db eva
2970: 6c 20 7b 53 45 4c 45 43 54 20 70 61 63 6b 61 67  l {SELECT packag
2980: 65 2c 20 6f 73 2c 20 63 70 75 41 72 63 68 2c 20  e, os, cpuArch, 
2990: 76 65 72 73 69 6f 6e 20 46 52 4f 4d 20 70 61 63  version FROM pac
29a0: 6b 61 67 65 73 20 57 48 45 52 45 20 73 68 61 31  kages WHERE sha1
29b0: 20 3d 20 24 72 65 74 76 61 6c 28 70 61 63 6b 61   = $retval(packa
29c0: 67 65 5f 73 68 61 31 29 3b 7d 20 70 6b 67 69 6e  ge_sha1);} pkgin
29d0: 66 6f 20 7b 7d 0a 09 09 09 09 09 73 65 74 20 72  fo {}......set r
29e0: 65 74 76 61 6c 28 70 61 63 6b 61 67 65 29 20 24  etval(package) $
29f0: 70 6b 67 69 6e 66 6f 28 70 61 63 6b 61 67 65 29  pkginfo(package)
2a00: 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
2a10: 28 6f 73 29 20 24 70 6b 67 69 6e 66 6f 28 6f 73  (os) $pkginfo(os
2a20: 29 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61  )......set retva
2a30: 6c 28 63 70 75 29 20 24 70 6b 67 69 6e 66 6f 28  l(cpu) $pkginfo(
2a40: 63 70 75 41 72 63 68 29 0a 09 09 09 09 09 73 65  cpuArch)......se
2a50: 74 20 72 65 74 76 61 6c 28 76 65 72 73 69 6f 6e  t retval(version
2a60: 29 20 24 70 6b 67 69 6e 66 6f 28 76 65 72 73 69  ) $pkginfo(versi
2a70: 6f 6e 29 0a 0a 09 09 09 09 09 69 66 20 7b 24 70  on).......if {$p
2a80: 61 74 68 6c 65 6e 20 3e 20 32 7d 20 7b 0a 09 09  athlen > 2} {...
2a90: 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 66  ....set retval(f
2aa0: 69 6c 65 29 20 5b 6a 6f 69 6e 20 5b 6c 72 61 6e  ile) [join [lran
2ab0: 67 65 20 24 70 61 74 68 20 32 20 65 6e 64 5d 20  ge $path 2 end] 
2ac0: 22 2f 22 5d 0a 09 09 09 09 09 7d 20 65 6c 73 65  "/"]......} else
2ad0: 20 7b 0a 09 09 09 09 09 09 73 65 74 20 72 65 74   {.......set ret
2ae0: 76 61 6c 28 66 69 6c 65 29 20 22 22 0a 09 09 09  val(file) ""....
2af0: 09 09 7d 0a 09 09 09 09 7d 20 65 6c 73 65 20 7b  ..}.....} else {
2b00: 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
2b10: 28 70 61 63 6b 61 67 65 29 20 24 70 61 63 6b 61  (package) $packa
2b20: 67 65 0a 09 09 09 09 09 73 65 74 20 72 65 74 76  ge......set retv
2b30: 61 6c 28 5f 63 68 69 6c 64 72 65 6e 29 20 6f 73  al(_children) os
2b40: 2d 63 70 75 0a 09 09 09 09 09 73 65 74 20 72 65  -cpu......set re
2b50: 74 76 61 6c 28 5f 74 79 70 65 29 20 70 61 63 6b  tval(_type) pack
2b60: 61 67 65 73 0a 0a 09 09 09 09 09 69 66 20 7b 24  ages.......if {$
2b70: 70 61 74 68 6c 65 6e 20 3e 20 32 7d 20 7b 0a 09  pathlen > 2} {..
2b80: 09 09 09 09 09 73 65 74 20 6f 73 5f 63 70 75 20  .....set os_cpu 
2b90: 5b 6c 69 6e 64 65 78 20 24 70 61 74 68 20 32 5d  [lindex $path 2]
2ba0: 0a 09 09 09 09 09 09 73 65 74 20 6f 73 5f 63 70  .......set os_cp
2bb0: 75 20 5b 73 70 6c 69 74 20 24 6f 73 5f 63 70 75  u [split $os_cpu
2bc0: 20 22 2d 22 5d 0a 0a 09 09 09 09 09 09 73 65 74   "-"]........set
2bd0: 20 72 65 74 76 61 6c 28 6f 73 29 20 5b 6c 69 6e   retval(os) [lin
2be0: 64 65 78 20 24 6f 73 5f 63 70 75 20 30 5d 0a 09  dex $os_cpu 0]..
2bf0: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
2c00: 63 70 75 29 20 5b 6c 69 6e 64 65 78 20 24 6f 73  cpu) [lindex $os
2c10: 5f 63 70 75 20 31 5d 0a 09 09 09 09 09 09 73 65  _cpu 1].......se
2c20: 74 20 72 65 74 76 61 6c 28 5f 63 68 69 6c 64 72  t retval(_childr
2c30: 65 6e 29 20 76 65 72 73 69 6f 6e 73 0a 09 09 09  en) versions....
2c40: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 74  ...set retval(_t
2c50: 79 70 65 29 20 6f 73 2d 63 70 75 0a 0a 09 09 09  ype) os-cpu.....
2c60: 09 09 09 69 66 20 7b 24 70 61 74 68 6c 65 6e 20  ...if {$pathlen 
2c70: 3e 20 33 7d 20 7b 0a 09 09 09 09 09 09 09 73 65  > 3} {........se
2c80: 74 20 72 65 74 76 61 6c 28 76 65 72 73 69 6f 6e  t retval(version
2c90: 29 20 5b 6c 69 6e 64 65 78 20 24 70 61 74 68 20  ) [lindex $path 
2ca0: 33 5d 0a 09 09 09 09 09 09 09 73 65 74 20 72 65  3]........set re
2cb0: 74 76 61 6c 28 5f 63 68 69 6c 64 72 65 6e 29 20  tval(_children) 
2cc0: 66 69 6c 65 73 0a 09 09 09 09 09 09 09 73 65 74  files........set
2cd0: 20 72 65 74 76 61 6c 28 5f 74 79 70 65 29 20 76   retval(_type) v
2ce0: 65 72 73 69 6f 6e 73 0a 0a 09 09 09 09 09 09 09  ersions.........
2cf0: 73 65 74 20 72 65 74 76 61 6c 28 70 61 63 6b 61  set retval(packa
2d00: 67 65 5f 73 68 61 31 29 20 5b 3a 3a 61 70 70 66  ge_sha1) [::appf
2d10: 73 3a 3a 64 62 20 6f 6e 65 63 6f 6c 75 6d 6e 20  s::db onecolumn 
2d20: 7b 53 45 4c 45 43 54 20 73 68 61 31 20 46 52 4f  {SELECT sha1 FRO
2d30: 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45 52 45  M packages WHERE
2d40: 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 72 65 74   hostname = $ret
2d50: 76 61 6c 28 68 6f 73 74 6e 61 6d 65 29 20 41 4e  val(hostname) AN
2d60: 44 20 70 61 63 6b 61 67 65 20 3d 20 24 72 65 74  D package = $ret
2d70: 76 61 6c 28 70 61 63 6b 61 67 65 29 20 41 4e 44  val(package) AND
2d80: 20 6f 73 20 3d 20 24 72 65 74 76 61 6c 28 6f 73   os = $retval(os
2d90: 29 20 41 4e 44 20 63 70 75 41 72 63 68 20 3d 20  ) AND cpuArch = 
2da0: 24 72 65 74 76 61 6c 28 63 70 75 29 20 41 4e 44  $retval(cpu) AND
2db0: 20 76 65 72 73 69 6f 6e 20 3d 20 24 72 65 74 76   version = $retv
2dc0: 61 6c 28 76 65 72 73 69 6f 6e 29 3b 7d 5d 0a 09  al(version);}]..
2dd0: 09 09 09 09 09 09 69 66 20 7b 24 72 65 74 76 61  ......if {$retva
2de0: 6c 28 70 61 63 6b 61 67 65 5f 73 68 61 31 29 20  l(package_sha1) 
2df0: 3d 3d 20 22 22 7d 20 7b 0a 09 09 09 09 09 09 09  == ""} {........
2e00: 09 73 65 74 20 72 65 74 76 61 6c 28 5f 63 68 69  .set retval(_chi
2e10: 6c 64 72 65 6e 29 20 64 65 61 64 0a 09 09 09 09  ldren) dead.....
2e20: 09 09 09 09 72 65 74 75 72 6e 20 5b 61 72 72 61  ....return [arra
2e30: 79 20 67 65 74 20 72 65 74 76 61 6c 5d 0a 09 09  y get retval]...
2e40: 09 09 09 09 09 7d 0a 0a 09 09 09 09 09 09 09 69  .....}.........i
2e50: 66 20 7b 24 70 61 74 68 6c 65 6e 20 3e 20 34 7d  f {$pathlen > 4}
2e60: 20 7b 0a 09 09 09 09 09 09 09 09 73 65 74 20 72   {.........set r
2e70: 65 74 76 61 6c 28 5f 74 79 70 65 29 20 66 69 6c  etval(_type) fil
2e80: 65 73 0a 09 09 09 09 09 09 09 09 73 65 74 20 72  es.........set r
2e90: 65 74 76 61 6c 28 66 69 6c 65 29 20 5b 6a 6f 69  etval(file) [joi
2ea0: 6e 20 5b 6c 72 61 6e 67 65 20 24 70 61 74 68 20  n [lrange $path 
2eb0: 34 20 65 6e 64 5d 20 22 2f 22 5d 0a 09 09 09 09  4 end] "/"].....
2ec0: 09 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09  ...} else {.....
2ed0: 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f  ....set retval(_
2ee0: 74 79 70 65 29 20 66 69 6c 65 73 0a 09 09 09 09  type) files.....
2ef0: 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 66  ....set retval(f
2f00: 69 6c 65 29 20 22 22 0a 09 09 09 09 09 09 09 7d  ile) ""........}
2f10: 0a 09 09 09 09 09 09 7d 0a 09 09 09 09 09 7d 0a  .......}......}.
2f20: 09 09 09 09 7d 0a 09 09 09 7d 0a 09 09 7d 0a 0a  ....}....}...}..
2f30: 09 09 72 65 74 75 72 6e 20 5b 61 72 72 61 79 20  ..return [array 
2f40: 67 65 74 20 72 65 74 76 61 6c 5d 0a 09 7d 0a 0a  get retval]..}..
2f50: 09 70 72 6f 63 20 67 65 74 63 68 69 6c 64 72 65  .proc getchildre
2f60: 6e 20 7b 64 69 72 7d 20 7b 0a 09 09 61 72 72 61  n {dir} {...arra
2f70: 79 20 73 65 74 20 70 61 74 68 69 6e 66 6f 20 5b  y set pathinfo [
2f80: 5f 70 61 72 73 65 70 61 74 68 20 24 64 69 72 5d  _parsepath $dir]
2f90: 0a 0a 09 09 73 77 69 74 63 68 20 2d 2d 20 24 70  ....switch -- $p
2fa0: 61 74 68 69 6e 66 6f 28 5f 63 68 69 6c 64 72 65  athinfo(_childre
2fb0: 6e 29 20 7b 0a 09 09 09 22 73 69 74 65 73 22 20  n) {...."sites" 
2fc0: 7b 0a 09 09 09 09 72 65 74 75 72 6e 20 5b 3a 3a  {.....return [::
2fd0: 61 70 70 66 73 3a 3a 64 62 20 65 76 61 6c 20 7b  appfs::db eval {
2fe0: 53 45 4c 45 43 54 20 44 49 53 54 49 4e 43 54 20  SELECT DISTINCT 
2ff0: 68 6f 73 74 6e 61 6d 65 20 46 52 4f 4d 20 70 61  hostname FROM pa
3000: 63 6b 61 67 65 73 3b 7d 5d 0a 09 09 09 7d 0a 09  ckages;}]....}..
3010: 09 09 22 70 61 63 6b 61 67 65 73 22 20 7b 0a 09  .."packages" {..
3020: 09 09 09 63 61 74 63 68 20 7b 0a 09 09 09 09 09  ...catch {......
3030: 3a 3a 61 70 70 66 73 3a 3a 67 65 74 69 6e 64 65  ::appfs::getinde
3040: 78 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74  x $pathinfo(host
3050: 6e 61 6d 65 29 0a 09 09 09 09 7d 0a 0a 09 09 09  name).....}.....
3060: 09 72 65 74 75 72 6e 20 5b 3a 3a 61 70 70 66 73  .return [::appfs
3070: 3a 3a 64 62 20 65 76 61 6c 20 7b 53 45 4c 45 43  ::db eval {SELEC
3080: 54 20 44 49 53 54 49 4e 43 54 20 70 61 63 6b 61  T DISTINCT packa
3090: 67 65 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73  ge FROM packages
30a0: 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65 20   WHERE hostname 
30b0: 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74  = $pathinfo(host
30c0: 6e 61 6d 65 29 3b 7d 5d 0a 09 09 09 7d 0a 09 09  name);}]....}...
30d0: 09 22 6f 73 2d 63 70 75 22 20 7b 0a 09 09 09 09  ."os-cpu" {.....
30e0: 73 65 74 20 72 65 74 76 61 6c 20 5b 3a 3a 61 70  set retval [::ap
30f0: 70 66 73 3a 3a 64 62 20 65 76 61 6c 20 7b 53 45  pfs::db eval {SE
3100: 4c 45 43 54 20 44 49 53 54 49 4e 43 54 20 6f 73  LECT DISTINCT os
3110: 20 7c 7c 20 22 2d 22 20 7c 7c 20 63 70 75 41 72   || "-" || cpuAr
3120: 63 68 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73  ch FROM packages
3130: 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65 20   WHERE hostname 
3140: 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74  = $pathinfo(host
3150: 6e 61 6d 65 29 20 41 4e 44 20 70 61 63 6b 61 67  name) AND packag
3160: 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 70 61  e = $pathinfo(pa
3170: 63 6b 61 67 65 29 3b 7d 5d 0a 0a 09 09 09 09 6c  ckage);}]......l
3180: 61 70 70 65 6e 64 20 72 65 74 76 61 6c 20 22 70  append retval "p
3190: 6c 61 74 66 6f 72 6d 22 0a 0a 09 09 09 09 72 65  latform"......re
31a0: 74 75 72 6e 20 24 72 65 74 76 61 6c 0a 09 09 09  turn $retval....
31b0: 7d 0a 09 09 09 22 76 65 72 73 69 6f 6e 73 22 20  }...."versions" 
31c0: 7b 0a 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  {.....set retval
31d0: 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65 76   [::appfs::db ev
31e0: 61 6c 20 7b 0a 09 09 09 09 09 53 45 4c 45 43 54  al {......SELECT
31f0: 20 44 49 53 54 49 4e 43 54 20 76 65 72 73 69 6f   DISTINCT versio
3200: 6e 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73 20  n FROM packages 
3210: 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65 20 3d  WHERE hostname =
3220: 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e   $pathinfo(hostn
3230: 61 6d 65 29 20 41 4e 44 20 70 61 63 6b 61 67 65  ame) AND package
3240: 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63   = $pathinfo(pac
3250: 6b 61 67 65 29 20 41 4e 44 20 6f 73 20 3d 20 24  kage) AND os = $
3260: 70 61 74 68 69 6e 66 6f 28 6f 73 29 20 41 4e 44  pathinfo(os) AND
3270: 20 63 70 75 41 72 63 68 20 3d 20 24 70 61 74 68   cpuArch = $path
3280: 69 6e 66 6f 28 63 70 75 29 3b 0a 09 09 09 09 7d  info(cpu);.....}
3290: 5d 0a 0a 09 09 09 09 3a 3a 61 70 70 66 73 3a 3a  ]......::appfs::
32a0: 64 62 20 65 76 61 6c 20 7b 53 45 4c 45 43 54 20  db eval {SELECT 
32b0: 76 65 72 73 69 6f 6e 20 46 52 4f 4d 20 70 61 63  version FROM pac
32c0: 6b 61 67 65 73 20 57 48 45 52 45 20 69 73 4c 61  kages WHERE isLa
32d0: 74 65 73 74 20 3d 20 31 20 41 4e 44 20 68 6f 73  test = 1 AND hos
32e0: 74 6e 61 6d 65 20 3d 20 24 70 61 74 68 69 6e 66  tname = $pathinf
32f0: 6f 28 68 6f 73 74 6e 61 6d 65 29 20 41 4e 44 20  o(hostname) AND 
3300: 70 61 63 6b 61 67 65 20 3d 20 24 70 61 74 68 69  package = $pathi
3310: 6e 66 6f 28 70 61 63 6b 61 67 65 29 20 41 4e 44  nfo(package) AND
3320: 20 6f 73 20 3d 20 24 70 61 74 68 69 6e 66 6f 28   os = $pathinfo(
3330: 6f 73 29 20 41 4e 44 20 63 70 75 41 72 63 68 20  os) AND cpuArch 
3340: 3d 20 24 70 61 74 68 69 6e 66 6f 28 63 70 75 29  = $pathinfo(cpu)
3350: 20 4c 49 4d 49 54 20 31 3b 7d 20 6c 61 74 65 73   LIMIT 1;} lates
3360: 74 5f 69 6e 66 6f 20 7b 7d 0a 0a 09 09 09 09 69  t_info {}......i
3370: 66 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20  f {[info exists 
3380: 6c 61 74 65 73 74 5f 69 6e 66 6f 28 76 65 72 73  latest_info(vers
3390: 69 6f 6e 29 5d 7d 20 7b 0a 09 09 09 09 09 6c 61  ion)]} {......la
33a0: 70 70 65 6e 64 20 72 65 74 76 61 6c 20 22 6c 61  ppend retval "la
33b0: 74 65 73 74 22 0a 09 09 09 09 7d 0a 0a 09 09 09  test".....}.....
33c0: 09 72 65 74 75 72 6e 20 24 72 65 74 76 61 6c 0a  .return $retval.
33d0: 09 09 09 7d 0a 09 09 09 22 66 69 6c 65 73 22 20  ...}...."files" 
33e0: 7b 0a 09 09 09 09 63 61 74 63 68 20 7b 0a 09 09  {.....catch {...
33f0: 09 09 09 3a 3a 61 70 70 66 73 3a 3a 67 65 74 70  ...::appfs::getp
3400: 6b 67 6d 61 6e 69 66 65 73 74 20 24 70 61 74 68  kgmanifest $path
3410: 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24  info(hostname) $
3420: 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65  pathinfo(package
3430: 5f 73 68 61 31 29 0a 09 09 09 09 7d 0a 0a 09 09  _sha1).....}....
3440: 09 09 73 65 74 20 72 65 74 76 61 6c 20 5b 3a 3a  ..set retval [::
3450: 61 70 70 66 73 3a 3a 64 62 20 65 76 61 6c 20 7b  appfs::db eval {
3460: 53 45 4c 45 43 54 20 44 49 53 54 49 4e 43 54 20  SELECT DISTINCT 
3470: 66 69 6c 65 5f 6e 61 6d 65 20 46 52 4f 4d 20 66  file_name FROM f
3480: 69 6c 65 73 20 57 48 45 52 45 20 70 61 63 6b 61  iles WHERE packa
3490: 67 65 5f 73 68 61 31 20 3d 20 24 70 61 74 68 69  ge_sha1 = $pathi
34a0: 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73 68 61 31  nfo(package_sha1
34b0: 29 20 41 4e 44 20 66 69 6c 65 5f 64 69 72 65 63  ) AND file_direc
34c0: 74 6f 72 79 20 3d 20 24 70 61 74 68 69 6e 66 6f  tory = $pathinfo
34d0: 28 66 69 6c 65 29 3b 7d 5d 0a 0a 09 09 09 09 69  (file);}]......i
34e0: 66 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20  f {[info exists 
34f0: 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65  pathinfo(package
3500: 29 5d 20 26 26 20 5b 69 6e 66 6f 20 65 78 69 73  )] && [info exis
3510: 74 73 20 70 61 74 68 69 6e 66 6f 28 68 6f 73 74  ts pathinfo(host
3520: 6e 61 6d 65 29 5d 20 26 26 20 5b 69 6e 66 6f 20  name)] && [info 
3530: 65 78 69 73 74 73 20 70 61 74 68 69 6e 66 6f 28  exists pathinfo(
3540: 66 69 6c 65 29 5d 7d 20 7b 0a 09 09 09 09 09 5f  file)]} {......_
3550: 61 73 5f 75 73 65 72 20 7b 0a 09 09 09 09 09 09  as_user {.......
3560: 73 65 74 20 64 69 72 20 5b 5f 6c 6f 63 61 6c 70  set dir [_localp
3570: 61 74 68 20 24 70 61 74 68 69 6e 66 6f 28 70 61  ath $pathinfo(pa
3580: 63 6b 61 67 65 29 20 24 70 61 74 68 69 6e 66 6f  ckage) $pathinfo
3590: 28 68 6f 73 74 6e 61 6d 65 29 20 24 70 61 74 68  (hostname) $path
35a0: 69 6e 66 6f 28 66 69 6c 65 29 5d 0a 09 09 09 09  info(file)].....
35b0: 09 09 73 65 74 20 77 68 69 74 65 6f 75 74 64 69  ..set whiteoutdi
35c0: 72 20 5b 73 74 72 69 6e 67 20 72 61 6e 67 65 20  r [string range 
35d0: 5b 5f 77 68 69 74 65 6f 75 74 70 61 74 68 20 24  [_whiteoutpath $
35e0: 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65  pathinfo(package
35f0: 29 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74  ) $pathinfo(host
3600: 6e 61 6d 65 29 20 24 70 61 74 68 69 6e 66 6f 28  name) $pathinfo(
3610: 66 69 6c 65 29 5d 20 30 20 65 6e 64 2d 31 35 5d  file)] 0 end-15]
3620: 0a 0a 09 09 09 09 09 09 69 66 20 7b 24 77 68 69  ........if {$whi
3630: 74 65 6f 75 74 64 69 72 20 21 3d 20 22 22 7d 20  teoutdir != ""} 
3640: 7b 0a 09 09 09 09 09 09 09 66 6f 72 65 61 63 68  {........foreach
3650: 20 66 69 6c 65 20 5b 67 6c 6f 62 20 2d 6e 6f 63   file [glob -noc
3660: 6f 6d 70 6c 61 69 6e 20 2d 74 61 69 6c 73 20 2d  omplain -tails -
3670: 64 69 72 65 63 74 6f 72 79 20 24 77 68 69 74 65  directory $white
3680: 6f 75 74 64 69 72 20 7b 7b 2e 2c 7d 2a 2e 41 50  outdir {{.,}*.AP
3690: 50 46 53 2e 57 48 49 54 45 4f 55 54 7d 5d 20 7b  PFS.WHITEOUT}] {
36a0: 0a 09 09 09 09 09 09 09 09 73 65 74 20 72 65 6d  .........set rem
36b0: 6f 76 65 20 5b 73 74 72 69 6e 67 20 72 61 6e 67  ove [string rang
36c0: 65 20 24 66 69 6c 65 20 30 20 65 6e 64 2d 31 35  e $file 0 end-15
36d0: 5d 0a 09 09 09 09 09 09 09 09 73 65 74 20 69 64  ].........set id
36e0: 78 20 5b 6c 73 65 61 72 63 68 20 2d 65 78 61 63  x [lsearch -exac
36f0: 74 20 24 72 65 74 76 61 6c 20 24 72 65 6d 6f 76  t $retval $remov
3700: 65 5d 0a 09 09 09 09 09 09 09 09 69 66 20 7b 24  e].........if {$
3710: 69 64 78 20 21 3d 20 2d 31 7d 20 7b 0a 09 09 09  idx != -1} {....
3720: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
3730: 20 5b 6c 72 65 70 6c 61 63 65 20 24 72 65 74 76   [lreplace $retv
3740: 61 6c 20 24 69 64 78 20 24 69 64 78 5d 0a 09 09  al $idx $idx]...
3750: 09 09 09 09 09 09 7d 0a 09 09 09 09 09 09 09 7d  ......}........}
3760: 0a 09 09 09 09 09 09 7d 0a 0a 09 09 09 09 09 09  .......}........
3770: 69 66 20 7b 24 64 69 72 20 21 3d 20 22 22 7d 20  if {$dir != ""} 
3780: 7b 0a 09 09 09 09 09 09 09 66 6f 72 65 61 63 68  {........foreach
3790: 20 66 69 6c 65 20 5b 67 6c 6f 62 20 2d 6e 6f 63   file [glob -noc
37a0: 6f 6d 70 6c 61 69 6e 20 2d 74 61 69 6c 73 20 2d  omplain -tails -
37b0: 64 69 72 65 63 74 6f 72 79 20 24 64 69 72 20 7b  directory $dir {
37c0: 7b 2e 2c 7d 2a 7d 5d 20 7b 0a 09 09 09 09 09 09  {.,}*}] {.......
37d0: 09 09 69 66 20 7b 24 66 69 6c 65 20 3d 3d 20 22  ..if {$file == "
37e0: 2e 22 20 7c 7c 20 24 66 69 6c 65 20 3d 3d 20 22  ." || $file == "
37f0: 2e 2e 22 7d 20 7b 0a 09 09 09 09 09 09 09 09 09  .."} {..........
3800: 63 6f 6e 74 69 6e 75 65 0a 09 09 09 09 09 09 09  continue........
3810: 09 7d 0a 0a 09 09 09 09 09 09 09 09 69 66 20 7b  .}..........if {
3820: 24 66 69 6c 65 20 3d 3d 20 22 2e 41 50 50 46 53  $file == ".APPFS
3830: 2e 57 48 49 54 45 4f 55 54 22 7d 20 7b 0a 09 09  .WHITEOUT"} {...
3840: 09 09 09 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a  .......continue.
3850: 09 09 09 09 09 09 09 09 7d 0a 0a 09 09 09 09 09  ........}.......
3860: 09 09 09 69 66 20 7b 5b 6c 73 65 61 72 63 68 20  ...if {[lsearch 
3870: 2d 65 78 61 63 74 20 24 72 65 74 76 61 6c 20 24  -exact $retval $
3880: 66 69 6c 65 5d 20 21 3d 20 2d 31 7d 20 7b 0a 09  file] != -1} {..
3890: 09 09 09 09 09 09 09 09 63 6f 6e 74 69 6e 75 65  ........continue
38a0: 0a 09 09 09 09 09 09 09 09 7d 0a 0a 09 09 09 09  .........}......
38b0: 09 09 09 09 6c 61 70 70 65 6e 64 20 72 65 74 76  ....lappend retv
38c0: 61 6c 20 24 66 69 6c 65 0a 09 09 09 09 09 09 09  al $file........
38d0: 7d 0a 09 09 09 09 09 09 7d 0a 09 09 09 09 09 7d  }.......}......}
38e0: 0a 09 09 09 09 7d 0a 0a 09 09 09 09 72 65 74 75  .....}......retu
38f0: 72 6e 20 24 72 65 74 76 61 6c 0a 09 09 09 7d 0a  rn $retval....}.
3900: 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e 20 2d 63  ..}....return -c
3910: 6f 64 65 20 65 72 72 6f 72 20 22 49 6e 76 61 6c  ode error "Inval
3920: 69 64 20 6f 72 20 75 6e 61 63 63 65 70 74 61 62  id or unacceptab
3930: 6c 65 20 70 61 74 68 3a 20 24 64 69 72 22 0a 09  le path: $dir"..
3940: 7d 0a 0a 09 70 72 6f 63 20 67 65 74 61 74 74 72  }...proc getattr
3950: 20 7b 70 61 74 68 7d 20 7b 0a 09 09 61 72 72 61   {path} {...arra
3960: 79 20 73 65 74 20 70 61 74 68 69 6e 66 6f 20 5b  y set pathinfo [
3970: 5f 70 61 72 73 65 70 61 74 68 20 24 70 61 74 68  _parsepath $path
3980: 5d 0a 09 09 61 72 72 61 79 20 73 65 74 20 72 65  ]...array set re
3990: 74 76 61 6c 20 5b 6c 69 73 74 5d 0a 0a 09 09 63  tval [list]....c
39a0: 61 74 63 68 20 7b 0a 09 09 09 3a 3a 61 70 70 66  atch {....::appf
39b0: 73 3a 3a 67 65 74 69 6e 64 65 78 20 24 70 61 74  s::getindex $pat
39c0: 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 0a  hinfo(hostname).
39d0: 09 09 09 3a 3a 61 70 70 66 73 3a 3a 67 65 74 70  ...::appfs::getp
39e0: 6b 67 6d 61 6e 69 66 65 73 74 20 24 70 61 74 68  kgmanifest $path
39f0: 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24  info(hostname) $
3a00: 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65  pathinfo(package
3a10: 5f 73 68 61 31 29 0a 09 09 7d 0a 0a 09 09 73 77  _sha1)...}....sw
3a20: 69 74 63 68 20 2d 2d 20 24 70 61 74 68 69 6e 66  itch -- $pathinf
3a30: 6f 28 5f 74 79 70 65 29 20 7b 0a 09 09 09 22 74  o(_type) {...."t
3a40: 6f 70 6c 65 76 65 6c 22 20 7b 0a 09 09 09 09 73  oplevel" {.....s
3a50: 65 74 20 72 65 74 76 61 6c 28 74 79 70 65 29 20  et retval(type) 
3a60: 64 69 72 65 63 74 6f 72 79 0a 09 09 09 09 73 65  directory.....se
3a70: 74 20 72 65 74 76 61 6c 28 63 68 69 6c 64 63 6f  t retval(childco
3a80: 75 6e 74 29 20 5b 6c 6c 65 6e 67 74 68 20 5b 67  unt) [llength [g
3a90: 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61 74 68  etchildren $path
3aa0: 5d 5d 0a 09 09 09 7d 0a 09 09 09 22 73 69 74 65  ]]....}...."site
3ab0: 73 22 20 7b 0a 09 09 09 09 73 65 74 20 63 68 65  s" {.....set che
3ac0: 63 6b 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20  ck [::appfs::db 
3ad0: 6f 6e 65 63 6f 6c 75 6d 6e 20 7b 53 45 4c 45 43  onecolumn {SELEC
3ae0: 54 20 31 20 46 52 4f 4d 20 70 61 63 6b 61 67 65  T 1 FROM package
3af0: 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65  s WHERE hostname
3b00: 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73   = $pathinfo(hos
3b10: 74 6e 61 6d 65 29 3b 7d 5d 0a 09 09 09 09 69 66  tname);}].....if
3b20: 20 7b 24 63 68 65 63 6b 20 3d 3d 20 22 31 22 7d   {$check == "1"}
3b30: 20 7b 0a 09 09 09 09 09 73 65 74 20 72 65 74 76   {......set retv
3b40: 61 6c 28 74 79 70 65 29 20 64 69 72 65 63 74 6f  al(type) directo
3b50: 72 79 0a 09 09 09 09 09 73 65 74 20 72 65 74 76  ry......set retv
3b60: 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29 20 5b  al(childcount) [
3b70: 6c 6c 65 6e 67 74 68 20 5b 67 65 74 63 68 69 6c  llength [getchil
3b80: 64 72 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09 09  dren $path]]....
3b90: 09 7d 0a 09 09 09 7d 0a 09 09 09 22 70 61 63 6b  .}....}...."pack
3ba0: 61 67 65 73 22 20 7b 0a 09 09 09 09 73 65 74 20  ages" {.....set 
3bb0: 63 68 65 63 6b 20 5b 3a 3a 61 70 70 66 73 3a 3a  check [::appfs::
3bc0: 64 62 20 6f 6e 65 63 6f 6c 75 6d 6e 20 7b 53 45  db onecolumn {SE
3bd0: 4c 45 43 54 20 31 20 46 52 4f 4d 20 70 61 63 6b  LECT 1 FROM pack
3be0: 61 67 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e  ages WHERE hostn
3bf0: 61 6d 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28  ame = $pathinfo(
3c00: 68 6f 73 74 6e 61 6d 65 29 20 41 4e 44 20 70 61  hostname) AND pa
3c10: 63 6b 61 67 65 20 3d 20 24 70 61 74 68 69 6e 66  ckage = $pathinf
3c20: 6f 28 70 61 63 6b 61 67 65 29 3b 7d 5d 0a 09 09  o(package);}]...
3c30: 09 09 69 66 20 7b 24 63 68 65 63 6b 20 3d 3d 20  ..if {$check == 
3c40: 22 31 22 7d 20 7b 0a 09 09 09 09 09 73 65 74 20  "1"} {......set 
3c50: 72 65 74 76 61 6c 28 74 79 70 65 29 20 64 69 72  retval(type) dir
3c60: 65 63 74 6f 72 79 0a 09 09 09 09 09 73 65 74 20  ectory......set 
3c70: 72 65 74 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e  retval(childcoun
3c80: 74 29 20 5b 6c 6c 65 6e 67 74 68 20 5b 67 65 74  t) [llength [get
3c90: 63 68 69 6c 64 72 65 6e 20 24 70 61 74 68 5d 5d  children $path]]
3ca0: 0a 09 09 09 09 7d 0a 09 09 09 7d 0a 09 09 09 22  .....}....}...."
3cb0: 6f 73 2d 63 70 75 22 20 7b 0a 09 09 09 09 69 66  os-cpu" {.....if
3cc0: 20 7b 24 70 61 74 68 69 6e 66 6f 28 6f 73 29 20   {$pathinfo(os) 
3cd0: 3d 3d 20 22 70 6c 61 74 66 6f 72 6d 22 20 26 26  == "platform" &&
3ce0: 20 24 70 61 74 68 69 6e 66 6f 28 63 70 75 29 20   $pathinfo(cpu) 
3cf0: 3d 3d 20 22 22 7d 20 7b 0a 09 09 09 09 09 73 65  == ""} {......se
3d00: 74 20 72 65 74 76 61 6c 28 74 79 70 65 29 20 73  t retval(type) s
3d10: 79 6d 6c 69 6e 6b 0a 09 09 09 09 09 73 65 74 20  ymlink......set 
3d20: 72 65 74 76 61 6c 28 73 6f 75 72 63 65 29 20 5b  retval(source) [
3d30: 70 6c 61 74 66 6f 72 6d 3a 3a 67 65 6e 65 72 69  platform::generi
3d40: 63 5d 0a 09 09 09 09 7d 20 65 6c 73 65 20 7b 0a  c].....} else {.
3d50: 09 09 09 09 09 73 65 74 20 63 68 65 63 6b 20 5b  .....set check [
3d60: 3a 3a 61 70 70 66 73 3a 3a 64 62 20 6f 6e 65 63  ::appfs::db onec
3d70: 6f 6c 75 6d 6e 20 7b 0a 09 09 09 09 09 09 53 45  olumn {.......SE
3d80: 4c 45 43 54 20 31 20 46 52 4f 4d 20 70 61 63 6b  LECT 1 FROM pack
3d90: 61 67 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e  ages WHERE hostn
3da0: 61 6d 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28  ame = $pathinfo(
3db0: 68 6f 73 74 6e 61 6d 65 29 20 41 4e 44 20 70 61  hostname) AND pa
3dc0: 63 6b 61 67 65 20 3d 20 24 70 61 74 68 69 6e 66  ckage = $pathinf
3dd0: 6f 28 70 61 63 6b 61 67 65 29 20 41 4e 44 20 6f  o(package) AND o
3de0: 73 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 6f 73  s = $pathinfo(os
3df0: 29 20 41 4e 44 20 63 70 75 41 72 63 68 20 3d 20  ) AND cpuArch = 
3e00: 24 70 61 74 68 69 6e 66 6f 28 63 70 75 29 3b 0a  $pathinfo(cpu);.
3e10: 09 09 09 09 09 7d 5d 0a 09 09 09 09 09 69 66 20  .....}]......if 
3e20: 7b 24 63 68 65 63 6b 20 3d 3d 20 22 31 22 7d 20  {$check == "1"} 
3e30: 7b 0a 09 09 09 09 09 09 73 65 74 20 72 65 74 76  {.......set retv
3e40: 61 6c 28 74 79 70 65 29 20 64 69 72 65 63 74 6f  al(type) directo
3e50: 72 79 0a 09 09 09 09 09 09 73 65 74 20 72 65 74  ry.......set ret
3e60: 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29 20  val(childcount) 
3e70: 5b 6c 6c 65 6e 67 74 68 20 5b 67 65 74 63 68 69  [llength [getchi
3e80: 6c 64 72 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09  ldren $path]]...
3e90: 09 09 09 7d 0a 09 09 09 09 7d 0a 09 09 09 7d 0a  ...}.....}....}.
3ea0: 09 09 09 22 76 65 72 73 69 6f 6e 73 22 20 7b 0a  ..."versions" {.
3eb0: 09 09 09 09 69 66 20 7b 24 70 61 74 68 69 6e 66  ....if {$pathinf
3ec0: 6f 28 76 65 72 73 69 6f 6e 29 20 3d 3d 20 22 6c  o(version) == "l
3ed0: 61 74 65 73 74 22 7d 20 7b 0a 09 09 09 09 09 3a  atest"} {......:
3ee0: 3a 61 70 70 66 73 3a 3a 64 62 20 65 76 61 6c 20  :appfs::db eval 
3ef0: 7b 53 45 4c 45 43 54 20 76 65 72 73 69 6f 6e 20  {SELECT version 
3f00: 46 52 4f 4d 20 70 61 63 6b 61 67 65 73 20 57 48  FROM packages WH
3f10: 45 52 45 20 69 73 4c 61 74 65 73 74 20 3d 20 31  ERE isLatest = 1
3f20: 20 41 4e 44 20 68 6f 73 74 6e 61 6d 65 20 3d 20   AND hostname = 
3f30: 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61  $pathinfo(hostna
3f40: 6d 65 29 20 41 4e 44 20 70 61 63 6b 61 67 65 20  me) AND package 
3f50: 3d 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b  = $pathinfo(pack
3f60: 61 67 65 29 20 41 4e 44 20 6f 73 20 3d 20 24 70  age) AND os = $p
3f70: 61 74 68 69 6e 66 6f 28 6f 73 29 20 41 4e 44 20  athinfo(os) AND 
3f80: 63 70 75 41 72 63 68 20 3d 20 24 70 61 74 68 69  cpuArch = $pathi
3f90: 6e 66 6f 28 63 70 75 29 20 4c 49 4d 49 54 20 31  nfo(cpu) LIMIT 1
3fa0: 3b 7d 20 6c 61 74 65 73 74 5f 69 6e 66 6f 20 7b  ;} latest_info {
3fb0: 7d 0a 0a 09 09 09 09 09 69 66 20 7b 5b 69 6e 66  }.......if {[inf
3fc0: 6f 20 65 78 69 73 74 73 20 6c 61 74 65 73 74 5f  o exists latest_
3fd0: 69 6e 66 6f 28 76 65 72 73 69 6f 6e 29 5d 7d 20  info(version)]} 
3fe0: 7b 0a 09 09 09 09 09 09 73 65 74 20 72 65 74 76  {.......set retv
3ff0: 61 6c 28 74 79 70 65 29 20 73 79 6d 6c 69 6e 6b  al(type) symlink
4000: 0a 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61  .......set retva
4010: 6c 28 73 6f 75 72 63 65 29 20 24 6c 61 74 65 73  l(source) $lates
4020: 74 5f 69 6e 66 6f 28 76 65 72 73 69 6f 6e 29 0a  t_info(version).
4030: 09 09 09 09 09 7d 0a 09 09 09 09 7d 20 65 6c 73  .....}.....} els
4040: 65 20 7b 0a 09 09 09 09 09 69 66 20 7b 5b 69 6e  e {......if {[in
4050: 66 6f 20 65 78 69 73 74 73 20 70 61 74 68 69 6e  fo exists pathin
4060: 66 6f 28 70 61 63 6b 61 67 65 5f 73 68 61 31 29  fo(package_sha1)
4070: 5d 20 26 26 20 24 70 61 74 68 69 6e 66 6f 28 70  ] && $pathinfo(p
4080: 61 63 6b 61 67 65 5f 73 68 61 31 29 20 21 3d 20  ackage_sha1) != 
4090: 22 22 7d 20 7b 0a 09 09 09 09 09 09 73 65 74 20  ""} {.......set 
40a0: 72 65 74 76 61 6c 28 74 79 70 65 29 20 64 69 72  retval(type) dir
40b0: 65 63 74 6f 72 79 0a 09 09 09 09 09 09 73 65 74  ectory.......set
40c0: 20 72 65 74 76 61 6c 28 63 68 69 6c 64 63 6f 75   retval(childcou
40d0: 6e 74 29 20 5b 6c 6c 65 6e 67 74 68 20 5b 67 65  nt) [llength [ge
40e0: 74 63 68 69 6c 64 72 65 6e 20 24 70 61 74 68 5d  tchildren $path]
40f0: 5d 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 0a 09  ]......}.....}..
4100: 09 09 7d 0a 09 09 09 22 66 69 6c 65 73 22 20 7b  ..}...."files" {
4110: 0a 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
4120: 70 61 63 6b 61 67 65 64 29 20 31 0a 0a 09 09 09  packaged) 1.....
4130: 09 73 65 74 20 6c 6f 63 61 6c 70 61 74 68 20 5b  .set localpath [
4140: 5f 6c 6f 63 61 6c 70 61 74 68 20 24 70 61 74 68  _localpath $path
4150: 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 20 24 70  info(package) $p
4160: 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65  athinfo(hostname
4170: 29 20 24 70 61 74 68 69 6e 66 6f 28 66 69 6c 65  ) $pathinfo(file
4180: 29 5d 0a 09 09 09 09 73 65 74 20 77 68 69 74 65  )].....set white
4190: 6f 75 74 70 61 74 68 20 20 5b 5f 77 68 69 74 65  outpath  [_white
41a0: 6f 75 74 70 61 74 68 20 24 70 61 74 68 69 6e 66  outpath $pathinf
41b0: 6f 28 70 61 63 6b 61 67 65 29 20 24 70 61 74 68  o(package) $path
41c0: 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24  info(hostname) $
41d0: 70 61 74 68 69 6e 66 6f 28 66 69 6c 65 29 5d 0a  pathinfo(file)].
41e0: 0a 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
41f0: 6c 6f 63 61 6c 70 61 74 68 29 20 24 6c 6f 63 61  localpath) $loca
4200: 6c 70 61 74 68 0a 09 09 09 09 73 65 74 20 72 65  lpath.....set re
4210: 74 76 61 6c 28 77 68 69 74 65 6f 75 74 70 61 74  tval(whiteoutpat
4220: 68 29 20 24 77 68 69 74 65 6f 75 74 70 61 74 68  h) $whiteoutpath
4230: 0a 0a 09 09 09 09 69 66 20 7b 24 6c 6f 63 61 6c  ......if {$local
4240: 70 61 74 68 20 21 3d 20 22 22 20 26 26 20 5b 66  path != "" && [f
4250: 69 6c 65 20 65 78 69 73 74 73 20 24 6c 6f 63 61  ile exists $loca
4260: 6c 70 61 74 68 5d 7d 20 7b 0a 09 09 09 09 09 73  lpath]} {......s
4270: 65 74 20 72 65 74 76 61 6c 28 69 73 5f 6c 6f 63  et retval(is_loc
4280: 61 6c 66 69 6c 65 29 20 31 0a 09 09 09 09 09 63  alfile) 1......c
4290: 61 74 63 68 20 7b 0a 09 09 09 09 09 09 5f 61 73  atch {......._as
42a0: 5f 75 73 65 72 20 7b 0a 09 09 09 09 09 09 09 66  _user {........f
42b0: 69 6c 65 20 6c 73 74 61 74 20 24 6c 6f 63 61 6c  ile lstat $local
42c0: 70 61 74 68 20 6c 6f 63 61 6c 70 61 74 68 69 6e  path localpathin
42d0: 66 6f 0a 09 09 09 09 09 09 7d 0a 09 09 09 09 09  fo.......}......
42e0: 09 73 65 74 20 72 65 74 76 61 6c 28 74 69 6d 65  .set retval(time
42f0: 29 20 24 6c 6f 63 61 6c 70 61 74 68 69 6e 66 6f  ) $localpathinfo
4300: 28 6d 74 69 6d 65 29 0a 0a 09 09 09 09 09 09 73  (mtime)........s
4310: 77 69 74 63 68 20 2d 2d 20 24 6c 6f 63 61 6c 70  witch -- $localp
4320: 61 74 68 69 6e 66 6f 28 74 79 70 65 29 20 7b 0a  athinfo(type) {.
4330: 09 09 09 09 09 09 09 22 64 69 72 65 63 74 6f 72  ......."director
4340: 79 22 20 7b 0a 09 09 09 09 09 09 09 09 73 65 74  y" {.........set
4350: 20 72 65 74 76 61 6c 28 74 79 70 65 29 20 22 64   retval(type) "d
4360: 69 72 65 63 74 6f 72 79 22 0a 09 09 09 09 09 09  irectory".......
4370: 09 09 73 65 74 20 72 65 74 76 61 6c 28 63 68 69  ..set retval(chi
4380: 6c 64 63 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67 74  ldcount) [llengt
4390: 68 20 5b 67 65 74 63 68 69 6c 64 72 65 6e 20 24  h [getchildren $
43a0: 70 61 74 68 5d 5d 0a 09 09 09 09 09 09 09 7d 0a  path]]........}.
43b0: 09 09 09 09 09 09 09 22 66 69 6c 65 22 20 7b 0a  ......."file" {.
43c0: 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ........set retv
43d0: 61 6c 28 74 79 70 65 29 20 22 66 69 6c 65 22 0a  al(type) "file".
43e0: 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ........set retv
43f0: 61 6c 28 73 69 7a 65 29 20 24 6c 6f 63 61 6c 70  al(size) $localp
4400: 61 74 68 69 6e 66 6f 28 73 69 7a 65 29 0a 09 09  athinfo(size)...
4410: 09 09 09 09 09 09 5f 61 73 5f 75 73 65 72 20 7b  ......_as_user {
4420: 0a 09 09 09 09 09 09 09 09 09 69 66 20 7b 5b 66  ..........if {[f
4430: 69 6c 65 20 65 78 65 63 75 74 61 62 6c 65 20 24  ile executable $
4440: 6c 6f 63 61 6c 70 61 74 68 5d 7d 20 7b 0a 09 09  localpath]} {...
4450: 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ........set retv
4460: 61 6c 28 70 65 72 6d 73 29 20 22 78 22 0a 09 09  al(perms) "x"...
4470: 09 09 09 09 09 09 09 7d 20 65 6c 73 65 20 7b 0a  .......} else {.
4480: 09 09 09 09 09 09 09 09 09 09 73 65 74 20 72 65  ..........set re
4490: 74 76 61 6c 28 70 65 72 6d 73 29 20 22 22 0a 09  tval(perms) ""..
44a0: 09 09 09 09 09 09 09 09 7d 0a 09 09 09 09 09 09  ........}.......
44b0: 09 09 7d 0a 09 09 09 09 09 09 09 7d 0a 09 09 09  ..}........}....
44c0: 09 09 09 09 22 6c 69 6e 6b 22 20 7b 0a 09 09 09  ...."link" {....
44d0: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
44e0: 74 79 70 65 29 20 22 73 79 6d 6c 69 6e 6b 22 0a  type) "symlink".
44f0: 0a 09 09 09 09 09 09 09 09 5f 61 73 5f 75 73 65  ........._as_use
4500: 72 20 7b 0a 09 09 09 09 09 09 09 09 09 73 65 74  r {..........set
4510: 20 72 65 74 76 61 6c 28 73 6f 75 72 63 65 29 20   retval(source) 
4520: 5b 66 69 6c 65 20 72 65 61 64 6c 69 6e 6b 20 24  [file readlink $
4530: 6c 6f 63 61 6c 70 61 74 68 5d 0a 09 09 09 09 09  localpath]......
4540: 09 09 09 7d 0a 09 09 09 09 09 09 09 7d 0a 09 09  ...}........}...
4550: 09 09 09 09 09 22 66 69 66 6f 22 20 7b 0a 09 09  ....."fifo" {...
4560: 09 09 09 09 09 09 23 20 43 61 70 69 74 61 6c 69  ......# Capitali
4570: 7a 65 64 20 73 6f 20 74 68 61 74 20 74 68 65 20  zed so that the 
4580: 66 69 72 73 74 20 63 68 61 72 20 69 73 20 75 6e  first char is un
4590: 69 71 75 65 0a 09 09 09 09 09 09 09 09 73 65 74  ique.........set
45a0: 20 72 65 74 76 61 6c 28 74 79 70 65 29 20 22 46   retval(type) "F
45b0: 69 66 6f 22 0a 09 09 09 09 09 09 09 7d 0a 09 09  ifo"........}...
45c0: 09 09 09 09 09 22 73 6f 63 6b 65 74 22 20 7b 0a  ....."socket" {.
45d0: 09 09 09 09 09 09 09 09 23 20 43 61 70 69 74 61  ........# Capita
45e0: 6c 69 7a 65 64 20 73 6f 20 74 68 61 74 20 74 68  lized so that th
45f0: 65 20 66 69 72 73 74 20 63 68 61 72 20 69 73 20  e first char is 
4600: 75 6e 69 71 75 65 0a 09 09 09 09 09 09 09 09 73  unique.........s
4610: 65 74 20 72 65 74 76 61 6c 28 74 79 70 65 29 20  et retval(type) 
4620: 22 53 6f 63 6b 65 74 22 0a 09 09 09 09 09 09 09  "Socket"........
4630: 7d 0a 09 09 09 09 09 09 7d 0a 09 09 09 09 09 7d  }.......}......}
4640: 20 65 72 72 0a 09 09 09 09 7d 20 65 6c 73 65 20   err.....} else 
4650: 7b 0a 09 09 09 09 09 69 66 20 7b 24 77 68 69 74  {......if {$whit
4660: 65 6f 75 74 70 61 74 68 20 3d 3d 20 22 22 20 7c  eoutpath == "" |
4670: 7c 20 21 5b 66 69 6c 65 20 65 78 69 73 74 73 20  | ![file exists 
4680: 24 77 68 69 74 65 6f 75 74 70 61 74 68 5d 7d 20  $whiteoutpath]} 
4690: 7b 0a 09 09 09 09 09 09 73 65 74 20 72 65 74 76  {.......set retv
46a0: 61 6c 28 69 73 5f 72 65 6d 6f 74 65 66 69 6c 65  al(is_remotefile
46b0: 29 20 31 0a 0a 09 09 09 09 09 09 73 65 74 20 77  ) 1........set w
46c0: 6f 72 6b 20 5b 73 70 6c 69 74 20 24 70 61 74 68  ork [split $path
46d0: 69 6e 66 6f 28 66 69 6c 65 29 20 22 2f 22 5d 0a  info(file) "/"].
46e0: 09 09 09 09 09 09 73 65 74 20 64 69 72 65 63 74  ......set direct
46f0: 6f 72 79 20 5b 6a 6f 69 6e 20 5b 6c 72 61 6e 67  ory [join [lrang
4700: 65 20 24 77 6f 72 6b 20 30 20 65 6e 64 2d 31 5d  e $work 0 end-1]
4710: 20 22 2f 22 5d 0a 09 09 09 09 09 09 73 65 74 20   "/"].......set 
4720: 66 69 6c 65 20 5b 6c 69 6e 64 65 78 20 24 77 6f  file [lindex $wo
4730: 72 6b 20 65 6e 64 5d 0a 0a 09 09 09 09 09 09 69  rk end]........i
4740: 66 20 7b 24 64 69 72 65 63 74 6f 72 79 20 3d 3d  f {$directory ==
4750: 20 22 22 20 26 26 20 24 66 69 6c 65 20 3d 3d 20   "" && $file == 
4760: 22 22 7d 20 7b 0a 09 09 09 09 09 09 09 61 72 72  ""} {........arr
4770: 61 79 20 73 65 74 20 72 65 74 76 61 6c 20 5b 6c  ay set retval [l
4780: 69 73 74 20 74 79 70 65 20 64 69 72 65 63 74 6f  ist type directo
4790: 72 79 5d 0a 09 09 09 09 09 09 7d 0a 0a 09 09 09  ry].......}.....
47a0: 09 09 09 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65  ...::appfs::db e
47b0: 76 61 6c 20 7b 53 45 4c 45 43 54 20 74 79 70 65  val {SELECT type
47c0: 2c 20 74 69 6d 65 2c 20 73 6f 75 72 63 65 2c 20  , time, source, 
47d0: 73 69 7a 65 2c 20 70 65 72 6d 73 20 46 52 4f 4d  size, perms FROM
47e0: 20 66 69 6c 65 73 20 57 48 45 52 45 20 70 61 63   files WHERE pac
47f0: 6b 61 67 65 5f 73 68 61 31 20 3d 20 24 70 61 74  kage_sha1 = $pat
4800: 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73 68  hinfo(package_sh
4810: 61 31 29 20 41 4e 44 20 66 69 6c 65 5f 64 69 72  a1) AND file_dir
4820: 65 63 74 6f 72 79 20 3d 20 24 64 69 72 65 63 74  ectory = $direct
4830: 6f 72 79 20 41 4e 44 20 66 69 6c 65 5f 6e 61 6d  ory AND file_nam
4840: 65 20 3d 20 24 66 69 6c 65 3b 7d 20 72 65 74 76  e = $file;} retv
4850: 61 6c 20 7b 7d 0a 0a 09 09 09 09 09 09 69 66 20  al {}........if 
4860: 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 72 65  {[info exists re
4870: 74 76 61 6c 28 74 79 70 65 29 5d 20 26 26 20 24  tval(type)] && $
4880: 72 65 74 76 61 6c 28 74 79 70 65 29 20 3d 3d 20  retval(type) == 
4890: 22 64 69 72 65 63 74 6f 72 79 22 7d 20 7b 0a 09  "directory"} {..
48a0: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
48b0: 28 63 68 69 6c 64 63 6f 75 6e 74 29 20 5b 6c 6c  (childcount) [ll
48c0: 65 6e 67 74 68 20 5b 67 65 74 63 68 69 6c 64 72  ength [getchildr
48d0: 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09 09 09 09  en $path]]......
48e0: 09 7d 0a 0a 09 09 09 09 09 09 75 6e 73 65 74 20  .}........unset 
48f0: 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 72 65 74 76  -nocomplain retv
4900: 61 6c 28 2a 29 0a 09 09 09 09 09 7d 0a 09 09 09  al(*)......}....
4910: 09 7d 0a 0a 09 09 09 7d 0a 09 09 7d 0a 0a 09 09  .}.....}...}....
4920: 69 66 20 7b 21 5b 69 6e 66 6f 20 65 78 69 73 74  if {![info exist
4930: 73 20 72 65 74 76 61 6c 28 74 79 70 65 29 5d 7d  s retval(type)]}
4940: 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d 63 6f   {....return -co
4950: 64 65 20 65 72 72 6f 72 20 22 4e 6f 20 73 75 63  de error "No suc
4960: 68 20 66 69 6c 65 20 6f 72 20 64 69 72 65 63 74  h file or direct
4970: 6f 72 79 22 0a 09 09 7d 0a 0a 09 09 72 65 74 75  ory"...}....retu
4980: 72 6e 20 5b 61 72 72 61 79 20 67 65 74 20 72 65  rn [array get re
4990: 74 76 61 6c 5d 0a 09 7d 0a 0a 09 70 72 6f 63 20  tval]..}...proc 
49a0: 6f 70 65 6e 70 61 74 68 20 7b 70 61 74 68 20 6d  openpath {path m
49b0: 6f 64 65 7d 20 7b 0a 09 09 61 72 72 61 79 20 73  ode} {...array s
49c0: 65 74 20 70 61 74 68 69 6e 66 6f 20 5b 5f 70 61  et pathinfo [_pa
49d0: 72 73 65 70 61 74 68 20 24 70 61 74 68 5d 0a 0a  rsepath $path]..
49e0: 09 09 69 66 20 7b 24 70 61 74 68 69 6e 66 6f 28  ..if {$pathinfo(
49f0: 5f 74 79 70 65 29 20 21 3d 20 22 66 69 6c 65 73  _type) != "files
4a00: 22 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d  "} {....return -
4a10: 63 6f 64 65 20 65 72 72 6f 72 20 22 69 6e 76 61  code error "inva
4a20: 6c 69 64 20 74 79 70 65 22 0a 09 09 7d 0a 0a 09  lid type"...}...
4a30: 09 73 65 74 20 6c 6f 63 61 6c 70 61 74 68 20 5b  .set localpath [
4a40: 5f 6c 6f 63 61 6c 70 61 74 68 20 24 70 61 74 68  _localpath $path
4a50: 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 20 24 70  info(package) $p
4a60: 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65  athinfo(hostname
4a70: 29 20 24 70 61 74 68 69 6e 66 6f 28 66 69 6c 65  ) $pathinfo(file
4a80: 29 5d 0a 0a 09 09 69 66 20 7b 24 6d 6f 64 65 20  )]....if {$mode 
4a90: 3d 3d 20 22 63 72 65 61 74 65 22 7d 20 7b 0a 09  == "create"} {..
4aa0: 09 09 69 66 20 7b 24 6c 6f 63 61 6c 70 61 74 68  ..if {$localpath
4ab0: 20 3d 3d 20 22 22 7d 20 7b 0a 09 09 09 09 72 65   == ""} {.....re
4ac0: 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72  turn -code error
4ad0: 20 22 41 73 6b 65 64 20 74 6f 20 63 72 65 61 74   "Asked to creat
4ae0: 65 2c 20 62 75 74 20 6e 6f 20 68 6f 6d 65 20 64  e, but no home d
4af0: 69 72 65 63 74 6f 72 79 2e 22 0a 09 09 09 7d 0a  irectory."....}.
4b00: 0a 09 09 09 72 65 74 75 72 6e 20 24 6c 6f 63 61  ....return $loca
4b10: 6c 70 61 74 68 0a 09 09 7d 0a 0a 09 09 69 66 20  lpath...}....if 
4b20: 7b 24 6c 6f 63 61 6c 70 61 74 68 20 21 3d 20 22  {$localpath != "
4b30: 22 20 26 26 20 5b 66 69 6c 65 20 65 78 69 73 74  " && [file exist
4b40: 73 20 24 6c 6f 63 61 6c 70 61 74 68 5d 7d 20 7b  s $localpath]} {
4b50: 0a 09 09 09 72 65 74 75 72 6e 20 24 6c 6f 63 61  ....return $loca
4b60: 6c 70 61 74 68 0a 09 09 7d 0a 0a 09 09 73 65 74  lpath...}....set
4b70: 20 77 6f 72 6b 20 5b 73 70 6c 69 74 20 24 70 61   work [split $pa
4b80: 74 68 69 6e 66 6f 28 66 69 6c 65 29 20 22 2f 22  thinfo(file) "/"
4b90: 5d 0a 09 09 73 65 74 20 64 69 72 65 63 74 6f 72  ]...set director
4ba0: 79 20 5b 6a 6f 69 6e 20 5b 6c 72 61 6e 67 65 20  y [join [lrange 
4bb0: 24 77 6f 72 6b 20 30 20 65 6e 64 2d 31 5d 20 22  $work 0 end-1] "
4bc0: 2f 22 5d 0a 09 09 73 65 74 20 66 69 6c 65 20 5b  /"]...set file [
4bd0: 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 65 6e 64  lindex $work end
4be0: 5d 0a 09 09 3a 3a 61 70 70 66 73 3a 3a 64 62 20  ]...::appfs::db 
4bf0: 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 66 69 6c  eval {SELECT fil
4c00: 65 5f 73 68 61 31 2c 20 70 65 72 6d 73 20 46 52  e_sha1, perms FR
4c10: 4f 4d 20 66 69 6c 65 73 20 57 48 45 52 45 20 70  OM files WHERE p
4c20: 61 63 6b 61 67 65 5f 73 68 61 31 20 3d 20 24 70  ackage_sha1 = $p
4c30: 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 5f  athinfo(package_
4c40: 73 68 61 31 29 20 41 4e 44 20 66 69 6c 65 5f 6e  sha1) AND file_n
4c50: 61 6d 65 20 3d 20 24 66 69 6c 65 20 41 4e 44 20  ame = $file AND 
4c60: 66 69 6c 65 5f 64 69 72 65 63 74 6f 72 79 20 3d  file_directory =
4c70: 20 24 64 69 72 65 63 74 6f 72 79 3b 7d 20 70 6b   $directory;} pk
4c80: 67 70 61 74 68 69 6e 66 6f 20 7b 7d 0a 0a 09 09  gpathinfo {}....
4c90: 69 66 20 7b 24 70 6b 67 70 61 74 68 69 6e 66 6f  if {$pkgpathinfo
4ca0: 28 66 69 6c 65 5f 73 68 61 31 29 20 3d 3d 20 22  (file_sha1) == "
4cb0: 22 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d  "} {....return -
4cc0: 63 6f 64 65 20 65 72 72 6f 72 20 22 4e 6f 20 73  code error "No s
4cd0: 75 63 68 20 66 69 6c 65 20 6f 72 20 64 69 72 65  uch file or dire
4ce0: 63 74 6f 72 79 22 0a 09 09 7d 0a 0a 09 09 73 65  ctory"...}....se
4cf0: 74 20 6c 6f 63 61 6c 63 61 63 68 65 66 69 6c 65  t localcachefile
4d00: 20 5b 64 6f 77 6e 6c 6f 61 64 20 24 70 61 74 68   [download $path
4d10: 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24  info(hostname) $
4d20: 70 6b 67 70 61 74 68 69 6e 66 6f 28 66 69 6c 65  pkgpathinfo(file
4d30: 5f 73 68 61 31 29 5d 0a 0a 09 09 69 66 20 7b 24  _sha1)]....if {$
4d40: 6d 6f 64 65 20 3d 3d 20 22 77 72 69 74 65 22 7d  mode == "write"}
4d50: 20 7b 0a 09 09 09 5f 61 73 5f 75 73 65 72 20 7b   {...._as_user {
4d60: 0a 09 09 09 09 73 65 74 20 74 6d 70 6c 6f 63 61  .....set tmploca
4d70: 6c 70 61 74 68 20 22 24 7b 6c 6f 63 61 6c 70 61  lpath "${localpa
4d80: 74 68 7d 2e 5b 65 78 70 72 20 72 61 6e 64 28 29  th}.[expr rand()
4d90: 5d 5b 63 6c 6f 63 6b 20 63 6c 69 63 6b 73 5d 22  ][clock clicks]"
4da0: 0a 0a 09 09 09 09 73 65 74 20 66 61 69 6c 65 64  ......set failed
4db0: 20 30 0a 09 09 09 09 69 66 20 7b 5b 63 61 74 63   0.....if {[catc
4dc0: 68 20 7b 0a 09 09 09 09 09 66 69 6c 65 20 6d 6b  h {......file mk
4dd0: 64 69 72 20 5b 66 69 6c 65 20 64 69 72 6e 61 6d  dir [file dirnam
4de0: 65 20 24 6c 6f 63 61 6c 70 61 74 68 5d 0a 09 09  e $localpath]...
4df0: 09 09 09 66 69 6c 65 20 63 6f 70 79 20 2d 66 6f  ...file copy -fo
4e00: 72 63 65 20 2d 2d 20 24 6c 6f 63 61 6c 63 61 63  rce -- $localcac
4e10: 68 65 66 69 6c 65 20 24 74 6d 70 6c 6f 63 61 6c  hefile $tmplocal
4e20: 70 61 74 68 0a 0a 09 09 09 09 09 69 66 20 7b 24  path.......if {$
4e30: 70 6b 67 70 61 74 68 69 6e 66 6f 28 70 65 72 6d  pkgpathinfo(perm
4e40: 73 29 20 3d 3d 20 22 78 22 7d 20 7b 0a 09 09 09  s) == "x"} {....
4e50: 09 09 09 66 69 6c 65 20 61 74 74 72 69 62 75 74  ...file attribut
4e60: 65 73 20 24 74 6d 70 6c 6f 63 61 6c 70 61 74 68  es $tmplocalpath
4e70: 20 2d 70 65 72 6d 69 73 73 69 6f 6e 73 20 2b 78   -permissions +x
4e80: 0a 09 09 09 09 09 7d 0a 0a 09 09 09 09 09 66 69  ......}.......fi
4e90: 6c 65 20 72 65 6e 61 6d 65 20 2d 66 6f 72 63 65  le rename -force
4ea0: 20 2d 2d 20 24 74 6d 70 6c 6f 63 61 6c 70 61 74   -- $tmplocalpat
4eb0: 68 20 24 6c 6f 63 61 6c 70 61 74 68 0a 09 09 09  h $localpath....
4ec0: 09 7d 20 65 72 72 5d 7d 20 7b 0a 09 09 09 09 09  .} err]} {......
4ed0: 73 65 74 20 66 61 69 6c 65 64 20 31 0a 09 09 09  set failed 1....
4ee0: 09 7d 0a 09 09 09 09 63 61 74 63 68 20 7b 0a 09  .}.....catch {..
4ef0: 09 09 09 09 66 69 6c 65 20 64 65 6c 65 74 65 20  ....file delete 
4f00: 2d 66 6f 72 63 65 20 2d 2d 20 24 74 6d 70 6c 6f  -force -- $tmplo
4f10: 63 61 6c 70 61 74 68 0a 09 09 09 09 7d 0a 09 09  calpath.....}...
4f20: 09 7d 0a 0a 09 09 09 69 66 20 7b 24 66 61 69 6c  .}.....if {$fail
4f30: 65 64 7d 20 7b 0a 09 09 09 09 72 65 74 75 72 6e  ed} {.....return
4f40: 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 24 65 72   -code error $er
4f50: 72 0a 09 09 09 7d 0a 0a 09 09 09 72 65 74 75 72  r....}.....retur
4f60: 6e 20 24 6c 6f 63 61 6c 70 61 74 68 0a 09 09 7d  n $localpath...}
4f70: 0a 0a 09 09 72 65 74 75 72 6e 20 24 6c 6f 63 61  ....return $loca
4f80: 6c 63 61 63 68 65 66 69 6c 65 0a 09 7d 0a 0a 09  lcachefile..}...
4f90: 70 72 6f 63 20 6c 6f 63 61 6c 70 61 74 68 20 7b  proc localpath {
4fa0: 70 61 74 68 7d 20 7b 0a 09 09 61 72 72 61 79 20  path} {...array 
4fb0: 73 65 74 20 70 61 74 68 69 6e 66 6f 20 5b 5f 70  set pathinfo [_p
4fc0: 61 72 73 65 70 61 74 68 20 24 70 61 74 68 5d 0a  arsepath $path].
4fd0: 0a 09 09 69 66 20 7b 24 70 61 74 68 69 6e 66 6f  ...if {$pathinfo
4fe0: 28 5f 74 79 70 65 29 20 21 3d 20 22 66 69 6c 65  (_type) != "file
4ff0: 73 22 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  s"} {....return 
5000: 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 69 6e 76  -code error "inv
5010: 61 6c 69 64 20 74 79 70 65 22 0a 09 09 7d 0a 0a  alid type"...}..
5020: 09 09 73 65 74 20 6c 6f 63 61 6c 70 61 74 68 20  ..set localpath 
5030: 5b 5f 6c 6f 63 61 6c 70 61 74 68 20 24 70 61 74  [_localpath $pat
5040: 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 20 24  hinfo(package) $
5050: 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d  pathinfo(hostnam
5060: 65 29 20 24 70 61 74 68 69 6e 66 6f 28 66 69 6c  e) $pathinfo(fil
5070: 65 29 5d 0a 0a 09 09 72 65 74 75 72 6e 20 24 6c  e)]....return $l
5080: 6f 63 61 6c 70 61 74 68 0a 09 7d 0a 0a 09 70 72  ocalpath..}...pr
5090: 6f 63 20 65 78 69 73 74 73 20 7b 70 61 74 68 7d  oc exists {path}
50a0: 20 7b 0a 09 09 63 61 74 63 68 20 7b 0a 09 09 09   {...catch {....
50b0: 73 65 74 20 69 6e 66 6f 20 5b 67 65 74 61 74 74  set info [getatt
50c0: 72 20 24 70 61 74 68 5d 0a 09 09 7d 20 65 72 72  r $path]...} err
50d0: 0a 0a 09 09 69 66 20 7b 21 5b 69 6e 66 6f 20 65  ....if {![info e
50e0: 78 69 73 74 73 20 69 6e 66 6f 5d 7d 20 7b 0a 09  xists info]} {..
50f0: 09 09 69 66 20 7b 24 65 72 72 20 3d 3d 20 22 4e  ..if {$err == "N
5100: 6f 20 73 75 63 68 20 66 69 6c 65 20 6f 72 20 64  o such file or d
5110: 69 72 65 63 74 6f 72 79 22 7d 20 7b 0a 09 09 09  irectory"} {....
5120: 09 72 65 74 75 72 6e 20 5b 6c 69 73 74 5d 0a 09  .return [list]..
5130: 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09 72  ..} else {.....r
5140: 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f  eturn -code erro
5150: 72 20 24 65 72 72 0a 09 09 09 7d 0a 09 09 7d 0a  r $err....}...}.
5160: 0a 09 09 72 65 74 75 72 6e 20 24 69 6e 66 6f 0a  ...return $info.
5170: 09 7d 0a 0a 09 70 72 6f 63 20 70 72 65 70 61 72  .}...proc prepar
5180: 65 5f 74 6f 5f 63 72 65 61 74 65 20 7b 70 61 74  e_to_create {pat
5190: 68 20 7b 6d 75 73 74 5f 6e 6f 74 5f 65 78 69 73  h {must_not_exis
51a0: 74 20 31 7d 7d 20 7b 0a 09 09 69 66 20 7b 24 6d  t 1}} {...if {$m
51b0: 75 73 74 5f 6e 6f 74 5f 65 78 69 73 74 7d 20 7b  ust_not_exist} {
51c0: 0a 09 09 09 69 66 20 7b 5b 65 78 69 73 74 73 20  ....if {[exists 
51d0: 24 70 61 74 68 5d 20 21 3d 20 22 22 7d 20 7b 0a  $path] != ""} {.
51e0: 09 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65  ....return -code
51f0: 20 65 72 72 6f 72 20 22 46 69 6c 65 20 61 6c 72   error "File alr
5200: 65 61 64 79 20 65 78 69 73 74 73 22 0a 09 09 09  eady exists"....
5210: 7d 0a 09 09 7d 0a 0a 09 09 73 65 74 20 66 69 6c  }...}....set fil
5220: 65 6e 61 6d 65 20 5b 6c 6f 63 61 6c 70 61 74 68  ename [localpath
5230: 20 24 70 61 74 68 5d 0a 0a 09 09 69 66 20 7b 24   $path]....if {$
5240: 66 69 6c 65 6e 61 6d 65 20 3d 3d 20 22 22 7d 20  filename == ""} 
5250: 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64  {....return -cod
5260: 65 20 65 72 72 6f 72 20 22 41 73 6b 65 64 20 74  e error "Asked t
5270: 6f 20 63 72 65 61 74 65 2c 20 62 75 74 20 6e 6f  o create, but no
5280: 20 68 6f 6d 65 20 64 69 72 65 63 74 6f 72 79 2e   home directory.
5290: 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20 64 69 72  "...}....set dir
52a0: 6e 61 6d 65 20 5b 66 69 6c 65 20 64 69 72 6e 61  name [file dirna
52b0: 6d 65 20 24 66 69 6c 65 6e 61 6d 65 5d 0a 0a 09  me $filename]...
52c0: 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09 09 09 66  ._as_user {....f
52d0: 69 6c 65 20 6d 6b 64 69 72 20 24 64 69 72 6e 61  ile mkdir $dirna
52e0: 6d 65 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e  me...}....return
52f0: 20 24 66 69 6c 65 6e 61 6d 65 0a 09 7d 0a 0a 09   $filename..}...
5300: 70 72 6f 63 20 75 6e 6c 69 6e 6b 70 61 74 68 20  proc unlinkpath 
5310: 7b 70 61 74 68 7d 20 7b 0a 09 09 61 72 72 61 79  {path} {...array
5320: 20 73 65 74 20 70 61 74 68 61 74 74 72 73 20 5b   set pathattrs [
5330: 65 78 69 73 74 73 20 24 70 61 74 68 5d 0a 0a 09  exists $path]...
5340: 09 69 66 20 7b 21 5b 69 6e 66 6f 20 65 78 69 73  .if {![info exis
5350: 74 73 20 70 61 74 68 61 74 74 72 73 28 70 61 63  ts pathattrs(pac
5360: 6b 61 67 65 64 29 5d 7d 20 7b 0a 09 09 09 72 65  kaged)]} {....re
5370: 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72  turn -code error
5380: 20 22 69 6e 76 61 6c 69 64 20 74 79 70 65 22 0a   "invalid type".
5390: 09 09 7d 0a 0a 09 09 73 65 74 20 6c 6f 63 61 6c  ..}....set local
53a0: 70 61 74 68 20 24 70 61 74 68 61 74 74 72 73 28  path $pathattrs(
53b0: 6c 6f 63 61 6c 70 61 74 68 29 0a 0a 09 09 69 66  localpath)....if
53c0: 20 7b 24 6c 6f 63 61 6c 70 61 74 68 20 3d 3d 20   {$localpath == 
53d0: 22 22 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  ""} {....return 
53e0: 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 41 73 6b  -code error "Ask
53f0: 65 64 20 74 6f 20 64 65 6c 65 74 65 2c 20 62 75  ed to delete, bu
5400: 74 20 6e 6f 20 68 6f 6d 65 20 64 69 72 65 63 74  t no home direct
5410: 6f 72 79 2e 22 0a 09 09 7d 0a 0a 09 09 69 66 20  ory."...}....if 
5420: 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 70 61  {[info exists pa
5430: 74 68 61 74 74 72 73 28 69 73 5f 6c 6f 63 61 6c  thattrs(is_local
5440: 66 69 6c 65 29 5d 7d 20 7b 0a 09 09 09 69 66 20  file)]} {....if 
5450: 7b 5b 66 69 6c 65 20 69 73 64 69 72 65 63 74 6f  {[file isdirecto
5460: 72 79 20 24 6c 6f 63 61 6c 70 61 74 68 5d 7d 20  ry $localpath]} 
5470: 7b 0a 09 09 09 09 73 65 74 20 63 68 69 6c 64 72  {.....set childr
5480: 65 6e 20 5b 67 65 74 63 68 69 6c 64 72 65 6e 20  en [getchildren 
5490: 24 70 61 74 68 5d 0a 0a 09 09 09 09 69 66 20 7b  $path]......if {
54a0: 5b 6c 6c 65 6e 67 74 68 20 24 63 68 69 6c 64 72  [llength $childr
54b0: 65 6e 5d 20 21 3d 20 30 7d 20 7b 0a 09 09 09 09  en] != 0} {.....
54c0: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72  .return -code er
54d0: 72 6f 72 20 22 41 73 6b 65 64 20 74 6f 20 64 65  ror "Asked to de
54e0: 6c 65 74 65 20 6e 6f 6e 2d 65 6d 70 74 79 20 64  lete non-empty d
54f0: 69 72 65 63 74 6f 72 79 22 0a 09 09 09 09 7d 0a  irectory".....}.
5500: 09 09 09 7d 0a 0a 09 09 09 5f 61 73 5f 75 73 65  ...}....._as_use
5510: 72 20 7b 0a 09 09 09 09 66 69 6c 65 20 64 65 6c  r {.....file del
5520: 65 74 65 20 2d 66 6f 72 63 65 20 2d 2d 20 24 6c  ete -force -- $l
5530: 6f 63 61 6c 70 61 74 68 0a 09 09 09 7d 0a 09 09  ocalpath....}...
5540: 7d 20 65 6c 73 65 69 66 20 7b 5b 69 6e 66 6f 20  } elseif {[info 
5550: 65 78 69 73 74 73 20 70 61 74 68 61 74 74 72 73  exists pathattrs
5560: 28 69 73 5f 72 65 6d 6f 74 65 66 69 6c 65 29 5d  (is_remotefile)]
5570: 7d 20 7b 0a 09 09 09 69 66 20 7b 24 70 61 74 68  } {....if {$path
5580: 61 74 74 72 73 28 74 79 70 65 29 20 3d 3d 20 22  attrs(type) == "
5590: 64 69 72 65 63 74 6f 72 79 22 7d 20 7b 0a 09 09  directory"} {...
55a0: 09 09 73 65 74 20 63 68 69 6c 64 72 65 6e 20 5b  ..set children [
55b0: 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61 74  getchildren $pat
55c0: 68 5d 0a 0a 09 09 09 09 69 66 20 7b 5b 6c 6c 65  h]......if {[lle
55d0: 6e 67 74 68 20 24 63 68 69 6c 64 72 65 6e 5d 20  ngth $children] 
55e0: 21 3d 20 30 7d 20 7b 0a 09 09 09 09 09 72 65 74  != 0} {......ret
55f0: 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20  urn -code error 
5600: 22 41 73 6b 65 64 20 74 6f 20 64 65 6c 65 74 65  "Asked to delete
5610: 20 6e 6f 6e 2d 65 6d 70 74 79 20 64 69 72 65 63   non-empty direc
5620: 74 6f 72 79 22 0a 09 09 09 09 7d 0a 09 09 09 7d  tory".....}....}
5630: 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 72  ...} else {....r
5640: 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f  eturn -code erro
5650: 72 20 22 55 6e 6b 6e 6f 77 6e 20 69 66 20 66 69  r "Unknown if fi
5660: 6c 65 20 69 73 20 72 65 6d 6f 74 65 20 6f 72 20  le is remote or 
5670: 6c 6f 63 61 6c 20 21 3f 22 0a 09 09 7d 0a 0a 09  local !?"...}...
5680: 09 73 65 74 20 77 68 69 74 65 6f 75 74 66 69 6c  .set whiteoutfil
5690: 65 20 24 70 61 74 68 61 74 74 72 73 28 77 68 69  e $pathattrs(whi
56a0: 74 65 6f 75 74 70 61 74 68 29 0a 09 09 73 65 74  teoutpath)...set
56b0: 20 77 68 69 74 65 6f 75 74 64 69 72 20 5b 66 69   whiteoutdir [fi
56c0: 6c 65 20 64 69 72 6e 61 6d 65 20 24 77 68 69 74  le dirname $whit
56d0: 65 6f 75 74 66 69 6c 65 5d 0a 0a 09 09 5f 61 73  eoutfile]...._as
56e0: 5f 75 73 65 72 20 7b 0a 09 09 09 66 69 6c 65 20  _user {....file 
56f0: 6d 6b 64 69 72 20 24 77 68 69 74 65 6f 75 74 64  mkdir $whiteoutd
5700: 69 72 0a 09 09 09 63 6c 6f 73 65 20 5b 6f 70 65  ir....close [ope
5710: 6e 20 24 77 68 69 74 65 6f 75 74 66 69 6c 65 20  n $whiteoutfile 
5720: 77 5d 0a 09 09 7d 0a 09 7d 0a 7d 0a              w]...}..}.}.