cfw: Fix iOS 27 Sileo-at-setup — register JB apps via containerized LS API (vpregister)

On iOS 27, -[LSApplicationWorkspace registerApplicationDictionary:] (what the
first-boot `uicache -a` uses) is a deprecated no-op stub — lsd logs "You cannot
use -[LSApplicationWorkspace registerApplicationDictionary:] to register
applications anymore. These interfaces have been deprecated for years." and it
returns NO. So vphone_jb_setup.sh installs Sileo's .deb but never registers it:
the files land in /var/jb/Applications but Sileo never appears on the home
screen. There is no gate to patch here — Apple removed the implementation.

Fix: register JB apps via the modern containerized API instead.
- scripts/vpregister/vpregister.m: standalone helper that registers
  /var/jb/Applications/*.app (or given paths) via
  registerContainerizedApplicationWithInfoDictionaries:...:registrationError:,
  treating a nil error as success (it returns NO even when it registers). Works
  once lsd's embedded-reg gate is patched (cfw_patch_lsd_embedded_reg, applied
  by cfw_install.sh, which cfw_install_jb.sh and cfw_install_exp.sh both chain).
  It lives in its OWN dir (scripts/vpregister/), NOT scripts/vphoned/, so it is
  not swept up by the `scripts/vphoned/*.m` globs that build vphoned
  (cfw_install.sh, cfw_install_dev.sh, and the vphoned Makefile all glob that
  dir) — otherwise its main() collides with vphoned's ("duplicate symbol
  '_main'"). Built separately by the JB/EXP installers.
- cfw_install_jb.sh / cfw_install_exp.sh: build + sign (vphoned entitlements +
  CFW signcert) + deploy vpregister to /cores.
- vphone_jb_setup.sh: after dpkg + uicache -a, invoke /cores/vpregister to
  register JB apps via the containerized path. Guarded by [ -x ].

vpregister verified on 17,3_27.0_24A5380h + cloudOS 26.4 (JB): registers Sileo
via the containerized API (uicache -l 0->1) on the gate-patched VM. The exact
cfw_install.sh vphoned build (VPHONED_SRCS glob + clang) now links cleanly with
vpregister.m relocated. Doc: item 12 in research/0_binary_patch_comparison.md.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_013pk5tsoBeuu3jhkmnRFtic
This commit is contained in:
zqxwce
2026-07-20 16:20:34 +03:00
committed by zqxwce
co-authored by Claude Opus 4.8
parent 82485d988d
commit de725d26aa
5 changed files with 162 additions and 1 deletions
+1 -1
View File
@@ -182,7 +182,7 @@ do NOT execute these).
| 9 | `mov w3,#<size>` -> `mov w3,#<base-size>` in `_kern_SwapEnd`**26.0/26.0.1 and 18.x** | DSC `IOMobileFramebuffer` | Fixes host VZ GUI black-screen with the available PCC vphone600 userclient: the userclient does an exact `checkStructureInputSize` check on external-method-5 (SwapEnd) input, so a userland whose `_kern_SwapEnd` sends a different-sized state gets `kIOReturnBadArgument` and the host display stays black (guest still renders — the Apple logo is visible over VNC, just not in the vphone-cli view). **The accepted size is a property of the base kernel, not the userland**: 26.1 base -> **0x560**, 26.4 base (xnu-12377) -> **0x588**. The 0x588 value is confirmed two ways: the sole dispatch-shaped entry in `kernelcache.*.vphone600` with `checkStructureInputSize==0x588` (scalarIn=0, scalarOut=0, structOut=0, preceded by a ptrauth code ptr, at decompressed file offset 0x9c7228), and empirically — native 26.5 userland sends 0x588 and displays correctly on this stack. Source (userland-sent) sizes observed: 18.6.2 = 0x514, 26.0/26.0.1 = 0x548, 27.0 (24A5380h) = 0x6e0. The patcher is semantic (anchors on `mov w1,#5` -> `mov w3,#imm` -> `mov x4,#0`/`mov x5,#0` -> `bl` inside `_kern_SwapEnd`) and idempotent — rewrites the size to `--target-size` regardless of source and re-attests the modified DSC page. Install gate: `26.0*` / `18.*` -> 0x560 (26.1 base). Validated after host install on `17,3_26.0_23A341`, `17,3_26.0.1_23A355`, and `17,3_18.6.2_22G100` (Apple logo renders) against the 26.1 base. **CORRECTION (2026-07-15): iOS 27.0 is NO LONGER handled here.** 27 presents the paravirt display via IOMFB's `_virt_*` callback path — external method 5 is NEVER called — so no SwapEnd *size* change can help 27 (confirmed by kernel trace + live AppleParavirtGPU idle scheduler). iOS 27 now uses **force-kern (item 11)** to route present back onto method 5; this row applies to 26.0/26.0.1/18.x only. | Y | Y | Y |
| 10 | Zero `maxSlide` in `dyld_cache_header` (`@0xF0`) — **iOS 27.0 / any userland whose cache overflows the 6 GiB region** | DSC `dyld_shared_cache_arm64e` header | Fixes pid-1 `launchd` panic at boot on the vphone600 26.x kernel. The kernel reserves `SHARED_REGION_SIZE_ARM64 = 0x180000000` (6 GiB) and, at map time, needs room for the cache's mapped span **plus** the header `maxSlide` (ASLR range). iOS 27.0's cache (span `0x17c830000` ≈ 5.95 GiB) + `maxSlide 0x20000000` = `0x19c830000` > 6 GiB, so `_shared_region_map_and_slide` returns `ENOMEM`, dyld cannot map `libSystem.B.dylib`, and `launchd` panics (`initproc failed to start`). Zeroing `maxSlide` (LE u64) in the main chunk maps the cache at slide 0 (fits with ~58 MiB spare). Self-gating (`patch-dsc-maxslide`): no-op unless span + maxSlide > `0x180000000`, so 26.x / 18.x are untouched. **No** page re-attestation (header metadata, not a `cs_validate`'d code page — confirmed empirically). Validated on `17,3_27.0_24A5380h` + cloudOS 26.4 (`c0ecdb4b…`): `dyld cache mapped system-wide`, launchd reaches first unlock, vphoned connects as iOS 27.0.0, 0 panics. See `scripts/patchers/cfw_patch_dsc_maxslide.py`. | Y | Y | Y |
| 11 | Retarget public `_IOMobileFramebufferSwap*` trampolines -> `b _kern_Swap*` (force-kern) — **iOS 27.0** | DSC `IOMobileFramebuffer` | **iOS-27 VZ-view (host paravirt-GPU scanout) fix, userland half.** The host `VZVirtualMachineView` is fed by the guest `AppleParavirtGPU` scanout, which the 26.4 kernel drives ONLY from the IOMFB userclient SwapEnd (external method 5) — the `_kern_Swap*` path. iOS 27 defaults the paravirt display's present to IOMFB's parallel `_virt_Swap*` path (`_virt_SwapEnd` does no userclient call — it invokes an in-process callback `blraaz [conn+0xe68]` and hands the IOSurface to a virtual-display consumer), so the paravirt GPU never scans out → host VZ window black (guest still composites; GUI visible over in-guest TrollVNC; AppleParavirtGPU `SchedulerState` idle). The public `_IOMobileFramebufferSwap*` entrypoints are thin trampolines (`cbz x0; ldr xN,[x0,#slot]; cbz xN; braaz xN`) that tail-call the per-connection swap fp (kern or virt impl). This patch rewrites each trampoline's first insn to `b _kern_Swap<Name>`, forcing present onto method 5 regardless of how 27 classified the display (tail-call, args intact → behaviourally identical to selecting the kern fp). Fully dynamic: public + `_kern_` addrs resolved by name via `ipsw dyld symaddr`, trampoline shape verified by Capstone, branch bytes from Keystone `asm_at()`, modified DSC code pages re-attested. Requires ≥{SwapBegin,SwapEnd,SwapSetLayer} or raises (dry-run retargets 31 entrypoints on 24A5380h, skips 4 non-trampolines). **Pairs with the JB kernel patches (`patchIomfbSwapEndVariableSize` + `patchIomfbSwapEndHandlerSize`)** which relax the 26.4 userclient's two exact `0x588` size gates to accept 27's native `0x6e0` IOMFBSwapRec (prefix matches 26.x, so the paravirt swap handler reads valid fields). Install gate: `27.*`. See `scripts/patchers/cfw_patch_iomfb_force_kern.py`. **VALIDATED on-device (2026-07-15, `17,3_27.0_24A5380h` + cloudOS 26.4 `c0ecdb4b…`, JB): iOS 27 userland renders AND is interactive in the native VZ view (not just TrollVNC); clean boot — no `kIOReturnBadArgument`/SwapEnd rejection/panic.** Runtime confirmed 31 entrypoints retargeted (4 non-trampoline setters left on virt). | Y | Y | Y |
| 12 | NOP `-[_LSDModifyClient clientIsEntitledForEmbeddedRegistrationOperations]` entitlement gate + per-page re-attest — **iOS 27.0** | DSC `CoreServices` (LaunchServices) | **iOS-27 app-registration fix.** lsd gates `-[_LSDModifyClient performPostInstallationRegistration:operationUUID:reply:]` (and the containerized/rebuild registration paths) behind `clientIsEntitledForEmbeddedRegistrationOperations`, which does `xpc_connection_copy_entitlement_value` on the XPC peer for any of `com.apple.private.coreservices.lsaw` / `com.apple.private.installcoordinationd.daemon` / `com.apple.private.coreservices.can-register-install-results`. A client without one gets `NSOSStatusErrorDomain -54` (permErr, `LSDModifyService.mm:1639`), so `registerApplicationDictionary:` / `registerContainerizedApplicationWithInfoDictionaries:` fail and no app can (re)register — blocking vphoned's installer, TrollStore, and uicache/Sileo alike. The entitlement route is a dead end even for a launchd platform daemon (vphoned) whose validated csblob (`csops CS_OPS_ENTITLEMENTS_BLOB`) contains all three: LS registration is proxied, so the XPC peer lsd inspects is not the registering process. Fix: NOP the final `cbz w0, <not_entitled>` (the conditional branch whose fall-through sets the `mov w<reg>,#1` result) so the method always returns YES. Fully dynamic: method resolved via the DSC's own `.symbols` in-image local-symbol table (ipsw `symaddr -a`/`a2s` time out on this cache), gate located by control-flow shape in Capstone, NOP from Keystone `asm("nop")`, modified 16 KiB page re-attested (`cfw_dsc_codesign.py`; TXM enforces per-page). The resulting CDHash change is accepted by the JB always-true AMFI cdhash-trust patch. Self-gating (`patch-lsd-embedded-reg`): no-op on pre-iOS-27 userlands where the method is absent. **Pairs with vphoned's `vp_register_path` containerized-registration fallback** (`registerContainerizedApplicationWithInfoDictionaries:...:registrationError:`, treating a nil `registrationError` as success since it returns NO even when it registers). **VALIDATED (2026-07-17, `17,3_27.0_24A5380h` + cloudOS 26.4, JB): -54 gone; Sileo registers (`uicache -l` 0→1); vphoned installs+registers a test IPA (`com.vphone.vptest`) to `/var/containers/Bundle/Application/` end-to-end. Clean boot (re-attest correct; no CoreServices page rejection).** See `scripts/patchers/cfw_patch_lsd_embedded_reg.py`. | Y | Y | Y |
| 12 | NOP `-[_LSDModifyClient clientIsEntitledForEmbeddedRegistrationOperations]` entitlement gate + per-page re-attest — **iOS 27.0** | DSC `CoreServices` (LaunchServices) | **iOS-27 app-registration fix.** lsd gates `-[_LSDModifyClient performPostInstallationRegistration:operationUUID:reply:]` (and the containerized/rebuild registration paths) behind `clientIsEntitledForEmbeddedRegistrationOperations`, which does `xpc_connection_copy_entitlement_value` on the XPC peer for any of `com.apple.private.coreservices.lsaw` / `com.apple.private.installcoordinationd.daemon` / `com.apple.private.coreservices.can-register-install-results`. A client without one gets `NSOSStatusErrorDomain -54` (permErr, `LSDModifyService.mm:1639`), so `registerApplicationDictionary:` / `registerContainerizedApplicationWithInfoDictionaries:` fail and no app can (re)register — blocking vphoned's installer, TrollStore, and uicache/Sileo alike. The entitlement route is a dead end even for a launchd platform daemon (vphoned) whose validated csblob (`csops CS_OPS_ENTITLEMENTS_BLOB`) contains all three: LS registration is proxied, so the XPC peer lsd inspects is not the registering process. Fix: NOP the final `cbz w0, <not_entitled>` (the conditional branch whose fall-through sets the `mov w<reg>,#1` result) so the method always returns YES. Fully dynamic: method resolved via the DSC's own `.symbols` in-image local-symbol table (ipsw `symaddr -a`/`a2s` time out on this cache), gate located by control-flow shape in Capstone, NOP from Keystone `asm("nop")`, modified 16 KiB page re-attested (`cfw_dsc_codesign.py`; TXM enforces per-page). The resulting CDHash change is accepted by the JB always-true AMFI cdhash-trust patch. Self-gating (`patch-lsd-embedded-reg`): no-op on pre-iOS-27 userlands where the method is absent. **Pairs with vphoned's `vp_register_path` containerized-registration fallback** (`registerContainerizedApplicationWithInfoDictionaries:...:registrationError:`, treating a nil `registrationError` as success since it returns NO even when it registers). Also paired with **`/cores/vpregister`** (built + deployed by `cfw_install_jb.sh` / `cfw_install_exp.sh`, invoked by `vphone_jb_setup.sh` at first boot): it registers JB app bundles (Sileo) via the same containerized API, because `uicache -a`'s `registerApplicationDictionary:` is a deprecated no-op on iOS 27 (lsd logs *"you cannot use ... to register applications anymore. These interfaces have been deprecated for years."*). **VALIDATED (2026-07-17, `17,3_27.0_24A5380h` + cloudOS 26.4, JB): -54 gone; Sileo registers (`uicache -l` 0→1) via `vpregister`; vphoned installs+registers a test IPA (`com.vphone.vptest`) to `/var/containers/Bundle/Application/` end-to-end. Clean boot (re-attest correct; no CoreServices page rejection).** See `scripts/patchers/cfw_patch_lsd_embedded_reg.py` and `scripts/vphoned/vpregister.m`. | Y | Y | Y |
### Installed Components
+49
View File
@@ -203,6 +203,34 @@ build_tweakloader() {
echo "$out"
}
# Build vpregister — registers JB apps via the containerized LaunchServices API on
# iOS 27, where -[LSApplicationWorkspace registerApplicationDictionary:] (uicache -a)
# is a deprecated no-op stub. Needs the lsd embedded-reg gate patch
# (cfw_patch_lsd_embedded_reg, applied by cfw_install.sh). Deployed to /cores and
# invoked by vphone_jb_setup.sh at first boot.
build_vpregister() {
local src="$SCRIPT_DIR/vpregister/vpregister.m"
local out="$TEMP_DIR/vpregister"
local sdk cc
[[ -f "$src" ]] || die "Missing vpregister source at $src"
sdk="$(xcrun --sdk iphoneos --show-sdk-path)"
cc="$(xcrun --sdk iphoneos -f clang)"
"$cc" -isysroot "$sdk" \
-arch arm64e \
-miphoneos-version-min=15.0 \
-fobjc-arc -Os \
-framework Foundation \
-Wl,-undefined,dynamic_lookup \
-o "$out" \
"$src"
ldid_sign_ent "$out" "$SCRIPT_DIR/vphoned/entitlements.plist"
echo "$out"
}
# Builds the libvcamcaptured.dylib injected into /usr/libexec/cameracaptured
# via the TweakLoader allowlist. Output goes to TEMP_DIR; caller copies the
# binary + companion plist to procursus/Library/MobileSubstrate/DynamicLibraries.
@@ -613,6 +641,27 @@ if [[ -f "$SETUP_SCRIPT" ]]; then
/bin/chmod 0755 $MNT1/cores/vphone_jb_setup.sh
echo " [+] vphone_jb_setup.sh -> /cores/"
fi
# vpregister: registers JB apps via the containerized LS API at first boot (uicache -a's
# registerApplicationDictionary is a deprecated no-op on iOS 27). Deployed ONLY on a 27
# base — it needs the 27-only lsd embedded-reg gate, and on 26.x/18.x uicache registers
# apps normally. Its /cores presence IS the runtime gate in vphone_jb_setup.sh (that
# script must NOT version-check on guest sw_vers — the hybrid guest does not reliably
# report the 27 userland version at first boot). Version read from the mounted rootfs
# SystemVersion.plist, the same source cfw_install.sh gates its 27 patches on.
JB_BASE_IOS=$(/usr/bin/plutil -extract ProductVersion raw -o - "$MNT1/System/Library/CoreServices/SystemVersion.plist" 2>/dev/null || true)
case "$JB_BASE_IOS" in
27.*)
VPREGISTER="$(build_vpregister)"
if [[ -f "$VPREGISTER" ]]; then
cp -R "$VPREGISTER" "$MNT1/cores/vpregister"
/bin/chmod 0755 $MNT1/cores/vpregister
echo " [+] vpregister -> /cores/ (iOS $JB_BASE_IOS)"
fi
;;
*)
echo " [skip] vpregister (base iOS ${JB_BASE_IOS:-unknown} — 27-only; uicache registers apps on older bases)"
;;
esac
if [[ -f "$SETUP_PLIST" ]]; then
cp -R "$SETUP_PLIST" "$MNT1/System/Library/LaunchDaemons/com.vphone.jb-setup.plist"
/bin/chmod 0644 $MNT1/System/Library/LaunchDaemons/com.vphone.jb-setup.plist
+49
View File
@@ -104,6 +104,34 @@ build_tweakloader() {
echo "$out"
}
# Build vpregister — registers JB app bundles via the containerized LaunchServices
# API on iOS 27, where -[LSApplicationWorkspace registerApplicationDictionary:]
# (uicache -a) is a deprecated no-op stub. Needs the lsd embedded-reg gate patch
# (cfw_patch_lsd_embedded_reg, applied by cfw_install.sh). Deployed to /cores and
# invoked by vphone_jb_setup.sh at first boot.
build_vpregister() {
local src="$SCRIPT_DIR/vpregister/vpregister.m"
local out="$TEMP_DIR/vpregister"
local sdk cc
[[ -f "$src" ]] || die "Missing vpregister source at $src"
sdk="$(xcrun --sdk iphoneos --show-sdk-path)"
cc="$(xcrun --sdk iphoneos -f clang)"
"$cc" -isysroot "$sdk" \
-arch arm64e \
-miphoneos-version-min=15.0 \
-fobjc-arc -Os \
-framework Foundation \
-Wl,-undefined,dynamic_lookup \
-o "$out" \
"$src"
ldid_sign_ent "$out" "$SCRIPT_DIR/vphoned/entitlements.plist"
echo "$out"
}
get_boot_manifest_hash() {
/bin/ls $MNT5 2>/dev/null | awk 'length($0)==96{print; exit}'
}
@@ -367,6 +395,27 @@ if [[ -f "$SETUP_SCRIPT" ]]; then
/bin/chmod 0755 $MNT1/cores/vphone_jb_setup.sh
echo " [+] vphone_jb_setup.sh -> /cores/"
fi
# vpregister: registers JB apps via the containerized LS API at first boot (uicache -a's
# registerApplicationDictionary is a deprecated no-op on iOS 27). Deployed ONLY on a 27
# base — it needs the 27-only lsd embedded-reg gate, and on 26.x/18.x uicache registers
# apps normally. Its /cores presence IS the runtime gate in vphone_jb_setup.sh (that
# script must NOT version-check on guest sw_vers — the hybrid guest does not reliably
# report the 27 userland version at first boot). Version read from the mounted rootfs
# SystemVersion.plist, the same source cfw_install.sh gates its 27 patches on.
JB_BASE_IOS=$(/usr/bin/plutil -extract ProductVersion raw -o - "$MNT1/System/Library/CoreServices/SystemVersion.plist" 2>/dev/null || true)
case "$JB_BASE_IOS" in
27.*)
VPREGISTER="$(build_vpregister)"
if [[ -f "$VPREGISTER" ]]; then
cp -R "$VPREGISTER" "$MNT1/cores/vpregister"
/bin/chmod 0755 $MNT1/cores/vpregister
echo " [+] vpregister -> /cores/ (iOS $JB_BASE_IOS)"
fi
;;
*)
echo " [skip] vpregister (base iOS ${JB_BASE_IOS:-unknown} — 27-only; uicache registers apps on older bases)"
;;
esac
if [[ -f "$SETUP_PLIST" ]]; then
cp -R "$SETUP_PLIST" "$MNT1/System/Library/LaunchDaemons/com.vphone.jb-setup.plist"
/bin/chmod 0644 $MNT1/System/Library/LaunchDaemons/com.vphone.jb-setup.plist
+14
View File
@@ -267,6 +267,20 @@ fi
uicache -a 2>/dev/null || true
log " uicache refreshed"
# iOS 27: -[LSApplicationWorkspace registerApplicationDictionary:] (what uicache -a
# uses) is a deprecated no-op stub on 27, so uicache cannot register JB apps. Register
# them via the containerized LS API instead (needs the lsd embedded-reg gate patch from
# cfw_patch_lsd_embedded_reg). The 27 gate is the /cores/vpregister PRESENCE: it is
# deployed only on a 27 base (cfw_install_jb.sh gates the deploy on the mounted
# SystemVersion.plist), so a 26.x/18.x base has no /cores/vpregister and skips this.
# Do NOT re-add a guest-side `sw_vers` version check here — the hybrid guest's sw_vers
# does not reliably report the 27 userland version at first boot, which silently
# skipped this step and left Sileo unregistered.
if [ -x /cores/vpregister ]; then
log " Registering JB apps via containerized LS API (iOS 27 path)..."
/cores/vpregister 2>&1 | while IFS= read -r vpr_line; do log " $vpr_line"; done
fi
# ═══════════ 8/8 SHELL PROFILES FOR SSH ═══════════════════════
log "[8/8] Setting up shell profiles for SSH..."
# .bashrc — non-login interactive shells (dropbear default)
+49
View File
@@ -0,0 +1,49 @@
// vpregister register JB app bundles on iOS 27 via the containerized
// LaunchServices API (the modern replacement for the deprecated, gutted
// -[LSApplicationWorkspace registerApplicationDictionary:] that uicache -a
// uses). Requires the lsd clientIsEntitledForEmbeddedRegistrationOperations
// gate patch (cfw_patch_lsd_embedded_reg). Usage: vpregister [app.app ...]
// (no args = scan /var/jb/Applications/*.app).
#import <Foundation/Foundation.h>
#import <dlfcn.h>
@interface LSApplicationWorkspace : NSObject
+ (instancetype)defaultWorkspace;
- (BOOL)registerContainerizedApplicationWithInfoDictionaries:(NSArray *)infos
operationUUID:(NSUUID *)uuid
requestContext:(id)context
saveObserver:(id)observer
registrationError:(NSError **)error;
@end
static BOOL regapp(LSApplicationWorkspace *ws, NSString *path) {
NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Info.plist"]];
NSString *bid = info[@"CFBundleIdentifier"];
if (bid.length == 0) return NO;
NSMutableDictionary *d = [NSMutableDictionary dictionary];
d[@"Path"] = path; d[@"CFBundleIdentifier"] = bid; d[@"CodeInfoIdentifier"] = bid;
d[@"ApplicationType"] = @"System"; d[@"CompatibilityState"] = @0;
d[@"SignerIdentity"] = @"Apple iPhone OS Application Signing";
d[@"SignerOrganization"] = @"Apple Inc."; d[@"IsAdHocSigned"] = @YES;
d[@"SignatureVersion"] = @132352; d[@"IsDeletable"] = @YES;
NSError *err = nil;
[ws registerContainerizedApplicationWithInfoDictionaries:@[d] operationUUID:[NSUUID UUID]
requestContext:nil saveObserver:nil registrationError:&err];
if (err) fprintf(stderr, " err: %s\n", err.description.UTF8String);
return err == nil;
}
int main(int argc, char **argv) {
@autoreleasepool {
dlopen("/System/Library/Frameworks/CoreServices.framework/CoreServices", RTLD_NOW);
LSApplicationWorkspace *ws = [LSApplicationWorkspace defaultWorkspace];
NSMutableArray *paths = [NSMutableArray array];
if (argc > 1) { for (int i = 1; i < argc; i++) [paths addObject:@(argv[i])]; }
else {
NSString *dir = @"/var/jb/Applications";
for (NSString *n in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:nil])
if ([n hasSuffix:@".app"]) [paths addObject:[dir stringByAppendingPathComponent:n]];
}
int ok = 0, fail = 0;
for (NSString *p in paths) { BOOL r = regapp(ws, p); printf("%-4s %s\n", r ? "OK" : "FAIL", p.UTF8String); r ? ok++ : fail++; }
printf("registered %d, failed %d\n", ok, fail);
return fail ? 1 : 0;
}
}