diff options
Diffstat (limited to 'v_windows/v/old/vlib/os/os.v')
-rw-r--r-- | v_windows/v/old/vlib/os/os.v | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/os/os.v b/v_windows/v/old/vlib/os/os.v new file mode 100644 index 0000000..088aeb7 --- /dev/null +++ b/v_windows/v/old/vlib/os/os.v @@ -0,0 +1,662 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module os + +pub const ( + args = []string{} + max_path_len = 4096 + wd_at_startup = getwd() +) + +const ( + f_ok = 0 + x_ok = 1 + w_ok = 2 + r_ok = 4 +) + +pub struct Result { +pub: + exit_code int + output string + // stderr string // TODO +} + +[unsafe] +pub fn (mut result Result) free() { + unsafe { result.output.free() } +} + +// cp_all will recursively copy `src` to `dst`, +// optionally overwriting files or dirs in `dst`. +pub fn cp_all(src string, dst string, overwrite bool) ? { + source_path := real_path(src) + dest_path := real_path(dst) + if !exists(source_path) { + return error("Source path doesn't exist") + } + // single file copy + if !is_dir(source_path) { + adjusted_path := if is_dir(dest_path) { + join_path(dest_path, file_name(source_path)) + } else { + dest_path + } + if exists(adjusted_path) { + if overwrite { + rm(adjusted_path) ? + } else { + return error('Destination file path already exist') + } + } + cp(source_path, adjusted_path) ? + return + } + if !exists(dest_path) { + mkdir(dest_path) ? + } + if !is_dir(dest_path) { + return error('Destination path is not a valid directory') + } + files := ls(source_path) ? + for file in files { + sp := join_path(source_path, file) + dp := join_path(dest_path, file) + if is_dir(sp) { + if !exists(dp) { + mkdir(dp) ? + } + } + cp_all(sp, dp, overwrite) or { + rmdir(dp) or { return err } + return err + } + } +} + +// mv_by_cp first copies the source file, and if it is copied successfully, deletes the source file. +// may be used when you are not sure that the source and target are on the same mount/partition. +pub fn mv_by_cp(source string, target string) ? { + cp(source, target) ? + rm(source) ? +} + +// read_lines reads the file in `path` into an array of lines. +pub fn read_lines(path string) ?[]string { + buf := read_file(path) ? + res := buf.split_into_lines() + unsafe { buf.free() } + return res +} + +// sigint_to_signal_name will translate `si` signal integer code to it's string code representation. +pub fn sigint_to_signal_name(si int) string { + // POSIX signals: + match si { + 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' } + else {} + } + $if linux { + // From `man 7 signal` on linux: + match si { + // TODO dependent on platform + // works only on x86/ARM/most others + 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' } + // ///////////////////////////// + 5 { return 'SIGTRAP' } + 7 { return 'SIGBUS' } + else {} + } + } + return 'unknown' +} + +// rmdir_all recursively removes the specified directory. +pub fn rmdir_all(path string) ? { + mut ret_err := '' + items := ls(path) ? + for item in items { + fullpath := join_path(path, item) + if is_dir(fullpath) { + rmdir_all(fullpath) or { ret_err = err.msg } + } else { + rm(fullpath) or { ret_err = err.msg } + } + } + rmdir(path) or { ret_err = err.msg } + if ret_err.len > 0 { + return error(ret_err) + } +} + +// is_dir_empty will return a `bool` whether or not `path` is empty. +pub fn is_dir_empty(path string) bool { + items := ls(path) or { return true } + return items.len == 0 +} + +// file_ext will return the part after the last occurence of `.` in `path`. +// The `.` is included. +pub fn file_ext(path string) string { + pos := path.last_index('.') or { return '' } + return path[pos..] +} + +// 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. +pub fn dir(opath string) string { + if opath == '' { + return '.' + } + path := opath.replace_each(['/', path_separator, r'\', path_separator]) + pos := path.last_index(path_separator) or { return '.' } + if pos == 0 && path_separator == '/' { + return '/' + } + return path[..pos] +} + +// 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(opath string) string { + if opath == '' { + return '.' + } + path := opath.replace_each(['/', path_separator, r'\', path_separator]) + if path == path_separator { + return path_separator + } + if path.ends_with(path_separator) { + path2 := path[..path.len - 1] + pos := path2.last_index(path_separator) or { return path2.clone() } + return path2[pos + 1..] + } + pos := path.last_index(path_separator) or { return path.clone() } + return path[pos + 1..] +} + +// file_name will return all characters found after the last occurence of `path_separator`. +// file extension is included. +pub fn file_name(opath string) string { + path := opath.replace_each(['/', path_separator, r'\', path_separator]) + return path.all_after_last(path_separator) +} + +// 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 { + print(prompt) + flush() + 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 +} + +// get_line returns a one-line string from stdin +pub fn get_line() string { + str := get_raw_line() + $if windows { + return str.trim_right('\r\n') + } + return str.trim_right('\n') +} + +// get_lines returns an array of strings read from from stdin. +// reading is stopped when an empty line is read. +pub fn get_lines() []string { + mut line := '' + mut inputstr := []string{} + for { + line = get_line() + if line.len <= 0 { + break + } + line = line.trim_space() + inputstr << line + } + return inputstr +} + +// get_lines_joined returns a string of the values read from from stdin. +// reading is stopped when an empty line is read. +pub fn get_lines_joined() string { + mut line := '' + mut inputstr := '' + for { + line = get_line() + if line.len <= 0 { + break + } + line = line.trim_space() + inputstr += line + } + return inputstr +} + +// 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 +} + +// user_os returns current user operating system name. +pub fn user_os() string { + $if linux { + return 'linux' + } + $if macos { + return 'macos' + } + $if windows { + return 'windows' + } + $if freebsd { + return 'freebsd' + } + $if openbsd { + return 'openbsd' + } + $if netbsd { + return 'netbsd' + } + $if dragonfly { + return 'dragonfly' + } + $if android { + return 'android' + } + $if solaris { + return 'solaris' + } + $if haiku { + return 'haiku' + } + $if serenity { + return 'serenity' + } + $if vinix { + return 'vinix' + } + return 'unknown' +} + +// home_dir returns path to the user's home directory. +pub fn home_dir() string { + $if windows { + return getenv('USERPROFILE') + } $else { + // println('home_dir() call') + // res:= os.getenv('HOME') + // println('res="$res"') + return getenv('HOME') + } +} + +// write_file writes `text` data to a file in `path`. +pub fn write_file(path string, text string) ? { + mut f := create(path) ? + unsafe { f.write_full_buffer(text.str, size_t(text.len)) ? } + f.close() +} + +// write_file_array writes the data in `buffer` to a file in `path`. +pub fn write_file_array(path string, buffer array) ? { + mut f := create(path) ? + unsafe { f.write_full_buffer(buffer.data, size_t(buffer.len * buffer.element_size)) ? } + f.close() +} + +// 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 +// all cases, but it should be better, than just using os.args[0] directly. +fn executable_fallback() string { + if os.args.len == 0 { + // we are early in the bootstrap, os.args has not been initialized yet :-| + return '' + } + mut exepath := os.args[0] + $if windows { + if !exepath.contains('.exe') { + exepath += '.exe' + } + } + if !is_abs_path(exepath) { + rexepath := exepath.replace_each(['/', path_separator, r'\', path_separator]) + if rexepath.contains(path_separator) { + exepath = join_path(os.wd_at_startup, exepath) + } else { + // no choice but to try to walk the PATH folders :-| ... + foundpath := find_abs_path_of_executable(exepath) or { '' } + if foundpath.len > 0 { + exepath = foundpath + } + } + } + exepath = real_path(exepath) + 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 { + if exepath == '' { + return error('expected non empty `exepath`') + } + if is_abs_path(exepath) { + return real_path(exepath) + } + mut res := '' + paths := getenv('PATH').split(path_delimiter) + for p in paths { + found_abs_path := join_path(p, exepath) + if exists(found_abs_path) && is_executable(found_abs_path) { + res = found_abs_path + break + } + } + if res.len > 0 { + return real_path(res) + } + return error('failed to find executable') +} + +// exists_in_system_path returns `true` if `prog` exists in the system's PATH +pub fn exists_in_system_path(prog string) bool { + find_abs_path_of_executable(prog) or { return false } + return true +} + +// 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) +} + +// is_abs_path returns `true` if `path` is absolute. +pub fn is_abs_path(path string) bool { + if path.len == 0 { + return false + } + $if windows { + return path[0] == `/` || // incase we're in MingGW bash + (path[0].is_letter() && path.len > 1 && path[1] == `:`) + } + return path[0] == `/` +} + +// join_path returns a path as string from input string parameter(s). +[manualfree] +pub fn join_path(base string, dirs ...string) string { + mut result := []string{} + result << base.trim_right('\\/') + for d in dirs { + result << d + } + res := result.join(path_separator) + unsafe { result.free() } + return res +} + +// walk_ext returns a recursive list of all files in `path` ending with `ext`. +pub fn walk_ext(path string, ext string) []string { + if !is_dir(path) { + return [] + } + mut files := ls(path) or { return [] } + mut res := []string{} + separator := if path.ends_with(path_separator) { '' } else { path_separator } + for file in files { + if file.starts_with('.') { + continue + } + p := path + separator + file + if is_dir(p) && !is_link(p) { + res << walk_ext(p, ext) + } else if file.ends_with(ext) { + res << p + } + } + return res +} + +// walk recursively traverses the given directory `path`. +// When a file is encountred it will call the callback function with current file as argument. +pub fn walk(path string, f fn (string)) { + if !is_dir(path) { + return + } + mut files := ls(path) or { return } + mut local_path_separator := path_separator + if path.ends_with(path_separator) { + local_path_separator = '' + } + for file in files { + p := path + local_path_separator + file + if is_dir(p) && !is_link(p) { + walk(p, f) + } else if exists(p) { + f(p) + } + } + return +} + +// log will print "os.log: "+`s` ... +pub fn log(s string) { + //$if macos { + // Use NSLog() on macos + // C.darwin_log(s) + //} $else { + println('os.log: ' + s) + //} +} + +// mkdir_all will create a valid full path of all directories given in `path`. +pub fn mkdir_all(path string) ? { + mut p := if path.starts_with(path_separator) { path_separator } else { '' } + path_parts := path.trim_left(path_separator).split(path_separator) + for subdir in path_parts { + p += subdir + path_separator + if exists(p) && is_dir(p) { + continue + } + mkdir(p) or { return error('folder: $p, error: $err') } + } +} + +// cache_dir returns the path to a *writable* user specific folder, suitable for writing non-essential data. +pub fn cache_dir() string { + // 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 { + xdg_cache_home := getenv('XDG_CACHE_HOME') + if xdg_cache_home != '' { + return xdg_cache_home + } + } + cdir := join_path(home_dir(), '.cache') + if !is_dir(cdir) && !is_link(cdir) { + mkdir(cdir) or { panic(err) } + } + return cdir +} + +// temp_dir returns the path to a folder, that is suitable for storing temporary files. +pub fn temp_dir() string { + mut path := getenv('TMPDIR') + $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 + path = getenv('TEMP') + if path == '' { + path = getenv('TMP') + } + if path == '' { + path = 'C:/tmp' + } + } + } + $if macos { + // avoid /var/folders/6j/cmsk8gd90pd.... on macs + return '/tmp' + } + $if android { + // TODO test+use '/data/local/tmp' on Android before using cache_dir() + if path == '' { + path = cache_dir() + } + } + if path == '' { + path = '/tmp' + } + return path +} + +fn default_vmodules_path() string { + return join_path(home_dir(), '.vmodules') +} + +// vmodules_dir returns the path to a folder, where v stores its global modules. +pub fn vmodules_dir() string { + paths := vmodules_paths() + if paths.len > 0 { + return paths[0] + } + return default_vmodules_path() +} + +// 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 { + mut path := getenv('VMODULES') + if path == '' { + path = default_vmodules_path() + } + list := path.split(path_delimiter).map(it.trim_right(path_separator)) + return list +} + +// resource_abs_path returns an absolute path, for the given `path`. +// (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. +[manualfree] +pub fn resource_abs_path(path string) string { + exe := executable() + dexe := dir(exe) + mut base_path := real_path(dexe) + vresource := getenv('V_RESOURCE_PATH') + if vresource.len != 0 { + base_path = vresource + } + fp := join_path(base_path, path) + res := real_path(fp) + unsafe { + fp.free() + base_path.free() + } + return res +} + +pub struct Uname { +pub mut: + sysname string + nodename string + release string + version string + machine string +} + +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 +} + +pub fn execute_or_exit(cmd string) Result { + res := execute(cmd) + if res.exit_code != 0 { + eprintln('failed cmd: $cmd') + eprintln('failed code: $res.exit_code') + eprintln(res.output) + exit(1) + } + return res +} + +// is_atty returns 1 if the `fd` file descriptor is open and refers to a terminal +pub fn is_atty(fd int) int { + $if windows { + mut mode := u32(0) + osfh := voidptr(C._get_osfhandle(fd)) + C.GetConsoleMode(osfh, voidptr(&mode)) + return int(mode) + } $else { + return C.isatty(fd) + } +} + +pub fn glob(patterns ...string) ?[]string { + mut matches := []string{} + for pattern in patterns { + native_glob_pattern(pattern, mut matches) ? + } + matches.sort() + return matches +} |