-
-
Notifications
You must be signed in to change notification settings - Fork 276
/
path-search.ts
84 lines (73 loc) · 3.14 KB
/
path-search.ts
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
import { log } from './logger'
// import { OS } from '../typings/xpcom'
// https://searchfox.org/mozilla-central/source/toolkit/modules/subprocess/subprocess_win.jsm#135 doesn't seem to work on Windows.
export async function findBinary(bin: string, installationDirectory: { mac?: string[], win?: string[] } = {}): Promise<string> {
const pref = `translators.better-bibtex.path.${bin}`
let location: string = Zotero.Prefs.get(pref)
if (location && (await OS.File.exists(location))) return location
location = await pathSearch(bin, installationDirectory)
if (typeof location === 'string') Zotero.Prefs.set(pref, location)
return location
}
async function* asyncGenerator<T>(array: T[]): AsyncGenerator<T, void, unknown> {
for (const item of array) {
yield await Promise.resolve(item)
}
}
const ENV = Components.classes['@mozilla.org/process/environment;1'].getService(Components.interfaces.nsIEnvironment)
const VarRef = Zotero.isWin ? /%([A-Z][A-Z0-9]*)%/ig : /[$]([A-Z][A-Z0-9]*)/ig
function expandVars(name: string, expanded: Record<string, string>): string {
if (typeof expanded[name] !== 'string') {
expanded[name] = ENV.get(name) || ''
let more = true
while (more) {
more = false
expanded[name] = expanded[name].replace(VarRef, (match, varref) => {
more = true
return expandVars(varref, expanded)
})
}
}
return expanded[name]
}
async function pathSearch(bin: string, installationDirectory: { mac?: string[], win?: string[] } = {}): Promise<string> {
const env = Components.classes['@mozilla.org/process/environment;1'].getService(Components.interfaces.nsIEnvironment)
let paths: string[] = ENV.get('PATH').split(Zotero.isWin ? ';' : ':')
const expanded = {}
paths = paths.map(p => expandVars(p, expanded))
if (Zotero.isWin && installationDirectory.win) paths.unshift(...(installationDirectory.win))
if (Zotero.isMac && installationDirectory.mac) paths.unshift(...(installationDirectory.mac))
paths = paths.filter(p => p)
if (!paths.length) {
log.error('path-search: PATH not set')
return ''
}
const extensions: string[] = Zotero.isWin ? ENV.get('PATHEXT').split(';').filter((e: string) => e.match(/^[.].+/)) : ['']
if (Zotero.isWin && !extensions.length) {
log.error('path-search: PATHEXT not set')
return ''
}
for await (const path of asyncGenerator(paths)) {
for (const ext of extensions) {
try {
const exe: string = OS.Path.join(path, bin + ext)
if (!(await OS.File.exists(exe))) continue
// eslint-disable-next-line @typescript-eslint/await-thenable
const stat = await OS.File.stat(exe)
if (stat.isDir) continue
// eslint-disable-next-line no-bitwise
if (!Zotero.isWin && (stat.unixMode & 111) === 0) { // bit iffy -- we don't know if *we* can execute this.
log.error(`path-search: ${exe} exists but has mode ${(stat.unixMode).toString(8)}`)
continue
}
log.debug(`path-search: ${bin} found at ${exe}`)
return exe
}
catch (err) {
log.error('path-search:', err)
}
}
}
log.debug('path-search:', bin, 'not found in', env.path)
return ''
}