From d51a4195523c68075ae45e311d62c52b91149c6b Mon Sep 17 00:00:00 2001 From: ge Date: Tue, 9 Dec 2025 11:46:57 +0300 Subject: [PATCH] init --- .editorconfig | 8 ++++++ .gitattributes | 8 ++++++ .gitignore | 24 +++++++++++++++++ README.md | 20 ++++++++++++++ UNLICENSE | 22 +++++++++++++++ os_release.v | 69 +++++++++++++++++++++++++++++++++++++++++++++++ os_release_test.v | 21 +++++++++++++++ v.mod | 7 +++++ 8 files changed, 179 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.md create mode 100644 UNLICENSE create mode 100644 os_release.v create mode 100644 os_release_test.v create mode 100644 v.mod diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..01072ca --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.v] +indent_style = tab diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9a98968 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +* text=auto eol=lf +*.bat eol=crlf + +*.v linguist-language=V +*.vv linguist-language=V +*.vsh linguist-language=V +v.mod linguist-language=V +.vdocignore linguist-language=ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bb321b --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Binaries for programs and plugins +main +os_release +*.exe +*.exe~ +*.so +*.dylib +*.dll + +# Ignore binary output folders +bin/ + +# Ignore common editor/system specific metadata +.DS_Store +.idea/ +.vscode/ +*.iml + +# ENV +.env + +# vweb and database +*.db +*.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..a429ade --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Freedesktop OS Release + +`os_release` module implements the os-release, initrd-release and extension-release +file parser per Freedesktop standard. Those files are intended for operating system +identification. Learn more at +[os-release(5)](https://www.freedesktop.org/software/systemd/man/latest/os-release.html) +manual page. + +Usage example: + +```v +import os_release + +fn main() { + release_data := os_release.os_release() + os_name := release_data['PRETTY_NAME'] or { 'Linux' } + os_version := release_data['VERSION_ID'] or { '0' } + println('OS: ${os_name} ${os_version}') +} +``` diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..c91541e --- /dev/null +++ b/UNLICENSE @@ -0,0 +1,22 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and +successors. We intend this dedication to be an overt act of relinquishment in +perpetuity of all present and future rights to this software under copyright +law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/os_release.v b/os_release.v new file mode 100644 index 0000000..2b1f7c3 --- /dev/null +++ b/os_release.v @@ -0,0 +1,69 @@ +module os_release + +import os +import strings + +// options contains list of default os-release, initrd-release, extension-release options. +pub const options = ['NAME', 'ID', 'ID_LIKE', 'PRETTY_NAME', 'CPE_NAME', 'VARIANT', 'VARIANT_ID', + 'VERSION', 'VERSION_ID', 'VERSION_CODENAME', 'BUILD_ID', 'IMAGE_ID', 'IMAGE_VERSION', + 'RELEASE_TYPE', 'HOME_URL', 'DOCUMENTATION_URL', 'SUPPORT_URL', 'BUG_REPORT_URL', + 'PRIVACY_POLICY_URL', 'SUPPORT_END', 'LOGO', 'ANSI_COLOR', 'VENDOR_NAME', 'VENDOR_URL', + 'EXPERIMENT', 'EXPERIMENT_URL', 'DEFAULT_HOSTNAME', 'ARCHITECTURE', 'SYSEXT_LEVEL', + 'CONFEXT_LEVEL', 'SYSEXT_SCOPE', 'CONFEXT_SCOPE', 'PORTABLE_PREFIXES']! + +// os_release reads `/etc/os-release` and `/usr/lib/os-release` files and returns parsed +// os-release data. Empty map will be returned if files does not exits or reading issues +// caused. The resulting map will contain only options that is present in actual os-release +// file. +pub fn os_release() map[string]string { + mut ret := map[string]string{} + for file in ['/etc/os-release', '/usr/lib/os-release'] { + ret = parse_file(file) or { continue } + } + return ret +} + +// parse_file reads the `file` and returns map of os-release key-value pairs. +pub fn parse_file(file string) !map[string]string { + return parse(os.read_file(file)!) +} + +// parse parses the `content` string and returns map of os-release key-value pairs. +// Example: +// ``` +// assert os_release.parse('PRETTY_NAME="Debian GNU/Linux 13 (trixie)"') == {'PRETTY_NAME': 'Debian GNU/Linux 13 (trixie)'} +// ``` +pub fn parse(content string) map[string]string { + mut ret := map[string]string{} + for raw_line in content.split_into_lines() { + line := raw_line.trim_space() + if line.is_blank() || line.starts_with('#') { + continue + } + key, value := line.split_once('=') or { continue } + if key == '' { + continue + } + ret = { + ...ret + key: unescape(value) + } + } + return ret +} + +fn unescape(s string) string { + mut ret := strings.new_builder(128) + for i := 0; i < s.len; i++ { + if (i == 0 || i == s.len - 1) && s[i] in [`"`, `'`] { + continue // trim leading and trailing quotes + } + if s[i] == `\\` && i + 1 < s.len { + ret.write_byte(s[i + 1]) + i++ + } else { + ret.write_byte(s[i]) + } + } + return ret.str() +} diff --git a/os_release_test.v b/os_release_test.v new file mode 100644 index 0000000..0e42b75 --- /dev/null +++ b/os_release_test.v @@ -0,0 +1,21 @@ +module os_release + +fn test_os_release() { + $if !windows { + r := os_release() + dump(r) + assert r['NAME'] != '' + } +} + +fn test_os_release_parser() { + content := r'ID=linux + NAME="Linux" + PRETTY_NAME="Linux" + VARIANT="\$erver\ Edition"'.trim_indent() + result := parse(content) + assert result['ID'] == 'linux' + assert result['NAME'] == 'Linux' + assert result['PRETTY_NAME'] == 'Linux' + assert result['VARIANT'] == r'$erver Edition' +} diff --git a/v.mod b/v.mod new file mode 100644 index 0000000..2bd3770 --- /dev/null +++ b/v.mod @@ -0,0 +1,7 @@ +Module { + name: 'os_release' + description: 'Freedesktop OS Release Parser' + version: '1.0.0' + license: 'Unlicense' + dependencies: [] +}