Hex Artifact Content

Artifact 85c7175a5128f45cab5244590da5c8bb82621eae:


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 6e 61 6d 65 73 70 61 63 65 20 65 76 61 6c  ..namespace eval
00b0: 20 3a 3a 61 70 70 66 73 20 7b 0a 09 76 61 72 69   ::appfs {..vari
00c0: 61 62 6c 65 20 63 61 63 68 65 64 69 72 20 22 2f  able cachedir "/
00d0: 74 6d 70 2f 61 70 70 66 73 2d 63 61 63 68 65 22  tmp/appfs-cache"
00e0: 0a 09 76 61 72 69 61 62 6c 65 20 74 74 6c 20 33  ..variable ttl 3
00f0: 36 30 30 0a 09 76 61 72 69 61 62 6c 65 20 6e 74  600..variable nt
0100: 74 6c 20 36 30 0a 0a 09 23 20 55 73 65 72 2d 72  tl 60...# User-r
0110: 65 70 6c 61 63 61 62 6c 65 20 66 75 6e 63 74 69  eplacable functi
0120: 6f 6e 20 74 6f 20 63 6f 6e 76 65 72 74 20 61 20  on to convert a 
0130: 68 6f 73 74 6e 61 6d 65 2f 68 61 73 68 2f 6d 65  hostname/hash/me
0140: 74 68 6f 64 20 74 6f 20 61 6e 20 55 52 4c 0a 09  thod to an URL..
0150: 70 72 6f 63 20 5f 63 6f 6e 73 74 72 75 63 74 5f  proc _construct_
0160: 75 72 6c 20 7b 68 6f 73 74 6e 61 6d 65 20 68 61  url {hostname ha
0170: 73 68 20 6d 65 74 68 6f 64 7d 20 7b 0a 09 09 72  sh method} {...r
0180: 65 74 75 72 6e 20 22 68 74 74 70 3a 2f 2f 24 68  eturn "http://$h
0190: 6f 73 74 6e 61 6d 65 2f 61 70 70 66 73 2f 24 6d  ostname/appfs/$m
01a0: 65 74 68 6f 64 2f 24 68 61 73 68 22 0a 09 7d 0a  ethod/$hash"..}.
01b0: 0a 09 70 72 6f 63 20 5f 68 61 73 68 5f 73 65 70  ..proc _hash_sep
01c0: 20 7b 68 61 73 68 20 7b 73 65 70 73 20 34 7d 7d   {hash {seps 4}}
01d0: 20 7b 0a 09 09 66 6f 72 20 7b 73 65 74 20 69 64   {...for {set id
01e0: 78 20 30 7d 20 7b 24 69 64 78 20 3c 20 24 73 65  x 0} {$idx < $se
01f0: 70 73 7d 20 7b 69 6e 63 72 20 69 64 78 7d 20 7b  ps} {incr idx} {
0200: 0a 09 09 09 61 70 70 65 6e 64 20 72 65 74 76 61  ....append retva
0210: 6c 20 22 5b 73 74 72 69 6e 67 20 72 61 6e 67 65  l "[string range
0220: 20 24 68 61 73 68 20 5b 65 78 70 72 20 7b 24 69   $hash [expr {$i
0230: 64 78 20 2a 20 32 7d 5d 20 5b 65 78 70 72 20 7b  dx * 2}] [expr {
0240: 28 24 69 64 78 20 2a 20 32 29 20 2b 20 31 7d 5d  ($idx * 2) + 1}]
0250: 5d 2f 22 0a 09 09 7d 0a 09 09 61 70 70 65 6e 64  ]/"...}...append
0260: 20 72 65 74 76 61 6c 20 22 5b 73 74 72 69 6e 67   retval "[string
0270: 20 72 61 6e 67 65 20 24 68 61 73 68 20 5b 65 78   range $hash [ex
0280: 70 72 20 7b 24 69 64 78 20 2a 20 32 7d 5d 20 65  pr {$idx * 2}] e
0290: 6e 64 5d 22 0a 0a 09 09 72 65 74 75 72 6e 20 24  nd]"....return $
02a0: 72 65 74 76 61 6c 0a 09 7d 0a 0a 09 70 72 6f 63  retval..}...proc
02b0: 20 5f 63 61 63 68 65 66 69 6c 65 20 7b 75 72 6c   _cachefile {url
02c0: 20 6b 65 79 20 7b 6b 65 79 49 73 48 61 73 68 20   key {keyIsHash 
02d0: 31 7d 7d 20 7b 0a 09 09 73 65 74 20 66 69 6c 65  1}} {...set file
02e0: 6b 65 79 20 24 6b 65 79 0a 09 09 69 66 20 7b 24  key $key...if {$
02f0: 6b 65 79 49 73 48 61 73 68 7d 20 7b 0a 09 09 09  keyIsHash} {....
0300: 73 65 74 20 66 69 6c 65 6b 65 79 20 5b 5f 68 61  set filekey [_ha
0310: 73 68 5f 73 65 70 20 24 66 69 6c 65 6b 65 79 5d  sh_sep $filekey]
0320: 0a 09 09 7d 0a 0a 09 09 73 65 74 20 66 69 6c 65  ...}....set file
0330: 20 5b 66 69 6c 65 20 6a 6f 69 6e 20 24 3a 3a 61   [file join $::a
0340: 70 70 66 73 3a 3a 63 61 63 68 65 64 69 72 20 24  ppfs::cachedir $
0350: 66 69 6c 65 6b 65 79 5d 0a 0a 09 09 66 69 6c 65  filekey]....file
0360: 20 6d 6b 64 69 72 20 5b 66 69 6c 65 20 64 69 72   mkdir [file dir
0370: 6e 61 6d 65 20 24 66 69 6c 65 5d 0a 0a 09 09 69  name $file]....i
0380: 66 20 7b 5b 66 69 6c 65 20 65 78 69 73 74 73 20  f {[file exists 
0390: 24 66 69 6c 65 5d 7d 20 7b 0a 09 09 09 72 65 74  $file]} {....ret
03a0: 75 72 6e 20 24 66 69 6c 65 0a 09 09 7d 0a 0a 09  urn $file...}...
03b0: 09 73 65 74 20 74 6d 70 66 69 6c 65 20 22 24 7b  .set tmpfile "${
03c0: 66 69 6c 65 7d 2e 5b 65 78 70 72 20 7b 72 61 6e  file}.[expr {ran
03d0: 64 28 29 7d 5d 5b 63 6c 6f 63 6b 20 63 6c 69 63  d()}][clock clic
03e0: 6b 73 5d 22 0a 0a 09 09 73 65 74 20 66 64 20 5b  ks]"....set fd [
03f0: 6f 70 65 6e 20 24 74 6d 70 66 69 6c 65 20 22 77  open $tmpfile "w
0400: 22 5d 0a 09 09 66 63 6f 6e 66 69 67 75 72 65 20  "]...fconfigure 
0410: 24 66 64 20 2d 74 72 61 6e 73 6c 61 74 69 6f 6e  $fd -translation
0420: 20 62 69 6e 61 72 79 0a 0a 09 09 63 61 74 63 68   binary....catch
0430: 20 7b 0a 09 09 09 73 65 74 20 74 6f 6b 65 6e 20   {....set token 
0440: 5b 3a 3a 68 74 74 70 3a 3a 67 65 74 75 72 6c 20  [::http::geturl 
0450: 24 75 72 6c 20 2d 63 68 61 6e 6e 65 6c 20 24 66  $url -channel $f
0460: 64 20 2d 62 69 6e 61 72 79 20 74 72 75 65 5d 0a  d -binary true].
0470: 09 09 7d 0a 0a 09 09 69 66 20 7b 5b 69 6e 66 6f  ..}....if {[info
0480: 20 65 78 69 73 74 73 20 74 6f 6b 65 6e 5d 7d 20   exists token]} 
0490: 7b 0a 09 09 09 73 65 74 20 6e 63 6f 64 65 20 5b  {....set ncode [
04a0: 3a 3a 68 74 74 70 3a 3a 6e 63 6f 64 65 20 24 74  ::http::ncode $t
04b0: 6f 6b 65 6e 5d 0a 09 09 09 3a 3a 68 74 74 70 3a  oken]....::http:
04c0: 3a 72 65 73 65 74 20 24 74 6f 6b 65 6e 0a 09 09  :reset $token...
04d0: 7d 20 65 6c 73 65 20 7b 0a 09 09 09 73 65 74 20  } else {....set 
04e0: 6e 63 6f 64 65 20 22 39 30 30 22 0a 09 09 7d 0a  ncode "900"...}.
04f0: 0a 09 09 63 6c 6f 73 65 20 24 66 64 0a 0a 09 09  ...close $fd....
0500: 69 66 20 7b 24 6b 65 79 49 73 48 61 73 68 7d 20  if {$keyIsHash} 
0510: 7b 0a 09 09 09 73 65 74 20 68 61 73 68 20 5b 73  {....set hash [s
0520: 74 72 69 6e 67 20 74 6f 6c 6f 77 65 72 20 5b 73  tring tolower [s
0530: 68 61 31 3a 3a 73 68 61 31 20 2d 68 65 78 20 2d  ha1::sha1 -hex -
0540: 66 69 6c 65 20 24 74 6d 70 66 69 6c 65 5d 5d 0a  file $tmpfile]].
0550: 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 73 65  ..} else {....se
0560: 74 20 68 61 73 68 20 24 6b 65 79 0a 09 09 7d 0a  t hash $key...}.
0570: 0a 09 09 69 66 20 7b 24 6e 63 6f 64 65 20 3d 3d  ...if {$ncode ==
0580: 20 22 32 30 30 22 20 26 26 20 24 68 61 73 68 20   "200" && $hash 
0590: 3d 3d 20 24 6b 65 79 7d 20 7b 0a 09 09 09 66 69  == $key} {....fi
05a0: 6c 65 20 72 65 6e 61 6d 65 20 2d 66 6f 72 63 65  le rename -force
05b0: 20 2d 2d 20 24 74 6d 70 66 69 6c 65 20 24 66 69   -- $tmpfile $fi
05c0: 6c 65 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09  le...} else {...
05d0: 09 66 69 6c 65 20 64 65 6c 65 74 65 20 2d 66 6f  .file delete -fo
05e0: 72 63 65 20 2d 2d 20 24 74 6d 70 66 69 6c 65 0a  rce -- $tmpfile.
05f0: 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e 20 24 66  ..}....return $f
0600: 69 6c 65 0a 09 7d 0a 0a 0a 09 70 72 6f 63 20 5f  ile..}....proc _
0610: 69 73 48 61 73 68 20 7b 76 61 6c 75 65 7d 20 7b  isHash {value} {
0620: 0a 09 09 73 65 74 20 76 61 6c 75 65 20 5b 73 74  ...set value [st
0630: 72 69 6e 67 20 74 6f 6c 6f 77 65 72 20 24 76 61  ring tolower $va
0640: 6c 75 65 5d 0a 0a 09 09 69 66 20 7b 5b 73 74 72  lue]....if {[str
0650: 69 6e 67 20 6c 65 6e 67 74 68 20 24 76 61 6c 75  ing length $valu
0660: 65 5d 20 21 3d 20 34 30 7d 20 7b 0a 09 09 09 72  e] != 40} {....r
0670: 65 74 75 72 6e 20 66 61 6c 73 65 0a 09 09 7d 0a  eturn false...}.
0680: 0a 09 09 69 66 20 7b 21 5b 72 65 67 65 78 70 20  ...if {![regexp 
0690: 7b 5e 5b 30 2d 39 61 2d 66 5d 2a 24 7d 20 24 76  {^[0-9a-f]*$} $v
06a0: 61 6c 75 65 5d 7d 20 7b 0a 09 09 09 72 65 74 75  alue]} {....retu
06b0: 72 6e 20 66 61 6c 73 65 0a 09 09 7d 0a 0a 09 09  rn false...}....
06c0: 72 65 74 75 72 6e 20 74 72 75 65 0a 09 7d 0a 0a  return true..}..
06d0: 09 70 72 6f 63 20 5f 6e 6f 72 6d 61 6c 69 7a 65  .proc _normalize
06e0: 4f 53 20 7b 6f 73 7d 20 7b 0a 09 09 73 65 74 20  OS {os} {...set 
06f0: 6f 73 20 5b 73 74 72 69 6e 67 20 74 6f 6c 6f 77  os [string tolow
0700: 65 72 20 5b 73 74 72 69 6e 67 20 74 72 69 6d 20  er [string trim 
0710: 24 6f 73 5d 5d 0a 0a 09 09 73 77 69 74 63 68 20  $os]]....switch 
0720: 2d 2d 20 24 6f 73 20 7b 0a 09 09 09 22 6c 69 6e  -- $os {...."lin
0730: 75 78 22 20 2d 20 22 66 72 65 65 62 73 64 22 20  ux" - "freebsd" 
0740: 2d 20 22 6f 70 65 6e 62 73 64 22 20 2d 20 22 6e  - "openbsd" - "n
0750: 65 74 62 73 64 22 20 7b 0a 09 09 09 09 72 65 74  etbsd" {.....ret
0760: 75 72 6e 20 24 6f 73 0a 09 09 09 7d 0a 09 09 09  urn $os....}....
0770: 22 73 75 6e 6f 73 22 20 7b 0a 09 09 09 09 72 65  "sunos" {.....re
0780: 74 75 72 6e 20 22 73 6f 6c 61 72 69 73 22 0a 09  turn "solaris"..
0790: 09 09 7d 0a 09 09 09 22 6e 6f 61 72 63 68 22 20  ..}...."noarch" 
07a0: 2d 20 22 6e 6f 6e 65 22 20 2d 20 22 61 6e 79 22  - "none" - "any"
07b0: 20 2d 20 22 61 6c 6c 22 20 7b 0a 09 09 09 09 72   - "all" {.....r
07c0: 65 74 75 72 6e 20 22 6e 6f 61 72 63 68 22 0a 09  eturn "noarch"..
07d0: 09 09 7d 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72  ..}...}....retur
07e0: 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 55  n -code error "U
07f0: 6e 61 62 6c 65 20 74 6f 20 6e 6f 72 6d 61 6c 69  nable to normali
0800: 7a 65 20 4f 53 3a 20 24 6f 73 22 0a 09 7d 0a 0a  ze OS: $os"..}..
0810: 09 70 72 6f 63 20 5f 6e 6f 72 6d 61 6c 69 7a 65  .proc _normalize
0820: 43 50 55 20 7b 63 70 75 7d 20 7b 0a 09 09 73 65  CPU {cpu} {...se
0830: 74 20 63 70 75 20 5b 73 74 72 69 6e 67 20 74 6f  t cpu [string to
0840: 6c 6f 77 65 72 20 5b 73 74 72 69 6e 67 20 74 72  lower [string tr
0850: 69 6d 20 24 63 70 75 5d 5d 0a 0a 09 09 73 77 69  im $cpu]]....swi
0860: 74 63 68 20 2d 67 6c 6f 62 20 2d 2d 20 24 63 70  tch -glob -- $cp
0870: 75 20 7b 0a 09 09 09 22 69 3f 38 36 22 20 7b 0a  u {...."i?86" {.
0880: 09 09 09 09 72 65 74 75 72 6e 20 22 69 78 38 36  ....return "ix86
0890: 22 0a 09 09 09 7d 0a 09 09 09 22 78 38 36 5f 36  "....}...."x86_6
08a0: 34 22 20 7b 0a 09 09 09 09 72 65 74 75 72 6e 20  4" {.....return 
08b0: 24 63 70 75 0a 09 09 09 7d 0a 09 09 09 22 6e 6f  $cpu....}...."no
08c0: 61 72 63 68 22 20 2d 20 22 6e 6f 6e 65 22 20 2d  arch" - "none" -
08d0: 20 22 61 6e 79 22 20 2d 20 22 61 6c 6c 22 20 7b   "any" - "all" {
08e0: 0a 09 09 09 09 72 65 74 75 72 6e 20 22 6e 6f 61  .....return "noa
08f0: 72 63 68 22 0a 09 09 09 7d 0a 09 09 7d 0a 0a 09  rch"....}...}...
0900: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72  .return -code er
0910: 72 6f 72 20 22 55 6e 61 62 6c 65 20 74 6f 20 6e  ror "Unable to n
0920: 6f 72 6d 61 6c 69 7a 65 20 43 50 55 3a 20 24 63  ormalize CPU: $c
0930: 70 75 22 0a 09 7d 0a 0a 09 70 72 6f 63 20 5f 61  pu"..}...proc _a
0940: 73 5f 75 73 65 72 20 7b 63 6f 64 65 7d 20 7b 0a  s_user {code} {.
0950: 09 09 3a 3a 61 70 70 66 73 64 3a 3a 73 69 6d 75  ..::appfsd::simu
0960: 6c 61 74 65 5f 75 73 65 72 5f 66 73 5f 65 6e 74  late_user_fs_ent
0970: 65 72 0a 0a 09 09 73 65 74 20 72 65 74 63 6f 64  er....set retcod
0980: 65 20 5b 63 61 74 63 68 20 5b 6c 69 73 74 20 75  e [catch [list u
0990: 70 6c 65 76 65 6c 20 24 63 6f 64 65 5d 20 72 65  plevel $code] re
09a0: 74 73 74 72 5d 0a 0a 09 09 3a 3a 61 70 70 66 73  tstr]....::appfs
09b0: 64 3a 3a 73 69 6d 75 6c 61 74 65 5f 75 73 65 72  d::simulate_user
09c0: 5f 66 73 5f 6c 65 61 76 65 0a 0a 09 09 72 65 74  _fs_leave....ret
09d0: 75 72 6e 20 2d 63 6f 64 65 20 24 72 65 74 63 6f  urn -code $retco
09e0: 64 65 20 24 72 65 74 73 74 72 0a 09 7d 0a 0a 09  de $retstr..}...
09f0: 70 72 6f 63 20 69 6e 69 74 20 7b 7d 20 7b 0a 09  proc init {} {..
0a00: 09 69 66 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74  .if {[info exist
0a10: 73 20 3a 3a 61 70 70 66 73 3a 3a 69 6e 69 74 5f  s ::appfs::init_
0a20: 63 61 6c 6c 65 64 5d 7d 20 7b 0a 09 09 09 72 65  called]} {....re
0a30: 74 75 72 6e 0a 09 09 7d 0a 0a 09 09 23 20 46 6f  turn...}....# Fo
0a40: 72 63 65 20 5b 70 61 72 72 61 79 5d 20 74 6f 20  rce [parray] to 
0a50: 62 65 20 6c 6f 61 64 65 64 0a 09 09 63 61 74 63  be loaded...catc
0a60: 68 20 7b 0a 09 09 09 70 61 72 72 61 79 20 64 6f  h {....parray do
0a70: 65 73 5f 6e 6f 74 5f 65 78 69 73 74 0a 09 09 7d  es_not_exist...}
0a80: 0a 0a 09 09 73 65 74 20 3a 3a 61 70 70 66 73 3a  ....set ::appfs:
0a90: 3a 69 6e 69 74 5f 63 61 6c 6c 65 64 20 31 0a 0a  :init_called 1..
0aa0: 09 09 23 20 4c 6f 61 64 20 63 6f 6e 66 69 67 75  ..# Load configu
0ab0: 72 61 74 69 6f 6e 20 66 69 6c 65 0a 09 09 73 65  ration file...se
0ac0: 74 20 63 6f 6e 66 69 67 5f 66 69 6c 65 20 5b 66  t config_file [f
0ad0: 69 6c 65 20 6a 6f 69 6e 20 24 3a 3a 61 70 70 66  ile join $::appf
0ae0: 73 3a 3a 63 61 63 68 65 64 69 72 20 63 6f 6e 66  s::cachedir conf
0af0: 69 67 5d 0a 09 09 69 66 20 7b 5b 66 69 6c 65 20  ig]...if {[file 
0b00: 65 78 69 73 74 73 20 24 63 6f 6e 66 69 67 5f 66  exists $config_f
0b10: 69 6c 65 5d 7d 20 7b 0a 09 09 09 73 6f 75 72 63  ile]} {....sourc
0b20: 65 20 24 63 6f 6e 66 69 67 5f 66 69 6c 65 0a 09  e $config_file..
0b30: 09 7d 0a 0a 09 09 69 66 20 7b 21 5b 69 6e 66 6f  .}....if {![info
0b40: 20 65 78 69 73 74 73 20 3a 3a 61 70 70 66 73 3a   exists ::appfs:
0b50: 3a 64 62 5d 7d 20 7b 0a 09 09 09 66 69 6c 65 20  :db]} {....file 
0b60: 6d 6b 64 69 72 20 24 3a 3a 61 70 70 66 73 3a 3a  mkdir $::appfs::
0b70: 63 61 63 68 65 64 69 72 0a 0a 09 09 09 73 71 6c  cachedir.....sql
0b80: 69 74 65 33 20 3a 3a 61 70 70 66 73 3a 3a 64 62  ite3 ::appfs::db
0b90: 20 5b 66 69 6c 65 20 6a 6f 69 6e 20 24 3a 3a 61   [file join $::a
0ba0: 70 70 66 73 3a 3a 63 61 63 68 65 64 69 72 20 63  ppfs::cachedir c
0bb0: 61 63 68 65 2e 64 62 5d 0a 09 09 7d 0a 0a 09 09  ache.db]...}....
0bc0: 23 20 43 72 65 61 74 65 20 74 61 62 6c 65 73 0a  # Create tables.
0bd0: 09 09 64 62 20 65 76 61 6c 20 7b 43 52 45 41 54  ..db eval {CREAT
0be0: 45 20 54 41 42 4c 45 20 49 46 20 4e 4f 54 20 45  E TABLE IF NOT E
0bf0: 58 49 53 54 53 20 73 69 74 65 73 28 68 6f 73 74  XISTS sites(host
0c00: 6e 61 6d 65 20 50 52 49 4d 41 52 59 20 4b 45 59  name PRIMARY KEY
0c10: 2c 20 6c 61 73 74 55 70 64 61 74 65 2c 20 74 74  , lastUpdate, tt
0c20: 6c 29 3b 7d 0a 09 09 64 62 20 65 76 61 6c 20 7b  l);}...db eval {
0c30: 43 52 45 41 54 45 20 54 41 42 4c 45 20 49 46 20  CREATE TABLE IF 
0c40: 4e 4f 54 20 45 58 49 53 54 53 20 70 61 63 6b 61  NOT EXISTS packa
0c50: 67 65 73 28 68 6f 73 74 6e 61 6d 65 2c 20 73 68  ges(hostname, sh
0c60: 61 31 2c 20 70 61 63 6b 61 67 65 2c 20 76 65 72  a1, package, ver
0c70: 73 69 6f 6e 2c 20 6f 73 2c 20 63 70 75 41 72 63  sion, os, cpuArc
0c80: 68 2c 20 69 73 4c 61 74 65 73 74 2c 20 68 61 76  h, isLatest, hav
0c90: 65 4d 61 6e 69 66 65 73 74 29 3b 7d 0a 09 09 64  eManifest);}...d
0ca0: 62 20 65 76 61 6c 20 7b 43 52 45 41 54 45 20 54  b eval {CREATE T
0cb0: 41 42 4c 45 20 49 46 20 4e 4f 54 20 45 58 49 53  ABLE IF NOT EXIS
0cc0: 54 53 20 66 69 6c 65 73 28 70 61 63 6b 61 67 65  TS files(package
0cd0: 5f 73 68 61 31 2c 20 74 79 70 65 2c 20 74 69 6d  _sha1, type, tim
0ce0: 65 2c 20 73 6f 75 72 63 65 2c 20 73 69 7a 65 2c  e, source, size,
0cf0: 20 70 65 72 6d 73 2c 20 66 69 6c 65 5f 73 68 61   perms, file_sha
0d00: 31 2c 20 66 69 6c 65 5f 6e 61 6d 65 2c 20 66 69  1, file_name, fi
0d10: 6c 65 5f 64 69 72 65 63 74 6f 72 79 29 3b 7d 0a  le_directory);}.
0d20: 0a 09 09 23 20 43 72 65 61 74 65 20 69 6e 64 65  ...# Create inde
0d30: 78 65 73 0a 09 09 64 62 20 65 76 61 6c 20 7b 43  xes...db eval {C
0d40: 52 45 41 54 45 20 49 4e 44 45 58 20 49 46 20 4e  REATE INDEX IF N
0d50: 4f 54 20 45 58 49 53 54 53 20 73 69 74 65 73 5f  OT EXISTS sites_
0d60: 69 6e 64 65 78 20 4f 4e 20 73 69 74 65 73 20 28  index ON sites (
0d70: 68 6f 73 74 6e 61 6d 65 29 3b 7d 0a 09 09 64 62  hostname);}...db
0d80: 20 65 76 61 6c 20 7b 43 52 45 41 54 45 20 49 4e   eval {CREATE IN
0d90: 44 45 58 20 49 46 20 4e 4f 54 20 45 58 49 53 54  DEX IF NOT EXIST
0da0: 53 20 70 61 63 6b 61 67 65 73 5f 69 6e 64 65 78  S packages_index
0db0: 20 4f 4e 20 70 61 63 6b 61 67 65 73 20 28 68 6f   ON packages (ho
0dc0: 73 74 6e 61 6d 65 2c 20 70 61 63 6b 61 67 65 2c  stname, package,
0dd0: 20 76 65 72 73 69 6f 6e 2c 20 6f 73 2c 20 63 70   version, os, cp
0de0: 75 41 72 63 68 29 3b 7d 0a 09 09 64 62 20 65 76  uArch);}...db ev
0df0: 61 6c 20 7b 43 52 45 41 54 45 20 49 4e 44 45 58  al {CREATE INDEX
0e00: 20 49 46 20 4e 4f 54 20 45 58 49 53 54 53 20 66   IF NOT EXISTS f
0e10: 69 6c 65 73 5f 69 6e 64 65 78 20 4f 4e 20 66 69  iles_index ON fi
0e20: 6c 65 73 20 28 70 61 63 6b 61 67 65 5f 73 68 61  les (package_sha
0e30: 31 2c 20 66 69 6c 65 5f 6e 61 6d 65 2c 20 66 69  1, file_name, fi
0e40: 6c 65 5f 64 69 72 65 63 74 6f 72 79 29 3b 7d 0a  le_directory);}.
0e50: 09 7d 0a 0a 09 70 72 6f 63 20 64 6f 77 6e 6c 6f  .}...proc downlo
0e60: 61 64 20 7b 68 6f 73 74 6e 61 6d 65 20 68 61 73  ad {hostname has
0e70: 68 20 7b 6d 65 74 68 6f 64 20 73 68 61 31 7d 7d  h {method sha1}}
0e80: 20 7b 0a 09 09 73 65 74 20 75 72 6c 20 5b 5f 63   {...set url [_c
0e90: 6f 6e 73 74 72 75 63 74 5f 75 72 6c 20 24 68 6f  onstruct_url $ho
0ea0: 73 74 6e 61 6d 65 20 24 68 61 73 68 20 24 6d 65  stname $hash $me
0eb0: 74 68 6f 64 5d 0a 09 09 73 65 74 20 66 69 6c 65  thod]...set file
0ec0: 20 5b 5f 63 61 63 68 65 66 69 6c 65 20 24 75 72   [_cachefile $ur
0ed0: 6c 20 24 68 61 73 68 5d 0a 0a 09 09 69 66 20 7b  l $hash]....if {
0ee0: 21 5b 66 69 6c 65 20 65 78 69 73 74 73 20 24 66  ![file exists $f
0ef0: 69 6c 65 5d 7d 20 7b 0a 09 09 09 72 65 74 75 72  ile]} {....retur
0f00: 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 55  n -code error "U
0f10: 6e 61 62 6c 65 20 74 6f 20 66 65 74 63 68 20 28  nable to fetch (
0f20: 66 69 6c 65 20 64 6f 65 73 20 6e 6f 74 20 65 78  file does not ex
0f30: 69 73 74 3a 20 24 66 69 6c 65 29 22 0a 09 09 7d  ist: $file)"...}
0f40: 0a 0a 09 09 72 65 74 75 72 6e 20 24 66 69 6c 65  ....return $file
0f50: 0a 09 7d 0a 0a 09 70 72 6f 63 20 67 65 74 69 6e  ..}...proc getin
0f60: 64 65 78 20 7b 68 6f 73 74 6e 61 6d 65 7d 20 7b  dex {hostname} {
0f70: 0a 09 09 73 65 74 20 6e 6f 77 20 5b 63 6c 6f 63  ...set now [cloc
0f80: 6b 20 73 65 63 6f 6e 64 73 5d 0a 0a 09 09 73 65  k seconds]....se
0f90: 74 20 6c 61 73 74 55 70 64 61 74 65 73 20 5b 64  t lastUpdates [d
0fa0: 62 20 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 6c  b eval {SELECT l
0fb0: 61 73 74 55 70 64 61 74 65 2c 20 74 74 6c 20 46  astUpdate, ttl F
0fc0: 52 4f 4d 20 73 69 74 65 73 20 57 48 45 52 45 20  ROM sites WHERE 
0fd0: 68 6f 73 74 6e 61 6d 65 20 3d 20 24 68 6f 73 74  hostname = $host
0fe0: 6e 61 6d 65 20 4c 49 4d 49 54 20 31 3b 7d 5d 0a  name LIMIT 1;}].
0ff0: 09 09 69 66 20 7b 5b 6c 6c 65 6e 67 74 68 20 24  ..if {[llength $
1000: 6c 61 73 74 55 70 64 61 74 65 73 5d 20 3d 3d 20  lastUpdates] == 
1010: 30 7d 20 7b 0a 09 09 09 73 65 74 20 6c 61 73 74  0} {....set last
1020: 55 70 64 61 74 65 20 30 0a 09 09 09 73 65 74 20  Update 0....set 
1030: 74 74 6c 20 30 0a 09 09 7d 20 65 6c 73 65 20 7b  ttl 0...} else {
1040: 0a 09 09 09 73 65 74 20 6c 61 73 74 55 70 64 61  ....set lastUpda
1050: 74 65 20 5b 6c 69 6e 64 65 78 20 24 6c 61 73 74  te [lindex $last
1060: 55 70 64 61 74 65 73 20 30 5d 0a 09 09 09 73 65  Updates 0]....se
1070: 74 20 74 74 6c 20 5b 6c 69 6e 64 65 78 20 24 6c  t ttl [lindex $l
1080: 61 73 74 55 70 64 61 74 65 73 20 31 5d 0a 09 09  astUpdates 1]...
1090: 7d 0a 0a 09 09 69 66 20 7b 24 6e 6f 77 20 3c 20  }....if {$now < 
10a0: 28 24 6c 61 73 74 55 70 64 61 74 65 20 2b 20 24  ($lastUpdate + $
10b0: 74 74 6c 29 7d 20 7b 0a 09 09 09 72 65 74 75 72  ttl)} {....retur
10c0: 6e 20 43 4f 4d 50 4c 45 54 45 0a 09 09 7d 0a 0a  n COMPLETE...}..
10d0: 09 09 69 66 20 7b 5b 73 74 72 69 6e 67 20 6d 61  ..if {[string ma
10e0: 74 63 68 20 22 2a 5c 5b 2f 7e 5c 5d 2a 22 20 24  tch "*\[/~\]*" $
10f0: 68 6f 73 74 6e 61 6d 65 5d 7d 20 7b 0a 09 09 09  hostname]} {....
1100: 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72  return -code err
1110: 6f 72 20 22 49 6e 76 61 6c 69 64 20 68 6f 73 74  or "Invalid host
1120: 6e 61 6d 65 22 0a 09 09 7d 0a 0a 09 09 73 65 74  name"...}....set
1130: 20 75 72 6c 20 22 68 74 74 70 3a 2f 2f 24 68 6f   url "http://$ho
1140: 73 74 6e 61 6d 65 2f 61 70 70 66 73 2f 69 6e 64  stname/appfs/ind
1150: 65 78 22 0a 0a 09 09 63 61 74 63 68 20 7b 0a 09  ex"....catch {..
1160: 09 09 73 65 74 20 74 6f 6b 65 6e 20 5b 3a 3a 68  ..set token [::h
1170: 74 74 70 3a 3a 67 65 74 75 72 6c 20 24 75 72 6c  ttp::geturl $url
1180: 5d 0a 09 09 09 69 66 20 7b 5b 3a 3a 68 74 74 70  ]....if {[::http
1190: 3a 3a 6e 63 6f 64 65 20 24 74 6f 6b 65 6e 5d 20  ::ncode $token] 
11a0: 3d 3d 20 22 32 30 30 22 7d 20 7b 0a 09 09 09 09  == "200"} {.....
11b0: 73 65 74 20 69 6e 64 65 78 68 61 73 68 5f 64 61  set indexhash_da
11c0: 74 61 20 5b 3a 3a 68 74 74 70 3a 3a 64 61 74 61  ta [::http::data
11d0: 20 24 74 6f 6b 65 6e 5d 0a 09 09 09 7d 0a 09 09   $token]....}...
11e0: 09 3a 3a 68 74 74 70 3a 3a 72 65 73 65 74 20 24  .::http::reset $
11f0: 74 6f 6b 65 6e 0a 09 09 09 3a 3a 68 74 74 70 3a  token....::http:
1200: 3a 63 6c 65 61 6e 75 70 20 24 74 6f 6b 65 6e 0a  :cleanup $token.
1210: 09 09 7d 0a 0a 09 09 69 66 20 7b 21 5b 69 6e 66  ..}....if {![inf
1220: 6f 20 65 78 69 73 74 73 20 69 6e 64 65 78 68 61  o exists indexha
1230: 73 68 5f 64 61 74 61 5d 7d 20 7b 0a 09 09 09 23  sh_data]} {....#
1240: 20 43 61 63 68 65 20 74 68 69 73 20 72 65 73 75   Cache this resu
1250: 6c 74 20 66 6f 72 20 36 30 20 73 65 63 6f 6e 64  lt for 60 second
1260: 73 0a 09 09 09 64 62 20 65 76 61 6c 20 7b 49 4e  s....db eval {IN
1270: 53 45 52 54 20 4f 52 20 52 45 50 4c 41 43 45 20  SERT OR REPLACE 
1280: 49 4e 54 4f 20 73 69 74 65 73 20 28 68 6f 73 74  INTO sites (host
1290: 6e 61 6d 65 2c 20 6c 61 73 74 55 70 64 61 74 65  name, lastUpdate
12a0: 2c 20 74 74 6c 29 20 56 41 4c 55 45 53 20 28 24  , ttl) VALUES ($
12b0: 68 6f 73 74 6e 61 6d 65 2c 20 24 6e 6f 77 2c 20  hostname, $now, 
12c0: 24 3a 3a 61 70 70 66 73 3a 3a 6e 74 74 6c 29 3b  $::appfs::nttl);
12d0: 7d 0a 0a 09 09 09 72 65 74 75 72 6e 20 2d 63 6f  }.....return -co
12e0: 64 65 20 65 72 72 6f 72 20 22 55 6e 61 62 6c 65  de error "Unable
12f0: 20 74 6f 20 66 65 74 63 68 20 24 75 72 6c 22 0a   to fetch $url".
1300: 09 09 7d 0a 0a 09 09 73 65 74 20 69 6e 64 65 78  ..}....set index
1310: 68 61 73 68 20 5b 6c 69 6e 64 65 78 20 5b 73 70  hash [lindex [sp
1320: 6c 69 74 20 24 69 6e 64 65 78 68 61 73 68 5f 64  lit $indexhash_d
1330: 61 74 61 20 22 2c 22 5d 20 30 5d 0a 0a 09 09 69  ata ","] 0]....i
1340: 66 20 7b 21 5b 5f 69 73 48 61 73 68 20 24 69 6e  f {![_isHash $in
1350: 64 65 78 68 61 73 68 5d 7d 20 7b 0a 09 09 09 72  dexhash]} {....r
1360: 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f  eturn -code erro
1370: 72 20 22 49 6e 76 61 6c 69 64 20 68 61 73 68 3a  r "Invalid hash:
1380: 20 24 69 6e 64 65 78 68 61 73 68 22 0a 09 09 7d   $indexhash"...}
1390: 0a 0a 09 09 73 65 74 20 66 69 6c 65 20 5b 64 6f  ....set file [do
13a0: 77 6e 6c 6f 61 64 20 24 68 6f 73 74 6e 61 6d 65  wnload $hostname
13b0: 20 24 69 6e 64 65 78 68 61 73 68 5d 0a 09 09 73   $indexhash]...s
13c0: 65 74 20 66 64 20 5b 6f 70 65 6e 20 24 66 69 6c  et fd [open $fil
13d0: 65 5d 0a 09 09 73 65 74 20 64 61 74 61 20 5b 72  e]...set data [r
13e0: 65 61 64 20 24 66 64 5d 0a 09 09 63 6c 6f 73 65  ead $fd]...close
13f0: 20 24 66 64 0a 0a 09 09 73 65 74 20 63 75 72 72   $fd....set curr
1400: 5f 70 61 63 6b 61 67 65 73 20 5b 6c 69 73 74 5d  _packages [list]
1410: 0a 09 09 66 6f 72 65 61 63 68 20 6c 69 6e 65 20  ...foreach line 
1420: 5b 73 70 6c 69 74 20 24 64 61 74 61 20 22 5c 6e  [split $data "\n
1430: 22 5d 20 7b 0a 09 09 09 73 65 74 20 6c 69 6e 65  "] {....set line
1440: 20 5b 73 74 72 69 6e 67 20 74 72 69 6d 20 24 6c   [string trim $l
1450: 69 6e 65 5d 0a 0a 09 09 09 69 66 20 7b 5b 73 74  ine].....if {[st
1460: 72 69 6e 67 20 6d 61 74 63 68 20 22 2a 2f 2a 22  ring match "*/*"
1470: 20 24 6c 69 6e 65 5d 7d 20 7b 0a 09 09 09 09 63   $line]} {.....c
1480: 6f 6e 74 69 6e 75 65 0a 09 09 09 7d 0a 0a 09 09  ontinue....}....
1490: 09 69 66 20 7b 24 6c 69 6e 65 20 3d 3d 20 22 22  .if {$line == ""
14a0: 7d 20 7b 0a 09 09 09 09 63 6f 6e 74 69 6e 75 65  } {.....continue
14b0: 0a 09 09 09 7d 0a 0a 09 09 09 73 65 74 20 77 6f  ....}.....set wo
14c0: 72 6b 20 5b 73 70 6c 69 74 20 24 6c 69 6e 65 20  rk [split $line 
14d0: 22 2c 22 5d 0a 0a 09 09 09 75 6e 73 65 74 20 2d  ","].....unset -
14e0: 6e 6f 63 6f 6d 70 6c 61 69 6e 20 70 6b 67 49 6e  nocomplain pkgIn
14f0: 66 6f 0a 09 09 09 69 66 20 7b 5b 63 61 74 63 68  fo....if {[catch
1500: 20 7b 0a 09 09 09 09 73 65 74 20 70 6b 67 49 6e   {.....set pkgIn
1510: 66 6f 28 70 61 63 6b 61 67 65 29 20 20 5b 6c 69  fo(package)  [li
1520: 6e 64 65 78 20 24 77 6f 72 6b 20 30 5d 0a 09 09  ndex $work 0]...
1530: 09 09 73 65 74 20 70 6b 67 49 6e 66 6f 28 76 65  ..set pkgInfo(ve
1540: 72 73 69 6f 6e 29 20 20 5b 6c 69 6e 64 65 78 20  rsion)  [lindex 
1550: 24 77 6f 72 6b 20 31 5d 0a 09 09 09 09 73 65 74  $work 1].....set
1560: 20 70 6b 67 49 6e 66 6f 28 6f 73 29 20 20 20 20   pkgInfo(os)    
1570: 20 20 20 5b 5f 6e 6f 72 6d 61 6c 69 7a 65 4f 53     [_normalizeOS
1580: 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 32   [lindex $work 2
1590: 5d 5d 0a 09 09 09 09 73 65 74 20 70 6b 67 49 6e  ]].....set pkgIn
15a0: 66 6f 28 63 70 75 41 72 63 68 29 20 20 5b 5f 6e  fo(cpuArch)  [_n
15b0: 6f 72 6d 61 6c 69 7a 65 43 50 55 20 5b 6c 69 6e  ormalizeCPU [lin
15c0: 64 65 78 20 24 77 6f 72 6b 20 33 5d 5d 0a 09 09  dex $work 3]]...
15d0: 09 09 73 65 74 20 70 6b 67 49 6e 66 6f 28 68 61  ..set pkgInfo(ha
15e0: 73 68 29 20 20 20 20 20 5b 73 74 72 69 6e 67 20  sh)     [string 
15f0: 74 6f 6c 6f 77 65 72 20 5b 6c 69 6e 64 65 78 20  tolower [lindex 
1600: 24 77 6f 72 6b 20 34 5d 5d 0a 09 09 09 09 73 65  $work 4]].....se
1610: 74 20 70 6b 67 49 6e 66 6f 28 68 61 73 68 5f 74  t pkgInfo(hash_t
1620: 79 70 65 29 20 22 73 68 61 31 22 0a 09 09 09 09  ype) "sha1".....
1630: 73 65 74 20 70 6b 67 49 6e 66 6f 28 69 73 4c 61  set pkgInfo(isLa
1640: 74 65 73 74 29 20 5b 65 78 70 72 20 7b 21 21 5b  test) [expr {!![
1650: 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 35 5d 7d  lindex $work 5]}
1660: 5d 0a 09 09 09 7d 5d 7d 20 7b 0a 09 09 09 09 63  ]....}]} {.....c
1670: 6f 6e 74 69 6e 75 65 0a 09 09 09 7d 0a 0a 09 09  ontinue....}....
1680: 09 69 66 20 7b 21 5b 5f 69 73 48 61 73 68 20 24  .if {![_isHash $
1690: 70 6b 67 49 6e 66 6f 28 68 61 73 68 29 5d 7d 20  pkgInfo(hash)]} 
16a0: 7b 0a 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a 09  {.....continue..
16b0: 09 09 7d 0a 0a 09 09 09 6c 61 70 70 65 6e 64 20  ..}.....lappend 
16c0: 63 75 72 72 5f 70 61 63 6b 61 67 65 73 20 24 70  curr_packages $p
16d0: 6b 67 49 6e 66 6f 28 68 61 73 68 29 0a 0a 09 09  kgInfo(hash)....
16e0: 09 23 20 44 6f 20 6e 6f 74 20 64 6f 20 61 6e 79  .# Do not do any
16f0: 20 61 64 64 69 74 69 6f 6e 61 6c 20 77 6f 72 6b   additional work
1700: 20 69 66 20 77 65 20 61 6c 72 65 61 64 79 20 68   if we already h
1710: 61 76 65 20 74 68 69 73 20 70 61 63 6b 61 67 65  ave this package
1720: 0a 09 09 09 73 65 74 20 65 78 69 73 74 69 6e 67  ....set existing
1730: 5f 70 61 63 6b 61 67 65 73 20 5b 64 62 20 65 76  _packages [db ev
1740: 61 6c 20 7b 53 45 4c 45 43 54 20 70 61 63 6b 61  al {SELECT packa
1750: 67 65 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73  ge FROM packages
1760: 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65 20   WHERE hostname 
1770: 3d 20 24 68 6f 73 74 6e 61 6d 65 20 41 4e 44 20  = $hostname AND 
1780: 73 68 61 31 20 3d 20 24 70 6b 67 49 6e 66 6f 28  sha1 = $pkgInfo(
1790: 68 61 73 68 29 3b 7d 5d 0a 09 09 09 69 66 20 7b  hash);}]....if {
17a0: 5b 6c 73 65 61 72 63 68 20 2d 65 78 61 63 74 20  [lsearch -exact 
17b0: 24 65 78 69 73 74 69 6e 67 5f 70 61 63 6b 61 67  $existing_packag
17c0: 65 73 20 24 70 6b 67 49 6e 66 6f 28 70 61 63 6b  es $pkgInfo(pack
17d0: 61 67 65 29 5d 20 21 3d 20 2d 31 7d 20 7b 0a 09  age)] != -1} {..
17e0: 09 09 09 63 6f 6e 74 69 6e 75 65 0a 09 09 09 7d  ...continue....}
17f0: 0a 0a 09 09 09 69 66 20 7b 24 70 6b 67 49 6e 66  .....if {$pkgInf
1800: 6f 28 69 73 4c 61 74 65 73 74 29 7d 20 7b 0a 09  o(isLatest)} {..
1810: 09 09 09 64 62 20 65 76 61 6c 20 7b 55 50 44 41  ...db eval {UPDA
1820: 54 45 20 70 61 63 6b 61 67 65 73 20 53 45 54 20  TE packages SET 
1830: 69 73 4c 61 74 65 73 74 20 3d 20 30 20 57 48 45  isLatest = 0 WHE
1840: 52 45 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 68  RE hostname = $h
1850: 6f 73 74 6e 61 6d 65 20 41 4e 44 20 70 61 63 6b  ostname AND pack
1860: 61 67 65 20 3d 20 24 70 6b 67 49 6e 66 6f 28 24  age = $pkgInfo($
1870: 70 61 63 6b 61 67 65 29 20 41 4e 44 20 6f 73 20  package) AND os 
1880: 3d 20 24 70 6b 67 49 6e 66 6f 28 24 70 61 63 6b  = $pkgInfo($pack
1890: 61 67 65 29 20 41 4e 44 20 63 70 75 41 72 63 68  age) AND cpuArch
18a0: 20 3d 20 24 70 6b 67 49 6e 66 6f 28 63 70 75 41   = $pkgInfo(cpuA
18b0: 72 63 68 29 3b 7d 0a 09 09 09 7d 0a 0a 09 09 09  rch);}....}.....
18c0: 64 62 20 65 76 61 6c 20 7b 49 4e 53 45 52 54 20  db eval {INSERT 
18d0: 49 4e 54 4f 20 70 61 63 6b 61 67 65 73 20 28 68  INTO packages (h
18e0: 6f 73 74 6e 61 6d 65 2c 20 73 68 61 31 2c 20 70  ostname, sha1, p
18f0: 61 63 6b 61 67 65 2c 20 76 65 72 73 69 6f 6e 2c  ackage, version,
1900: 20 6f 73 2c 20 63 70 75 41 72 63 68 2c 20 69 73   os, cpuArch, is
1910: 4c 61 74 65 73 74 2c 20 68 61 76 65 4d 61 6e 69  Latest, haveMani
1920: 66 65 73 74 29 20 56 41 4c 55 45 53 20 28 24 68  fest) VALUES ($h
1930: 6f 73 74 6e 61 6d 65 2c 20 24 70 6b 67 49 6e 66  ostname, $pkgInf
1940: 6f 28 68 61 73 68 29 2c 20 24 70 6b 67 49 6e 66  o(hash), $pkgInf
1950: 6f 28 70 61 63 6b 61 67 65 29 2c 20 24 70 6b 67  o(package), $pkg
1960: 49 6e 66 6f 28 76 65 72 73 69 6f 6e 29 2c 20 24  Info(version), $
1970: 70 6b 67 49 6e 66 6f 28 6f 73 29 2c 20 24 70 6b  pkgInfo(os), $pk
1980: 67 49 6e 66 6f 28 63 70 75 41 72 63 68 29 2c 20  gInfo(cpuArch), 
1990: 24 70 6b 67 49 6e 66 6f 28 69 73 4c 61 74 65 73  $pkgInfo(isLates
19a0: 74 29 2c 20 30 29 3b 7d 0a 09 09 7d 0a 0a 09 09  t), 0);}...}....
19b0: 23 20 4c 6f 6f 6b 20 66 6f 72 20 70 61 63 6b 61  # Look for packa
19c0: 67 65 73 20 74 68 61 74 20 68 61 76 65 20 62 65  ges that have be
19d0: 65 6e 20 64 65 6c 65 74 65 64 0a 09 09 73 65 74  en deleted...set
19e0: 20 66 6f 75 6e 64 5f 70 61 63 6b 61 67 65 73 20   found_packages 
19f0: 5b 64 62 20 65 76 61 6c 20 7b 53 45 4c 45 43 54  [db eval {SELECT
1a00: 20 73 68 61 31 20 46 52 4f 4d 20 70 61 63 6b 61   sha1 FROM packa
1a10: 67 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61  ges WHERE hostna
1a20: 6d 65 20 3d 20 24 68 6f 73 74 6e 61 6d 65 3b 7d  me = $hostname;}
1a30: 5d 0a 09 09 66 6f 72 65 61 63 68 20 70 61 63 6b  ]...foreach pack
1a40: 61 67 65 20 24 66 6f 75 6e 64 5f 70 61 63 6b 61  age $found_packa
1a50: 67 65 73 20 7b 0a 09 09 09 73 65 74 20 66 6f 75  ges {....set fou
1a60: 6e 64 5f 70 61 63 6b 61 67 65 73 5f 61 72 72 28  nd_packages_arr(
1a70: 24 70 61 63 6b 61 67 65 29 20 31 0a 09 09 7d 0a  $package) 1...}.
1a80: 0a 09 09 66 6f 72 65 61 63 68 20 70 61 63 6b 61  ...foreach packa
1a90: 67 65 20 24 63 75 72 72 5f 70 61 63 6b 61 67 65  ge $curr_package
1aa0: 73 20 7b 0a 09 09 09 75 6e 73 65 74 20 2d 6e 6f  s {....unset -no
1ab0: 63 6f 6d 70 6c 61 69 6e 20 66 6f 75 6e 64 5f 70  complain found_p
1ac0: 61 63 6b 61 67 65 73 5f 61 72 72 28 24 70 61 63  ackages_arr($pac
1ad0: 6b 61 67 65 29 0a 09 09 7d 0a 0a 09 09 66 6f 72  kage)...}....for
1ae0: 65 61 63 68 20 70 61 63 6b 61 67 65 20 5b 61 72  each package [ar
1af0: 72 61 79 20 6e 61 6d 65 73 20 66 6f 75 6e 64 5f  ray names found_
1b00: 70 61 63 6b 61 67 65 73 5f 61 72 72 5d 20 7b 0a  packages_arr] {.
1b10: 09 09 09 64 62 20 65 76 61 6c 20 7b 44 45 4c 45  ...db eval {DELE
1b20: 54 45 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73  TE FROM packages
1b30: 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65 20   WHERE hostname 
1b40: 3d 20 24 68 6f 73 74 6e 61 6d 65 20 41 4e 44 20  = $hostname AND 
1b50: 73 68 61 31 20 3d 20 24 70 61 63 6b 61 67 65 3b  sha1 = $package;
1b60: 7d 0a 09 09 7d 0a 0a 09 09 64 62 20 65 76 61 6c  }...}....db eval
1b70: 20 7b 49 4e 53 45 52 54 20 4f 52 20 52 45 50 4c   {INSERT OR REPL
1b80: 41 43 45 20 49 4e 54 4f 20 73 69 74 65 73 20 28  ACE INTO sites (
1b90: 68 6f 73 74 6e 61 6d 65 2c 20 6c 61 73 74 55 70  hostname, lastUp
1ba0: 64 61 74 65 2c 20 74 74 6c 29 20 56 41 4c 55 45  date, ttl) VALUE
1bb0: 53 20 28 24 68 6f 73 74 6e 61 6d 65 2c 20 24 6e  S ($hostname, $n
1bc0: 6f 77 2c 20 24 3a 3a 61 70 70 66 73 3a 3a 74 74  ow, $::appfs::tt
1bd0: 6c 29 3b 7d 0a 0a 09 09 72 65 74 75 72 6e 20 43  l);}....return C
1be0: 4f 4d 50 4c 45 54 45 0a 09 7d 0a 0a 09 70 72 6f  OMPLETE..}...pro
1bf0: 63 20 67 65 74 70 6b 67 6d 61 6e 69 66 65 73 74  c getpkgmanifest
1c00: 20 7b 68 6f 73 74 6e 61 6d 65 20 70 61 63 6b 61   {hostname packa
1c10: 67 65 5f 73 68 61 31 7d 20 7b 0a 09 09 73 65 74  ge_sha1} {...set
1c20: 20 68 61 76 65 4d 61 6e 69 66 65 73 74 73 20 5b   haveManifests [
1c30: 64 62 20 65 76 61 6c 20 7b 53 45 4c 45 43 54 20  db eval {SELECT 
1c40: 68 61 76 65 4d 61 6e 69 66 65 73 74 20 46 52 4f  haveManifest FRO
1c50: 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45 52 45  M packages WHERE
1c60: 20 73 68 61 31 20 3d 20 24 70 61 63 6b 61 67 65   sha1 = $package
1c70: 5f 73 68 61 31 20 4c 49 4d 49 54 20 31 3b 7d 5d  _sha1 LIMIT 1;}]
1c80: 0a 09 09 73 65 74 20 68 61 76 65 4d 61 6e 69 66  ...set haveManif
1c90: 65 73 74 20 5b 6c 69 6e 64 65 78 20 24 68 61 76  est [lindex $hav
1ca0: 65 4d 61 6e 69 66 65 73 74 73 20 30 5d 0a 0a 09  eManifests 0]...
1cb0: 09 69 66 20 7b 24 68 61 76 65 4d 61 6e 69 66 65  .if {$haveManife
1cc0: 73 74 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  st} {....return 
1cd0: 43 4f 4d 50 4c 45 54 45 0a 09 09 7d 0a 0a 09 09  COMPLETE...}....
1ce0: 69 66 20 7b 21 5b 5f 69 73 48 61 73 68 20 24 70  if {![_isHash $p
1cf0: 61 63 6b 61 67 65 5f 73 68 61 31 5d 7d 20 7b 0a  ackage_sha1]} {.
1d00: 09 09 09 72 65 74 75 72 6e 20 46 41 49 4c 0a 09  ...return FAIL..
1d10: 09 7d 0a 0a 09 09 73 65 74 20 66 69 6c 65 20 5b  .}....set file [
1d20: 64 6f 77 6e 6c 6f 61 64 20 24 68 6f 73 74 6e 61  download $hostna
1d30: 6d 65 20 24 70 61 63 6b 61 67 65 5f 73 68 61 31  me $package_sha1
1d40: 5d 0a 09 09 73 65 74 20 66 64 20 5b 6f 70 65 6e  ]...set fd [open
1d50: 20 24 66 69 6c 65 5d 0a 09 09 73 65 74 20 70 6b   $file]...set pk
1d60: 67 64 61 74 61 20 5b 72 65 61 64 20 24 66 64 5d  gdata [read $fd]
1d70: 0a 09 09 63 6c 6f 73 65 20 24 66 64 0a 0a 09 09  ...close $fd....
1d80: 64 62 20 74 72 61 6e 73 61 63 74 69 6f 6e 20 7b  db transaction {
1d90: 0a 09 09 09 66 6f 72 65 61 63 68 20 6c 69 6e 65  ....foreach line
1da0: 20 5b 73 70 6c 69 74 20 24 70 6b 67 64 61 74 61   [split $pkgdata
1db0: 20 22 5c 6e 22 5d 20 7b 0a 09 09 09 09 73 65 74   "\n"] {.....set
1dc0: 20 6c 69 6e 65 20 5b 73 74 72 69 6e 67 20 74 72   line [string tr
1dd0: 69 6d 20 24 6c 69 6e 65 5d 0a 0a 09 09 09 09 69  im $line]......i
1de0: 66 20 7b 24 6c 69 6e 65 20 3d 3d 20 22 22 7d 20  f {$line == ""} 
1df0: 7b 0a 09 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a  {......continue.
1e00: 09 09 09 09 7d 0a 0a 09 09 09 09 73 65 74 20 77  ....}......set w
1e10: 6f 72 6b 20 5b 73 70 6c 69 74 20 24 6c 69 6e 65  ork [split $line
1e20: 20 22 2c 22 5d 0a 0a 09 09 09 09 75 6e 73 65 74   ","]......unset
1e30: 20 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 66 69 6c   -nocomplain fil
1e40: 65 49 6e 66 6f 0a 09 09 09 09 73 65 74 20 66 69  eInfo.....set fi
1e50: 6c 65 49 6e 66 6f 28 74 79 70 65 29 20 5b 6c 69  leInfo(type) [li
1e60: 6e 64 65 78 20 24 77 6f 72 6b 20 30 5d 0a 09 09  ndex $work 0]...
1e70: 09 09 73 65 74 20 66 69 6c 65 49 6e 66 6f 28 74  ..set fileInfo(t
1e80: 69 6d 65 29 20 5b 6c 69 6e 64 65 78 20 24 77 6f  ime) [lindex $wo
1e90: 72 6b 20 31 5d 0a 0a 09 09 09 09 73 65 74 20 77  rk 1]......set w
1ea0: 6f 72 6b 20 5b 6c 72 61 6e 67 65 20 24 77 6f 72  ork [lrange $wor
1eb0: 6b 20 32 20 65 6e 64 5d 0a 09 09 09 09 73 77 69  k 2 end].....swi
1ec0: 74 63 68 20 2d 2d 20 24 66 69 6c 65 49 6e 66 6f  tch -- $fileInfo
1ed0: 28 74 79 70 65 29 20 7b 0a 09 09 09 09 09 22 66  (type) {......"f
1ee0: 69 6c 65 22 20 7b 0a 09 09 09 09 09 09 73 65 74  ile" {.......set
1ef0: 20 66 69 6c 65 49 6e 66 6f 28 73 69 7a 65 29 20   fileInfo(size) 
1f00: 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 30 5d  [lindex $work 0]
1f10: 0a 09 09 09 09 09 09 73 65 74 20 66 69 6c 65 49  .......set fileI
1f20: 6e 66 6f 28 70 65 72 6d 73 29 20 5b 6c 69 6e 64  nfo(perms) [lind
1f30: 65 78 20 24 77 6f 72 6b 20 31 5d 0a 09 09 09 09  ex $work 1].....
1f40: 09 09 73 65 74 20 66 69 6c 65 49 6e 66 6f 28 73  ..set fileInfo(s
1f50: 68 61 31 29 20 5b 6c 69 6e 64 65 78 20 24 77 6f  ha1) [lindex $wo
1f60: 72 6b 20 32 5d 0a 0a 09 09 09 09 09 09 73 65 74  rk 2]........set
1f70: 20 77 6f 72 6b 20 5b 6c 72 61 6e 67 65 20 24 77   work [lrange $w
1f80: 6f 72 6b 20 33 20 65 6e 64 5d 0a 09 09 09 09 09  ork 3 end]......
1f90: 7d 0a 09 09 09 09 09 22 73 79 6d 6c 69 6e 6b 22  }......"symlink"
1fa0: 20 7b 0a 09 09 09 09 09 09 73 65 74 20 66 69 6c   {.......set fil
1fb0: 65 49 6e 66 6f 28 73 6f 75 72 63 65 29 20 5b 6c  eInfo(source) [l
1fc0: 69 6e 64 65 78 20 24 77 6f 72 6b 20 30 5d 0a 09  index $work 0]..
1fd0: 09 09 09 09 09 73 65 74 20 77 6f 72 6b 20 5b 6c  .....set work [l
1fe0: 72 61 6e 67 65 20 24 77 6f 72 6b 20 31 20 65 6e  range $work 1 en
1ff0: 64 5d 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 0a  d]......}.....}.
2000: 0a 09 09 09 09 73 65 74 20 66 69 6c 65 49 6e 66  .....set fileInf
2010: 6f 28 6e 61 6d 65 29 20 5b 6a 6f 69 6e 20 24 77  o(name) [join $w
2020: 6f 72 6b 20 22 2c 22 5d 0a 09 09 09 09 73 65 74  ork ","].....set
2030: 20 66 69 6c 65 49 6e 66 6f 28 6e 61 6d 65 29 20   fileInfo(name) 
2040: 5b 73 70 6c 69 74 20 5b 73 74 72 69 6e 67 20 74  [split [string t
2050: 72 69 6d 20 24 66 69 6c 65 49 6e 66 6f 28 6e 61  rim $fileInfo(na
2060: 6d 65 29 20 22 2f 22 5d 20 22 2f 22 5d 0a 09 09  me) "/"] "/"]...
2070: 09 09 73 65 74 20 66 69 6c 65 49 6e 66 6f 28 64  ..set fileInfo(d
2080: 69 72 65 63 74 6f 72 79 29 20 5b 6a 6f 69 6e 20  irectory) [join 
2090: 5b 6c 72 61 6e 67 65 20 24 66 69 6c 65 49 6e 66  [lrange $fileInf
20a0: 6f 28 6e 61 6d 65 29 20 30 20 65 6e 64 2d 31 5d  o(name) 0 end-1]
20b0: 20 22 2f 22 5d 0a 09 09 09 09 73 65 74 20 66 69   "/"].....set fi
20c0: 6c 65 49 6e 66 6f 28 6e 61 6d 65 29 20 5b 6c 69  leInfo(name) [li
20d0: 6e 64 65 78 20 24 66 69 6c 65 49 6e 66 6f 28 6e  ndex $fileInfo(n
20e0: 61 6d 65 29 20 65 6e 64 5d 0a 0a 09 09 09 09 64  ame) end]......d
20f0: 62 20 65 76 61 6c 20 7b 49 4e 53 45 52 54 20 49  b eval {INSERT I
2100: 4e 54 4f 20 66 69 6c 65 73 20 28 70 61 63 6b 61  NTO files (packa
2110: 67 65 5f 73 68 61 31 2c 20 74 79 70 65 2c 20 74  ge_sha1, type, t
2120: 69 6d 65 2c 20 73 6f 75 72 63 65 2c 20 73 69 7a  ime, source, siz
2130: 65 2c 20 70 65 72 6d 73 2c 20 66 69 6c 65 5f 73  e, perms, file_s
2140: 68 61 31 2c 20 66 69 6c 65 5f 6e 61 6d 65 2c 20  ha1, file_name, 
2150: 66 69 6c 65 5f 64 69 72 65 63 74 6f 72 79 29 20  file_directory) 
2160: 56 41 4c 55 45 53 20 28 24 70 61 63 6b 61 67 65  VALUES ($package
2170: 5f 73 68 61 31 2c 20 24 66 69 6c 65 49 6e 66 6f  _sha1, $fileInfo
2180: 28 74 79 70 65 29 2c 20 24 66 69 6c 65 49 6e 66  (type), $fileInf
2190: 6f 28 74 69 6d 65 29 2c 20 24 66 69 6c 65 49 6e  o(time), $fileIn
21a0: 66 6f 28 73 6f 75 72 63 65 29 2c 20 24 66 69 6c  fo(source), $fil
21b0: 65 49 6e 66 6f 28 73 69 7a 65 29 2c 20 24 66 69  eInfo(size), $fi
21c0: 6c 65 49 6e 66 6f 28 70 65 72 6d 73 29 2c 20 24  leInfo(perms), $
21d0: 66 69 6c 65 49 6e 66 6f 28 73 68 61 31 29 2c 20  fileInfo(sha1), 
21e0: 24 66 69 6c 65 49 6e 66 6f 28 6e 61 6d 65 29 2c  $fileInfo(name),
21f0: 20 24 66 69 6c 65 49 6e 66 6f 28 64 69 72 65 63   $fileInfo(direc
2200: 74 6f 72 79 29 20 29 3b 7d 0a 09 09 09 09 64 62  tory) );}.....db
2210: 20 65 76 61 6c 20 7b 55 50 44 41 54 45 20 70 61   eval {UPDATE pa
2220: 63 6b 61 67 65 73 20 53 45 54 20 68 61 76 65 4d  ckages SET haveM
2230: 61 6e 69 66 65 73 74 20 3d 20 31 20 57 48 45 52  anifest = 1 WHER
2240: 45 20 73 68 61 31 20 3d 20 24 70 61 63 6b 61 67  E sha1 = $packag
2250: 65 5f 73 68 61 31 3b 7d 0a 09 09 09 7d 0a 09 09  e_sha1;}....}...
2260: 7d 0a 0a 09 09 72 65 74 75 72 6e 20 43 4f 4d 50  }....return COMP
2270: 4c 45 54 45 0a 09 7d 0a 0a 09 70 72 6f 63 20 5f  LETE..}...proc _
2280: 6c 6f 63 61 6c 70 61 74 68 20 7b 70 61 63 6b 61  localpath {packa
2290: 67 65 20 68 6f 73 74 6e 61 6d 65 20 66 69 6c 65  ge hostname file
22a0: 7d 20 7b 0a 09 09 73 65 74 20 68 6f 6d 65 64 69  } {...set homedi
22b0: 72 20 5b 3a 3a 61 70 70 66 73 64 3a 3a 67 65 74  r [::appfsd::get
22c0: 5f 68 6f 6d 65 64 69 72 5d 0a 09 09 73 65 74 20  _homedir]...set 
22d0: 64 69 72 20 5b 66 69 6c 65 20 6a 6f 69 6e 20 24  dir [file join $
22e0: 68 6f 6d 65 64 69 72 20 2e 61 70 70 66 73 20 22  homedir .appfs "
22f0: 2e 2f 24 7b 70 61 63 6b 61 67 65 7d 40 24 7b 68  ./${package}@${h
2300: 6f 73 74 6e 61 6d 65 7d 22 20 22 2e 2f 24 7b 66  ostname}" "./${f
2310: 69 6c 65 7d 22 5d 0a 09 09 72 65 74 75 72 6e 20  ile}"]...return 
2320: 24 64 69 72 0a 09 7d 0a 0a 09 70 72 6f 63 20 5f  $dir..}...proc _
2330: 77 68 69 74 65 6f 75 74 70 61 74 68 20 7b 70 61  whiteoutpath {pa
2340: 63 6b 61 67 65 20 68 6f 73 74 6e 61 6d 65 20 66  ckage hostname f
2350: 69 6c 65 7d 20 7b 0a 09 09 73 65 74 20 68 6f 6d  ile} {...set hom
2360: 65 64 69 72 20 5b 3a 3a 61 70 70 66 73 64 3a 3a  edir [::appfsd::
2370: 67 65 74 5f 68 6f 6d 65 64 69 72 5d 0a 09 09 73  get_homedir]...s
2380: 65 74 20 64 69 72 20 5b 66 69 6c 65 20 6a 6f 69  et dir [file joi
2390: 6e 20 24 68 6f 6d 65 64 69 72 20 2e 61 70 70 66  n $homedir .appf
23a0: 73 20 22 2e 2f 24 7b 70 61 63 6b 61 67 65 7d 40  s "./${package}@
23b0: 24 7b 68 6f 73 74 6e 61 6d 65 7d 22 20 22 2e 41  ${hostname}" ".A
23c0: 50 50 46 53 2e 57 48 49 54 45 4f 55 54 22 20 22  PPFS.WHITEOUT" "
23d0: 2e 2f 24 7b 66 69 6c 65 7d 2e 41 50 50 46 53 2e  ./${file}.APPFS.
23e0: 57 48 49 54 45 4f 55 54 22 5d 0a 09 09 72 65 74  WHITEOUT"]...ret
23f0: 75 72 6e 20 24 64 69 72 0a 09 7d 0a 0a 09 70 72  urn $dir..}...pr
2400: 6f 63 20 5f 70 61 72 73 65 70 61 74 68 20 7b 70  oc _parsepath {p
2410: 61 74 68 7d 20 7b 0a 09 09 73 65 74 20 70 61 74  ath} {...set pat
2420: 68 20 5b 73 74 72 69 6e 67 20 74 72 69 6d 20 24  h [string trim $
2430: 70 61 74 68 20 22 2f 22 5d 0a 09 09 73 65 74 20  path "/"]...set 
2440: 70 61 74 68 20 5b 73 70 6c 69 74 20 24 70 61 74  path [split $pat
2450: 68 20 22 2f 22 5d 0a 09 09 73 65 74 20 70 61 74  h "/"]...set pat
2460: 68 6c 65 6e 20 5b 6c 6c 65 6e 67 74 68 20 24 70  hlen [llength $p
2470: 61 74 68 5d 0a 0a 09 09 61 72 72 61 79 20 73 65  ath]....array se
2480: 74 20 72 65 74 76 61 6c 20 5b 6c 69 73 74 20 5f  t retval [list _
2490: 63 68 69 6c 64 72 65 6e 20 73 69 74 65 73 20 5f  children sites _
24a0: 74 79 70 65 20 74 6f 70 6c 65 76 65 6c 5d 0a 0a  type toplevel]..
24b0: 09 09 69 66 20 7b 24 70 61 74 68 6c 65 6e 20 3e  ..if {$pathlen >
24c0: 20 30 7d 20 7b 0a 09 09 09 73 65 74 20 72 65 74   0} {....set ret
24d0: 76 61 6c 28 68 6f 73 74 6e 61 6d 65 29 20 5b 6c  val(hostname) [l
24e0: 69 6e 64 65 78 20 24 70 61 74 68 20 30 5d 0a 09  index $path 0]..
24f0: 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 63 68  ..set retval(_ch
2500: 69 6c 64 72 65 6e 29 20 70 61 63 6b 61 67 65 73  ildren) packages
2510: 0a 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f  ....set retval(_
2520: 74 79 70 65 29 20 73 69 74 65 73 0a 0a 09 09 09  type) sites.....
2530: 69 66 20 7b 24 70 61 74 68 6c 65 6e 20 3e 20 31  if {$pathlen > 1
2540: 7d 20 7b 0a 09 09 09 09 73 65 74 20 70 61 63 6b  } {.....set pack
2550: 61 67 65 20 5b 6c 69 6e 64 65 78 20 24 70 61 74  age [lindex $pat
2560: 68 20 31 5d 0a 09 09 09 09 69 66 20 7b 5b 73 74  h 1].....if {[st
2570: 72 69 6e 67 20 6c 65 6e 67 74 68 20 24 70 61 63  ring length $pac
2580: 6b 61 67 65 5d 20 3d 3d 20 22 34 30 22 20 26 26  kage] == "40" &&
2590: 20 5b 72 65 67 65 78 70 20 7b 5e 5b 61 2d 66 41   [regexp {^[a-fA
25a0: 2d 46 30 2d 39 5d 2a 24 7d 20 24 70 61 63 6b 61  -F0-9]*$} $packa
25b0: 67 65 5d 7d 20 7b 0a 09 09 09 09 09 73 65 74 20  ge]} {......set 
25c0: 72 65 74 76 61 6c 28 70 61 63 6b 61 67 65 5f 73  retval(package_s
25d0: 68 61 31 29 20 24 70 61 63 6b 61 67 65 0a 09 09  ha1) $package...
25e0: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 63  ...set retval(_c
25f0: 68 69 6c 64 72 65 6e 29 20 66 69 6c 65 73 0a 09  hildren) files..
2600: 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f  ....set retval(_
2610: 74 79 70 65 29 20 66 69 6c 65 73 0a 0a 09 09 09  type) files.....
2620: 09 09 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65 76  ..::appfs::db ev
2630: 61 6c 20 7b 53 45 4c 45 43 54 20 70 61 63 6b 61  al {SELECT packa
2640: 67 65 2c 20 6f 73 2c 20 63 70 75 41 72 63 68 2c  ge, os, cpuArch,
2650: 20 76 65 72 73 69 6f 6e 20 46 52 4f 4d 20 70 61   version FROM pa
2660: 63 6b 61 67 65 73 20 57 48 45 52 45 20 73 68 61  ckages WHERE sha
2670: 31 20 3d 20 24 72 65 74 76 61 6c 28 70 61 63 6b  1 = $retval(pack
2680: 61 67 65 5f 73 68 61 31 29 3b 7d 20 70 6b 67 69  age_sha1);} pkgi
2690: 6e 66 6f 20 7b 7d 0a 09 09 09 09 09 73 65 74 20  nfo {}......set 
26a0: 72 65 74 76 61 6c 28 70 61 63 6b 61 67 65 29 20  retval(package) 
26b0: 24 70 6b 67 69 6e 66 6f 28 70 61 63 6b 61 67 65  $pkginfo(package
26c0: 29 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61  )......set retva
26d0: 6c 28 6f 73 29 20 24 70 6b 67 69 6e 66 6f 28 6f  l(os) $pkginfo(o
26e0: 73 29 0a 09 09 09 09 09 73 65 74 20 72 65 74 76  s)......set retv
26f0: 61 6c 28 63 70 75 29 20 24 70 6b 67 69 6e 66 6f  al(cpu) $pkginfo
2700: 28 63 70 75 41 72 63 68 29 0a 09 09 09 09 09 73  (cpuArch)......s
2710: 65 74 20 72 65 74 76 61 6c 28 76 65 72 73 69 6f  et retval(versio
2720: 6e 29 20 24 70 6b 67 69 6e 66 6f 28 76 65 72 73  n) $pkginfo(vers
2730: 69 6f 6e 29 0a 0a 09 09 09 09 09 69 66 20 7b 24  ion).......if {$
2740: 70 61 74 68 6c 65 6e 20 3e 20 32 7d 20 7b 0a 09  pathlen > 2} {..
2750: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
2760: 66 69 6c 65 29 20 5b 6a 6f 69 6e 20 5b 6c 72 61  file) [join [lra
2770: 6e 67 65 20 24 70 61 74 68 20 32 20 65 6e 64 5d  nge $path 2 end]
2780: 20 22 2f 22 5d 0a 09 09 09 09 09 7d 20 65 6c 73   "/"]......} els
2790: 65 20 7b 0a 09 09 09 09 09 09 73 65 74 20 72 65  e {.......set re
27a0: 74 76 61 6c 28 66 69 6c 65 29 20 22 22 0a 09 09  tval(file) ""...
27b0: 09 09 09 7d 0a 09 09 09 09 7d 20 65 6c 73 65 20  ...}.....} else 
27c0: 7b 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61  {......set retva
27d0: 6c 28 70 61 63 6b 61 67 65 29 20 24 70 61 63 6b  l(package) $pack
27e0: 61 67 65 0a 09 09 09 09 09 73 65 74 20 72 65 74  age......set ret
27f0: 76 61 6c 28 5f 63 68 69 6c 64 72 65 6e 29 20 6f  val(_children) o
2800: 73 2d 63 70 75 0a 09 09 09 09 09 73 65 74 20 72  s-cpu......set r
2810: 65 74 76 61 6c 28 5f 74 79 70 65 29 20 70 61 63  etval(_type) pac
2820: 6b 61 67 65 73 0a 0a 09 09 09 09 09 69 66 20 7b  kages.......if {
2830: 24 70 61 74 68 6c 65 6e 20 3e 20 32 7d 20 7b 0a  $pathlen > 2} {.
2840: 09 09 09 09 09 09 73 65 74 20 6f 73 5f 63 70 75  ......set os_cpu
2850: 20 5b 6c 69 6e 64 65 78 20 24 70 61 74 68 20 32   [lindex $path 2
2860: 5d 0a 09 09 09 09 09 09 73 65 74 20 6f 73 5f 63  ].......set os_c
2870: 70 75 20 5b 73 70 6c 69 74 20 24 6f 73 5f 63 70  pu [split $os_cp
2880: 75 20 22 2d 22 5d 0a 0a 09 09 09 09 09 09 73 65  u "-"]........se
2890: 74 20 72 65 74 76 61 6c 28 6f 73 29 20 5b 6c 69  t retval(os) [li
28a0: 6e 64 65 78 20 24 6f 73 5f 63 70 75 20 30 5d 0a  ndex $os_cpu 0].
28b0: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
28c0: 28 63 70 75 29 20 5b 6c 69 6e 64 65 78 20 24 6f  (cpu) [lindex $o
28d0: 73 5f 63 70 75 20 31 5d 0a 09 09 09 09 09 09 73  s_cpu 1].......s
28e0: 65 74 20 72 65 74 76 61 6c 28 5f 63 68 69 6c 64  et retval(_child
28f0: 72 65 6e 29 20 76 65 72 73 69 6f 6e 73 0a 09 09  ren) versions...
2900: 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f  ....set retval(_
2910: 74 79 70 65 29 20 6f 73 2d 63 70 75 0a 0a 09 09  type) os-cpu....
2920: 09 09 09 09 69 66 20 7b 24 70 61 74 68 6c 65 6e  ....if {$pathlen
2930: 20 3e 20 33 7d 20 7b 0a 09 09 09 09 09 09 09 73   > 3} {........s
2940: 65 74 20 72 65 74 76 61 6c 28 76 65 72 73 69 6f  et retval(versio
2950: 6e 29 20 5b 6c 69 6e 64 65 78 20 24 70 61 74 68  n) [lindex $path
2960: 20 33 5d 0a 09 09 09 09 09 09 09 73 65 74 20 72   3]........set r
2970: 65 74 76 61 6c 28 5f 63 68 69 6c 64 72 65 6e 29  etval(_children)
2980: 20 66 69 6c 65 73 0a 09 09 09 09 09 09 09 73 65   files........se
2990: 74 20 72 65 74 76 61 6c 28 5f 74 79 70 65 29 20  t retval(_type) 
29a0: 76 65 72 73 69 6f 6e 73 0a 0a 09 09 09 09 09 09  versions........
29b0: 09 73 65 74 20 72 65 74 76 61 6c 28 70 61 63 6b  .set retval(pack
29c0: 61 67 65 5f 73 68 61 31 29 20 5b 3a 3a 61 70 70  age_sha1) [::app
29d0: 66 73 3a 3a 64 62 20 6f 6e 65 63 6f 6c 75 6d 6e  fs::db onecolumn
29e0: 20 7b 53 45 4c 45 43 54 20 73 68 61 31 20 46 52   {SELECT sha1 FR
29f0: 4f 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45 52  OM packages WHER
2a00: 45 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 72 65  E hostname = $re
2a10: 74 76 61 6c 28 68 6f 73 74 6e 61 6d 65 29 20 41  tval(hostname) A
2a20: 4e 44 20 6f 73 20 3d 20 24 72 65 74 76 61 6c 28  ND os = $retval(
2a30: 6f 73 29 20 41 4e 44 20 63 70 75 41 72 63 68 20  os) AND cpuArch 
2a40: 3d 20 24 72 65 74 76 61 6c 28 63 70 75 29 20 41  = $retval(cpu) A
2a50: 4e 44 20 76 65 72 73 69 6f 6e 20 3d 20 24 72 65  ND version = $re
2a60: 74 76 61 6c 28 76 65 72 73 69 6f 6e 29 3b 7d 5d  tval(version);}]
2a70: 0a 09 09 09 09 09 09 09 69 66 20 7b 24 72 65 74  ........if {$ret
2a80: 76 61 6c 28 70 61 63 6b 61 67 65 5f 73 68 61 31  val(package_sha1
2a90: 29 20 3d 3d 20 22 22 7d 20 7b 0a 09 09 09 09 09  ) == ""} {......
2aa0: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 63  ...set retval(_c
2ab0: 68 69 6c 64 72 65 6e 29 20 64 65 61 64 0a 09 09  hildren) dead...
2ac0: 09 09 09 09 09 09 72 65 74 75 72 6e 20 5b 61 72  ......return [ar
2ad0: 72 61 79 20 67 65 74 20 72 65 74 76 61 6c 5d 0a  ray get retval].
2ae0: 09 09 09 09 09 09 09 7d 0a 0a 09 09 09 09 09 09  .......}........
2af0: 09 69 66 20 7b 24 70 61 74 68 6c 65 6e 20 3e 20  .if {$pathlen > 
2b00: 34 7d 20 7b 0a 09 09 09 09 09 09 09 09 73 65 74  4} {.........set
2b10: 20 72 65 74 76 61 6c 28 5f 74 79 70 65 29 20 66   retval(_type) f
2b20: 69 6c 65 73 0a 09 09 09 09 09 09 09 09 73 65 74  iles.........set
2b30: 20 72 65 74 76 61 6c 28 66 69 6c 65 29 20 5b 6a   retval(file) [j
2b40: 6f 69 6e 20 5b 6c 72 61 6e 67 65 20 24 70 61 74  oin [lrange $pat
2b50: 68 20 34 20 65 6e 64 5d 20 22 2f 22 5d 0a 09 09  h 4 end] "/"]...
2b60: 09 09 09 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09  .....} else {...
2b70: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
2b80: 28 5f 74 79 70 65 29 20 66 69 6c 65 73 0a 09 09  (_type) files...
2b90: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
2ba0: 28 66 69 6c 65 29 20 22 22 0a 09 09 09 09 09 09  (file) "".......
2bb0: 09 7d 0a 09 09 09 09 09 09 7d 0a 09 09 09 09 09  .}.......}......
2bc0: 7d 0a 09 09 09 09 7d 0a 09 09 09 7d 0a 09 09 7d  }.....}....}...}
2bd0: 0a 0a 09 09 72 65 74 75 72 6e 20 5b 61 72 72 61  ....return [arra
2be0: 79 20 67 65 74 20 72 65 74 76 61 6c 5d 0a 09 7d  y get retval]..}
2bf0: 0a 0a 09 70 72 6f 63 20 67 65 74 63 68 69 6c 64  ...proc getchild
2c00: 72 65 6e 20 7b 64 69 72 7d 20 7b 0a 09 09 61 72  ren {dir} {...ar
2c10: 72 61 79 20 73 65 74 20 70 61 74 68 69 6e 66 6f  ray set pathinfo
2c20: 20 5b 5f 70 61 72 73 65 70 61 74 68 20 24 64 69   [_parsepath $di
2c30: 72 5d 0a 0a 09 09 73 77 69 74 63 68 20 2d 2d 20  r]....switch -- 
2c40: 24 70 61 74 68 69 6e 66 6f 28 5f 63 68 69 6c 64  $pathinfo(_child
2c50: 72 65 6e 29 20 7b 0a 09 09 09 22 73 69 74 65 73  ren) {...."sites
2c60: 22 20 7b 0a 09 09 09 09 72 65 74 75 72 6e 20 5b  " {.....return [
2c70: 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65 76 61 6c  ::appfs::db eval
2c80: 20 7b 53 45 4c 45 43 54 20 44 49 53 54 49 4e 43   {SELECT DISTINC
2c90: 54 20 68 6f 73 74 6e 61 6d 65 20 46 52 4f 4d 20  T hostname FROM 
2ca0: 70 61 63 6b 61 67 65 73 3b 7d 5d 0a 09 09 09 7d  packages;}]....}
2cb0: 0a 09 09 09 22 70 61 63 6b 61 67 65 73 22 20 7b  ...."packages" {
2cc0: 0a 09 09 09 09 63 61 74 63 68 20 7b 0a 09 09 09  .....catch {....
2cd0: 09 09 3a 3a 61 70 70 66 73 3a 3a 67 65 74 69 6e  ..::appfs::getin
2ce0: 64 65 78 20 24 70 61 74 68 69 6e 66 6f 28 68 6f  dex $pathinfo(ho
2cf0: 73 74 6e 61 6d 65 29 0a 09 09 09 09 7d 0a 0a 09  stname).....}...
2d00: 09 09 09 72 65 74 75 72 6e 20 5b 3a 3a 61 70 70  ...return [::app
2d10: 66 73 3a 3a 64 62 20 65 76 61 6c 20 7b 53 45 4c  fs::db eval {SEL
2d20: 45 43 54 20 44 49 53 54 49 4e 43 54 20 70 61 63  ECT DISTINCT pac
2d30: 6b 61 67 65 20 46 52 4f 4d 20 70 61 63 6b 61 67  kage FROM packag
2d40: 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d  es WHERE hostnam
2d50: 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f  e = $pathinfo(ho
2d60: 73 74 6e 61 6d 65 29 3b 7d 5d 0a 09 09 09 7d 0a  stname);}]....}.
2d70: 09 09 09 22 6f 73 2d 63 70 75 22 20 7b 0a 09 09  ..."os-cpu" {...
2d80: 09 09 73 65 74 20 72 65 74 76 61 6c 20 5b 3a 3a  ..set retval [::
2d90: 61 70 70 66 73 3a 3a 64 62 20 65 76 61 6c 20 7b  appfs::db eval {
2da0: 53 45 4c 45 43 54 20 44 49 53 54 49 4e 43 54 20  SELECT DISTINCT 
2db0: 6f 73 20 7c 7c 20 22 2d 22 20 7c 7c 20 63 70 75  os || "-" || cpu
2dc0: 41 72 63 68 20 46 52 4f 4d 20 70 61 63 6b 61 67  Arch FROM packag
2dd0: 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d  es WHERE hostnam
2de0: 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f  e = $pathinfo(ho
2df0: 73 74 6e 61 6d 65 29 20 41 4e 44 20 70 61 63 6b  stname) AND pack
2e00: 61 67 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28  age = $pathinfo(
2e10: 70 61 63 6b 61 67 65 29 3b 7d 5d 0a 0a 09 09 09  package);}].....
2e20: 09 6c 61 70 70 65 6e 64 20 72 65 74 76 61 6c 20  .lappend retval 
2e30: 22 70 6c 61 74 66 6f 72 6d 22 0a 0a 09 09 09 09  "platform"......
2e40: 72 65 74 75 72 6e 20 24 72 65 74 76 61 6c 0a 09  return $retval..
2e50: 09 09 7d 0a 09 09 09 22 76 65 72 73 69 6f 6e 73  ..}...."versions
2e60: 22 20 7b 0a 09 09 09 09 73 65 74 20 72 65 74 76  " {.....set retv
2e70: 61 6c 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20  al [::appfs::db 
2e80: 65 76 61 6c 20 7b 0a 09 09 09 09 09 53 45 4c 45  eval {......SELE
2e90: 43 54 20 44 49 53 54 49 4e 43 54 20 76 65 72 73  CT DISTINCT vers
2ea0: 69 6f 6e 20 46 52 4f 4d 20 70 61 63 6b 61 67 65  ion FROM package
2eb0: 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65  s WHERE hostname
2ec0: 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73   = $pathinfo(hos
2ed0: 74 6e 61 6d 65 29 20 41 4e 44 20 70 61 63 6b 61  tname) AND packa
2ee0: 67 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 70  ge = $pathinfo(p
2ef0: 61 63 6b 61 67 65 29 20 41 4e 44 20 6f 73 20 3d  ackage) AND os =
2f00: 20 24 70 61 74 68 69 6e 66 6f 28 6f 73 29 20 41   $pathinfo(os) A
2f10: 4e 44 20 63 70 75 41 72 63 68 20 3d 20 24 70 61  ND cpuArch = $pa
2f20: 74 68 69 6e 66 6f 28 63 70 75 29 3b 0a 09 09 09  thinfo(cpu);....
2f30: 09 7d 5d 0a 0a 09 09 09 09 6c 61 70 70 65 6e 64  .}]......lappend
2f40: 20 72 65 74 76 61 6c 20 22 6c 61 74 65 73 74 22   retval "latest"
2f50: 0a 0a 09 09 09 09 72 65 74 75 72 6e 20 24 72 65  ......return $re
2f60: 74 76 61 6c 0a 09 09 09 7d 0a 09 09 09 22 66 69  tval....}...."fi
2f70: 6c 65 73 22 20 7b 0a 09 09 09 09 63 61 74 63 68  les" {.....catch
2f80: 20 7b 0a 09 09 09 09 09 3a 3a 61 70 70 66 73 3a   {......::appfs:
2f90: 3a 67 65 74 70 6b 67 6d 61 6e 69 66 65 73 74 20  :getpkgmanifest 
2fa0: 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61  $pathinfo(hostna
2fb0: 6d 65 29 20 24 70 61 74 68 69 6e 66 6f 28 70 61  me) $pathinfo(pa
2fc0: 63 6b 61 67 65 5f 73 68 61 31 29 0a 09 09 09 09  ckage_sha1).....
2fd0: 7d 0a 0a 09 09 09 09 73 65 74 20 72 65 74 76 61  }......set retva
2fe0: 6c 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65  l [::appfs::db e
2ff0: 76 61 6c 20 7b 53 45 4c 45 43 54 20 44 49 53 54  val {SELECT DIST
3000: 49 4e 43 54 20 66 69 6c 65 5f 6e 61 6d 65 20 46  INCT file_name F
3010: 52 4f 4d 20 66 69 6c 65 73 20 57 48 45 52 45 20  ROM files WHERE 
3020: 70 61 63 6b 61 67 65 5f 73 68 61 31 20 3d 20 24  package_sha1 = $
3030: 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65  pathinfo(package
3040: 5f 73 68 61 31 29 20 41 4e 44 20 66 69 6c 65 5f  _sha1) AND file_
3050: 64 69 72 65 63 74 6f 72 79 20 3d 20 24 70 61 74  directory = $pat
3060: 68 69 6e 66 6f 28 66 69 6c 65 29 3b 7d 5d 0a 0a  hinfo(file);}]..
3070: 09 09 09 09 69 66 20 7b 5b 69 6e 66 6f 20 65 78  ....if {[info ex
3080: 69 73 74 73 20 70 61 74 68 69 6e 66 6f 28 70 61  ists pathinfo(pa
3090: 63 6b 61 67 65 29 5d 20 26 26 20 5b 69 6e 66 6f  ckage)] && [info
30a0: 20 65 78 69 73 74 73 20 70 61 74 68 69 6e 66 6f   exists pathinfo
30b0: 28 68 6f 73 74 6e 61 6d 65 29 5d 20 26 26 20 5b  (hostname)] && [
30c0: 69 6e 66 6f 20 65 78 69 73 74 73 20 70 61 74 68  info exists path
30d0: 69 6e 66 6f 28 66 69 6c 65 29 5d 7d 20 7b 0a 09  info(file)]} {..
30e0: 09 09 09 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09  ...._as_user {..
30f0: 09 09 09 09 09 73 65 74 20 64 69 72 20 5b 5f 6c  .....set dir [_l
3100: 6f 63 61 6c 70 61 74 68 20 24 70 61 74 68 69 6e  ocalpath $pathin
3110: 66 6f 28 70 61 63 6b 61 67 65 29 20 24 70 61 74  fo(package) $pat
3120: 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20  hinfo(hostname) 
3130: 24 70 61 74 68 69 6e 66 6f 28 66 69 6c 65 29 5d  $pathinfo(file)]
3140: 0a 09 09 09 09 09 09 73 65 74 20 77 68 69 74 65  .......set white
3150: 6f 75 74 64 69 72 20 5b 73 74 72 69 6e 67 20 72  outdir [string r
3160: 61 6e 67 65 20 5b 5f 77 68 69 74 65 6f 75 74 70  ange [_whiteoutp
3170: 61 74 68 20 24 70 61 74 68 69 6e 66 6f 28 70 61  ath $pathinfo(pa
3180: 63 6b 61 67 65 29 20 24 70 61 74 68 69 6e 66 6f  ckage) $pathinfo
3190: 28 68 6f 73 74 6e 61 6d 65 29 20 24 70 61 74 68  (hostname) $path
31a0: 69 6e 66 6f 28 66 69 6c 65 29 5d 20 30 20 65 6e  info(file)] 0 en
31b0: 64 2d 31 35 5d 0a 0a 09 09 09 09 09 09 66 6f 72  d-15]........for
31c0: 65 61 63 68 20 66 69 6c 65 20 5b 67 6c 6f 62 20  each file [glob 
31d0: 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 2d 74 61 69  -nocomplain -tai
31e0: 6c 73 20 2d 64 69 72 65 63 74 6f 72 79 20 24 77  ls -directory $w
31f0: 68 69 74 65 6f 75 74 64 69 72 20 7b 7b 2e 2c 7d  hiteoutdir {{.,}
3200: 2a 2e 41 50 50 46 53 2e 57 48 49 54 45 4f 55 54  *.APPFS.WHITEOUT
3210: 7d 5d 20 7b 0a 09 09 09 09 09 09 09 73 65 74 20  }] {........set 
3220: 72 65 6d 6f 76 65 20 5b 73 74 72 69 6e 67 20 72  remove [string r
3230: 61 6e 67 65 20 24 66 69 6c 65 20 30 20 65 6e 64  ange $file 0 end
3240: 2d 31 35 5d 0a 09 09 09 09 09 09 09 73 65 74 20  -15]........set 
3250: 69 64 78 20 5b 6c 73 65 61 72 63 68 20 2d 65 78  idx [lsearch -ex
3260: 61 63 74 20 24 72 65 74 76 61 6c 20 24 72 65 6d  act $retval $rem
3270: 6f 76 65 5d 0a 09 09 09 09 09 09 09 69 66 20 7b  ove]........if {
3280: 24 69 64 78 20 21 3d 20 2d 31 7d 20 7b 0a 09 09  $idx != -1} {...
3290: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
32a0: 20 5b 6c 72 65 70 6c 61 63 65 20 24 72 65 74 76   [lreplace $retv
32b0: 61 6c 20 24 69 64 78 20 24 69 64 78 5d 0a 09 09  al $idx $idx]...
32c0: 09 09 09 09 09 7d 0a 09 09 09 09 09 09 7d 0a 0a  .....}.......}..
32d0: 09 09 09 09 09 09 66 6f 72 65 61 63 68 20 66 69  ......foreach fi
32e0: 6c 65 20 5b 67 6c 6f 62 20 2d 6e 6f 63 6f 6d 70  le [glob -nocomp
32f0: 6c 61 69 6e 20 2d 74 61 69 6c 73 20 2d 64 69 72  lain -tails -dir
3300: 65 63 74 6f 72 79 20 24 64 69 72 20 2d 74 79 70  ectory $dir -typ
3310: 65 73 20 7b 64 20 66 20 6c 20 70 20 73 7d 20 7b  es {d f l p s} {
3320: 7b 2e 2c 7d 2a 7d 5d 20 7b 0a 09 09 09 09 09 09  {.,}*}] {.......
3330: 09 69 66 20 7b 24 66 69 6c 65 20 3d 3d 20 22 2e  .if {$file == ".
3340: 22 20 7c 7c 20 24 66 69 6c 65 20 3d 3d 20 22 2e  " || $file == ".
3350: 2e 22 7d 20 7b 0a 09 09 09 09 09 09 09 09 63 6f  ."} {.........co
3360: 6e 74 69 6e 75 65 0a 09 09 09 09 09 09 09 7d 0a  ntinue........}.
3370: 0a 09 09 09 09 09 09 09 69 66 20 7b 24 66 69 6c  ........if {$fil
3380: 65 20 3d 3d 20 22 2e 41 50 50 46 53 2e 57 48 49  e == ".APPFS.WHI
3390: 54 45 4f 55 54 22 7d 20 7b 0a 09 09 09 09 09 09  TEOUT"} {.......
33a0: 09 09 63 6f 6e 74 69 6e 75 65 0a 09 09 09 09 09  ..continue......
33b0: 09 09 7d 0a 0a 09 09 09 09 09 09 09 69 66 20 7b  ..}.........if {
33c0: 5b 6c 73 65 61 72 63 68 20 2d 65 78 61 63 74 20  [lsearch -exact 
33d0: 24 72 65 74 76 61 6c 20 24 66 69 6c 65 5d 20 21  $retval $file] !
33e0: 3d 20 2d 31 7d 20 7b 0a 09 09 09 09 09 09 09 09  = -1} {.........
33f0: 63 6f 6e 74 69 6e 75 65 0a 09 09 09 09 09 09 09  continue........
3400: 7d 0a 0a 09 09 09 09 09 09 09 6c 61 70 70 65 6e  }.........lappen
3410: 64 20 72 65 74 76 61 6c 20 24 66 69 6c 65 0a 09  d retval $file..
3420: 09 09 09 09 09 7d 0a 09 09 09 09 09 7d 0a 09 09  .....}......}...
3430: 09 09 7d 0a 0a 09 09 09 09 72 65 74 75 72 6e 20  ..}......return 
3440: 24 72 65 74 76 61 6c 0a 09 09 09 7d 0a 09 09 7d  $retval....}...}
3450: 0a 0a 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65  ....return -code
3460: 20 65 72 72 6f 72 20 22 49 6e 76 61 6c 69 64 20   error "Invalid 
3470: 6f 72 20 75 6e 61 63 63 65 70 74 61 62 6c 65 20  or unacceptable 
3480: 70 61 74 68 3a 20 24 64 69 72 22 0a 09 7d 0a 0a  path: $dir"..}..
3490: 09 70 72 6f 63 20 67 65 74 61 74 74 72 20 7b 70  .proc getattr {p
34a0: 61 74 68 7d 20 7b 0a 09 09 61 72 72 61 79 20 73  ath} {...array s
34b0: 65 74 20 70 61 74 68 69 6e 66 6f 20 5b 5f 70 61  et pathinfo [_pa
34c0: 72 73 65 70 61 74 68 20 24 70 61 74 68 5d 0a 09  rsepath $path]..
34d0: 09 61 72 72 61 79 20 73 65 74 20 72 65 74 76 61  .array set retva
34e0: 6c 20 5b 6c 69 73 74 5d 0a 0a 09 09 63 61 74 63  l [list]....catc
34f0: 68 20 7b 0a 09 09 09 3a 3a 61 70 70 66 73 3a 3a  h {....::appfs::
3500: 67 65 74 69 6e 64 65 78 20 24 70 61 74 68 69 6e  getindex $pathin
3510: 66 6f 28 68 6f 73 74 6e 61 6d 65 29 0a 09 09 09  fo(hostname)....
3520: 3a 3a 61 70 70 66 73 3a 3a 67 65 74 70 6b 67 6d  ::appfs::getpkgm
3530: 61 6e 69 66 65 73 74 20 24 70 61 74 68 69 6e 66  anifest $pathinf
3540: 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24 70 61 74  o(hostname) $pat
3550: 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73 68  hinfo(package_sh
3560: 61 31 29 0a 09 09 7d 0a 0a 09 09 73 77 69 74 63  a1)...}....switc
3570: 68 20 2d 2d 20 24 70 61 74 68 69 6e 66 6f 28 5f  h -- $pathinfo(_
3580: 74 79 70 65 29 20 7b 0a 09 09 09 22 74 6f 70 6c  type) {...."topl
3590: 65 76 65 6c 22 20 7b 0a 09 09 09 09 73 65 74 20  evel" {.....set 
35a0: 72 65 74 76 61 6c 28 74 79 70 65 29 20 64 69 72  retval(type) dir
35b0: 65 63 74 6f 72 79 0a 09 09 09 09 73 65 74 20 72  ectory.....set r
35c0: 65 74 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74  etval(childcount
35d0: 29 20 5b 6c 6c 65 6e 67 74 68 20 5b 67 65 74 63  ) [llength [getc
35e0: 68 69 6c 64 72 65 6e 20 24 70 61 74 68 5d 5d 0a  hildren $path]].
35f0: 09 09 09 7d 0a 09 09 09 22 73 69 74 65 73 22 20  ...}...."sites" 
3600: 7b 0a 09 09 09 09 73 65 74 20 63 68 65 63 6b 20  {.....set check 
3610: 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20 6f 6e 65  [::appfs::db one
3620: 63 6f 6c 75 6d 6e 20 7b 53 45 4c 45 43 54 20 31  column {SELECT 1
3630: 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73 20 57   FROM packages W
3640: 48 45 52 45 20 68 6f 73 74 6e 61 6d 65 20 3d 20  HERE hostname = 
3650: 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61  $pathinfo(hostna
3660: 6d 65 29 3b 7d 5d 0a 09 09 09 09 69 66 20 7b 24  me);}].....if {$
3670: 63 68 65 63 6b 20 3d 3d 20 22 31 22 7d 20 7b 0a  check == "1"} {.
3680: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
3690: 74 79 70 65 29 20 64 69 72 65 63 74 6f 72 79 0a  type) directory.
36a0: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
36b0: 63 68 69 6c 64 63 6f 75 6e 74 29 20 5b 6c 6c 65  childcount) [lle
36c0: 6e 67 74 68 20 5b 67 65 74 63 68 69 6c 64 72 65  ngth [getchildre
36d0: 6e 20 24 70 61 74 68 5d 5d 0a 09 09 09 09 7d 0a  n $path]].....}.
36e0: 09 09 09 7d 0a 09 09 09 22 70 61 63 6b 61 67 65  ...}...."package
36f0: 73 22 20 7b 0a 09 09 09 09 73 65 74 20 63 68 65  s" {.....set che
3700: 63 6b 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20  ck [::appfs::db 
3710: 6f 6e 65 63 6f 6c 75 6d 6e 20 7b 53 45 4c 45 43  onecolumn {SELEC
3720: 54 20 31 20 46 52 4f 4d 20 70 61 63 6b 61 67 65  T 1 FROM package
3730: 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65  s WHERE hostname
3740: 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73   = $pathinfo(hos
3750: 74 6e 61 6d 65 29 20 41 4e 44 20 70 61 63 6b 61  tname) AND packa
3760: 67 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 70  ge = $pathinfo(p
3770: 61 63 6b 61 67 65 29 3b 7d 5d 0a 09 09 09 09 69  ackage);}].....i
3780: 66 20 7b 24 63 68 65 63 6b 20 3d 3d 20 22 31 22  f {$check == "1"
3790: 7d 20 7b 0a 09 09 09 09 09 73 65 74 20 72 65 74  } {......set ret
37a0: 76 61 6c 28 74 79 70 65 29 20 64 69 72 65 63 74  val(type) direct
37b0: 6f 72 79 0a 09 09 09 09 09 73 65 74 20 72 65 74  ory......set ret
37c0: 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29 20  val(childcount) 
37d0: 5b 6c 6c 65 6e 67 74 68 20 5b 67 65 74 63 68 69  [llength [getchi
37e0: 6c 64 72 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09  ldren $path]]...
37f0: 09 09 7d 0a 09 09 09 7d 0a 09 09 09 22 6f 73 2d  ..}....}...."os-
3800: 63 70 75 22 20 7b 0a 09 09 09 09 69 66 20 7b 24  cpu" {.....if {$
3810: 70 61 74 68 69 6e 66 6f 28 6f 73 29 20 3d 3d 20  pathinfo(os) == 
3820: 22 70 6c 61 74 66 6f 72 6d 22 20 26 26 20 24 70  "platform" && $p
3830: 61 74 68 69 6e 66 6f 28 63 70 75 29 20 3d 3d 20  athinfo(cpu) == 
3840: 22 22 7d 20 7b 0a 09 09 09 09 09 73 65 74 20 72  ""} {......set r
3850: 65 74 76 61 6c 28 74 79 70 65 29 20 73 79 6d 6c  etval(type) syml
3860: 69 6e 6b 0a 09 09 09 09 09 73 65 74 20 72 65 74  ink......set ret
3870: 76 61 6c 28 73 6f 75 72 63 65 29 20 5b 70 6c 61  val(source) [pla
3880: 74 66 6f 72 6d 3a 3a 67 65 6e 65 72 69 63 5d 0a  tform::generic].
3890: 09 09 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09  ....} else {....
38a0: 09 09 73 65 74 20 63 68 65 63 6b 20 5b 3a 3a 61  ..set check [::a
38b0: 70 70 66 73 3a 3a 64 62 20 6f 6e 65 63 6f 6c 75  ppfs::db onecolu
38c0: 6d 6e 20 7b 0a 09 09 09 09 09 09 53 45 4c 45 43  mn {.......SELEC
38d0: 54 20 31 20 46 52 4f 4d 20 70 61 63 6b 61 67 65  T 1 FROM package
38e0: 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65  s WHERE hostname
38f0: 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73   = $pathinfo(hos
3900: 74 6e 61 6d 65 29 20 41 4e 44 20 70 61 63 6b 61  tname) AND packa
3910: 67 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 70  ge = $pathinfo(p
3920: 61 63 6b 61 67 65 29 20 41 4e 44 20 6f 73 20 3d  ackage) AND os =
3930: 20 24 70 61 74 68 69 6e 66 6f 28 6f 73 29 20 41   $pathinfo(os) A
3940: 4e 44 20 63 70 75 41 72 63 68 20 3d 20 24 70 61  ND cpuArch = $pa
3950: 74 68 69 6e 66 6f 28 63 70 75 29 3b 0a 09 09 09  thinfo(cpu);....
3960: 09 09 7d 5d 0a 09 09 09 09 09 69 66 20 7b 24 63  ..}]......if {$c
3970: 68 65 63 6b 20 3d 3d 20 22 31 22 7d 20 7b 0a 09  heck == "1"} {..
3980: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
3990: 74 79 70 65 29 20 64 69 72 65 63 74 6f 72 79 0a  type) directory.
39a0: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
39b0: 28 63 68 69 6c 64 63 6f 75 6e 74 29 20 5b 6c 6c  (childcount) [ll
39c0: 65 6e 67 74 68 20 5b 67 65 74 63 68 69 6c 64 72  ength [getchildr
39d0: 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09 09 09 09  en $path]]......
39e0: 7d 0a 09 09 09 09 7d 0a 09 09 09 7d 0a 09 09 09  }.....}....}....
39f0: 22 76 65 72 73 69 6f 6e 73 22 20 7b 0a 09 09 09  "versions" {....
3a00: 09 69 66 20 7b 24 70 61 74 68 69 6e 66 6f 28 76  .if {$pathinfo(v
3a10: 65 72 73 69 6f 6e 29 20 3d 3d 20 22 6c 61 74 65  ersion) == "late
3a20: 73 74 22 7d 20 7b 0a 09 09 09 09 09 73 65 74 20  st"} {......set 
3a30: 72 65 74 76 61 6c 28 74 79 70 65 29 20 73 79 6d  retval(type) sym
3a40: 6c 69 6e 6b 0a 09 09 09 09 09 73 65 74 20 72 65  link......set re
3a50: 74 76 61 6c 28 73 6f 75 72 63 65 29 20 22 31 2e  tval(source) "1.
3a60: 30 22 0a 09 09 09 09 7d 20 65 6c 73 65 20 7b 0a  0".....} else {.
3a70: 09 09 09 09 09 69 66 20 7b 5b 69 6e 66 6f 20 65  .....if {[info e
3a80: 78 69 73 74 73 20 70 61 74 68 69 6e 66 6f 28 70  xists pathinfo(p
3a90: 61 63 6b 61 67 65 5f 73 68 61 31 29 5d 20 26 26  ackage_sha1)] &&
3aa0: 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61   $pathinfo(packa
3ab0: 67 65 5f 73 68 61 31 29 20 21 3d 20 22 22 7d 20  ge_sha1) != ""} 
3ac0: 7b 0a 09 09 09 09 09 09 73 65 74 20 72 65 74 76  {.......set retv
3ad0: 61 6c 28 74 79 70 65 29 20 64 69 72 65 63 74 6f  al(type) directo
3ae0: 72 79 0a 09 09 09 09 09 09 73 65 74 20 72 65 74  ry.......set ret
3af0: 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29 20  val(childcount) 
3b00: 5b 6c 6c 65 6e 67 74 68 20 5b 67 65 74 63 68 69  [llength [getchi
3b10: 6c 64 72 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09  ldren $path]]...
3b20: 09 09 09 7d 0a 09 09 09 09 7d 0a 09 09 09 7d 0a  ...}.....}....}.
3b30: 09 09 09 22 66 69 6c 65 73 22 20 7b 0a 09 09 09  ..."files" {....
3b40: 09 73 65 74 20 72 65 74 76 61 6c 28 70 61 63 6b  .set retval(pack
3b50: 61 67 65 64 29 20 31 0a 0a 09 09 09 09 73 65 74  aged) 1......set
3b60: 20 6c 6f 63 61 6c 70 61 74 68 20 5b 5f 6c 6f 63   localpath [_loc
3b70: 61 6c 70 61 74 68 20 24 70 61 74 68 69 6e 66 6f  alpath $pathinfo
3b80: 28 70 61 63 6b 61 67 65 29 20 24 70 61 74 68 69  (package) $pathi
3b90: 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24 70  nfo(hostname) $p
3ba0: 61 74 68 69 6e 66 6f 28 66 69 6c 65 29 5d 0a 09  athinfo(file)]..
3bb0: 09 09 09 73 65 74 20 77 68 69 74 65 6f 75 74 70  ...set whiteoutp
3bc0: 61 74 68 20 20 5b 5f 77 68 69 74 65 6f 75 74 70  ath  [_whiteoutp
3bd0: 61 74 68 20 24 70 61 74 68 69 6e 66 6f 28 70 61  ath $pathinfo(pa
3be0: 63 6b 61 67 65 29 20 24 70 61 74 68 69 6e 66 6f  ckage) $pathinfo
3bf0: 28 68 6f 73 74 6e 61 6d 65 29 20 24 70 61 74 68  (hostname) $path
3c00: 69 6e 66 6f 28 66 69 6c 65 29 5d 0a 0a 09 09 09  info(file)].....
3c10: 09 73 65 74 20 72 65 74 76 61 6c 28 6c 6f 63 61  .set retval(loca
3c20: 6c 70 61 74 68 29 20 24 6c 6f 63 61 6c 70 61 74  lpath) $localpat
3c30: 68 0a 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  h.....set retval
3c40: 28 77 68 69 74 65 6f 75 74 70 61 74 68 29 20 24  (whiteoutpath) $
3c50: 77 68 69 74 65 6f 75 74 70 61 74 68 0a 0a 09 09  whiteoutpath....
3c60: 09 09 69 66 20 7b 5b 66 69 6c 65 20 65 78 69 73  ..if {[file exis
3c70: 74 73 20 24 6c 6f 63 61 6c 70 61 74 68 5d 7d 20  ts $localpath]} 
3c80: 7b 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61  {......set retva
3c90: 6c 28 69 73 5f 6c 6f 63 61 6c 66 69 6c 65 29 20  l(is_localfile) 
3ca0: 31 0a 09 09 09 09 09 63 61 74 63 68 20 7b 0a 09  1......catch {..
3cb0: 09 09 09 09 09 5f 61 73 5f 75 73 65 72 20 7b 0a  ....._as_user {.
3cc0: 09 09 09 09 09 09 09 66 69 6c 65 20 6c 73 74 61  .......file lsta
3cd0: 74 20 24 6c 6f 63 61 6c 70 61 74 68 20 6c 6f 63  t $localpath loc
3ce0: 61 6c 70 61 74 68 69 6e 66 6f 0a 09 09 09 09 09  alpathinfo......
3cf0: 09 7d 0a 09 09 09 09 09 09 73 65 74 20 72 65 74  .}.......set ret
3d00: 76 61 6c 28 74 69 6d 65 29 20 24 6c 6f 63 61 6c  val(time) $local
3d10: 70 61 74 68 69 6e 66 6f 28 6d 74 69 6d 65 29 0a  pathinfo(mtime).
3d20: 0a 09 09 09 09 09 09 73 77 69 74 63 68 20 2d 2d  .......switch --
3d30: 20 24 6c 6f 63 61 6c 70 61 74 68 69 6e 66 6f 28   $localpathinfo(
3d40: 74 79 70 65 29 20 7b 0a 09 09 09 09 09 09 09 22  type) {........"
3d50: 64 69 72 65 63 74 6f 72 79 22 20 7b 0a 09 09 09  directory" {....
3d60: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
3d70: 74 79 70 65 29 20 22 64 69 72 65 63 74 6f 72 79  type) "directory
3d80: 22 0a 09 09 09 09 09 09 09 09 73 65 74 20 72 65  ".........set re
3d90: 74 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29  tval(childcount)
3da0: 20 5b 6c 6c 65 6e 67 74 68 20 5b 67 65 74 63 68   [llength [getch
3db0: 69 6c 64 72 65 6e 20 24 70 61 74 68 5d 5d 0a 09  ildren $path]]..
3dc0: 09 09 09 09 09 09 7d 0a 09 09 09 09 09 09 09 22  ......}........"
3dd0: 66 69 6c 65 22 20 7b 0a 09 09 09 09 09 09 09 09  file" {.........
3de0: 73 65 74 20 72 65 74 76 61 6c 28 74 79 70 65 29  set retval(type)
3df0: 20 22 66 69 6c 65 22 0a 09 09 09 09 09 09 09 09   "file".........
3e00: 73 65 74 20 72 65 74 76 61 6c 28 73 69 7a 65 29  set retval(size)
3e10: 20 24 6c 6f 63 61 6c 70 61 74 68 69 6e 66 6f 28   $localpathinfo(
3e20: 73 69 7a 65 29 0a 09 09 09 09 09 09 09 09 5f 61  size)........._a
3e30: 73 5f 75 73 65 72 20 7b 0a 09 09 09 09 09 09 09  s_user {........
3e40: 09 09 69 66 20 7b 5b 66 69 6c 65 20 65 78 65 63  ..if {[file exec
3e50: 75 74 61 62 6c 65 20 24 6c 6f 63 61 6c 70 61 74  utable $localpat
3e60: 68 5d 7d 20 7b 0a 09 09 09 09 09 09 09 09 09 09  h]} {...........
3e70: 73 65 74 20 72 65 74 76 61 6c 28 70 65 72 6d 73  set retval(perms
3e80: 29 20 22 78 22 0a 09 09 09 09 09 09 09 09 09 7d  ) "x"..........}
3e90: 20 65 6c 73 65 20 7b 0a 09 09 09 09 09 09 09 09   else {.........
3ea0: 09 09 73 65 74 20 72 65 74 76 61 6c 28 70 65 72  ..set retval(per
3eb0: 6d 73 29 20 22 22 0a 09 09 09 09 09 09 09 09 09  ms) ""..........
3ec0: 7d 0a 09 09 09 09 09 09 09 09 7d 0a 09 09 09 09  }.........}.....
3ed0: 09 09 09 7d 0a 09 09 09 09 09 09 09 22 6c 69 6e  ...}........"lin
3ee0: 6b 22 20 7b 0a 09 09 09 09 09 09 09 09 73 65 74  k" {.........set
3ef0: 20 72 65 74 76 61 6c 28 74 79 70 65 29 20 22 73   retval(type) "s
3f00: 79 6d 6c 69 6e 6b 22 0a 0a 09 09 09 09 09 09 09  ymlink".........
3f10: 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09 09 09 09  ._as_user {.....
3f20: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
3f30: 73 6f 75 72 63 65 29 20 5b 66 69 6c 65 20 72 65  source) [file re
3f40: 61 64 6c 69 6e 6b 20 24 6c 6f 63 61 6c 70 61 74  adlink $localpat
3f50: 68 5d 0a 09 09 09 09 09 09 09 09 7d 0a 09 09 09  h].........}....
3f60: 09 09 09 09 7d 0a 09 09 09 09 09 09 09 22 66 69  ....}........"fi
3f70: 66 6f 22 20 7b 0a 09 09 09 09 09 09 09 09 23 20  fo" {.........# 
3f80: 43 61 70 69 74 61 6c 69 7a 65 64 20 73 6f 20 74  Capitalized so t
3f90: 68 61 74 20 74 68 65 20 66 69 72 73 74 20 63 68  hat the first ch
3fa0: 61 72 20 69 73 20 75 6e 69 71 75 65 0a 09 09 09  ar is unique....
3fb0: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
3fc0: 74 79 70 65 29 20 22 46 69 66 6f 22 0a 09 09 09  type) "Fifo"....
3fd0: 09 09 09 09 7d 0a 09 09 09 09 09 09 09 22 73 6f  ....}........"so
3fe0: 63 6b 65 74 22 20 7b 0a 09 09 09 09 09 09 09 09  cket" {.........
3ff0: 23 20 43 61 70 69 74 61 6c 69 7a 65 64 20 73 6f  # Capitalized so
4000: 20 74 68 61 74 20 74 68 65 20 66 69 72 73 74 20   that the first 
4010: 63 68 61 72 20 69 73 20 75 6e 69 71 75 65 0a 09  char is unique..
4020: 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61  .......set retva
4030: 6c 28 74 79 70 65 29 20 22 53 6f 63 6b 65 74 22  l(type) "Socket"
4040: 0a 09 09 09 09 09 09 09 7d 0a 09 09 09 09 09 09  ........}.......
4050: 7d 0a 09 09 09 09 09 7d 20 65 72 72 0a 09 09 09  }......} err....
4060: 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09 09 69  .} else {......i
4070: 66 20 7b 21 5b 66 69 6c 65 20 65 78 69 73 74 73  f {![file exists
4080: 20 24 77 68 69 74 65 6f 75 74 70 61 74 68 5d 7d   $whiteoutpath]}
4090: 20 7b 0a 09 09 09 09 09 09 73 65 74 20 72 65 74   {.......set ret
40a0: 76 61 6c 28 69 73 5f 72 65 6d 6f 74 65 66 69 6c  val(is_remotefil
40b0: 65 29 20 31 0a 0a 09 09 09 09 09 09 73 65 74 20  e) 1........set 
40c0: 77 6f 72 6b 20 5b 73 70 6c 69 74 20 24 70 61 74  work [split $pat
40d0: 68 69 6e 66 6f 28 66 69 6c 65 29 20 22 2f 22 5d  hinfo(file) "/"]
40e0: 0a 09 09 09 09 09 09 73 65 74 20 64 69 72 65 63  .......set direc
40f0: 74 6f 72 79 20 5b 6a 6f 69 6e 20 5b 6c 72 61 6e  tory [join [lran
4100: 67 65 20 24 77 6f 72 6b 20 30 20 65 6e 64 2d 31  ge $work 0 end-1
4110: 5d 20 22 2f 22 5d 0a 09 09 09 09 09 09 73 65 74  ] "/"].......set
4120: 20 66 69 6c 65 20 5b 6c 69 6e 64 65 78 20 24 77   file [lindex $w
4130: 6f 72 6b 20 65 6e 64 5d 0a 0a 09 09 09 09 09 09  ork end]........
4140: 69 66 20 7b 24 64 69 72 65 63 74 6f 72 79 20 3d  if {$directory =
4150: 3d 20 22 22 20 26 26 20 24 66 69 6c 65 20 3d 3d  = "" && $file ==
4160: 20 22 22 7d 20 7b 0a 09 09 09 09 09 09 09 61 72   ""} {........ar
4170: 72 61 79 20 73 65 74 20 72 65 74 76 61 6c 20 5b  ray set retval [
4180: 6c 69 73 74 20 74 79 70 65 20 64 69 72 65 63 74  list type direct
4190: 6f 72 79 5d 0a 09 09 09 09 09 09 7d 0a 0a 09 09  ory].......}....
41a0: 09 09 09 09 3a 3a 61 70 70 66 73 3a 3a 64 62 20  ....::appfs::db 
41b0: 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 74 79 70  eval {SELECT typ
41c0: 65 2c 20 74 69 6d 65 2c 20 73 6f 75 72 63 65 2c  e, time, source,
41d0: 20 73 69 7a 65 2c 20 70 65 72 6d 73 20 46 52 4f   size, perms FRO
41e0: 4d 20 66 69 6c 65 73 20 57 48 45 52 45 20 70 61  M files WHERE pa
41f0: 63 6b 61 67 65 5f 73 68 61 31 20 3d 20 24 70 61  ckage_sha1 = $pa
4200: 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73  thinfo(package_s
4210: 68 61 31 29 20 41 4e 44 20 66 69 6c 65 5f 64 69  ha1) AND file_di
4220: 72 65 63 74 6f 72 79 20 3d 20 24 64 69 72 65 63  rectory = $direc
4230: 74 6f 72 79 20 41 4e 44 20 66 69 6c 65 5f 6e 61  tory AND file_na
4240: 6d 65 20 3d 20 24 66 69 6c 65 3b 7d 20 72 65 74  me = $file;} ret
4250: 76 61 6c 20 7b 7d 0a 0a 09 09 09 09 09 09 69 66  val {}........if
4260: 20 7b 24 72 65 74 76 61 6c 28 74 79 70 65 29 20   {$retval(type) 
4270: 3d 3d 20 22 64 69 72 65 63 74 6f 72 79 22 7d 20  == "directory"} 
4280: 7b 0a 09 09 09 09 09 09 09 73 65 74 20 72 65 74  {........set ret
4290: 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29 20  val(childcount) 
42a0: 5b 6c 6c 65 6e 67 74 68 20 5b 67 65 74 63 68 69  [llength [getchi
42b0: 6c 64 72 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09  ldren $path]]...
42c0: 09 09 09 09 7d 0a 0a 09 09 09 09 09 09 75 6e 73  ....}........uns
42d0: 65 74 20 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 72  et -nocomplain r
42e0: 65 74 76 61 6c 28 2a 29 0a 09 09 09 09 09 7d 0a  etval(*)......}.
42f0: 09 09 09 09 7d 0a 0a 09 09 09 7d 0a 09 09 7d 0a  ....}.....}...}.
4300: 0a 09 09 69 66 20 7b 21 5b 69 6e 66 6f 20 65 78  ...if {![info ex
4310: 69 73 74 73 20 72 65 74 76 61 6c 28 74 79 70 65  ists retval(type
4320: 29 5d 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  )]} {....return 
4330: 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 4e 6f 20  -code error "No 
4340: 73 75 63 68 20 66 69 6c 65 20 6f 72 20 64 69 72  such file or dir
4350: 65 63 74 6f 72 79 22 0a 09 09 7d 0a 0a 09 09 72  ectory"...}....r
4360: 65 74 75 72 6e 20 5b 61 72 72 61 79 20 67 65 74  eturn [array get
4370: 20 72 65 74 76 61 6c 5d 0a 09 7d 0a 0a 09 70 72   retval]..}...pr
4380: 6f 63 20 6f 70 65 6e 70 61 74 68 20 7b 70 61 74  oc openpath {pat
4390: 68 20 6d 6f 64 65 7d 20 7b 0a 09 09 61 72 72 61  h mode} {...arra
43a0: 79 20 73 65 74 20 70 61 74 68 69 6e 66 6f 20 5b  y set pathinfo [
43b0: 5f 70 61 72 73 65 70 61 74 68 20 24 70 61 74 68  _parsepath $path
43c0: 5d 0a 0a 09 09 69 66 20 7b 24 70 61 74 68 69 6e  ]....if {$pathin
43d0: 66 6f 28 5f 74 79 70 65 29 20 21 3d 20 22 66 69  fo(_type) != "fi
43e0: 6c 65 73 22 7d 20 7b 0a 09 09 09 72 65 74 75 72  les"} {....retur
43f0: 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 69  n -code error "i
4400: 6e 76 61 6c 69 64 20 74 79 70 65 22 0a 09 09 7d  nvalid type"...}
4410: 0a 0a 09 09 73 65 74 20 6c 6f 63 61 6c 70 61 74  ....set localpat
4420: 68 20 5b 5f 6c 6f 63 61 6c 70 61 74 68 20 24 70  h [_localpath $p
4430: 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 29  athinfo(package)
4440: 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e   $pathinfo(hostn
4450: 61 6d 65 29 20 24 70 61 74 68 69 6e 66 6f 28 66  ame) $pathinfo(f
4460: 69 6c 65 29 5d 0a 0a 09 09 69 66 20 7b 24 6d 6f  ile)]....if {$mo
4470: 64 65 20 3d 3d 20 22 63 72 65 61 74 65 22 7d 20  de == "create"} 
4480: 7b 0a 09 09 09 72 65 74 75 72 6e 20 24 6c 6f 63  {....return $loc
4490: 61 6c 70 61 74 68 0a 09 09 7d 0a 0a 09 09 69 66  alpath...}....if
44a0: 20 7b 5b 66 69 6c 65 20 65 78 69 73 74 73 20 24   {[file exists $
44b0: 6c 6f 63 61 6c 70 61 74 68 5d 7d 20 7b 0a 09 09  localpath]} {...
44c0: 09 72 65 74 75 72 6e 20 24 6c 6f 63 61 6c 70 61  .return $localpa
44d0: 74 68 0a 09 09 7d 0a 0a 09 09 73 65 74 20 77 6f  th...}....set wo
44e0: 72 6b 20 5b 73 70 6c 69 74 20 24 70 61 74 68 69  rk [split $pathi
44f0: 6e 66 6f 28 66 69 6c 65 29 20 22 2f 22 5d 0a 09  nfo(file) "/"]..
4500: 09 73 65 74 20 64 69 72 65 63 74 6f 72 79 20 5b  .set directory [
4510: 6a 6f 69 6e 20 5b 6c 72 61 6e 67 65 20 24 77 6f  join [lrange $wo
4520: 72 6b 20 30 20 65 6e 64 2d 31 5d 20 22 2f 22 5d  rk 0 end-1] "/"]
4530: 0a 09 09 73 65 74 20 66 69 6c 65 20 5b 6c 69 6e  ...set file [lin
4540: 64 65 78 20 24 77 6f 72 6b 20 65 6e 64 5d 0a 09  dex $work end]..
4550: 09 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65 76 61  .::appfs::db eva
4560: 6c 20 7b 53 45 4c 45 43 54 20 66 69 6c 65 5f 73  l {SELECT file_s
4570: 68 61 31 2c 20 70 65 72 6d 73 20 46 52 4f 4d 20  ha1, perms FROM 
4580: 66 69 6c 65 73 20 57 48 45 52 45 20 70 61 63 6b  files WHERE pack
4590: 61 67 65 5f 73 68 61 31 20 3d 20 24 70 61 74 68  age_sha1 = $path
45a0: 69 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73 68 61  info(package_sha
45b0: 31 29 20 41 4e 44 20 66 69 6c 65 5f 6e 61 6d 65  1) AND file_name
45c0: 20 3d 20 24 66 69 6c 65 20 41 4e 44 20 66 69 6c   = $file AND fil
45d0: 65 5f 64 69 72 65 63 74 6f 72 79 20 3d 20 24 64  e_directory = $d
45e0: 69 72 65 63 74 6f 72 79 3b 7d 20 70 6b 67 70 61  irectory;} pkgpa
45f0: 74 68 69 6e 66 6f 20 7b 7d 0a 0a 09 09 69 66 20  thinfo {}....if 
4600: 7b 24 70 6b 67 70 61 74 68 69 6e 66 6f 28 66 69  {$pkgpathinfo(fi
4610: 6c 65 5f 73 68 61 31 29 20 3d 3d 20 22 22 7d 20  le_sha1) == ""} 
4620: 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64  {....return -cod
4630: 65 20 65 72 72 6f 72 20 22 4e 6f 20 73 75 63 68  e error "No such
4640: 20 66 69 6c 65 20 6f 72 20 64 69 72 65 63 74 6f   file or directo
4650: 72 79 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20 6c  ry"...}....set l
4660: 6f 63 61 6c 63 61 63 68 65 66 69 6c 65 20 5b 64  ocalcachefile [d
4670: 6f 77 6e 6c 6f 61 64 20 24 70 61 74 68 69 6e 66  ownload $pathinf
4680: 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24 70 6b 67  o(hostname) $pkg
4690: 70 61 74 68 69 6e 66 6f 28 66 69 6c 65 5f 73 68  pathinfo(file_sh
46a0: 61 31 29 5d 0a 0a 09 09 69 66 20 7b 24 6d 6f 64  a1)]....if {$mod
46b0: 65 20 3d 3d 20 22 77 72 69 74 65 22 7d 20 7b 0a  e == "write"} {.
46c0: 09 09 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09 09  ..._as_user {...
46d0: 09 09 73 65 74 20 74 6d 70 6c 6f 63 61 6c 70 61  ..set tmplocalpa
46e0: 74 68 20 22 24 7b 6c 6f 63 61 6c 70 61 74 68 7d  th "${localpath}
46f0: 2e 5b 65 78 70 72 20 72 61 6e 64 28 29 5d 5b 63  .[expr rand()][c
4700: 6c 6f 63 6b 20 63 6c 69 63 6b 73 5d 22 0a 0a 09  lock clicks]"...
4710: 09 09 09 73 65 74 20 66 61 69 6c 65 64 20 30 0a  ...set failed 0.
4720: 09 09 09 09 69 66 20 7b 5b 63 61 74 63 68 20 7b  ....if {[catch {
4730: 0a 09 09 09 09 09 66 69 6c 65 20 6d 6b 64 69 72  ......file mkdir
4740: 20 5b 66 69 6c 65 20 64 69 72 6e 61 6d 65 20 24   [file dirname $
4750: 6c 6f 63 61 6c 70 61 74 68 5d 0a 09 09 09 09 09  localpath]......
4760: 66 69 6c 65 20 63 6f 70 79 20 2d 66 6f 72 63 65  file copy -force
4770: 20 2d 2d 20 24 6c 6f 63 61 6c 63 61 63 68 65 66   -- $localcachef
4780: 69 6c 65 20 24 74 6d 70 6c 6f 63 61 6c 70 61 74  ile $tmplocalpat
4790: 68 0a 0a 09 09 09 09 09 69 66 20 7b 24 70 6b 67  h.......if {$pkg
47a0: 70 61 74 68 69 6e 66 6f 28 70 65 72 6d 73 29 20  pathinfo(perms) 
47b0: 3d 3d 20 22 78 22 7d 20 7b 0a 09 09 09 09 09 09  == "x"} {.......
47c0: 66 69 6c 65 20 61 74 74 72 69 62 75 74 65 73 20  file attributes 
47d0: 24 74 6d 70 6c 6f 63 61 6c 70 61 74 68 20 2d 70  $tmplocalpath -p
47e0: 65 72 6d 69 73 73 69 6f 6e 73 20 2b 78 0a 09 09  ermissions +x...
47f0: 09 09 09 7d 0a 0a 09 09 09 09 09 66 69 6c 65 20  ...}.......file 
4800: 72 65 6e 61 6d 65 20 2d 66 6f 72 63 65 20 2d 2d  rename -force --
4810: 20 24 74 6d 70 6c 6f 63 61 6c 70 61 74 68 20 24   $tmplocalpath $
4820: 6c 6f 63 61 6c 70 61 74 68 0a 09 09 09 09 7d 20  localpath.....} 
4830: 65 72 72 5d 7d 20 7b 0a 09 09 09 09 09 73 65 74  err]} {......set
4840: 20 66 61 69 6c 65 64 20 31 0a 09 09 09 09 7d 0a   failed 1.....}.
4850: 09 09 09 09 63 61 74 63 68 20 7b 0a 09 09 09 09  ....catch {.....
4860: 09 66 69 6c 65 20 64 65 6c 65 74 65 20 2d 66 6f  .file delete -fo
4870: 72 63 65 20 2d 2d 20 24 74 6d 70 6c 6f 63 61 6c  rce -- $tmplocal
4880: 70 61 74 68 0a 09 09 09 09 7d 0a 09 09 09 7d 0a  path.....}....}.
4890: 0a 09 09 09 69 66 20 7b 24 66 61 69 6c 65 64 7d  ....if {$failed}
48a0: 20 7b 0a 09 09 09 09 72 65 74 75 72 6e 20 2d 63   {.....return -c
48b0: 6f 64 65 20 65 72 72 6f 72 20 24 65 72 72 0a 09  ode error $err..
48c0: 09 09 7d 0a 0a 09 09 09 72 65 74 75 72 6e 20 24  ..}.....return $
48d0: 6c 6f 63 61 6c 70 61 74 68 0a 09 09 7d 0a 0a 09  localpath...}...
48e0: 09 72 65 74 75 72 6e 20 24 6c 6f 63 61 6c 63 61  .return $localca
48f0: 63 68 65 66 69 6c 65 0a 09 7d 0a 0a 09 70 72 6f  chefile..}...pro
4900: 63 20 6c 6f 63 61 6c 70 61 74 68 20 7b 70 61 74  c localpath {pat
4910: 68 7d 20 7b 0a 09 09 61 72 72 61 79 20 73 65 74  h} {...array set
4920: 20 70 61 74 68 69 6e 66 6f 20 5b 5f 70 61 72 73   pathinfo [_pars
4930: 65 70 61 74 68 20 24 70 61 74 68 5d 0a 0a 09 09  epath $path]....
4940: 69 66 20 7b 24 70 61 74 68 69 6e 66 6f 28 5f 74  if {$pathinfo(_t
4950: 79 70 65 29 20 21 3d 20 22 66 69 6c 65 73 22 7d  ype) != "files"}
4960: 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d 63 6f   {....return -co
4970: 64 65 20 65 72 72 6f 72 20 22 69 6e 76 61 6c 69  de error "invali
4980: 64 20 74 79 70 65 22 0a 09 09 7d 0a 0a 09 09 73  d type"...}....s
4990: 65 74 20 6c 6f 63 61 6c 70 61 74 68 20 5b 5f 6c  et localpath [_l
49a0: 6f 63 61 6c 70 61 74 68 20 24 70 61 74 68 69 6e  ocalpath $pathin
49b0: 66 6f 28 70 61 63 6b 61 67 65 29 20 24 70 61 74  fo(package) $pat
49c0: 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20  hinfo(hostname) 
49d0: 24 70 61 74 68 69 6e 66 6f 28 66 69 6c 65 29 5d  $pathinfo(file)]
49e0: 0a 0a 09 09 72 65 74 75 72 6e 20 24 6c 6f 63 61  ....return $loca
49f0: 6c 70 61 74 68 0a 09 7d 0a 0a 09 70 72 6f 63 20  lpath..}...proc 
4a00: 65 78 69 73 74 73 20 7b 70 61 74 68 7d 20 7b 0a  exists {path} {.
4a10: 09 09 63 61 74 63 68 20 7b 0a 09 09 09 73 65 74  ..catch {....set
4a20: 20 69 6e 66 6f 20 5b 67 65 74 61 74 74 72 20 24   info [getattr $
4a30: 70 61 74 68 5d 0a 09 09 7d 20 65 72 72 0a 0a 09  path]...} err...
4a40: 09 69 66 20 7b 21 5b 69 6e 66 6f 20 65 78 69 73  .if {![info exis
4a50: 74 73 20 69 6e 66 6f 5d 7d 20 7b 0a 09 09 09 69  ts info]} {....i
4a60: 66 20 7b 24 65 72 72 20 3d 3d 20 22 4e 6f 20 73  f {$err == "No s
4a70: 75 63 68 20 66 69 6c 65 20 6f 72 20 64 69 72 65  uch file or dire
4a80: 63 74 6f 72 79 22 7d 20 7b 0a 09 09 09 09 72 65  ctory"} {.....re
4a90: 74 75 72 6e 20 5b 6c 69 73 74 5d 0a 09 09 09 7d  turn [list]....}
4aa0: 20 65 6c 73 65 20 7b 0a 09 09 09 09 72 65 74 75   else {.....retu
4ab0: 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 24  rn -code error $
4ac0: 65 72 72 0a 09 09 09 7d 0a 09 09 7d 0a 0a 09 09  err....}...}....
4ad0: 72 65 74 75 72 6e 20 24 69 6e 66 6f 0a 09 7d 0a  return $info..}.
4ae0: 0a 09 70 72 6f 63 20 70 72 65 70 61 72 65 5f 74  ..proc prepare_t
4af0: 6f 5f 63 72 65 61 74 65 20 7b 70 61 74 68 20 7b  o_create {path {
4b00: 6d 75 73 74 5f 6e 6f 74 5f 65 78 69 73 74 20 31  must_not_exist 1
4b10: 7d 7d 20 7b 0a 09 09 69 66 20 7b 24 6d 75 73 74  }} {...if {$must
4b20: 5f 6e 6f 74 5f 65 78 69 73 74 7d 20 7b 0a 09 09  _not_exist} {...
4b30: 09 69 66 20 7b 5b 65 78 69 73 74 73 20 24 70 61  .if {[exists $pa
4b40: 74 68 5d 20 21 3d 20 22 22 7d 20 7b 0a 09 09 09  th] != ""} {....
4b50: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72  .return -code er
4b60: 72 6f 72 20 22 46 69 6c 65 20 61 6c 72 65 61 64  ror "File alread
4b70: 79 20 65 78 69 73 74 73 22 0a 09 09 09 7d 0a 09  y exists"....}..
4b80: 09 7d 0a 0a 09 09 73 65 74 20 66 69 6c 65 6e 61  .}....set filena
4b90: 6d 65 20 5b 6c 6f 63 61 6c 70 61 74 68 20 24 70  me [localpath $p
4ba0: 61 74 68 5d 0a 0a 09 09 73 65 74 20 64 69 72 6e  ath]....set dirn
4bb0: 61 6d 65 20 5b 66 69 6c 65 20 64 69 72 6e 61 6d  ame [file dirnam
4bc0: 65 20 24 66 69 6c 65 6e 61 6d 65 5d 0a 0a 09 09  e $filename]....
4bd0: 5f 61 73 5f 75 73 65 72 20 7b 0a 09 09 09 66 69  _as_user {....fi
4be0: 6c 65 20 6d 6b 64 69 72 20 24 64 69 72 6e 61 6d  le mkdir $dirnam
4bf0: 65 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e 20  e...}....return 
4c00: 24 66 69 6c 65 6e 61 6d 65 0a 09 7d 0a 0a 09 70  $filename..}...p
4c10: 72 6f 63 20 75 6e 6c 69 6e 6b 70 61 74 68 20 7b  roc unlinkpath {
4c20: 70 61 74 68 7d 20 7b 0a 09 09 61 72 72 61 79 20  path} {...array 
4c30: 73 65 74 20 70 61 74 68 61 74 74 72 73 20 5b 65  set pathattrs [e
4c40: 78 69 73 74 73 20 24 70 61 74 68 5d 0a 0a 09 09  xists $path]....
4c50: 69 66 20 7b 21 5b 69 6e 66 6f 20 65 78 69 73 74  if {![info exist
4c60: 73 20 70 61 74 68 61 74 74 72 73 28 70 61 63 6b  s pathattrs(pack
4c70: 61 67 65 64 29 5d 7d 20 7b 0a 09 09 09 72 65 74  aged)]} {....ret
4c80: 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20  urn -code error 
4c90: 22 69 6e 76 61 6c 69 64 20 74 79 70 65 22 0a 09  "invalid type"..
4ca0: 09 7d 0a 0a 09 09 73 65 74 20 6c 6f 63 61 6c 70  .}....set localp
4cb0: 61 74 68 20 24 70 61 74 68 61 74 74 72 73 28 6c  ath $pathattrs(l
4cc0: 6f 63 61 6c 70 61 74 68 29 0a 0a 09 09 69 66 20  ocalpath)....if 
4cd0: 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 70 61  {[info exists pa
4ce0: 74 68 61 74 74 72 73 28 69 73 5f 6c 6f 63 61 6c  thattrs(is_local
4cf0: 66 69 6c 65 29 5d 7d 20 7b 0a 09 09 09 69 66 20  file)]} {....if 
4d00: 7b 5b 66 69 6c 65 20 69 73 64 69 72 65 63 74 6f  {[file isdirecto
4d10: 72 79 20 24 6c 6f 63 61 6c 70 61 74 68 5d 7d 20  ry $localpath]} 
4d20: 7b 0a 09 09 09 09 73 65 74 20 63 68 69 6c 64 72  {.....set childr
4d30: 65 6e 20 5b 67 65 74 63 68 69 6c 64 72 65 6e 20  en [getchildren 
4d40: 24 70 61 74 68 5d 0a 0a 09 09 09 09 69 66 20 7b  $path]......if {
4d50: 5b 6c 6c 65 6e 67 74 68 20 24 63 68 69 6c 64 72  [llength $childr
4d60: 65 6e 5d 20 21 3d 20 30 7d 20 7b 0a 09 09 09 09  en] != 0} {.....
4d70: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72  .return -code er
4d80: 72 6f 72 20 22 41 73 6b 65 64 20 74 6f 20 64 65  ror "Asked to de
4d90: 6c 65 74 65 20 6e 6f 6e 2d 65 6d 70 74 79 20 64  lete non-empty d
4da0: 69 72 65 63 74 6f 72 79 22 0a 09 09 09 09 7d 0a  irectory".....}.
4db0: 09 09 09 7d 0a 0a 09 09 09 5f 61 73 5f 75 73 65  ...}....._as_use
4dc0: 72 20 7b 0a 09 09 09 09 66 69 6c 65 20 64 65 6c  r {.....file del
4dd0: 65 74 65 20 2d 66 6f 72 63 65 20 2d 2d 20 24 6c  ete -force -- $l
4de0: 6f 63 61 6c 70 61 74 68 0a 09 09 09 7d 0a 09 09  ocalpath....}...
4df0: 7d 20 65 6c 73 65 69 66 20 7b 5b 69 6e 66 6f 20  } elseif {[info 
4e00: 65 78 69 73 74 73 20 70 61 74 68 61 74 74 72 73  exists pathattrs
4e10: 28 69 73 5f 72 65 6d 6f 74 65 66 69 6c 65 29 5d  (is_remotefile)]
4e20: 7d 20 7b 0a 09 09 09 69 66 20 7b 24 70 61 74 68  } {....if {$path
4e30: 61 74 74 72 73 28 74 79 70 65 29 20 3d 3d 20 22  attrs(type) == "
4e40: 64 69 72 65 63 74 6f 72 79 22 7d 20 7b 0a 09 09  directory"} {...
4e50: 09 09 73 65 74 20 63 68 69 6c 64 72 65 6e 20 5b  ..set children [
4e60: 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61 74  getchildren $pat
4e70: 68 5d 0a 0a 09 09 09 09 69 66 20 7b 5b 6c 6c 65  h]......if {[lle
4e80: 6e 67 74 68 20 24 63 68 69 6c 64 72 65 6e 5d 20  ngth $children] 
4e90: 21 3d 20 30 7d 20 7b 0a 09 09 09 09 09 72 65 74  != 0} {......ret
4ea0: 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20  urn -code error 
4eb0: 22 41 73 6b 65 64 20 74 6f 20 64 65 6c 65 74 65  "Asked to delete
4ec0: 20 6e 6f 6e 2d 65 6d 70 74 79 20 64 69 72 65 63   non-empty direc
4ed0: 74 6f 72 79 22 0a 09 09 09 09 7d 0a 09 09 09 7d  tory".....}....}
4ee0: 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 72  ...} else {....r
4ef0: 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f  eturn -code erro
4f00: 72 20 22 55 6e 6b 6e 6f 77 6e 20 69 66 20 66 69  r "Unknown if fi
4f10: 6c 65 20 69 73 20 72 65 6d 6f 74 65 20 6f 72 20  le is remote or 
4f20: 6c 6f 63 61 6c 20 21 3f 22 0a 09 09 7d 0a 0a 09  local !?"...}...
4f30: 09 73 65 74 20 77 68 69 74 65 6f 75 74 66 69 6c  .set whiteoutfil
4f40: 65 20 24 70 61 74 68 61 74 74 72 73 28 77 68 69  e $pathattrs(whi
4f50: 74 65 6f 75 74 70 61 74 68 29 0a 09 09 73 65 74  teoutpath)...set
4f60: 20 77 68 69 74 65 6f 75 74 64 69 72 20 5b 66 69   whiteoutdir [fi
4f70: 6c 65 20 64 69 72 6e 61 6d 65 20 24 77 68 69 74  le dirname $whit
4f80: 65 6f 75 74 66 69 6c 65 5d 0a 0a 09 09 5f 61 73  eoutfile]...._as
4f90: 5f 75 73 65 72 20 7b 0a 09 09 09 66 69 6c 65 20  _user {....file 
4fa0: 6d 6b 64 69 72 20 24 77 68 69 74 65 6f 75 74 64  mkdir $whiteoutd
4fb0: 69 72 0a 09 09 09 63 6c 6f 73 65 20 5b 6f 70 65  ir....close [ope
4fc0: 6e 20 24 77 68 69 74 65 6f 75 74 66 69 6c 65 20  n $whiteoutfile 
4fd0: 77 5d 0a 09 09 7d 0a 09 7d 0a 7d 0a              w]...}..}.}.