Hex Artifact Content

Artifact f5542ce16c33d7cbfc3535dd84e32bb476348351:


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 69  ...::appfs::geti
3400: 6e 64 65 78 20 24 70 61 74 68 69 6e 66 6f 28 68  ndex $pathinfo(h
3410: 6f 73 74 6e 61 6d 65 29 0a 09 09 09 09 09 3a 3a  ostname)......::
3420: 61 70 70 66 73 3a 3a 67 65 74 70 6b 67 6d 61 6e  appfs::getpkgman
3430: 69 66 65 73 74 20 24 70 61 74 68 69 6e 66 6f 28  ifest $pathinfo(
3440: 68 6f 73 74 6e 61 6d 65 29 20 24 70 61 74 68 69  hostname) $pathi
3450: 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73 68 61 31  nfo(package_sha1
3460: 29 0a 09 09 09 09 7d 0a 0a 09 09 09 09 73 65 74  ).....}......set
3470: 20 72 65 74 76 61 6c 20 5b 3a 3a 61 70 70 66 73   retval [::appfs
3480: 3a 3a 64 62 20 65 76 61 6c 20 7b 53 45 4c 45 43  ::db eval {SELEC
3490: 54 20 44 49 53 54 49 4e 43 54 20 66 69 6c 65 5f  T DISTINCT file_
34a0: 6e 61 6d 65 20 46 52 4f 4d 20 66 69 6c 65 73 20  name FROM files 
34b0: 57 48 45 52 45 20 70 61 63 6b 61 67 65 5f 73 68  WHERE package_sh
34c0: 61 31 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 70  a1 = $pathinfo(p
34d0: 61 63 6b 61 67 65 5f 73 68 61 31 29 20 41 4e 44  ackage_sha1) AND
34e0: 20 66 69 6c 65 5f 64 69 72 65 63 74 6f 72 79 20   file_directory 
34f0: 3d 20 24 70 61 74 68 69 6e 66 6f 28 66 69 6c 65  = $pathinfo(file
3500: 29 3b 7d 5d 0a 0a 09 09 09 09 69 66 20 7b 5b 69  );}]......if {[i
3510: 6e 66 6f 20 65 78 69 73 74 73 20 70 61 74 68 69  nfo exists pathi
3520: 6e 66 6f 28 70 61 63 6b 61 67 65 29 5d 20 26 26  nfo(package)] &&
3530: 20 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 70 61   [info exists pa
3540: 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29  thinfo(hostname)
3550: 5d 20 26 26 20 5b 69 6e 66 6f 20 65 78 69 73 74  ] && [info exist
3560: 73 20 70 61 74 68 69 6e 66 6f 28 66 69 6c 65 29  s pathinfo(file)
3570: 5d 7d 20 7b 0a 09 09 09 09 09 5f 61 73 5f 75 73  ]} {......_as_us
3580: 65 72 20 7b 0a 09 09 09 09 09 09 73 65 74 20 64  er {.......set d
3590: 69 72 20 5b 5f 6c 6f 63 61 6c 70 61 74 68 20 24  ir [_localpath $
35a0: 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65  pathinfo(package
35b0: 29 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74  ) $pathinfo(host
35c0: 6e 61 6d 65 29 20 24 70 61 74 68 69 6e 66 6f 28  name) $pathinfo(
35d0: 66 69 6c 65 29 5d 0a 09 09 09 09 09 09 73 65 74  file)].......set
35e0: 20 77 68 69 74 65 6f 75 74 64 69 72 20 5b 73 74   whiteoutdir [st
35f0: 72 69 6e 67 20 72 61 6e 67 65 20 5b 5f 77 68 69  ring range [_whi
3600: 74 65 6f 75 74 70 61 74 68 20 24 70 61 74 68 69  teoutpath $pathi
3610: 6e 66 6f 28 70 61 63 6b 61 67 65 29 20 24 70 61  nfo(package) $pa
3620: 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29  thinfo(hostname)
3630: 20 24 70 61 74 68 69 6e 66 6f 28 66 69 6c 65 29   $pathinfo(file)
3640: 5d 20 30 20 65 6e 64 2d 31 35 5d 0a 0a 09 09 09  ] 0 end-15].....
3650: 09 09 09 69 66 20 7b 24 77 68 69 74 65 6f 75 74  ...if {$whiteout
3660: 64 69 72 20 21 3d 20 22 22 7d 20 7b 0a 09 09 09  dir != ""} {....
3670: 09 09 09 09 66 6f 72 65 61 63 68 20 66 69 6c 65  ....foreach file
3680: 20 5b 67 6c 6f 62 20 2d 6e 6f 63 6f 6d 70 6c 61   [glob -nocompla
3690: 69 6e 20 2d 74 61 69 6c 73 20 2d 64 69 72 65 63  in -tails -direc
36a0: 74 6f 72 79 20 24 77 68 69 74 65 6f 75 74 64 69  tory $whiteoutdi
36b0: 72 20 7b 7b 2e 2c 7d 2a 2e 41 50 50 46 53 2e 57  r {{.,}*.APPFS.W
36c0: 48 49 54 45 4f 55 54 7d 5d 20 7b 0a 09 09 09 09  HITEOUT}] {.....
36d0: 09 09 09 09 73 65 74 20 72 65 6d 6f 76 65 20 5b  ....set remove [
36e0: 73 74 72 69 6e 67 20 72 61 6e 67 65 20 24 66 69  string range $fi
36f0: 6c 65 20 30 20 65 6e 64 2d 31 35 5d 0a 09 09 09  le 0 end-15]....
3700: 09 09 09 09 09 73 65 74 20 69 64 78 20 5b 6c 73  .....set idx [ls
3710: 65 61 72 63 68 20 2d 65 78 61 63 74 20 24 72 65  earch -exact $re
3720: 74 76 61 6c 20 24 72 65 6d 6f 76 65 5d 0a 09 09  tval $remove]...
3730: 09 09 09 09 09 09 69 66 20 7b 24 69 64 78 20 21  ......if {$idx !
3740: 3d 20 2d 31 7d 20 7b 0a 09 09 09 09 09 09 09 09  = -1} {.........
3750: 09 73 65 74 20 72 65 74 76 61 6c 20 5b 6c 72 65  .set retval [lre
3760: 70 6c 61 63 65 20 24 72 65 74 76 61 6c 20 24 69  place $retval $i
3770: 64 78 20 24 69 64 78 5d 0a 09 09 09 09 09 09 09  dx $idx]........
3780: 09 7d 0a 09 09 09 09 09 09 09 7d 0a 09 09 09 09  .}........}.....
3790: 09 09 7d 0a 0a 09 09 09 09 09 09 69 66 20 7b 24  ..}........if {$
37a0: 64 69 72 20 21 3d 20 22 22 7d 20 7b 0a 09 09 09  dir != ""} {....
37b0: 09 09 09 09 66 6f 72 65 61 63 68 20 66 69 6c 65  ....foreach file
37c0: 20 5b 67 6c 6f 62 20 2d 6e 6f 63 6f 6d 70 6c 61   [glob -nocompla
37d0: 69 6e 20 2d 74 61 69 6c 73 20 2d 64 69 72 65 63  in -tails -direc
37e0: 74 6f 72 79 20 24 64 69 72 20 7b 7b 2e 2c 7d 2a  tory $dir {{.,}*
37f0: 7d 5d 20 7b 0a 09 09 09 09 09 09 09 09 69 66 20  }] {.........if 
3800: 7b 24 66 69 6c 65 20 3d 3d 20 22 2e 22 20 7c 7c  {$file == "." ||
3810: 20 24 66 69 6c 65 20 3d 3d 20 22 2e 2e 22 7d 20   $file == ".."} 
3820: 7b 0a 09 09 09 09 09 09 09 09 09 63 6f 6e 74 69  {..........conti
3830: 6e 75 65 0a 09 09 09 09 09 09 09 09 7d 0a 0a 09  nue.........}...
3840: 09 09 09 09 09 09 09 69 66 20 7b 24 66 69 6c 65  .......if {$file
3850: 20 3d 3d 20 22 2e 41 50 50 46 53 2e 57 48 49 54   == ".APPFS.WHIT
3860: 45 4f 55 54 22 7d 20 7b 0a 09 09 09 09 09 09 09  EOUT"} {........
3870: 09 09 63 6f 6e 74 69 6e 75 65 0a 09 09 09 09 09  ..continue......
3880: 09 09 09 7d 0a 0a 09 09 09 09 09 09 09 09 69 66  ...}..........if
3890: 20 7b 5b 6c 73 65 61 72 63 68 20 2d 65 78 61 63   {[lsearch -exac
38a0: 74 20 24 72 65 74 76 61 6c 20 24 66 69 6c 65 5d  t $retval $file]
38b0: 20 21 3d 20 2d 31 7d 20 7b 0a 09 09 09 09 09 09   != -1} {.......
38c0: 09 09 09 63 6f 6e 74 69 6e 75 65 0a 09 09 09 09  ...continue.....
38d0: 09 09 09 09 7d 0a 0a 09 09 09 09 09 09 09 09 6c  ....}..........l
38e0: 61 70 70 65 6e 64 20 72 65 74 76 61 6c 20 24 66  append retval $f
38f0: 69 6c 65 0a 09 09 09 09 09 09 09 7d 0a 09 09 09  ile........}....
3900: 09 09 09 7d 0a 09 09 09 09 09 7d 0a 09 09 09 09  ...}......}.....
3910: 7d 0a 0a 09 09 09 09 72 65 74 75 72 6e 20 24 72  }......return $r
3920: 65 74 76 61 6c 0a 09 09 09 7d 0a 09 09 7d 0a 0a  etval....}...}..
3930: 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65  ..return -code e
3940: 72 72 6f 72 20 22 49 6e 76 61 6c 69 64 20 6f 72  rror "Invalid or
3950: 20 75 6e 61 63 63 65 70 74 61 62 6c 65 20 70 61   unacceptable pa
3960: 74 68 3a 20 24 64 69 72 22 0a 09 7d 0a 0a 09 70  th: $dir"..}...p
3970: 72 6f 63 20 67 65 74 61 74 74 72 20 7b 70 61 74  roc getattr {pat
3980: 68 7d 20 7b 0a 09 09 61 72 72 61 79 20 73 65 74  h} {...array set
3990: 20 70 61 74 68 69 6e 66 6f 20 5b 5f 70 61 72 73   pathinfo [_pars
39a0: 65 70 61 74 68 20 24 70 61 74 68 5d 0a 09 09 61  epath $path]...a
39b0: 72 72 61 79 20 73 65 74 20 72 65 74 76 61 6c 20  rray set retval 
39c0: 5b 6c 69 73 74 5d 0a 0a 09 09 63 61 74 63 68 20  [list]....catch 
39d0: 7b 0a 09 09 09 3a 3a 61 70 70 66 73 3a 3a 67 65  {....::appfs::ge
39e0: 74 69 6e 64 65 78 20 24 70 61 74 68 69 6e 66 6f  tindex $pathinfo
39f0: 28 68 6f 73 74 6e 61 6d 65 29 0a 09 09 09 3a 3a  (hostname)....::
3a00: 61 70 70 66 73 3a 3a 67 65 74 70 6b 67 6d 61 6e  appfs::getpkgman
3a10: 69 66 65 73 74 20 24 70 61 74 68 69 6e 66 6f 28  ifest $pathinfo(
3a20: 68 6f 73 74 6e 61 6d 65 29 20 24 70 61 74 68 69  hostname) $pathi
3a30: 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73 68 61 31  nfo(package_sha1
3a40: 29 0a 09 09 7d 0a 0a 09 09 73 77 69 74 63 68 20  )...}....switch 
3a50: 2d 2d 20 24 70 61 74 68 69 6e 66 6f 28 5f 74 79  -- $pathinfo(_ty
3a60: 70 65 29 20 7b 0a 09 09 09 22 74 6f 70 6c 65 76  pe) {...."toplev
3a70: 65 6c 22 20 7b 0a 09 09 09 09 73 65 74 20 72 65  el" {.....set re
3a80: 74 76 61 6c 28 74 79 70 65 29 20 64 69 72 65 63  tval(type) direc
3a90: 74 6f 72 79 0a 09 09 09 09 73 65 74 20 72 65 74  tory.....set ret
3aa0: 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29 20  val(childcount) 
3ab0: 5b 6c 6c 65 6e 67 74 68 20 5b 67 65 74 63 68 69  [llength [getchi
3ac0: 6c 64 72 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09  ldren $path]]...
3ad0: 09 7d 0a 09 09 09 22 73 69 74 65 73 22 20 7b 0a  .}...."sites" {.
3ae0: 09 09 09 09 73 65 74 20 63 68 65 63 6b 20 5b 3a  ....set check [:
3af0: 3a 61 70 70 66 73 3a 3a 64 62 20 6f 6e 65 63 6f  :appfs::db oneco
3b00: 6c 75 6d 6e 20 7b 53 45 4c 45 43 54 20 31 20 46  lumn {SELECT 1 F
3b10: 52 4f 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45  ROM packages WHE
3b20: 52 45 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 70  RE hostname = $p
3b30: 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65  athinfo(hostname
3b40: 29 3b 7d 5d 0a 09 09 09 09 69 66 20 7b 24 63 68  );}].....if {$ch
3b50: 65 63 6b 20 3d 3d 20 22 31 22 7d 20 7b 0a 09 09  eck == "1"} {...
3b60: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 74 79  ...set retval(ty
3b70: 70 65 29 20 64 69 72 65 63 74 6f 72 79 0a 09 09  pe) directory...
3b80: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 63 68  ...set retval(ch
3b90: 69 6c 64 63 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67  ildcount) [lleng
3ba0: 74 68 20 5b 67 65 74 63 68 69 6c 64 72 65 6e 20  th [getchildren 
3bb0: 24 70 61 74 68 5d 5d 0a 09 09 09 09 7d 0a 09 09  $path]].....}...
3bc0: 09 7d 0a 09 09 09 22 70 61 63 6b 61 67 65 73 22  .}...."packages"
3bd0: 20 7b 0a 09 09 09 09 73 65 74 20 63 68 65 63 6b   {.....set check
3be0: 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20 6f 6e   [::appfs::db on
3bf0: 65 63 6f 6c 75 6d 6e 20 7b 53 45 4c 45 43 54 20  ecolumn {SELECT 
3c00: 31 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73 20  1 FROM packages 
3c10: 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65 20 3d  WHERE hostname =
3c20: 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e   $pathinfo(hostn
3c30: 61 6d 65 29 20 41 4e 44 20 70 61 63 6b 61 67 65  ame) AND package
3c40: 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63   = $pathinfo(pac
3c50: 6b 61 67 65 29 3b 7d 5d 0a 09 09 09 09 69 66 20  kage);}].....if 
3c60: 7b 24 63 68 65 63 6b 20 3d 3d 20 22 31 22 7d 20  {$check == "1"} 
3c70: 7b 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61  {......set retva
3c80: 6c 28 74 79 70 65 29 20 64 69 72 65 63 74 6f 72  l(type) director
3c90: 79 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61  y......set retva
3ca0: 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29 20 5b 6c  l(childcount) [l
3cb0: 6c 65 6e 67 74 68 20 5b 67 65 74 63 68 69 6c 64  length [getchild
3cc0: 72 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09 09 09  ren $path]].....
3cd0: 7d 0a 09 09 09 7d 0a 09 09 09 22 6f 73 2d 63 70  }....}...."os-cp
3ce0: 75 22 20 7b 0a 09 09 09 09 69 66 20 7b 24 70 61  u" {.....if {$pa
3cf0: 74 68 69 6e 66 6f 28 6f 73 29 20 3d 3d 20 22 70  thinfo(os) == "p
3d00: 6c 61 74 66 6f 72 6d 22 20 26 26 20 24 70 61 74  latform" && $pat
3d10: 68 69 6e 66 6f 28 63 70 75 29 20 3d 3d 20 22 22  hinfo(cpu) == ""
3d20: 7d 20 7b 0a 09 09 09 09 09 73 65 74 20 72 65 74  } {......set ret
3d30: 76 61 6c 28 74 79 70 65 29 20 73 79 6d 6c 69 6e  val(type) symlin
3d40: 6b 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61  k......set retva
3d50: 6c 28 73 6f 75 72 63 65 29 20 5b 70 6c 61 74 66  l(source) [platf
3d60: 6f 72 6d 3a 3a 67 65 6e 65 72 69 63 5d 0a 09 09  orm::generic]...
3d70: 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09 09  ..} else {......
3d80: 73 65 74 20 63 68 65 63 6b 20 5b 3a 3a 61 70 70  set check [::app
3d90: 66 73 3a 3a 64 62 20 6f 6e 65 63 6f 6c 75 6d 6e  fs::db onecolumn
3da0: 20 7b 0a 09 09 09 09 09 09 53 45 4c 45 43 54 20   {.......SELECT 
3db0: 31 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73 20  1 FROM packages 
3dc0: 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65 20 3d  WHERE hostname =
3dd0: 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e   $pathinfo(hostn
3de0: 61 6d 65 29 20 41 4e 44 20 70 61 63 6b 61 67 65  ame) AND package
3df0: 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63   = $pathinfo(pac
3e00: 6b 61 67 65 29 20 41 4e 44 20 6f 73 20 3d 20 24  kage) AND os = $
3e10: 70 61 74 68 69 6e 66 6f 28 6f 73 29 20 41 4e 44  pathinfo(os) AND
3e20: 20 63 70 75 41 72 63 68 20 3d 20 24 70 61 74 68   cpuArch = $path
3e30: 69 6e 66 6f 28 63 70 75 29 3b 0a 09 09 09 09 09  info(cpu);......
3e40: 7d 5d 0a 09 09 09 09 09 69 66 20 7b 24 63 68 65  }]......if {$che
3e50: 63 6b 20 3d 3d 20 22 31 22 7d 20 7b 0a 09 09 09  ck == "1"} {....
3e60: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 74 79  ...set retval(ty
3e70: 70 65 29 20 64 69 72 65 63 74 6f 72 79 0a 09 09  pe) directory...
3e80: 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 63  ....set retval(c
3e90: 68 69 6c 64 63 6f 75 6e 74 29 20 5b 6c 6c 65 6e  hildcount) [llen
3ea0: 67 74 68 20 5b 67 65 74 63 68 69 6c 64 72 65 6e  gth [getchildren
3eb0: 20 24 70 61 74 68 5d 5d 0a 09 09 09 09 09 7d 0a   $path]]......}.
3ec0: 09 09 09 09 7d 0a 09 09 09 7d 0a 09 09 09 22 76  ....}....}...."v
3ed0: 65 72 73 69 6f 6e 73 22 20 7b 0a 09 09 09 09 69  ersions" {.....i
3ee0: 66 20 7b 24 70 61 74 68 69 6e 66 6f 28 76 65 72  f {$pathinfo(ver
3ef0: 73 69 6f 6e 29 20 3d 3d 20 22 6c 61 74 65 73 74  sion) == "latest
3f00: 22 7d 20 7b 0a 09 09 09 09 09 3a 3a 61 70 70 66  "} {......::appf
3f10: 73 3a 3a 64 62 20 65 76 61 6c 20 7b 53 45 4c 45  s::db eval {SELE
3f20: 43 54 20 76 65 72 73 69 6f 6e 20 46 52 4f 4d 20  CT version FROM 
3f30: 70 61 63 6b 61 67 65 73 20 57 48 45 52 45 20 69  packages WHERE i
3f40: 73 4c 61 74 65 73 74 20 3d 20 31 20 41 4e 44 20  sLatest = 1 AND 
3f50: 68 6f 73 74 6e 61 6d 65 20 3d 20 24 70 61 74 68  hostname = $path
3f60: 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 41  info(hostname) A
3f70: 4e 44 20 70 61 63 6b 61 67 65 20 3d 20 24 70 61  ND package = $pa
3f80: 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 20  thinfo(package) 
3f90: 41 4e 44 20 6f 73 20 3d 20 24 70 61 74 68 69 6e  AND os = $pathin
3fa0: 66 6f 28 6f 73 29 20 41 4e 44 20 63 70 75 41 72  fo(os) AND cpuAr
3fb0: 63 68 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 63  ch = $pathinfo(c
3fc0: 70 75 29 20 4c 49 4d 49 54 20 31 3b 7d 20 6c 61  pu) LIMIT 1;} la
3fd0: 74 65 73 74 5f 69 6e 66 6f 20 7b 7d 0a 0a 09 09  test_info {}....
3fe0: 09 09 09 69 66 20 7b 5b 69 6e 66 6f 20 65 78 69  ...if {[info exi
3ff0: 73 74 73 20 6c 61 74 65 73 74 5f 69 6e 66 6f 28  sts latest_info(
4000: 76 65 72 73 69 6f 6e 29 5d 7d 20 7b 0a 09 09 09  version)]} {....
4010: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 74 79  ...set retval(ty
4020: 70 65 29 20 73 79 6d 6c 69 6e 6b 0a 09 09 09 09  pe) symlink.....
4030: 09 09 73 65 74 20 72 65 74 76 61 6c 28 73 6f 75  ..set retval(sou
4040: 72 63 65 29 20 24 6c 61 74 65 73 74 5f 69 6e 66  rce) $latest_inf
4050: 6f 28 76 65 72 73 69 6f 6e 29 0a 09 09 09 09 09  o(version)......
4060: 7d 0a 09 09 09 09 7d 20 65 6c 73 65 20 7b 0a 09  }.....} else {..
4070: 09 09 09 09 69 66 20 7b 5b 69 6e 66 6f 20 65 78  ....if {[info ex
4080: 69 73 74 73 20 70 61 74 68 69 6e 66 6f 28 70 61  ists pathinfo(pa
4090: 63 6b 61 67 65 5f 73 68 61 31 29 5d 20 26 26 20  ckage_sha1)] && 
40a0: 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67  $pathinfo(packag
40b0: 65 5f 73 68 61 31 29 20 21 3d 20 22 22 7d 20 7b  e_sha1) != ""} {
40c0: 0a 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61  .......set retva
40d0: 6c 28 74 79 70 65 29 20 64 69 72 65 63 74 6f 72  l(type) director
40e0: 79 0a 09 09 09 09 09 09 73 65 74 20 72 65 74 76  y.......set retv
40f0: 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29 20 5b  al(childcount) [
4100: 6c 6c 65 6e 67 74 68 20 5b 67 65 74 63 68 69 6c  llength [getchil
4110: 64 72 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09 09  dren $path]]....
4120: 09 09 7d 0a 09 09 09 09 7d 0a 09 09 09 7d 0a 09  ..}.....}....}..
4130: 09 09 22 66 69 6c 65 73 22 20 7b 0a 09 09 09 09  .."files" {.....
4140: 73 65 74 20 72 65 74 76 61 6c 28 70 61 63 6b 61  set retval(packa
4150: 67 65 64 29 20 31 0a 0a 09 09 09 09 73 65 74 20  ged) 1......set 
4160: 6c 6f 63 61 6c 70 61 74 68 20 5b 5f 6c 6f 63 61  localpath [_loca
4170: 6c 70 61 74 68 20 24 70 61 74 68 69 6e 66 6f 28  lpath $pathinfo(
4180: 70 61 63 6b 61 67 65 29 20 24 70 61 74 68 69 6e  package) $pathin
4190: 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24 70 61  fo(hostname) $pa
41a0: 74 68 69 6e 66 6f 28 66 69 6c 65 29 5d 0a 09 09  thinfo(file)]...
41b0: 09 09 73 65 74 20 77 68 69 74 65 6f 75 74 70 61  ..set whiteoutpa
41c0: 74 68 20 20 5b 5f 77 68 69 74 65 6f 75 74 70 61  th  [_whiteoutpa
41d0: 74 68 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63  th $pathinfo(pac
41e0: 6b 61 67 65 29 20 24 70 61 74 68 69 6e 66 6f 28  kage) $pathinfo(
41f0: 68 6f 73 74 6e 61 6d 65 29 20 24 70 61 74 68 69  hostname) $pathi
4200: 6e 66 6f 28 66 69 6c 65 29 5d 0a 0a 09 09 09 09  nfo(file)]......
4210: 73 65 74 20 72 65 74 76 61 6c 28 6c 6f 63 61 6c  set retval(local
4220: 70 61 74 68 29 20 24 6c 6f 63 61 6c 70 61 74 68  path) $localpath
4230: 0a 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
4240: 77 68 69 74 65 6f 75 74 70 61 74 68 29 20 24 77  whiteoutpath) $w
4250: 68 69 74 65 6f 75 74 70 61 74 68 0a 0a 09 09 09  hiteoutpath.....
4260: 09 69 66 20 7b 24 6c 6f 63 61 6c 70 61 74 68 20  .if {$localpath 
4270: 21 3d 20 22 22 20 26 26 20 5b 66 69 6c 65 20 65  != "" && [file e
4280: 78 69 73 74 73 20 24 6c 6f 63 61 6c 70 61 74 68  xists $localpath
4290: 5d 7d 20 7b 0a 09 09 09 09 09 73 65 74 20 72 65  ]} {......set re
42a0: 74 76 61 6c 28 69 73 5f 6c 6f 63 61 6c 66 69 6c  tval(is_localfil
42b0: 65 29 20 31 0a 09 09 09 09 09 63 61 74 63 68 20  e) 1......catch 
42c0: 7b 0a 09 09 09 09 09 09 5f 61 73 5f 75 73 65 72  {......._as_user
42d0: 20 7b 0a 09 09 09 09 09 09 09 66 69 6c 65 20 6c   {........file l
42e0: 73 74 61 74 20 24 6c 6f 63 61 6c 70 61 74 68 20  stat $localpath 
42f0: 6c 6f 63 61 6c 70 61 74 68 69 6e 66 6f 0a 09 09  localpathinfo...
4300: 09 09 09 09 7d 0a 09 09 09 09 09 09 73 65 74 20  ....}.......set 
4310: 72 65 74 76 61 6c 28 74 69 6d 65 29 20 24 6c 6f  retval(time) $lo
4320: 63 61 6c 70 61 74 68 69 6e 66 6f 28 6d 74 69 6d  calpathinfo(mtim
4330: 65 29 0a 0a 09 09 09 09 09 09 73 77 69 74 63 68  e)........switch
4340: 20 2d 2d 20 24 6c 6f 63 61 6c 70 61 74 68 69 6e   -- $localpathin
4350: 66 6f 28 74 79 70 65 29 20 7b 0a 09 09 09 09 09  fo(type) {......
4360: 09 09 22 64 69 72 65 63 74 6f 72 79 22 20 7b 0a  .."directory" {.
4370: 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ........set retv
4380: 61 6c 28 74 79 70 65 29 20 22 64 69 72 65 63 74  al(type) "direct
4390: 6f 72 79 22 0a 09 09 09 09 09 09 09 09 73 65 74  ory".........set
43a0: 20 72 65 74 76 61 6c 28 63 68 69 6c 64 63 6f 75   retval(childcou
43b0: 6e 74 29 20 5b 6c 6c 65 6e 67 74 68 20 5b 67 65  nt) [llength [ge
43c0: 74 63 68 69 6c 64 72 65 6e 20 24 70 61 74 68 5d  tchildren $path]
43d0: 5d 0a 09 09 09 09 09 09 09 7d 0a 09 09 09 09 09  ]........}......
43e0: 09 09 22 66 69 6c 65 22 20 7b 0a 09 09 09 09 09  .."file" {......
43f0: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 74 79  ...set retval(ty
4400: 70 65 29 20 22 66 69 6c 65 22 0a 09 09 09 09 09  pe) "file"......
4410: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 73 69  ...set retval(si
4420: 7a 65 29 20 24 6c 6f 63 61 6c 70 61 74 68 69 6e  ze) $localpathin
4430: 66 6f 28 73 69 7a 65 29 0a 09 09 09 09 09 09 09  fo(size)........
4440: 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09 09 09 09  ._as_user {.....
4450: 09 09 09 09 09 69 66 20 7b 5b 66 69 6c 65 20 65  .....if {[file e
4460: 78 65 63 75 74 61 62 6c 65 20 24 6c 6f 63 61 6c  xecutable $local
4470: 70 61 74 68 5d 7d 20 7b 0a 09 09 09 09 09 09 09  path]} {........
4480: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 70 65  ...set retval(pe
4490: 72 6d 73 29 20 22 78 22 0a 09 09 09 09 09 09 09  rms) "x"........
44a0: 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09 09  ..} else {......
44b0: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
44c0: 70 65 72 6d 73 29 20 22 22 0a 09 09 09 09 09 09  perms) "".......
44d0: 09 09 09 7d 0a 09 09 09 09 09 09 09 09 7d 0a 09  ...}.........}..
44e0: 09 09 09 09 09 09 7d 0a 09 09 09 09 09 09 09 22  ......}........"
44f0: 6c 69 6e 6b 22 20 7b 0a 09 09 09 09 09 09 09 09  link" {.........
4500: 73 65 74 20 72 65 74 76 61 6c 28 74 79 70 65 29  set retval(type)
4510: 20 22 73 79 6d 6c 69 6e 6b 22 0a 0a 09 09 09 09   "symlink"......
4520: 09 09 09 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09  ...._as_user {..
4530: 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ........set retv
4540: 61 6c 28 73 6f 75 72 63 65 29 20 5b 66 69 6c 65  al(source) [file
4550: 20 72 65 61 64 6c 69 6e 6b 20 24 6c 6f 63 61 6c   readlink $local
4560: 70 61 74 68 5d 0a 09 09 09 09 09 09 09 09 7d 0a  path].........}.
4570: 09 09 09 09 09 09 09 7d 0a 09 09 09 09 09 09 09  .......}........
4580: 22 66 69 66 6f 22 20 7b 0a 09 09 09 09 09 09 09  "fifo" {........
4590: 09 23 20 43 61 70 69 74 61 6c 69 7a 65 64 20 73  .# Capitalized s
45a0: 6f 20 74 68 61 74 20 74 68 65 20 66 69 72 73 74  o that the first
45b0: 20 63 68 61 72 20 69 73 20 75 6e 69 71 75 65 0a   char is unique.
45c0: 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ........set retv
45d0: 61 6c 28 74 79 70 65 29 20 22 46 69 66 6f 22 0a  al(type) "Fifo".
45e0: 09 09 09 09 09 09 09 7d 0a 09 09 09 09 09 09 09  .......}........
45f0: 22 73 6f 63 6b 65 74 22 20 7b 0a 09 09 09 09 09  "socket" {......
4600: 09 09 09 23 20 43 61 70 69 74 61 6c 69 7a 65 64  ...# Capitalized
4610: 20 73 6f 20 74 68 61 74 20 74 68 65 20 66 69 72   so that the fir
4620: 73 74 20 63 68 61 72 20 69 73 20 75 6e 69 71 75  st char is uniqu
4630: 65 0a 09 09 09 09 09 09 09 09 73 65 74 20 72 65  e.........set re
4640: 74 76 61 6c 28 74 79 70 65 29 20 22 53 6f 63 6b  tval(type) "Sock
4650: 65 74 22 0a 09 09 09 09 09 09 09 7d 0a 09 09 09  et"........}....
4660: 09 09 09 7d 0a 09 09 09 09 09 7d 20 65 72 72 0a  ...}......} err.
4670: 09 09 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09  ....} else {....
4680: 09 09 69 66 20 7b 24 77 68 69 74 65 6f 75 74 70  ..if {$whiteoutp
4690: 61 74 68 20 3d 3d 20 22 22 20 7c 7c 20 21 5b 66  ath == "" || ![f
46a0: 69 6c 65 20 65 78 69 73 74 73 20 24 77 68 69 74  ile exists $whit
46b0: 65 6f 75 74 70 61 74 68 5d 7d 20 7b 0a 09 09 09  eoutpath]} {....
46c0: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 69 73  ...set retval(is
46d0: 5f 72 65 6d 6f 74 65 66 69 6c 65 29 20 31 0a 0a  _remotefile) 1..
46e0: 09 09 09 09 09 09 73 65 74 20 77 6f 72 6b 20 5b  ......set work [
46f0: 73 70 6c 69 74 20 24 70 61 74 68 69 6e 66 6f 28  split $pathinfo(
4700: 66 69 6c 65 29 20 22 2f 22 5d 0a 09 09 09 09 09  file) "/"]......
4710: 09 73 65 74 20 64 69 72 65 63 74 6f 72 79 20 5b  .set directory [
4720: 6a 6f 69 6e 20 5b 6c 72 61 6e 67 65 20 24 77 6f  join [lrange $wo
4730: 72 6b 20 30 20 65 6e 64 2d 31 5d 20 22 2f 22 5d  rk 0 end-1] "/"]
4740: 0a 09 09 09 09 09 09 73 65 74 20 66 69 6c 65 20  .......set file 
4750: 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 65 6e  [lindex $work en
4760: 64 5d 0a 0a 09 09 09 09 09 09 69 66 20 7b 24 64  d]........if {$d
4770: 69 72 65 63 74 6f 72 79 20 3d 3d 20 22 22 20 26  irectory == "" &
4780: 26 20 24 66 69 6c 65 20 3d 3d 20 22 22 7d 20 7b  & $file == ""} {
4790: 0a 09 09 09 09 09 09 09 61 72 72 61 79 20 73 65  ........array se
47a0: 74 20 72 65 74 76 61 6c 20 5b 6c 69 73 74 20 74  t retval [list t
47b0: 79 70 65 20 64 69 72 65 63 74 6f 72 79 5d 0a 09  ype directory]..
47c0: 09 09 09 09 09 7d 0a 0a 09 09 09 09 09 09 3a 3a  .....}........::
47d0: 61 70 70 66 73 3a 3a 64 62 20 65 76 61 6c 20 7b  appfs::db eval {
47e0: 53 45 4c 45 43 54 20 74 79 70 65 2c 20 74 69 6d  SELECT type, tim
47f0: 65 2c 20 73 6f 75 72 63 65 2c 20 73 69 7a 65 2c  e, source, size,
4800: 20 70 65 72 6d 73 20 46 52 4f 4d 20 66 69 6c 65   perms FROM file
4810: 73 20 57 48 45 52 45 20 70 61 63 6b 61 67 65 5f  s WHERE package_
4820: 73 68 61 31 20 3d 20 24 70 61 74 68 69 6e 66 6f  sha1 = $pathinfo
4830: 28 70 61 63 6b 61 67 65 5f 73 68 61 31 29 20 41  (package_sha1) A
4840: 4e 44 20 66 69 6c 65 5f 64 69 72 65 63 74 6f 72  ND file_director
4850: 79 20 3d 20 24 64 69 72 65 63 74 6f 72 79 20 41  y = $directory A
4860: 4e 44 20 66 69 6c 65 5f 6e 61 6d 65 20 3d 20 24  ND file_name = $
4870: 66 69 6c 65 3b 7d 20 72 65 74 76 61 6c 20 7b 7d  file;} retval {}
4880: 0a 0a 09 09 09 09 09 09 69 66 20 7b 5b 69 6e 66  ........if {[inf
4890: 6f 20 65 78 69 73 74 73 20 72 65 74 76 61 6c 28  o exists retval(
48a0: 74 79 70 65 29 5d 20 26 26 20 24 72 65 74 76 61  type)] && $retva
48b0: 6c 28 74 79 70 65 29 20 3d 3d 20 22 64 69 72 65  l(type) == "dire
48c0: 63 74 6f 72 79 22 7d 20 7b 0a 09 09 09 09 09 09  ctory"} {.......
48d0: 09 73 65 74 20 72 65 74 76 61 6c 28 63 68 69 6c  .set retval(chil
48e0: 64 63 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67 74 68  dcount) [llength
48f0: 20 5b 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70   [getchildren $p
4900: 61 74 68 5d 5d 0a 09 09 09 09 09 09 7d 0a 0a 09  ath]].......}...
4910: 09 09 09 09 09 75 6e 73 65 74 20 2d 6e 6f 63 6f  .....unset -noco
4920: 6d 70 6c 61 69 6e 20 72 65 74 76 61 6c 28 2a 29  mplain retval(*)
4930: 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 0a 0a 09  ......}.....}...
4940: 09 09 7d 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 21  ..}...}....if {!
4950: 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 72 65 74  [info exists ret
4960: 76 61 6c 28 74 79 70 65 29 5d 7d 20 7b 0a 09 09  val(type)]} {...
4970: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72  .return -code er
4980: 72 6f 72 20 22 4e 6f 20 73 75 63 68 20 66 69 6c  ror "No such fil
4990: 65 20 6f 72 20 64 69 72 65 63 74 6f 72 79 22 0a  e or directory".
49a0: 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e 20 5b 61  ..}....return [a
49b0: 72 72 61 79 20 67 65 74 20 72 65 74 76 61 6c 5d  rray get retval]
49c0: 0a 09 7d 0a 0a 09 70 72 6f 63 20 6f 70 65 6e 70  ..}...proc openp
49d0: 61 74 68 20 7b 70 61 74 68 20 6d 6f 64 65 7d 20  ath {path mode} 
49e0: 7b 0a 09 09 61 72 72 61 79 20 73 65 74 20 70 61  {...array set pa
49f0: 74 68 69 6e 66 6f 20 5b 5f 70 61 72 73 65 70 61  thinfo [_parsepa
4a00: 74 68 20 24 70 61 74 68 5d 0a 0a 09 09 69 66 20  th $path]....if 
4a10: 7b 24 70 61 74 68 69 6e 66 6f 28 5f 74 79 70 65  {$pathinfo(_type
4a20: 29 20 21 3d 20 22 66 69 6c 65 73 22 7d 20 7b 0a  ) != "files"} {.
4a30: 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20  ...return -code 
4a40: 65 72 72 6f 72 20 22 69 6e 76 61 6c 69 64 20 74  error "invalid t
4a50: 79 70 65 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20  ype"...}....set 
4a60: 6c 6f 63 61 6c 70 61 74 68 20 5b 5f 6c 6f 63 61  localpath [_loca
4a70: 6c 70 61 74 68 20 24 70 61 74 68 69 6e 66 6f 28  lpath $pathinfo(
4a80: 70 61 63 6b 61 67 65 29 20 24 70 61 74 68 69 6e  package) $pathin
4a90: 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24 70 61  fo(hostname) $pa
4aa0: 74 68 69 6e 66 6f 28 66 69 6c 65 29 5d 0a 0a 09  thinfo(file)]...
4ab0: 09 69 66 20 7b 24 6d 6f 64 65 20 3d 3d 20 22 63  .if {$mode == "c
4ac0: 72 65 61 74 65 22 7d 20 7b 0a 09 09 09 69 66 20  reate"} {....if 
4ad0: 7b 24 6c 6f 63 61 6c 70 61 74 68 20 3d 3d 20 22  {$localpath == "
4ae0: 22 7d 20 7b 0a 09 09 09 09 72 65 74 75 72 6e 20  "} {.....return 
4af0: 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 41 73 6b  -code error "Ask
4b00: 65 64 20 74 6f 20 63 72 65 61 74 65 2c 20 62 75  ed to create, bu
4b10: 74 20 6e 6f 20 68 6f 6d 65 20 64 69 72 65 63 74  t no home direct
4b20: 6f 72 79 2e 22 0a 09 09 09 7d 0a 0a 09 09 09 72  ory."....}.....r
4b30: 65 74 75 72 6e 20 24 6c 6f 63 61 6c 70 61 74 68  eturn $localpath
4b40: 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 24 6c 6f 63  ...}....if {$loc
4b50: 61 6c 70 61 74 68 20 21 3d 20 22 22 20 26 26 20  alpath != "" && 
4b60: 5b 66 69 6c 65 20 65 78 69 73 74 73 20 24 6c 6f  [file exists $lo
4b70: 63 61 6c 70 61 74 68 5d 7d 20 7b 0a 09 09 09 72  calpath]} {....r
4b80: 65 74 75 72 6e 20 24 6c 6f 63 61 6c 70 61 74 68  eturn $localpath
4b90: 0a 09 09 7d 0a 0a 09 09 73 65 74 20 77 6f 72 6b  ...}....set work
4ba0: 20 5b 73 70 6c 69 74 20 24 70 61 74 68 69 6e 66   [split $pathinf
4bb0: 6f 28 66 69 6c 65 29 20 22 2f 22 5d 0a 09 09 73  o(file) "/"]...s
4bc0: 65 74 20 64 69 72 65 63 74 6f 72 79 20 5b 6a 6f  et directory [jo
4bd0: 69 6e 20 5b 6c 72 61 6e 67 65 20 24 77 6f 72 6b  in [lrange $work
4be0: 20 30 20 65 6e 64 2d 31 5d 20 22 2f 22 5d 0a 09   0 end-1] "/"]..
4bf0: 09 73 65 74 20 66 69 6c 65 20 5b 6c 69 6e 64 65  .set file [linde
4c00: 78 20 24 77 6f 72 6b 20 65 6e 64 5d 0a 09 09 3a  x $work end]...:
4c10: 3a 61 70 70 66 73 3a 3a 64 62 20 65 76 61 6c 20  :appfs::db eval 
4c20: 7b 53 45 4c 45 43 54 20 66 69 6c 65 5f 73 68 61  {SELECT file_sha
4c30: 31 2c 20 70 65 72 6d 73 20 46 52 4f 4d 20 66 69  1, perms FROM fi
4c40: 6c 65 73 20 57 48 45 52 45 20 70 61 63 6b 61 67  les WHERE packag
4c50: 65 5f 73 68 61 31 20 3d 20 24 70 61 74 68 69 6e  e_sha1 = $pathin
4c60: 66 6f 28 70 61 63 6b 61 67 65 5f 73 68 61 31 29  fo(package_sha1)
4c70: 20 41 4e 44 20 66 69 6c 65 5f 6e 61 6d 65 20 3d   AND file_name =
4c80: 20 24 66 69 6c 65 20 41 4e 44 20 66 69 6c 65 5f   $file AND file_
4c90: 64 69 72 65 63 74 6f 72 79 20 3d 20 24 64 69 72  directory = $dir
4ca0: 65 63 74 6f 72 79 3b 7d 20 70 6b 67 70 61 74 68  ectory;} pkgpath
4cb0: 69 6e 66 6f 20 7b 7d 0a 0a 09 09 69 66 20 7b 24  info {}....if {$
4cc0: 70 6b 67 70 61 74 68 69 6e 66 6f 28 66 69 6c 65  pkgpathinfo(file
4cd0: 5f 73 68 61 31 29 20 3d 3d 20 22 22 7d 20 7b 0a  _sha1) == ""} {.
4ce0: 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20  ...return -code 
4cf0: 65 72 72 6f 72 20 22 4e 6f 20 73 75 63 68 20 66  error "No such f
4d00: 69 6c 65 20 6f 72 20 64 69 72 65 63 74 6f 72 79  ile or directory
4d10: 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20 6c 6f 63  "...}....set loc
4d20: 61 6c 63 61 63 68 65 66 69 6c 65 20 5b 64 6f 77  alcachefile [dow
4d30: 6e 6c 6f 61 64 20 24 70 61 74 68 69 6e 66 6f 28  nload $pathinfo(
4d40: 68 6f 73 74 6e 61 6d 65 29 20 24 70 6b 67 70 61  hostname) $pkgpa
4d50: 74 68 69 6e 66 6f 28 66 69 6c 65 5f 73 68 61 31  thinfo(file_sha1
4d60: 29 5d 0a 0a 09 09 69 66 20 7b 24 6d 6f 64 65 20  )]....if {$mode 
4d70: 3d 3d 20 22 77 72 69 74 65 22 7d 20 7b 0a 09 09  == "write"} {...
4d80: 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09 09 09 09  ._as_user {.....
4d90: 73 65 74 20 74 6d 70 6c 6f 63 61 6c 70 61 74 68  set tmplocalpath
4da0: 20 22 24 7b 6c 6f 63 61 6c 70 61 74 68 7d 2e 5b   "${localpath}.[
4db0: 65 78 70 72 20 72 61 6e 64 28 29 5d 5b 63 6c 6f  expr rand()][clo
4dc0: 63 6b 20 63 6c 69 63 6b 73 5d 22 0a 0a 09 09 09  ck clicks]".....
4dd0: 09 73 65 74 20 66 61 69 6c 65 64 20 30 0a 09 09  .set failed 0...
4de0: 09 09 69 66 20 7b 5b 63 61 74 63 68 20 7b 0a 09  ..if {[catch {..
4df0: 09 09 09 09 66 69 6c 65 20 6d 6b 64 69 72 20 5b  ....file mkdir [
4e00: 66 69 6c 65 20 64 69 72 6e 61 6d 65 20 24 6c 6f  file dirname $lo
4e10: 63 61 6c 70 61 74 68 5d 0a 09 09 09 09 09 66 69  calpath]......fi
4e20: 6c 65 20 63 6f 70 79 20 2d 66 6f 72 63 65 20 2d  le copy -force -
4e30: 2d 20 24 6c 6f 63 61 6c 63 61 63 68 65 66 69 6c  - $localcachefil
4e40: 65 20 24 74 6d 70 6c 6f 63 61 6c 70 61 74 68 0a  e $tmplocalpath.
4e50: 0a 09 09 09 09 09 69 66 20 7b 24 70 6b 67 70 61  ......if {$pkgpa
4e60: 74 68 69 6e 66 6f 28 70 65 72 6d 73 29 20 3d 3d  thinfo(perms) ==
4e70: 20 22 78 22 7d 20 7b 0a 09 09 09 09 09 09 66 69   "x"} {.......fi
4e80: 6c 65 20 61 74 74 72 69 62 75 74 65 73 20 24 74  le attributes $t
4e90: 6d 70 6c 6f 63 61 6c 70 61 74 68 20 2d 70 65 72  mplocalpath -per
4ea0: 6d 69 73 73 69 6f 6e 73 20 2b 78 0a 09 09 09 09  missions +x.....
4eb0: 09 7d 0a 0a 09 09 09 09 09 66 69 6c 65 20 72 65  .}.......file re
4ec0: 6e 61 6d 65 20 2d 66 6f 72 63 65 20 2d 2d 20 24  name -force -- $
4ed0: 74 6d 70 6c 6f 63 61 6c 70 61 74 68 20 24 6c 6f  tmplocalpath $lo
4ee0: 63 61 6c 70 61 74 68 0a 09 09 09 09 7d 20 65 72  calpath.....} er
4ef0: 72 5d 7d 20 7b 0a 09 09 09 09 09 73 65 74 20 66  r]} {......set f
4f00: 61 69 6c 65 64 20 31 0a 09 09 09 09 7d 0a 09 09  ailed 1.....}...
4f10: 09 09 63 61 74 63 68 20 7b 0a 09 09 09 09 09 66  ..catch {......f
4f20: 69 6c 65 20 64 65 6c 65 74 65 20 2d 66 6f 72 63  ile delete -forc
4f30: 65 20 2d 2d 20 24 74 6d 70 6c 6f 63 61 6c 70 61  e -- $tmplocalpa
4f40: 74 68 0a 09 09 09 09 7d 0a 09 09 09 7d 0a 0a 09  th.....}....}...
4f50: 09 09 69 66 20 7b 24 66 61 69 6c 65 64 7d 20 7b  ..if {$failed} {
4f60: 0a 09 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64  .....return -cod
4f70: 65 20 65 72 72 6f 72 20 24 65 72 72 0a 09 09 09  e error $err....
4f80: 7d 0a 0a 09 09 09 72 65 74 75 72 6e 20 24 6c 6f  }.....return $lo
4f90: 63 61 6c 70 61 74 68 0a 09 09 7d 0a 0a 09 09 72  calpath...}....r
4fa0: 65 74 75 72 6e 20 24 6c 6f 63 61 6c 63 61 63 68  eturn $localcach
4fb0: 65 66 69 6c 65 0a 09 7d 0a 0a 09 70 72 6f 63 20  efile..}...proc 
4fc0: 6c 6f 63 61 6c 70 61 74 68 20 7b 70 61 74 68 7d  localpath {path}
4fd0: 20 7b 0a 09 09 61 72 72 61 79 20 73 65 74 20 70   {...array set p
4fe0: 61 74 68 69 6e 66 6f 20 5b 5f 70 61 72 73 65 70  athinfo [_parsep
4ff0: 61 74 68 20 24 70 61 74 68 5d 0a 0a 09 09 69 66  ath $path]....if
5000: 20 7b 24 70 61 74 68 69 6e 66 6f 28 5f 74 79 70   {$pathinfo(_typ
5010: 65 29 20 21 3d 20 22 66 69 6c 65 73 22 7d 20 7b  e) != "files"} {
5020: 0a 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65  ....return -code
5030: 20 65 72 72 6f 72 20 22 69 6e 76 61 6c 69 64 20   error "invalid 
5040: 74 79 70 65 22 0a 09 09 7d 0a 0a 09 09 73 65 74  type"...}....set
5050: 20 6c 6f 63 61 6c 70 61 74 68 20 5b 5f 6c 6f 63   localpath [_loc
5060: 61 6c 70 61 74 68 20 24 70 61 74 68 69 6e 66 6f  alpath $pathinfo
5070: 28 70 61 63 6b 61 67 65 29 20 24 70 61 74 68 69  (package) $pathi
5080: 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24 70  nfo(hostname) $p
5090: 61 74 68 69 6e 66 6f 28 66 69 6c 65 29 5d 0a 0a  athinfo(file)]..
50a0: 09 09 72 65 74 75 72 6e 20 24 6c 6f 63 61 6c 70  ..return $localp
50b0: 61 74 68 0a 09 7d 0a 0a 09 70 72 6f 63 20 65 78  ath..}...proc ex
50c0: 69 73 74 73 20 7b 70 61 74 68 7d 20 7b 0a 09 09  ists {path} {...
50d0: 63 61 74 63 68 20 7b 0a 09 09 09 73 65 74 20 69  catch {....set i
50e0: 6e 66 6f 20 5b 67 65 74 61 74 74 72 20 24 70 61  nfo [getattr $pa
50f0: 74 68 5d 0a 09 09 7d 20 65 72 72 0a 0a 09 09 69  th]...} err....i
5100: 66 20 7b 21 5b 69 6e 66 6f 20 65 78 69 73 74 73  f {![info exists
5110: 20 69 6e 66 6f 5d 7d 20 7b 0a 09 09 09 69 66 20   info]} {....if 
5120: 7b 24 65 72 72 20 3d 3d 20 22 4e 6f 20 73 75 63  {$err == "No suc
5130: 68 20 66 69 6c 65 20 6f 72 20 64 69 72 65 63 74  h file or direct
5140: 6f 72 79 22 7d 20 7b 0a 09 09 09 09 72 65 74 75  ory"} {.....retu
5150: 72 6e 20 5b 6c 69 73 74 5d 0a 09 09 09 7d 20 65  rn [list]....} e
5160: 6c 73 65 20 7b 0a 09 09 09 09 72 65 74 75 72 6e  lse {.....return
5170: 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 24 65 72   -code error $er
5180: 72 0a 09 09 09 7d 0a 09 09 7d 0a 0a 09 09 72 65  r....}...}....re
5190: 74 75 72 6e 20 24 69 6e 66 6f 0a 09 7d 0a 0a 09  turn $info..}...
51a0: 70 72 6f 63 20 70 72 65 70 61 72 65 5f 74 6f 5f  proc prepare_to_
51b0: 63 72 65 61 74 65 20 7b 70 61 74 68 20 7b 6d 75  create {path {mu
51c0: 73 74 5f 6e 6f 74 5f 65 78 69 73 74 20 31 7d 7d  st_not_exist 1}}
51d0: 20 7b 0a 09 09 69 66 20 7b 24 6d 75 73 74 5f 6e   {...if {$must_n
51e0: 6f 74 5f 65 78 69 73 74 7d 20 7b 0a 09 09 09 69  ot_exist} {....i
51f0: 66 20 7b 5b 65 78 69 73 74 73 20 24 70 61 74 68  f {[exists $path
5200: 5d 20 21 3d 20 22 22 7d 20 7b 0a 09 09 09 09 72  ] != ""} {.....r
5210: 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f  eturn -code erro
5220: 72 20 22 46 69 6c 65 20 61 6c 72 65 61 64 79 20  r "File already 
5230: 65 78 69 73 74 73 22 0a 09 09 09 7d 0a 09 09 7d  exists"....}...}
5240: 0a 0a 09 09 73 65 74 20 66 69 6c 65 6e 61 6d 65  ....set filename
5250: 20 5b 6c 6f 63 61 6c 70 61 74 68 20 24 70 61 74   [localpath $pat
5260: 68 5d 0a 0a 09 09 69 66 20 7b 24 66 69 6c 65 6e  h]....if {$filen
5270: 61 6d 65 20 3d 3d 20 22 22 7d 20 7b 0a 09 09 09  ame == ""} {....
5280: 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72  return -code err
5290: 6f 72 20 22 41 73 6b 65 64 20 74 6f 20 63 72 65  or "Asked to cre
52a0: 61 74 65 2c 20 62 75 74 20 6e 6f 20 68 6f 6d 65  ate, but no home
52b0: 20 64 69 72 65 63 74 6f 72 79 2e 22 0a 09 09 7d   directory."...}
52c0: 0a 0a 09 09 73 65 74 20 64 69 72 6e 61 6d 65 20  ....set dirname 
52d0: 5b 66 69 6c 65 20 64 69 72 6e 61 6d 65 20 24 66  [file dirname $f
52e0: 69 6c 65 6e 61 6d 65 5d 0a 0a 09 09 5f 61 73 5f  ilename]...._as_
52f0: 75 73 65 72 20 7b 0a 09 09 09 66 69 6c 65 20 6d  user {....file m
5300: 6b 64 69 72 20 24 64 69 72 6e 61 6d 65 0a 09 09  kdir $dirname...
5310: 7d 0a 0a 09 09 72 65 74 75 72 6e 20 24 66 69 6c  }....return $fil
5320: 65 6e 61 6d 65 0a 09 7d 0a 0a 09 70 72 6f 63 20  ename..}...proc 
5330: 75 6e 6c 69 6e 6b 70 61 74 68 20 7b 70 61 74 68  unlinkpath {path
5340: 7d 20 7b 0a 09 09 61 72 72 61 79 20 73 65 74 20  } {...array set 
5350: 70 61 74 68 61 74 74 72 73 20 5b 65 78 69 73 74  pathattrs [exist
5360: 73 20 24 70 61 74 68 5d 0a 0a 09 09 69 66 20 7b  s $path]....if {
5370: 21 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 70 61  ![info exists pa
5380: 74 68 61 74 74 72 73 28 70 61 63 6b 61 67 65 64  thattrs(packaged
5390: 29 5d 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  )]} {....return 
53a0: 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 69 6e 76  -code error "inv
53b0: 61 6c 69 64 20 74 79 70 65 22 0a 09 09 7d 0a 0a  alid type"...}..
53c0: 09 09 73 65 74 20 6c 6f 63 61 6c 70 61 74 68 20  ..set localpath 
53d0: 24 70 61 74 68 61 74 74 72 73 28 6c 6f 63 61 6c  $pathattrs(local
53e0: 70 61 74 68 29 0a 0a 09 09 69 66 20 7b 24 6c 6f  path)....if {$lo
53f0: 63 61 6c 70 61 74 68 20 3d 3d 20 22 22 7d 20 7b  calpath == ""} {
5400: 0a 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65  ....return -code
5410: 20 65 72 72 6f 72 20 22 41 73 6b 65 64 20 74 6f   error "Asked to
5420: 20 64 65 6c 65 74 65 2c 20 62 75 74 20 6e 6f 20   delete, but no 
5430: 68 6f 6d 65 20 64 69 72 65 63 74 6f 72 79 2e 22  home directory."
5440: 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 5b 69 6e 66  ...}....if {[inf
5450: 6f 20 65 78 69 73 74 73 20 70 61 74 68 61 74 74  o exists pathatt
5460: 72 73 28 69 73 5f 6c 6f 63 61 6c 66 69 6c 65 29  rs(is_localfile)
5470: 5d 7d 20 7b 0a 09 09 09 69 66 20 7b 5b 66 69 6c  ]} {....if {[fil
5480: 65 20 69 73 64 69 72 65 63 74 6f 72 79 20 24 6c  e isdirectory $l
5490: 6f 63 61 6c 70 61 74 68 5d 7d 20 7b 0a 09 09 09  ocalpath]} {....
54a0: 09 73 65 74 20 63 68 69 6c 64 72 65 6e 20 5b 67  .set children [g
54b0: 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61 74 68  etchildren $path
54c0: 5d 0a 0a 09 09 09 09 69 66 20 7b 5b 6c 6c 65 6e  ]......if {[llen
54d0: 67 74 68 20 24 63 68 69 6c 64 72 65 6e 5d 20 21  gth $children] !
54e0: 3d 20 30 7d 20 7b 0a 09 09 09 09 09 72 65 74 75  = 0} {......retu
54f0: 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22  rn -code error "
5500: 41 73 6b 65 64 20 74 6f 20 64 65 6c 65 74 65 20  Asked to delete 
5510: 6e 6f 6e 2d 65 6d 70 74 79 20 64 69 72 65 63 74  non-empty direct
5520: 6f 72 79 22 0a 09 09 09 09 7d 0a 09 09 09 7d 0a  ory".....}....}.
5530: 0a 09 09 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09  ...._as_user {..
5540: 09 09 09 66 69 6c 65 20 64 65 6c 65 74 65 20 2d  ...file delete -
5550: 66 6f 72 63 65 20 2d 2d 20 24 6c 6f 63 61 6c 70  force -- $localp
5560: 61 74 68 0a 09 09 09 7d 0a 09 09 7d 20 65 6c 73  ath....}...} els
5570: 65 69 66 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74  eif {[info exist
5580: 73 20 70 61 74 68 61 74 74 72 73 28 69 73 5f 72  s pathattrs(is_r
5590: 65 6d 6f 74 65 66 69 6c 65 29 5d 7d 20 7b 0a 09  emotefile)]} {..
55a0: 09 09 69 66 20 7b 24 70 61 74 68 61 74 74 72 73  ..if {$pathattrs
55b0: 28 74 79 70 65 29 20 3d 3d 20 22 64 69 72 65 63  (type) == "direc
55c0: 74 6f 72 79 22 7d 20 7b 0a 09 09 09 09 73 65 74  tory"} {.....set
55d0: 20 63 68 69 6c 64 72 65 6e 20 5b 67 65 74 63 68   children [getch
55e0: 69 6c 64 72 65 6e 20 24 70 61 74 68 5d 0a 0a 09  ildren $path]...
55f0: 09 09 09 69 66 20 7b 5b 6c 6c 65 6e 67 74 68 20  ...if {[llength 
5600: 24 63 68 69 6c 64 72 65 6e 5d 20 21 3d 20 30 7d  $children] != 0}
5610: 20 7b 0a 09 09 09 09 09 72 65 74 75 72 6e 20 2d   {......return -
5620: 63 6f 64 65 20 65 72 72 6f 72 20 22 41 73 6b 65  code error "Aske
5630: 64 20 74 6f 20 64 65 6c 65 74 65 20 6e 6f 6e 2d  d to delete non-
5640: 65 6d 70 74 79 20 64 69 72 65 63 74 6f 72 79 22  empty directory"
5650: 0a 09 09 09 09 7d 0a 09 09 09 7d 0a 09 09 7d 20  .....}....}...} 
5660: 65 6c 73 65 20 7b 0a 09 09 09 72 65 74 75 72 6e  else {....return
5670: 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 55 6e   -code error "Un
5680: 6b 6e 6f 77 6e 20 69 66 20 66 69 6c 65 20 69 73  known if file is
5690: 20 72 65 6d 6f 74 65 20 6f 72 20 6c 6f 63 61 6c   remote or local
56a0: 20 21 3f 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20   !?"...}....set 
56b0: 77 68 69 74 65 6f 75 74 66 69 6c 65 20 24 70 61  whiteoutfile $pa
56c0: 74 68 61 74 74 72 73 28 77 68 69 74 65 6f 75 74  thattrs(whiteout
56d0: 70 61 74 68 29 0a 09 09 73 65 74 20 77 68 69 74  path)...set whit
56e0: 65 6f 75 74 64 69 72 20 5b 66 69 6c 65 20 64 69  eoutdir [file di
56f0: 72 6e 61 6d 65 20 24 77 68 69 74 65 6f 75 74 66  rname $whiteoutf
5700: 69 6c 65 5d 0a 0a 09 09 5f 61 73 5f 75 73 65 72  ile]...._as_user
5710: 20 7b 0a 09 09 09 66 69 6c 65 20 6d 6b 64 69 72   {....file mkdir
5720: 20 24 77 68 69 74 65 6f 75 74 64 69 72 0a 09 09   $whiteoutdir...
5730: 09 63 6c 6f 73 65 20 5b 6f 70 65 6e 20 24 77 68  .close [open $wh
5740: 69 74 65 6f 75 74 66 69 6c 65 20 77 5d 0a 09 09  iteoutfile w]...
5750: 7d 0a 09 7d 0a 7d 0a                             }..}.}.