forked from trinitrix/core
1
0
Fork 0

style(treewide): Format and improve the headers

This commit represents one run of
`./scripts/renew_copyright_headers.sh`.
This commit is contained in:
Benedikt Peetz 2024-05-04 21:06:17 +02:00
parent a779fa250d
commit 940d82561d
19 changed files with 170 additions and 140 deletions

View File

@ -38,26 +38,31 @@ default-run = "trinitrix"
clap = { version = "4.5.4", features = ["derive"] } clap = { version = "4.5.4", features = ["derive"] }
cli-log = "2.0" cli-log = "2.0"
anyhow = "1.0" anyhow = "1.0"
tokio = { version = "1.37", features = ["macros", "rt-multi-thread", "fs", "time"] } tokio = { version = "1.37", features = [
tokio-util = {version = "0.7.10"} "macros",
"rt-multi-thread",
"fs",
"time",
] }
tokio-util = { version = "0.7.10" }
# config # config
trinitry = {version = "0.1.0"} trinitry = { version = "0.1.0" }
keymaps = {version = "0.1.1", features = ["crossterm"] } keymaps = { version = "0.1.1", features = ["crossterm"] }
directories = "5.0.1" directories = "5.0.1"
# c api # c api
libloading = "0.8.3" libloading = "0.8.3"
trixy = {version = "0.1.1"} trixy = { version = "0.1.1" }
# lua stuff # lua stuff
mlua = { version = "0.9.7", features = ["lua54", "async", "send", "serialize"] } mlua = { version = "0.9.7", features = ["lua54", "async", "send", "serialize"] }
once_cell = "1.19.0" once_cell = "1.19.0"
# tui feature specific parts # tui feature specific parts
tui = {version = "0.19"} tui = { version = "0.19" }
tui-textarea = { version = "0.2", features = ["crossterm"]} tui-textarea = { version = "0.2", features = ["crossterm"] }
crossterm = { version = "0.25"} crossterm = { version = "0.25" }
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"

View File

@ -27,12 +27,14 @@ OTHER DEALINGS IN THE SOFTWARE.
--> -->
# Trinitrix # Trinitrix
Trinitrix is a terminal UI client for the matrix chat protocol. Trinitrix is a terminal UI client for the matrix chat protocol.
Docs can be found [here](https://git.nerdcult.net/antifallobst/trinitrix/src/branch/master/docs). Docs can be found [here](https://git.nerdcult.net/antifallobst/trinitrix/src/branch/master/docs).
--- ______________________________________________________________________
# License (MIT) # License (MIT)
Copyright 2023 The Trinitrix Project <antifallobst@systemausfall.org> Copyright 2023 The Trinitrix Project <antifallobst@systemausfall.org>
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of

View File

@ -27,4 +27,5 @@ OTHER DEALINGS IN THE SOFTWARE.
--> -->
# License # License
The [square Trinitrix Logo](./square.svg) by Eric-Paul Ickhorn is marked with the CC0 1.0 Universal License. The [square Trinitrix Logo](./square.svg) by Eric-Paul Ickhorn is marked with the CC0 1.0 Universal License.

View File

@ -1,30 +1,30 @@
/* /*
* Copyright (C) 2024 - 2024: * Copyright (C) 2024 - 2024:
* The Trinitrix Project <bpeetz@b-peetz.de, antifallobst@systemausfall.org> * The Trinitrix Project <bpeetz@b-peetz.de, antifallobst@systemausfall.org>
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
* *
* This file is part of Trinitrix. * This file is part of Trinitrix.
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files * obtaining a copy of this software and associated documentation files
* (the Software), to deal in the Software without restriction, * (the Software), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, * including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software, * publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, * and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions: * subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be * The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software. * included in all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
#include "../../dist/interface.h" #include "../../dist/interface.h"
#include <assert.h> #include <assert.h>
@ -33,93 +33,114 @@
#include <string.h> #include <string.h>
#define println(args...) \ #define println(args...) \
fprintf(log_file, "\33[32;1m(plugin):\33[0m \33[34;1m"); \ fprintf (log_file, "\33[32;1m(plugin):\33[0m \33[34;1m"); \
fprintf(log_file, args); \ fprintf (log_file, args); \
fprintf(log_file, "\n\33[0m"); \ fprintf (log_file, "\n\33[0m"); \
fflush(log_file); fflush (log_file);
#define eprintln(args...) \ #define eprintln(args...) \
fprintf(log_file, "\33[32;1m(plugin):\33[0m\33[31;1m "); \ fprintf (log_file, "\33[32;1m(plugin):\33[0m\33[31;1m "); \
fprintf(log_file, args); \ fprintf (log_file, args); \
fprintf(log_file, "\n\33[0m"); \ fprintf (log_file, "\n\33[0m"); \
fflush(log_file); fflush (log_file);
int is_first_log_file_open = 1; int is_first_log_file_open = 1;
FILE *get_log_file() { FILE *
get_log_file ()
{
FILE *log_file; FILE *log_file;
if (is_first_log_file_open) { if (is_first_log_file_open)
{
is_first_log_file_open = 0; is_first_log_file_open = 0;
log_file = fopen("plugin.txt", "w"); log_file = fopen ("plugin.txt", "w");
} else { }
log_file = fopen("plugin.txt", "wa"); else
{
log_file = fopen ("plugin.txt", "wa");
} }
if (log_file == NULL) { if (log_file == NULL)
printf("Error opening file!\n"); {
exit(1); printf ("Error opening file!\n");
exit (1);
} }
return log_file; return log_file;
} }
void handle_error() { void
FILE *log_file = get_log_file(); handle_error ()
eprintln("GOT an error"); {
FILE *log_file = get_log_file ();
eprintln ("GOT an error");
int error_length = last_error_length(); int error_length = last_error_length ();
char *error = malloc(error_length); char *error = malloc (error_length);
last_error_message(error, error_length); last_error_message (error, error_length);
eprintln("Encountered error: %s", error); eprintln ("Encountered error: %s", error);
free(error); free (error);
} }
void set_normal_mode() { void
if (!trinitrix.api.ui.set_mode(Normal)) set_normal_mode ()
handle_error(); {
if (!trinitrix.api.ui.set_mode (Normal))
handle_error ();
} }
void set_command_mode() { void
if (!trinitrix.api.ui.set_mode(Command)) set_command_mode ()
handle_error(); {
if (!trinitrix.api.ui.set_mode (Command))
handle_error ();
} }
void set_insert_mode() { void
if (!trinitrix.api.ui.set_mode(Insert)) set_insert_mode ()
handle_error(); {
if (!trinitrix.api.ui.set_mode (Insert))
handle_error ();
} }
void print_hi() { void
if (!trinitrix.api.raw.raise_error("hi!")) print_hi ()
handle_error(); {
if (!trinitrix.api.raw.raise_error ("hi!"))
handle_error ();
} }
void print_warning() { void
if (!trinitrix.api.raw.raise_error( print_warning ()
{
if (!trinitrix.api.raw.raise_error (
"To exit trinitrix use 'trinitrix.api.exit()' instead!")) "To exit trinitrix use 'trinitrix.api.exit()' instead!"))
handle_error(); handle_error ();
} }
int plugin_main() { int
FILE *log_file = get_log_file(); plugin_main ()
{
FILE *log_file = get_log_file ();
println("Hi, setting first keymap!"); println ("Hi, setting first keymap!");
if (!trinitrix.api.keymaps.add("ci", "<ESC>", set_normal_mode)) if (!trinitrix.api.keymaps.add ("ci", "<ESC>", set_normal_mode))
handle_error(); handle_error ();
println("Done setting that keymap"); println ("Done setting that keymap");
if (!trinitrix.api.keymaps.add("n", ":", set_command_mode)) if (!trinitrix.api.keymaps.add ("n", ":", set_command_mode))
handle_error(); handle_error ();
trinitrix.api.keymaps.add("n", "i", set_insert_mode); trinitrix.api.keymaps.add ("n", "i", set_insert_mode);
trinitrix.api.keymaps.add("n", "<TAB>", trinitrix.api.ui.cycle_planes); trinitrix.api.keymaps.add ("n", "<TAB>", trinitrix.api.ui.cycle_planes);
// a simple test to prove that key chords work // a simple test to prove that key chords work
trinitrix.api.keymaps.add("ni", "jj", print_hi); trinitrix.api.keymaps.add ("ni", "jj", print_hi);
trinitrix.api.keymaps.add("n", "q", trinitrix.api.exit); trinitrix.api.keymaps.add ("n", "q", trinitrix.api.exit);
// Help people // Help people
trinitrix.api.keymaps.add("n", "<C-c>", print_warning); trinitrix.api.keymaps.add ("n", "<C-c>", print_warning);
// workaround to avoid c de-allocating our nice strings // workaround to avoid c de-allocating our nice strings
while (1) { while (1)
{
}; };
return EXIT_SUCCESS; return EXIT_SUCCESS;

View File

@ -27,7 +27,7 @@
-- FIXME(@soispha): The code here has been deprecated, update it when trixy supports lua <2024-05-03> -- FIXME(@soispha): The code here has been deprecated, update it when trixy supports lua <2024-05-03>
-- create the required tables under `std` -- create the required tables under `std`
trinitrix.std = { keymaps = {} }; trinitrix.std = { keymaps = {} }
--- Add a new keymap. This is just a convenience function which registers the function --- Add a new keymap. This is just a convenience function which registers the function
--- and at the same time deals with the fact that the whole trinitrix api is async. --- and at the same time deals with the fact that the whole trinitrix api is async.
@ -44,8 +44,6 @@ trinitrix.std.keymaps.add = function(mode, key, callback)
trinitrix.api.keymaps.add(mode, key, callback_key) trinitrix.api.keymaps.add(mode, key, callback_key)
end end
trinitrix.std.keymaps.add("ci", "<ESC>", trinitrix.api.ui.set_mode_normal) trinitrix.std.keymaps.add("ci", "<ESC>", trinitrix.api.ui.set_mode_normal)
trinitrix.std.keymaps.add("n", ":", trinitrix.api.ui.command_line_show) trinitrix.std.keymaps.add("n", ":", trinitrix.api.ui.command_line_show)
@ -53,10 +51,13 @@ trinitrix.std.keymaps.add("n", "i", trinitrix.api.ui.set_mode_insert)
trinitrix.std.keymaps.add("n", "<TAB>", trinitrix.api.ui.cycle_planes) trinitrix.std.keymaps.add("n", "<TAB>", trinitrix.api.ui.cycle_planes)
-- a simple test to prove that key chords work -- a simple test to prove that key chords work
trinitrix.std.keymaps.add("ni", "jj", function() print("hi") end) trinitrix.std.keymaps.add("ni", "jj", function()
print("hi")
end)
trinitrix.std.keymaps.add("n", "q", trinitrix.api.exit) trinitrix.std.keymaps.add("n", "q", trinitrix.api.exit)
-- Help people -- Help people
trinitrix.std.keymaps.add("n", "<C-c>", function() print("To exit trinitrix use 'trinitrix.api.exit()' instead!") end) trinitrix.std.keymaps.add("n", "<C-c>", function()
print("To exit trinitrix use 'trinitrix.api.exit()' instead!")
end)

View File

@ -27,9 +27,11 @@ OTHER DEALINGS IN THE SOFTWARE.
--> -->
# Command Line # Command Line
Trinitrix has a command line, which you can use to control and navigate the app. Trinitrix has a command line, which you can use to control and navigate the app.
You can activate it with the `CommandLineShow` command, which is mapped to `<Ctrl>+c` by default. You can activate it with the `CommandLineShow` command, which is mapped to `<Ctrl>+c` by default.
## Syntax (LUA) ## Syntax (LUA)
The command line uses [lua](https://www.lua.org/about.html) and provides a builtin function for every internal command. The command line uses [lua](https://www.lua.org/about.html) and provides a builtin function for every internal command.
The function names are the snake case variants of the command names. The function names are the snake case variants of the command names.

View File

@ -27,6 +27,7 @@ OTHER DEALINGS IN THE SOFTWARE.
--> -->
# Commands # Commands
Trinitrix relies heavily on the concept of commands. Trinitrix relies heavily on the concept of commands.
There is a command for every interaction with the application. There is a command for every interaction with the application.
By this, everything can use the same internal API, what makes the whole application extreme extensible and customizable. By this, everything can use the same internal API, what makes the whole application extreme extensible and customizable.

View File

@ -27,6 +27,7 @@ OTHER DEALINGS IN THE SOFTWARE.
--> -->
# Commit message style # Commit message style
*This specification is heavily inspired by the [AngularJS commit message format](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format).* *This specification is heavily inspired by the [AngularJS commit message format](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format).*
We have very precise rules over how our Git commit messages must be formatted. We have very precise rules over how our Git commit messages must be formatted.
@ -34,7 +35,6 @@ This format leads to **easier to read commit history**.
Each commit message consists of a **header**, a **body** (optional), and a **footer** (optional). Each commit message consists of a **header**, a **body** (optional), and a **footer** (optional).
``` ```
<header> <header>
<BLANK LINE> <BLANK LINE>
@ -50,7 +50,6 @@ When the body is present it must be at least 20 characters long and must conform
The `footer` is optional. The _Commit Message Footer format_ (described below) describes what the footer is used for and the structure it must have. The `footer` is optional. The _Commit Message Footer format_ (described below) describes what the footer is used for and the structure it must have.
## Commit Message Header ## Commit Message Header
``` ```
@ -65,29 +64,29 @@ The `footer` is optional. The _Commit Message Footer format_ (described below) d
The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional. The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
### type ### type
Must be one of the following: Must be one of the following:
* **Build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) - **Build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
* **Docs**: Documentation only changes - **Docs**: Documentation only changes
* **Feat**: A new feature - **Feat**: A new feature
* **Fix**: A bug fix - **Fix**: A bug fix
* **Perf**: A code change that improves performance - **Perf**: A code change that improves performance
* **Refactor**: A code change that neither fixes a bug nor adds a feature - **Refactor**: A code change that neither fixes a bug nor adds a feature
* **Test**: Adding missing tests or correcting existing tests - **Test**: Adding missing tests or correcting existing tests
* **Merge**: A merging commit - **Merge**: A merging commit
### Scope ### Scope
The scope should be the name of the module affected (as perceived by the person reading the changelog generated from commit messages). The scope should be the name of the module affected (as perceived by the person reading the changelog generated from commit messages).
Specify submodules if needed! Specify submodules if needed!
Example scopes: Example scopes:
* `app::events` - `app::events`
* `ui` - `ui`
When a commit affects the whole tree, use `treewide`. When a commit affects the whole tree, use `treewide`.
@ -95,10 +94,9 @@ When a commit affects the whole tree, use `treewide`.
Use the summary field to provide a succinct description of the change: Use the summary field to provide a succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes" - use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize the first letter - don't capitalize the first letter
* no dot (.) at the end - no dot (.) at the end
## Commit Message Body ## Commit Message Body

View File

@ -27,5 +27,6 @@ OTHER DEALINGS IN THE SOFTWARE.
--> -->
# Config # Config
To configure trinitrix, you can add a file named `config.lua` to the userdata path. To configure trinitrix, you can add a file named `config.lua` to the userdata path.
This file will be interpreted on startup with the same syntax as the Command Line. This file will be interpreted on startup with the same syntax as the Command Line.

View File

@ -29,16 +29,19 @@ OTHER DEALINGS IN THE SOFTWARE.
# Design Philosophy # Design Philosophy
## Customizability ## Customizability
Trinitrix aims to be highly customizable. Trinitrix aims to be highly customizable.
The command API provides a customizazion method, which can be used by an initial config script or live from the intern cli by the user. The command API provides a customizazion method, which can be used by an initial config script or live from the intern cli by the user.
The command API should cover at least 90% of the possible configuration settings. The command API should cover at least 90% of the possible configuration settings.
## Cross Platform ## Cross Platform
Trinitrix is cross platform. Trinitrix is cross platform.
Even if the targeted users are nerdy linux users xD. Even if the targeted users are nerdy linux users xD.
This can be easily achieved by using only cross platform libraries such as `crossterm` or `tui`. This can be easily achieved by using only cross platform libraries such as `crossterm` or `tui`.
## Future Proof / Extensible ## Future Proof / Extensible
Modules and Interfaces should be designed in a way, that they're not limited to only use case. Modules and Interfaces should be designed in a way, that they're not limited to only use case.
They should be open for every usecase, that makes sense. They should be open for every usecase, that makes sense.
A good example is the command API, which is a unified way to interact with the application. A good example is the command API, which is a unified way to interact with the application.

View File

@ -50,9 +50,9 @@ Trinitrix is a multi protocol client. That is implemented via cbs and plugins.
**--version**, **-v** **--version**, **-v**
: Displays the software version and exit. : Displays the software version and exit.
**--plugin-path \<SHARED_OBJECT\>**, **-p \<SHARED_OBJECT\>** **--plugin-path \<SHARED_OBJECT>**, **-p \<SHARED_OBJECT>**
: The SHARED_OBJECT to load as a dynamic library. The `plugin_main` function will be : The SHARED_OBJECT to load as a dynamic library. The `plugin_main` function will be
executed. executed.
# EXAMPLES # EXAMPLES

View File

@ -23,7 +23,6 @@
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE. # OTHER DEALINGS IN THE SOFTWARE.
{ {
description = "A multi protocol chat client"; description = "A multi protocol chat client";

View File

@ -1,3 +1,4 @@
#! /usr/bin/env sh
# Copyright (C) 2024 - 2024: # Copyright (C) 2024 - 2024:
# The Trinitrix Project <bpeetz@b-peetz.de, antifallobst@systemausfall.org> # The Trinitrix Project <bpeetz@b-peetz.de, antifallobst@systemausfall.org>
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
@ -24,9 +25,8 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE. # OTHER DEALINGS IN THE SOFTWARE.
#! /usr/bin/env sh
cd "$(dirname "$0")" || { cd "$(dirname "$0")" || {
echo "BUG: There is no parent dirname!"; echo "BUG: There is no parent dirname!"
exit 1 exit 1
} }
cd .. cd ..

View File

@ -32,7 +32,7 @@ use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers}
use crate::{ use crate::{
app::{ app::{
command_interface::{ command_interface::{
Api::{Exit, Ui, RoomMessageSend}, Api::{Exit, RoomMessageSend, Ui},
Command, Command,
Trinitrix::Api, Trinitrix::Api,
Ui::{CommandLineShow, CyclePlanes, CyclePlanesRev, SetModeInsert, SetModeNormal}, Ui::{CommandLineShow, CyclePlanes, CyclePlanesRev, SetModeInsert, SetModeNormal},

View File

@ -26,5 +26,5 @@
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
pub mod ui_trait;
pub mod repl; pub mod repl;
pub mod ui_trait;

View File

@ -178,8 +178,7 @@ pub struct UI<'a> {
pub cli: Option<TextArea<'a>>, pub cli: Option<TextArea<'a>>,
} }
impl<'r> TirinitrixUi for UI<'r> { impl<'r> TirinitrixUi for UI<'r> {}
}
impl Drop for UI<'_> { impl Drop for UI<'_> {
fn drop(&mut self) { fn drop(&mut self) {

View File

@ -27,7 +27,6 @@
*/ */
pub mod central; pub mod central;
pub mod ui_trait;
use std::{io, io::Stdout}; use std::{io, io::Stdout};

View File

@ -23,7 +23,6 @@
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE. # OTHER DEALINGS IN THE SOFTWARE.
{ {
treefmt-nix, treefmt-nix,
pkgs, pkgs,

View File

@ -1,3 +1,4 @@
#! /usr/bin/env sh
# Copyright (C) 2024 - 2024: # Copyright (C) 2024 - 2024:
# The Trinitrix Project <bpeetz@b-peetz.de, antifallobst@systemausfall.org> # The Trinitrix Project <bpeetz@b-peetz.de, antifallobst@systemausfall.org>
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
@ -24,8 +25,6 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE. # OTHER DEALINGS IN THE SOFTWARE.
#!/usr/bin/env sh
cargo update && cargo upgrade cargo update && cargo upgrade
# vim: ft=sh # vim: ft=sh