From 3e183031db0ff3b6b26ec46b217253f730f92a7f Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Fri, 13 Mar 2026 18:41:43 +1100 Subject: [PATCH] winlnk (windows shortcut) resolution fixes for non-windows os e.g WSL --- .../modules/punk/mix/util-0.1.0.tm | 5 +-- src/modules/punk/winlnk-999999.0a1.0.tm | 45 ++++++++++++++++++- .../modules/punk/mix/util-0.1.0.tm | 5 +-- .../modules/punk/mix/util-0.1.0.tm | 5 +-- .../modules/punk/mix/util-0.1.0.tm | 5 +-- .../modules/punk/winlnk-0.1.0.tm | 45 ++++++++++++++++++- 6 files changed, 96 insertions(+), 14 deletions(-) diff --git a/src/bootsupport/modules/punk/mix/util-0.1.0.tm b/src/bootsupport/modules/punk/mix/util-0.1.0.tm index 56585e02..e816e1ef 100644 --- a/src/bootsupport/modules/punk/mix/util-0.1.0.tm +++ b/src/bootsupport/modules/punk/mix/util-0.1.0.tm @@ -107,9 +107,6 @@ namespace eval punk::mix::util { set opts [dict remove $opts -noredirect] } - if {$::tcl_platform(platform) ne "windows"} { - return [fileutil::cat {*}$args] - } set finalpaths [list] set is_windows [string match *windows* $::tcl_platform(platform)] @@ -120,7 +117,9 @@ namespace eval punk::mix::util { lappend finalpaths $p } } + #fauxlink is platform agnostic. set has_fauxlink [expr {![catch {package require fauxlink}]}] + #While .lnk files are windows specific, we want to be able to resolve them on other platforms if possible, so we try to load punk::winlnk on all platforms. set has_winlnk [expr {![catch {package require punk::winlnk}]}] if {!$opt_noredirect && ($has_fauxlink || $has_winlnk)} { diff --git a/src/modules/punk/winlnk-999999.0a1.0.tm b/src/modules/punk/winlnk-999999.0a1.0.tm index 6994b69e..d00e606d 100644 --- a/src/modules/punk/winlnk-999999.0a1.0.tm +++ b/src/modules/punk/winlnk-999999.0a1.0.tm @@ -612,7 +612,50 @@ tcl::namespace::eval punk::winlnk { set linkfields [LinkInfo_get_fields $linkinfocontent] set localbase_path [dict get $linkfields localbasepath] set suffix_path [dict get $linkfields commonpathsuffix] - set link_target [file join $localbase_path $suffix_path] + if {"windows" eq $::tcl_platform(platform)} { + set link_target [file join $localbase_path $suffix_path] + } else { + if {[regexp {([a-zA-Z]):/(.*)} $localbase_path _match drive_letter tail]} { + set link_target "" + #shortcut basepath is a windows path with drive letter - try to resolve it on unix by looking for a corresponding mount from fstab or a point under /mnt + set mountinfo [exec mount] + foreach line [split $mountinfo "\n"] { + #review - a more specific mount target might exist that includes the drive letter as part of the mount point name and is a longer prefix of the localbase_path + #- we should probably look for the longest prefix match rather than just the drive letter + if {[regexp -nocase -- [string cat ^$drive_letter {:\\\s+on\s+(\S)}] $line _match mount_point]} { + set link_target [file join $mount_point $tail $suffix_path] + break + } + } + if {$link_target eq ""} { + #review - under what circumstances could this happen? If the drive letter doesn't match any mount points, then /mnt/drive_letter should generally already have been found above above + # - However, it may be possible for /mnt/drive_Letter to still exist even if it's not reflected in the output of mount or the output of mount is in an unexpected format. + + #nothing in mount result matches the drive letter - try looking for a mount point under /mnt with the drive letter as the name + if {[file exists /mnt/$drive_letter]} { + set link_target [file join /mnt/$drive_letter $tail $suffix_path] + } else { + if {$drive_letter eq [string tolower $drive_letter]]} { + set op_drive_letter [string toupper $drive_letter] + } else { + set op_drive_letter [string tolower $drive_letter] + } + if {[file exists /mnt/$op_drive_letter]} { + set link_target [file join /mnt/$op_drive_letter $tail $suffix_path] + } else { + #leave as is - probably won't resolve correctly but we have no better option + set link_target [file join $localbase_path $suffix_path] + } + } + } else { + #shortcut basepath is a windows path with drive letter and we found a matching mount point - link_target is set to the resolved path + } + } else { + #shortcut basepath doesn't match expected windows path format - just join it with the suffix and hope for the best + #could be something like a network path or it could be something else entirely + set link_target [file join $localbase_path $suffix_path] + } + } } set result [dict create\ diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/util-0.1.0.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/util-0.1.0.tm index 56585e02..e816e1ef 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/util-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/mix/util-0.1.0.tm @@ -107,9 +107,6 @@ namespace eval punk::mix::util { set opts [dict remove $opts -noredirect] } - if {$::tcl_platform(platform) ne "windows"} { - return [fileutil::cat {*}$args] - } set finalpaths [list] set is_windows [string match *windows* $::tcl_platform(platform)] @@ -120,7 +117,9 @@ namespace eval punk::mix::util { lappend finalpaths $p } } + #fauxlink is platform agnostic. set has_fauxlink [expr {![catch {package require fauxlink}]}] + #While .lnk files are windows specific, we want to be able to resolve them on other platforms if possible, so we try to load punk::winlnk on all platforms. set has_winlnk [expr {![catch {package require punk::winlnk}]}] if {!$opt_noredirect && ($has_fauxlink || $has_winlnk)} { diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/util-0.1.0.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/util-0.1.0.tm index 56585e02..e816e1ef 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/util-0.1.0.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/mix/util-0.1.0.tm @@ -107,9 +107,6 @@ namespace eval punk::mix::util { set opts [dict remove $opts -noredirect] } - if {$::tcl_platform(platform) ne "windows"} { - return [fileutil::cat {*}$args] - } set finalpaths [list] set is_windows [string match *windows* $::tcl_platform(platform)] @@ -120,7 +117,9 @@ namespace eval punk::mix::util { lappend finalpaths $p } } + #fauxlink is platform agnostic. set has_fauxlink [expr {![catch {package require fauxlink}]}] + #While .lnk files are windows specific, we want to be able to resolve them on other platforms if possible, so we try to load punk::winlnk on all platforms. set has_winlnk [expr {![catch {package require punk::winlnk}]}] if {!$opt_noredirect && ($has_fauxlink || $has_winlnk)} { diff --git a/src/vfs/_vfscommon.vfs/modules/punk/mix/util-0.1.0.tm b/src/vfs/_vfscommon.vfs/modules/punk/mix/util-0.1.0.tm index 56585e02..e816e1ef 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/mix/util-0.1.0.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/mix/util-0.1.0.tm @@ -107,9 +107,6 @@ namespace eval punk::mix::util { set opts [dict remove $opts -noredirect] } - if {$::tcl_platform(platform) ne "windows"} { - return [fileutil::cat {*}$args] - } set finalpaths [list] set is_windows [string match *windows* $::tcl_platform(platform)] @@ -120,7 +117,9 @@ namespace eval punk::mix::util { lappend finalpaths $p } } + #fauxlink is platform agnostic. set has_fauxlink [expr {![catch {package require fauxlink}]}] + #While .lnk files are windows specific, we want to be able to resolve them on other platforms if possible, so we try to load punk::winlnk on all platforms. set has_winlnk [expr {![catch {package require punk::winlnk}]}] if {!$opt_noredirect && ($has_fauxlink || $has_winlnk)} { diff --git a/src/vfs/_vfscommon.vfs/modules/punk/winlnk-0.1.0.tm b/src/vfs/_vfscommon.vfs/modules/punk/winlnk-0.1.0.tm index 03de3d4b..4bde677b 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/winlnk-0.1.0.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/winlnk-0.1.0.tm @@ -612,7 +612,50 @@ tcl::namespace::eval punk::winlnk { set linkfields [LinkInfo_get_fields $linkinfocontent] set localbase_path [dict get $linkfields localbasepath] set suffix_path [dict get $linkfields commonpathsuffix] - set link_target [file join $localbase_path $suffix_path] + if {"windows" eq $::tcl_platform(platform)} { + set link_target [file join $localbase_path $suffix_path] + } else { + if {[regexp {([a-zA-Z]):/(.*)} $localbase_path _match drive_letter tail]} { + set link_target "" + #shortcut basepath is a windows path with drive letter - try to resolve it on unix by looking for a corresponding mount from fstab or a point under /mnt + set mountinfo [exec mount] + foreach line [split $mountinfo "\n"] { + #review - a more specific mount target might exist that includes the drive letter as part of the mount point name and is a longer prefix of the localbase_path + #- we should probably look for the longest prefix match rather than just the drive letter + if {[regexp -nocase -- [string cat ^$drive_letter {:\\\s+on\s+(\S)}] $line _match mount_point]} { + set link_target [file join $mount_point $tail $suffix_path] + break + } + } + if {$link_target eq ""} { + #review - under what circumstances could this happen? If the drive letter doesn't match any mount points, then /mnt/drive_letter should generally already have been found above above + # - However, it may be possible for /mnt/drive_Letter to still exist even if it's not reflected in the output of mount or the output of mount is in an unexpected format. + + #nothing in mount result matches the drive letter - try looking for a mount point under /mnt with the drive letter as the name + if {[file exists /mnt/$drive_letter]} { + set link_target [file join /mnt/$drive_letter $tail $suffix_path] + } else { + if {$drive_letter eq [string tolower $drive_letter]]} { + set op_drive_letter [string toupper $drive_letter] + } else { + set op_drive_letter [string tolower $drive_letter] + } + if {[file exists /mnt/$op_drive_letter]} { + set link_target [file join /mnt/$op_drive_letter $tail $suffix_path] + } else { + #leave as is - probably won't resolve correctly but we have no better option + set link_target [file join $localbase_path $suffix_path] + } + } + } else { + #shortcut basepath is a windows path with drive letter and we found a matching mount point - link_target is set to the resolved path + } + } else { + #shortcut basepath doesn't match expected windows path format - just join it with the suffix and hope for the best + #could be something like a network path or it could be something else entirely + set link_target [file join $localbase_path $suffix_path] + } + } } set result [dict create\