2021-01-18 15:20:06 +03:00
|
|
|
// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
|
2019-06-23 05:21:30 +03:00
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
2019-06-22 21:20:28 +03:00
|
|
|
module os
|
|
|
|
|
2020-12-15 10:58:33 +03:00
|
|
|
pub struct Result {
|
|
|
|
pub:
|
|
|
|
exit_code int
|
|
|
|
output string
|
|
|
|
// stderr string // TODO
|
|
|
|
}
|
|
|
|
|
2019-10-24 12:36:57 +03:00
|
|
|
pub const (
|
2020-12-16 12:02:36 +03:00
|
|
|
args = []string{}
|
|
|
|
max_path_len = 4096
|
2020-12-15 10:58:33 +03:00
|
|
|
wd_at_startup = getwd()
|
2019-06-22 21:20:28 +03:00
|
|
|
)
|
|
|
|
|
2020-12-15 10:58:33 +03:00
|
|
|
const (
|
|
|
|
f_ok = 0
|
|
|
|
x_ok = 1
|
|
|
|
w_ok = 2
|
|
|
|
r_ok = 4
|
|
|
|
)
|
2019-11-01 00:57:16 +03:00
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// cp_all will recursively copy `src` to `dst`,
|
|
|
|
// optionally overwriting files or dirs in `dst`.
|
2020-10-15 13:32:28 +03:00
|
|
|
pub fn cp_all(src string, dst string, overwrite bool) ? {
|
2020-12-15 10:33:31 +03:00
|
|
|
source_path := real_path(src)
|
|
|
|
dest_path := real_path(dst)
|
|
|
|
if !exists(source_path) {
|
2021-01-13 08:05:27 +03:00
|
|
|
return error("Source path doesn't exist")
|
2019-11-06 23:05:35 +03:00
|
|
|
}
|
2019-12-20 00:29:37 +03:00
|
|
|
// single file copy
|
2020-12-15 10:33:31 +03:00
|
|
|
if !is_dir(source_path) {
|
2020-12-27 16:20:30 +03:00
|
|
|
adjusted_path := if is_dir(dest_path) {
|
|
|
|
join_path(dest_path, file_name(source_path))
|
|
|
|
} else {
|
|
|
|
dest_path
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
if exists(adjusted_path) {
|
2019-11-17 06:45:20 +03:00
|
|
|
if overwrite {
|
2021-01-26 17:43:10 +03:00
|
|
|
rm(adjusted_path) ?
|
2020-12-15 10:33:31 +03:00
|
|
|
} else {
|
2019-11-19 02:25:55 +03:00
|
|
|
return error('Destination file path already exist')
|
2019-11-17 06:45:20 +03:00
|
|
|
}
|
2019-11-06 23:05:35 +03:00
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
cp(source_path, adjusted_path) ?
|
2020-06-29 16:06:26 +03:00
|
|
|
return
|
2019-11-06 23:05:35 +03:00
|
|
|
}
|
2021-03-12 17:26:55 +03:00
|
|
|
if !exists(dest_path) {
|
|
|
|
mkdir(dest_path) ?
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
if !is_dir(dest_path) {
|
2019-11-06 23:05:35 +03:00
|
|
|
return error('Destination path is not a valid directory')
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
files := ls(source_path) ?
|
2019-11-06 23:05:35 +03:00
|
|
|
for file in files {
|
2020-12-15 10:33:31 +03:00
|
|
|
sp := join_path(source_path, file)
|
|
|
|
dp := join_path(dest_path, file)
|
|
|
|
if is_dir(sp) {
|
2021-02-12 02:55:36 +03:00
|
|
|
if !exists(dp) {
|
|
|
|
mkdir(dp) ?
|
|
|
|
}
|
2019-11-06 23:05:35 +03:00
|
|
|
}
|
2020-03-02 21:30:04 +03:00
|
|
|
cp_all(sp, dp, overwrite) or {
|
2021-02-28 23:20:21 +03:00
|
|
|
rmdir(dp) or { return err }
|
|
|
|
return err
|
2019-11-06 23:05:35 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-19 12:55:03 +03:00
|
|
|
// mv_by_cp first copies the source file, and if it is copied successfully, deletes the source file.
|
2020-08-27 17:57:01 +03:00
|
|
|
// may be used when you are not sure that the source and target are on the same mount/partition.
|
2020-06-25 13:06:47 +03:00
|
|
|
pub fn mv_by_cp(source string, target string) ? {
|
2020-12-15 10:33:31 +03:00
|
|
|
cp(source, target) ?
|
|
|
|
rm(source) ?
|
2019-11-19 12:55:03 +03:00
|
|
|
}
|
|
|
|
|
2019-06-23 03:28:29 +03:00
|
|
|
// read_lines reads the file in `path` into an array of lines.
|
2019-11-19 02:25:55 +03:00
|
|
|
pub fn read_lines(path string) ?[]string {
|
2020-12-15 10:33:31 +03:00
|
|
|
buf := read_file(path) ?
|
2021-03-22 12:20:55 +03:00
|
|
|
res := buf.split_into_lines()
|
|
|
|
unsafe { buf.free() }
|
|
|
|
return res
|
2019-06-22 21:20:28 +03:00
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// read_ulines reads the file in `path` into an array of ustring lines.
|
2019-11-19 02:25:55 +03:00
|
|
|
fn read_ulines(path string) ?[]ustring {
|
2020-12-15 10:33:31 +03:00
|
|
|
lines := read_lines(path) ?
|
2019-06-22 21:20:28 +03:00
|
|
|
// mut ulines := new_array(0, lines.len, sizeof(ustring))
|
2020-04-26 10:17:13 +03:00
|
|
|
mut ulines := []ustring{}
|
2019-06-22 21:20:28 +03:00
|
|
|
for myline in lines {
|
|
|
|
// ulines[i] = ustr
|
|
|
|
ulines << myline.ustring()
|
|
|
|
}
|
|
|
|
return ulines
|
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// sigint_to_signal_name will translate `si` signal integer code to it's string code representation.
|
2019-10-08 02:34:55 +03:00
|
|
|
pub fn sigint_to_signal_name(si int) string {
|
|
|
|
// POSIX signals:
|
2019-10-24 19:19:03 +03:00
|
|
|
match si {
|
2020-12-15 10:33:31 +03:00
|
|
|
1 { return 'SIGHUP' }
|
|
|
|
2 { return 'SIGINT' }
|
|
|
|
3 { return 'SIGQUIT' }
|
|
|
|
4 { return 'SIGILL' }
|
|
|
|
6 { return 'SIGABRT' }
|
|
|
|
8 { return 'SIGFPE' }
|
|
|
|
9 { return 'SIGKILL' }
|
|
|
|
11 { return 'SIGSEGV' }
|
|
|
|
13 { return 'SIGPIPE' }
|
|
|
|
14 { return 'SIGALRM' }
|
|
|
|
15 { return 'SIGTERM' }
|
2019-12-27 19:59:04 +03:00
|
|
|
else {}
|
|
|
|
}
|
2019-10-08 02:34:55 +03:00
|
|
|
$if linux {
|
|
|
|
// From `man 7 signal` on linux:
|
2019-10-24 19:19:03 +03:00
|
|
|
match si {
|
2020-04-24 17:04:39 +03:00
|
|
|
// TODO dependent on platform
|
|
|
|
// works only on x86/ARM/most others
|
2020-12-15 10:33:31 +03:00
|
|
|
10 /* , 30, 16 */ { return 'SIGUSR1' }
|
|
|
|
12 /* , 31, 17 */ { return 'SIGUSR2' }
|
|
|
|
17 /* , 20, 18 */ { return 'SIGCHLD' }
|
|
|
|
18 /* , 19, 25 */ { return 'SIGCONT' }
|
|
|
|
19 /* , 17, 23 */ { return 'SIGSTOP' }
|
|
|
|
20 /* , 18, 24 */ { return 'SIGTSTP' }
|
|
|
|
21 /* , 26 */ { return 'SIGTTIN' }
|
|
|
|
22 /* , 27 */ { return 'SIGTTOU' }
|
2019-12-20 00:29:37 +03:00
|
|
|
// /////////////////////////////
|
2020-12-15 10:33:31 +03:00
|
|
|
5 { return 'SIGTRAP' }
|
|
|
|
7 { return 'SIGBUS' }
|
2019-12-27 19:59:04 +03:00
|
|
|
else {}
|
2020-01-21 18:58:47 +03:00
|
|
|
}
|
2019-10-08 02:34:55 +03:00
|
|
|
}
|
|
|
|
return 'unknown'
|
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// rmdir_all recursively removes the specified directory.
|
2020-06-26 16:01:10 +03:00
|
|
|
pub fn rmdir_all(path string) ? {
|
|
|
|
mut ret_err := ''
|
2020-12-15 10:33:31 +03:00
|
|
|
items := ls(path) ?
|
2019-12-30 07:22:28 +03:00
|
|
|
for item in items {
|
2021-01-14 05:55:30 +03:00
|
|
|
fullpath := join_path(path, item)
|
|
|
|
if is_dir(fullpath) {
|
2021-03-10 19:45:12 +03:00
|
|
|
rmdir_all(fullpath) or { ret_err = err.msg }
|
2021-03-13 09:43:38 +03:00
|
|
|
} else {
|
|
|
|
rm(fullpath) or { ret_err = err.msg }
|
2019-12-30 07:22:28 +03:00
|
|
|
}
|
2020-06-26 16:01:10 +03:00
|
|
|
}
|
2021-03-10 19:45:12 +03:00
|
|
|
rmdir(path) or { ret_err = err.msg }
|
2020-06-26 16:01:10 +03:00
|
|
|
if ret_err.len > 0 {
|
|
|
|
return error(ret_err)
|
2019-12-30 07:22:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// is_dir_empty will return a `bool` whether or not `path` is empty.
|
2019-12-30 07:22:28 +03:00
|
|
|
pub fn is_dir_empty(path string) bool {
|
2020-12-15 10:33:31 +03:00
|
|
|
items := ls(path) or { return true }
|
2019-12-30 07:22:28 +03:00
|
|
|
return items.len == 0
|
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// file_ext will return the part after the last occurence of `.` in `path`.
|
|
|
|
// The `.` is included.
|
2020-03-26 16:18:08 +03:00
|
|
|
pub fn file_ext(path string) string {
|
2020-12-15 10:33:31 +03:00
|
|
|
pos := path.last_index('.') or { return '' }
|
2020-03-08 00:26:26 +03:00
|
|
|
return path[pos..]
|
2019-06-26 12:28:06 +03:00
|
|
|
}
|
|
|
|
|
2020-10-01 02:25:52 +03:00
|
|
|
// dir returns all but the last element of path, typically the path's directory.
|
|
|
|
// After dropping the final element, trailing slashes are removed.
|
|
|
|
// If the path is empty, dir returns ".". If the path consists entirely of separators,
|
|
|
|
// dir returns a single separator.
|
|
|
|
// The returned path does not end in a separator unless it is the root directory.
|
2019-07-16 02:57:03 +03:00
|
|
|
pub fn dir(path string) string {
|
2020-10-01 02:25:52 +03:00
|
|
|
if path == '' {
|
|
|
|
return '.'
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
pos := path.last_index(path_separator) or { return '.' }
|
2020-03-08 00:26:26 +03:00
|
|
|
return path[..pos]
|
2019-06-26 13:56:05 +03:00
|
|
|
}
|
|
|
|
|
2020-10-01 02:25:52 +03:00
|
|
|
// base returns the last element of path.
|
|
|
|
// Trailing path separators are removed before extracting the last element.
|
|
|
|
// If the path is empty, base returns ".". If the path consists entirely of separators, base returns a
|
|
|
|
// single separator.
|
|
|
|
pub fn base(path string) string {
|
|
|
|
if path == '' {
|
|
|
|
return '.'
|
|
|
|
}
|
|
|
|
if path == path_separator {
|
|
|
|
return path_separator
|
|
|
|
}
|
|
|
|
if path.ends_with(path_separator) {
|
2020-12-15 10:33:31 +03:00
|
|
|
path2 := path[..path.len - 1]
|
|
|
|
pos := path2.last_index(path_separator) or { return path2.clone() }
|
|
|
|
return path2[pos + 1..]
|
2020-03-08 00:26:26 +03:00
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
pos := path.last_index(path_separator) or { return path.clone() }
|
|
|
|
return path[pos + 1..]
|
2019-06-22 21:20:28 +03:00
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// file_name will return all characters found after the last occurence of `path_separator`.
|
|
|
|
// file extension is included.
|
2020-03-19 17:49:07 +03:00
|
|
|
pub fn file_name(path string) string {
|
2020-05-20 12:04:28 +03:00
|
|
|
return path.all_after_last(path_separator)
|
2019-06-22 21:20:28 +03:00
|
|
|
}
|
|
|
|
|
2021-02-27 13:50:15 +03:00
|
|
|
// input_opt returns a one-line string from stdin, after printing a prompt.
|
|
|
|
// In the event of error (end of input), it returns `none`.
|
|
|
|
pub fn input_opt(prompt string) ?string {
|
2020-04-25 22:03:51 +03:00
|
|
|
print(prompt)
|
|
|
|
flush()
|
2021-02-27 13:50:15 +03:00
|
|
|
res := get_raw_line()
|
|
|
|
if res.len > 0 {
|
|
|
|
return res.trim_right('\r\n')
|
|
|
|
}
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
|
|
|
|
// input returns a one-line string from stdin, after printing a prompt.
|
|
|
|
// In the event of error (end of input), it returns '<EOF>'.
|
|
|
|
pub fn input(prompt string) string {
|
|
|
|
res := input_opt(prompt) or { return '<EOF>' }
|
|
|
|
return res
|
2020-04-25 22:03:51 +03:00
|
|
|
}
|
|
|
|
|
2019-08-22 23:19:31 +03:00
|
|
|
// get_line returns a one-line string from stdin
|
2019-06-22 21:20:28 +03:00
|
|
|
pub fn get_line() string {
|
2019-12-20 00:29:37 +03:00
|
|
|
str := get_raw_line()
|
2019-09-15 19:07:40 +03:00
|
|
|
$if windows {
|
|
|
|
return str.trim_right('\r\n')
|
|
|
|
}
|
2021-02-27 13:50:15 +03:00
|
|
|
return str.trim_right('\n')
|
2019-06-22 21:20:28 +03:00
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// get_lines returns an array of strings read from from stdin.
|
|
|
|
// reading is stopped when an empty line is read.
|
2019-07-24 19:14:13 +03:00
|
|
|
pub fn get_lines() []string {
|
2019-12-20 00:29:37 +03:00
|
|
|
mut line := ''
|
2020-04-26 10:17:13 +03:00
|
|
|
mut inputstr := []string{}
|
2019-12-20 00:29:37 +03:00
|
|
|
for {
|
|
|
|
line = get_line()
|
2020-03-27 16:57:19 +03:00
|
|
|
if line.len <= 0 {
|
2019-12-20 00:29:37 +03:00
|
|
|
break
|
|
|
|
}
|
|
|
|
line = line.trim_space()
|
|
|
|
inputstr << line
|
|
|
|
}
|
|
|
|
return inputstr
|
2019-07-24 19:14:13 +03:00
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// get_lines_joined returns a string of the values read from from stdin.
|
|
|
|
// reading is stopped when an empty line is read.
|
2019-07-24 19:14:13 +03:00
|
|
|
pub fn get_lines_joined() string {
|
2019-10-26 20:21:07 +03:00
|
|
|
mut line := ''
|
|
|
|
mut inputstr := ''
|
|
|
|
for {
|
|
|
|
line = get_line()
|
|
|
|
if line.len <= 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
line = line.trim_space()
|
|
|
|
inputstr += line
|
|
|
|
}
|
|
|
|
return inputstr
|
2019-07-24 19:14:13 +03:00
|
|
|
}
|
|
|
|
|
2021-01-30 13:38:54 +03:00
|
|
|
// get_raw_lines_joined reads *all* input lines from stdin.
|
|
|
|
// It returns them as one large string. NB: unlike os.get_lines_joined,
|
|
|
|
// empty lines (that contain only `\r\n` or `\n`), will be present in
|
|
|
|
// the output.
|
|
|
|
// Reading is stopped, only on EOF of stdin.
|
|
|
|
pub fn get_raw_lines_joined() string {
|
|
|
|
mut line := ''
|
|
|
|
mut lines := []string{}
|
|
|
|
for {
|
|
|
|
line = get_raw_line()
|
|
|
|
if line.len <= 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
lines << line
|
|
|
|
}
|
|
|
|
res := lines.join('')
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2019-10-26 20:21:07 +03:00
|
|
|
// user_os returns current user operating system name.
|
2019-06-22 21:20:28 +03:00
|
|
|
pub fn user_os() string {
|
|
|
|
$if linux {
|
|
|
|
return 'linux'
|
|
|
|
}
|
2019-12-03 16:29:24 +03:00
|
|
|
$if macos {
|
2020-09-29 22:10:33 +03:00
|
|
|
return 'macos'
|
2019-06-22 21:20:28 +03:00
|
|
|
}
|
|
|
|
$if windows {
|
|
|
|
return 'windows'
|
|
|
|
}
|
2019-07-15 18:24:40 +03:00
|
|
|
$if freebsd {
|
2019-08-22 23:19:31 +03:00
|
|
|
return 'freebsd'
|
|
|
|
}
|
2019-07-21 13:22:41 +03:00
|
|
|
$if openbsd {
|
2019-08-22 23:19:31 +03:00
|
|
|
return 'openbsd'
|
|
|
|
}
|
2019-07-21 13:22:41 +03:00
|
|
|
$if netbsd {
|
2019-08-22 23:19:31 +03:00
|
|
|
return 'netbsd'
|
|
|
|
}
|
2019-07-21 13:22:41 +03:00
|
|
|
$if dragonfly {
|
2019-08-22 23:19:31 +03:00
|
|
|
return 'dragonfly'
|
2019-07-24 00:23:13 +03:00
|
|
|
}
|
2019-12-20 00:29:37 +03:00
|
|
|
$if android {
|
2019-09-16 18:23:11 +03:00
|
|
|
return 'android'
|
|
|
|
}
|
2019-09-27 00:30:41 +03:00
|
|
|
$if solaris {
|
|
|
|
return 'solaris'
|
|
|
|
}
|
2019-12-03 11:26:47 +03:00
|
|
|
$if haiku {
|
|
|
|
return 'haiku'
|
|
|
|
}
|
2019-06-22 21:20:28 +03:00
|
|
|
return 'unknown'
|
|
|
|
}
|
|
|
|
|
2020-12-15 10:58:33 +03:00
|
|
|
// home_dir returns path to the user's home directory.
|
2019-06-22 21:20:28 +03:00
|
|
|
pub fn home_dir() string {
|
|
|
|
$if windows {
|
2020-12-15 10:33:31 +03:00
|
|
|
return getenv('USERPROFILE')
|
2020-02-18 04:12:10 +03:00
|
|
|
} $else {
|
2020-12-15 10:33:31 +03:00
|
|
|
// println('home_dir() call')
|
|
|
|
// res:= os.getenv('HOME')
|
|
|
|
// println('res="$res"')
|
|
|
|
return getenv('HOME')
|
2019-06-22 21:20:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-22 23:19:31 +03:00
|
|
|
// write_file writes `text` data to a file in `path`.
|
2020-10-15 13:32:28 +03:00
|
|
|
pub fn write_file(path string, text string) ? {
|
2020-12-15 10:33:31 +03:00
|
|
|
mut f := create(path) ?
|
2021-03-19 05:36:27 +03:00
|
|
|
f.write_string(text) ?
|
2019-06-22 21:20:28 +03:00
|
|
|
f.close()
|
|
|
|
}
|
|
|
|
|
2020-07-13 10:45:10 +03:00
|
|
|
// write_file_array writes the data in `buffer` to a file in `path`.
|
|
|
|
pub fn write_file_array(path string, buffer array) ? {
|
2020-12-15 10:33:31 +03:00
|
|
|
mut f := create(path) ?
|
2021-03-20 10:02:28 +03:00
|
|
|
unsafe { f.write_ptr_at(buffer.data, (buffer.len * buffer.element_size), 0) }
|
2020-07-13 10:45:10 +03:00
|
|
|
f.close()
|
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// executable_fallback is used when there is not a more platform specific and accurate implementation.
|
|
|
|
// It relies on path manipulation of os.args[0] and os.wd_at_startup, so it may not work properly in
|
2020-03-14 00:19:02 +03:00
|
|
|
// all cases, but it should be better, than just using os.args[0] directly.
|
|
|
|
fn executable_fallback() string {
|
2021-01-25 12:26:20 +03:00
|
|
|
if os.args.len == 0 {
|
2020-04-23 21:52:13 +03:00
|
|
|
// we are early in the bootstrap, os.args has not been initialized yet :-|
|
|
|
|
return ''
|
|
|
|
}
|
2021-01-25 12:26:20 +03:00
|
|
|
mut exepath := os.args[0]
|
2020-07-16 19:33:26 +03:00
|
|
|
$if windows {
|
|
|
|
if !exepath.contains('.exe') {
|
|
|
|
exepath += '.exe'
|
|
|
|
}
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
if !is_abs_path(exepath) {
|
|
|
|
if exepath.contains(path_separator) {
|
2021-01-25 12:26:20 +03:00
|
|
|
exepath = join_path(os.wd_at_startup, exepath)
|
2020-12-15 10:33:31 +03:00
|
|
|
} else {
|
2020-03-14 00:19:02 +03:00
|
|
|
// no choice but to try to walk the PATH folders :-| ...
|
2020-12-15 10:33:31 +03:00
|
|
|
foundpath := find_abs_path_of_executable(exepath) or { '' }
|
2020-03-14 00:19:02 +03:00
|
|
|
if foundpath.len > 0 {
|
|
|
|
exepath = foundpath
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
exepath = real_path(exepath)
|
2020-03-14 00:19:02 +03:00
|
|
|
return exepath
|
|
|
|
}
|
|
|
|
|
|
|
|
// find_exe_path walks the environment PATH, just like most shell do, it returns
|
|
|
|
// the absolute path of the executable if found
|
|
|
|
pub fn find_abs_path_of_executable(exepath string) ?string {
|
2021-03-12 18:05:26 +03:00
|
|
|
if exepath == '' {
|
|
|
|
return error('expected non empty `exepath`')
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
if is_abs_path(exepath) {
|
|
|
|
return real_path(exepath)
|
2020-03-14 00:19:02 +03:00
|
|
|
}
|
|
|
|
mut res := ''
|
2020-12-15 10:33:31 +03:00
|
|
|
paths := getenv('PATH').split(path_delimiter)
|
2020-03-14 00:19:02 +03:00
|
|
|
for p in paths {
|
2020-12-15 10:33:31 +03:00
|
|
|
found_abs_path := join_path(p, exepath)
|
|
|
|
if exists(found_abs_path) && is_executable(found_abs_path) {
|
2020-03-14 00:19:02 +03:00
|
|
|
res = found_abs_path
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
if res.len > 0 {
|
|
|
|
return real_path(res)
|
2020-03-14 00:19:02 +03:00
|
|
|
}
|
|
|
|
return error('failed to find executable')
|
2019-06-27 23:17:13 +03:00
|
|
|
}
|
2019-06-30 15:55:48 +03:00
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// exists_in_system_path returns `true` if `prog` exists in the system's PATH
|
2020-05-16 23:46:05 +03:00
|
|
|
pub fn exists_in_system_path(prog string) bool {
|
2020-12-15 10:33:31 +03:00
|
|
|
find_abs_path_of_executable(prog) or { return false }
|
2020-05-16 18:10:34 +03:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-09-04 23:27:52 +03:00
|
|
|
// is_file returns a `bool` indicating whether the given `path` is a file.
|
|
|
|
pub fn is_file(path string) bool {
|
|
|
|
return exists(path) && !is_dir(path)
|
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// is_abs_path returns `true` if `path` is absolute.
|
2020-03-10 18:09:37 +03:00
|
|
|
pub fn is_abs_path(path string) bool {
|
2020-03-08 00:26:26 +03:00
|
|
|
$if windows {
|
2021-01-23 12:33:53 +03:00
|
|
|
return path[0] == `/` || // incase we're in MingGW bash
|
2020-03-08 00:26:26 +03:00
|
|
|
(path[0].is_letter() && path[1] == `:`)
|
|
|
|
}
|
|
|
|
return path[0] == `/`
|
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// join_path returns a path as string from input string parameter(s).
|
2021-01-14 05:55:30 +03:00
|
|
|
[manualfree]
|
2020-03-09 04:23:34 +03:00
|
|
|
pub fn join_path(base string, dirs ...string) string {
|
2020-04-26 10:17:13 +03:00
|
|
|
mut result := []string{}
|
2020-03-08 00:26:26 +03:00
|
|
|
result << base.trim_right('\\/')
|
|
|
|
for d in dirs {
|
|
|
|
result << d
|
|
|
|
}
|
2021-01-14 05:55:30 +03:00
|
|
|
res := result.join(path_separator)
|
|
|
|
unsafe { result.free() }
|
|
|
|
return res
|
2020-03-08 00:26:26 +03:00
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// walk_ext returns a recursive list of all files in `path` ending with `ext`.
|
2020-10-15 13:32:28 +03:00
|
|
|
pub fn walk_ext(path string, ext string) []string {
|
2020-12-15 10:33:31 +03:00
|
|
|
if !is_dir(path) {
|
2020-02-21 22:14:01 +03:00
|
|
|
return []
|
2019-12-20 00:29:37 +03:00
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
mut files := ls(path) or { return [] }
|
2020-04-26 10:17:13 +03:00
|
|
|
mut res := []string{}
|
2020-12-15 10:33:31 +03:00
|
|
|
separator := if path.ends_with(path_separator) { '' } else { path_separator }
|
2020-04-27 16:16:31 +03:00
|
|
|
for file in files {
|
2019-08-16 15:05:11 +03:00
|
|
|
if file.starts_with('.') {
|
2019-08-22 23:19:31 +03:00
|
|
|
continue
|
|
|
|
}
|
2019-11-09 19:35:26 +03:00
|
|
|
p := path + separator + file
|
2020-12-15 10:33:31 +03:00
|
|
|
if is_dir(p) && !is_link(p) {
|
2019-08-22 23:19:31 +03:00
|
|
|
res << walk_ext(p, ext)
|
2020-12-15 10:33:31 +03:00
|
|
|
} else if file.ends_with(ext) {
|
2019-08-22 23:19:31 +03:00
|
|
|
res << p
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
2019-06-30 15:55:48 +03:00
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// walk recursively traverses the given directory `path`.
|
2019-10-24 15:17:09 +03:00
|
|
|
// When a file is encountred it will call the callback function with current file as argument.
|
2020-12-15 10:33:31 +03:00
|
|
|
pub fn walk(path string, f fn (string)) {
|
|
|
|
if !is_dir(path) {
|
2020-02-21 22:14:01 +03:00
|
|
|
return
|
2019-12-20 00:29:37 +03:00
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
mut files := ls(path) or { return }
|
2021-02-10 19:48:01 +03:00
|
|
|
mut local_path_separator := path_separator
|
|
|
|
if path.ends_with(path_separator) {
|
|
|
|
local_path_separator = ''
|
|
|
|
}
|
2019-10-24 15:17:09 +03:00
|
|
|
for file in files {
|
2021-02-10 19:48:01 +03:00
|
|
|
p := path + local_path_separator + file
|
2020-12-15 10:33:31 +03:00
|
|
|
if is_dir(p) && !is_link(p) {
|
2020-02-11 12:26:46 +03:00
|
|
|
walk(p, f)
|
2020-12-15 10:33:31 +03:00
|
|
|
} else if exists(p) {
|
2020-02-11 12:26:46 +03:00
|
|
|
f(p)
|
2019-10-24 15:17:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// log will print "os.log: "+`s` ...
|
2019-09-21 21:38:12 +03:00
|
|
|
pub fn log(s string) {
|
2021-01-23 12:25:40 +03:00
|
|
|
//$if macos {
|
2021-01-23 12:32:00 +03:00
|
|
|
// Use NSLog() on macos
|
|
|
|
// C.darwin_log(s)
|
2021-01-23 12:25:40 +03:00
|
|
|
//} $else {
|
2021-01-23 12:32:00 +03:00
|
|
|
println('os.log: ' + s)
|
2021-01-23 12:25:40 +03:00
|
|
|
//}
|
2019-06-30 15:55:48 +03:00
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// mkdir_all will create a valid full path of all directories given in `path`.
|
2020-07-07 13:41:42 +03:00
|
|
|
pub fn mkdir_all(path string) ? {
|
2020-12-15 10:33:31 +03:00
|
|
|
mut p := if path.starts_with(path_separator) { path_separator } else { '' }
|
|
|
|
path_parts := path.trim_left(path_separator).split(path_separator)
|
2020-07-06 15:08:38 +03:00
|
|
|
for subdir in path_parts {
|
2020-12-15 10:33:31 +03:00
|
|
|
p += subdir + path_separator
|
|
|
|
if exists(p) && is_dir(p) {
|
2020-07-06 15:08:38 +03:00
|
|
|
continue
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
mkdir(p) or { return error('folder: $p, error: $err') }
|
2019-10-12 05:04:56 +03:00
|
|
|
}
|
2019-10-12 08:41:41 +03:00
|
|
|
}
|
2019-10-15 18:08:46 +03:00
|
|
|
|
2020-03-08 17:57:47 +03:00
|
|
|
// cache_dir returns the path to a *writable* user specific folder, suitable for writing non-essential data.
|
|
|
|
pub fn cache_dir() string {
|
2020-01-03 21:46:23 +03:00
|
|
|
// See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
|
|
|
// There is a single base directory relative to which user-specific non-essential
|
|
|
|
// (cached) data should be written. This directory is defined by the environment
|
|
|
|
// variable $XDG_CACHE_HOME.
|
|
|
|
// $XDG_CACHE_HOME defines the base directory relative to which user specific
|
|
|
|
// non-essential data files should be stored. If $XDG_CACHE_HOME is either not set
|
|
|
|
// or empty, a default equal to $HOME/.cache should be used.
|
|
|
|
$if !windows {
|
2020-12-15 10:33:31 +03:00
|
|
|
xdg_cache_home := getenv('XDG_CACHE_HOME')
|
2020-01-03 21:46:23 +03:00
|
|
|
if xdg_cache_home != '' {
|
|
|
|
return xdg_cache_home
|
|
|
|
}
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
cdir := join_path(home_dir(), '.cache')
|
|
|
|
if !is_dir(cdir) && !is_link(cdir) {
|
2021-03-01 02:18:14 +03:00
|
|
|
mkdir(cdir) or { panic(err) }
|
2020-01-03 21:46:23 +03:00
|
|
|
}
|
|
|
|
return cdir
|
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// temp_dir returns the path to a folder, that is suitable for storing temporary files.
|
2020-03-10 17:02:09 +03:00
|
|
|
pub fn temp_dir() string {
|
2020-12-15 10:33:31 +03:00
|
|
|
mut path := getenv('TMPDIR')
|
2019-11-17 06:45:20 +03:00
|
|
|
$if windows {
|
|
|
|
if path == '' {
|
|
|
|
// TODO see Qt's implementation?
|
|
|
|
// https://doc.qt.io/qt-5/qdir.html#tempPath
|
|
|
|
// https://github.com/qt/qtbase/blob/e164d61ca8263fc4b46fdd916e1ea77c7dd2b735/src/corelib/io/qfilesystemengine_win.cpp#L1275
|
2020-12-15 10:33:31 +03:00
|
|
|
path = getenv('TEMP')
|
2019-12-20 00:29:37 +03:00
|
|
|
if path == '' {
|
2020-12-15 10:33:31 +03:00
|
|
|
path = getenv('TMP')
|
2019-12-20 00:29:37 +03:00
|
|
|
}
|
|
|
|
if path == '' {
|
|
|
|
path = 'C:/tmp'
|
|
|
|
}
|
2019-11-17 06:45:20 +03:00
|
|
|
}
|
|
|
|
}
|
2020-08-05 20:02:57 +03:00
|
|
|
$if android {
|
|
|
|
// TODO test+use '/data/local/tmp' on Android before using cache_dir()
|
|
|
|
if path == '' {
|
2020-12-15 10:33:31 +03:00
|
|
|
path = cache_dir()
|
2020-08-05 20:02:57 +03:00
|
|
|
}
|
2020-01-03 21:46:23 +03:00
|
|
|
}
|
2019-12-30 07:25:26 +03:00
|
|
|
if path == '' {
|
|
|
|
path = '/tmp'
|
|
|
|
}
|
2019-11-17 06:45:20 +03:00
|
|
|
return path
|
|
|
|
}
|
2019-11-24 06:27:02 +03:00
|
|
|
|
2020-11-30 20:31:37 +03:00
|
|
|
fn default_vmodules_path() string {
|
2020-12-15 10:33:31 +03:00
|
|
|
return join_path(home_dir(), '.vmodules')
|
2020-11-30 20:31:37 +03:00
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
|
2020-11-08 09:07:17 +03:00
|
|
|
// vmodules_dir returns the path to a folder, where v stores its global modules.
|
|
|
|
pub fn vmodules_dir() string {
|
2020-11-30 20:31:37 +03:00
|
|
|
paths := vmodules_paths()
|
|
|
|
if paths.len > 0 {
|
|
|
|
return paths[0]
|
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
return default_vmodules_path()
|
2020-11-30 20:31:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// vmodules_paths returns a list of paths, where v looks up for modules.
|
|
|
|
// You can customize it through setting the environment variable VMODULES
|
|
|
|
pub fn vmodules_paths() []string {
|
2020-12-15 10:33:31 +03:00
|
|
|
mut path := getenv('VMODULES')
|
2020-11-08 09:07:17 +03:00
|
|
|
if path == '' {
|
2020-12-15 10:33:31 +03:00
|
|
|
path = default_vmodules_path()
|
2020-11-08 09:07:17 +03:00
|
|
|
}
|
2020-12-15 10:33:31 +03:00
|
|
|
list := path.split(path_delimiter).map(it.trim_right(path_separator))
|
2020-11-30 20:31:37 +03:00
|
|
|
return list
|
2020-11-08 09:07:17 +03:00
|
|
|
}
|
|
|
|
|
2020-08-27 17:57:01 +03:00
|
|
|
// resource_abs_path returns an absolute path, for the given `path`.
|
2020-01-12 21:35:06 +03:00
|
|
|
// (the path is expected to be relative to the executable program)
|
|
|
|
// See https://discordapp.com/channels/592103645835821068/592294828432424960/630806741373943808
|
|
|
|
// It gives a convenient way to access program resources like images, fonts, sounds and so on,
|
|
|
|
// *no matter* how the program was started, and what is the current working directory.
|
2021-01-14 05:55:30 +03:00
|
|
|
[manualfree]
|
2020-01-12 21:35:06 +03:00
|
|
|
pub fn resource_abs_path(path string) string {
|
2021-01-14 05:55:30 +03:00
|
|
|
exe := executable()
|
|
|
|
dexe := dir(exe)
|
|
|
|
mut base_path := real_path(dexe)
|
2020-12-15 10:33:31 +03:00
|
|
|
vresource := getenv('V_RESOURCE_PATH')
|
2020-01-12 21:35:06 +03:00
|
|
|
if vresource.len != 0 {
|
|
|
|
base_path = vresource
|
|
|
|
}
|
2021-01-14 05:55:30 +03:00
|
|
|
fp := join_path(base_path, path)
|
|
|
|
res := real_path(fp)
|
2021-02-14 21:31:42 +03:00
|
|
|
unsafe {
|
|
|
|
fp.free()
|
|
|
|
base_path.free()
|
|
|
|
}
|
2021-01-14 05:55:30 +03:00
|
|
|
return res
|
2020-01-12 21:35:06 +03:00
|
|
|
}
|
2020-05-19 13:38:06 +03:00
|
|
|
|
2020-06-14 16:46:30 +03:00
|
|
|
pub struct Uname {
|
|
|
|
pub mut:
|
|
|
|
sysname string
|
|
|
|
nodename string
|
|
|
|
release string
|
|
|
|
version string
|
|
|
|
machine string
|
|
|
|
}
|
2021-03-08 21:52:13 +03:00
|
|
|
|
|
|
|
[deprecated: 'use os.execute or os.execute_or_panic instead']
|
|
|
|
pub fn exec(cmd string) ?Result {
|
|
|
|
res := execute(cmd)
|
|
|
|
if res.exit_code < 0 {
|
|
|
|
return error_with_code(res.output, -1)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn execute_or_panic(cmd string) Result {
|
|
|
|
res := execute(cmd)
|
|
|
|
if res.exit_code != 0 {
|
|
|
|
eprintln('failed cmd: $cmd')
|
|
|
|
eprintln('failed code: $res.exit_code')
|
|
|
|
panic(res.output)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|