/
executable_path.cr
108 lines (90 loc) · 2.83 KB
/
executable_path.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# Reference:
# - https://github.com/gpakosz/whereami/blob/master/src/whereami.c
# - http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
class Process
PATH_DELIMITER = {% if flag?(:windows) %} ';' {% else %} ':' {% end %}
# :nodoc:
INITIAL_PATH = ENV["PATH"]?
# :nodoc:
INITIAL_PWD = Dir.current
# Returns an absolute path to the executable file of the currently running
# program. This is in opposition to `PROGRAM_NAME` which may be a relative or
# absolute path, just the executable file name or a symlink.
#
# The executable path will be canonicalized (all symlinks and relative paths
# will be expanded).
#
# Returns `nil` if the file can't be found.
def self.executable_path
if executable = executable_path_impl
begin
File.real_path(executable)
rescue Errno
end
end
end
# Searches an executable, checking for an absolute path, a path relative to
# *pwd* or absolute path, then eventually searching in directories declared
# in *path*.
def self.find_executable(name, path = ENV["PATH"]?, pwd = Dir.current)
if name.starts_with?(File::SEPARATOR)
return name
end
if name.includes?(File::SEPARATOR)
return File.expand_path(name, pwd)
end
return unless path
path.split(PATH_DELIMITER).each do |path|
executable = File.join(path, name)
return executable if File.exists?(executable)
end
nil
end
end
{% if flag?(:darwin) %}
lib LibC
PATH_MAX = 1024
fun _NSGetExecutablePath(buf : Char*, bufsize : UInt32*) : Int
end
class Process
private def self.executable_path_impl
buf = GC.malloc_atomic(LibC::PATH_MAX).as(UInt8*)
size = LibC::PATH_MAX.to_u32
if LibC._NSGetExecutablePath(buf, pointerof(size)) == -1
buf = GC.malloc_atomic(size).as(UInt8*)
return nil if LibC._NSGetExecutablePath(buf, pointerof(size)) == -1
end
String.new(buf)
end
end
{% elsif flag?(:freebsd) %}
lib LibC
PATH_MAX = 1024
CTL_KERN = 1
KERN_PROC = 14
KERN_PROC_PATHNAME = 12
fun sysctl(name : Int*, namelen : UInt, oldp : Void*, oldlenp : SizeT*, newp : Void*, newlen : SizeT) : Int
end
class Process
private def self.executable_path_impl
mib = Int32[LibC::CTL_KERN, LibC::KERN_PROC, LibC::KERN_PROC_PATHNAME, -1]
buf = GC.malloc_atomic(LibC::PATH_MAX).as(UInt8*)
size = LibC::SizeT.new(LibC::PATH_MAX)
if LibC.sysctl(mib, 4, buf, pointerof(size), nil, 0) == 0
String.new(buf, size - 1)
end
end
end
{% elsif flag?(:linux) %}
class Process
private def self.executable_path_impl
"/proc/self/exe"
end
end
{% else %} # openbsd, ...
class Process
private def self.executable_path_impl
find_executable(PROGRAM_NAME, INITIAL_PATH, INITIAL_PWD)
end
end
{% end %}