diff --git a/README.md b/README.md
index 2eb9e09..38b03cc 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,11 @@
Upload files to a server via ssh
+## Documentation
+
+- **End User**: coming soon...
+- **Plugin Developer**: coming soon...
+
## Installing
SSHare is not available on [PyPI](https://pypi.org).
@@ -18,250 +23,7 @@ Beyond that it only utilises the python standard library.
### Additional Functionality
The base install is fairly bare bones in terms of functionality.
-Included in the [`examples/`](https://forge.monodon.me/Gnarwhal/sshare_plugins) submodule, there is a sampling of
+Included in the [`examples/`](https://forge.monodon.me/Gnarwhal/sshare_plugins) submodule, there are included some
plugins which provide some additional conveniences
-Refer to the above documentation for information on installing plugins
-
-## Documentation - End User
-
-Getting started with SSHare is about getting familiar with configuring SSHare.
-SSHare configuration is all done in the `${XDG_CONFIG_DIR}/sshare` if set, or in `~/.config/sshare` otherwise.
-In this directory are two important items: the `config.toml` file and the `plugins/` directory.
-
-### `plugins/`
-
-The `plugins/` directory is home to all external plugins. Installing a plugin is as simple as saving the
-the plugin's `.py` file to the `plugins/`.
-
-### `config.toml`
-
-The configuration file can be broken down into three major components: `spec`, `config`, and `flags`.
-
-#### `spec`
-
-```toml
-
-[spec.default]
-name = [ "time", "extension" ]
-
-[spec.example]
-flag = { short = "e", long = "example" }
-name = [ "preserve", "extension" ]
-
-[spec.noshort]
-flag = { long = "only" }
-name = [ "time" ]
-help = "No short flag, only long flag >:("
-```
-
-The `spec` section is a collection of different runtime specifications. The name of a specification is
-unimportant with the exception of `default`, which is run when no other spec is activated. In each specification
-that isn't `default`, there is a `flag` attribute that sets which command line arguments activate the
-specification. The `flag` attribute must have one or both of `short` or `long`, where `short` will be set the flag
-`-{short}` and `long` will set the flag `--{long}`. The `name` attribute of a specification is an array of name type
-plugins which controls the order in which said plugins are executed. Specifications can also provide a brief description
-in the `help` attribute, which will be displayed when `sshare --help` is run. In the example above,
-running `sshare --help` would give
-```
-options:
- -e, --example Use example spec
- --only No short flag, only long flag >:(
-```
-#### `config`
-
-```toml
-[config.ssh]
-host = "example.com"
-path = "/directory/to/store/files/in"
-port = 22
-```
-
-The `config` section is used to set configuration options for plugins. Refer to documentation for a plugin to see
-what options can be set here. Documentation for default plugins is included [below](#Default-Plugins).
-
-#### `flags`
-
-```toml
-[flags.file]
-file = { short = "f" }
-
-[flags.stdin]
-stdin = { short = "" }
-
-[flags.example]
-option = { short = "o", long = "ooooption" }
-```
-
-The `flags` section is used to bind plugin arguments to a command line argument. They are specified in the same way
-as the `flag` option for `spec`s. If `long` is left unspecified, the long flag will be set to the default provided by
-the plugin. In the example above, running `sshare --help` would give:
-```
-options:
- -f file, --file file Upload a file
- -, --stdin Upload from stdin
- -o xmpl, --ooooption xmpl
- This is not a real plugin that exists
-```
-
-### Default Plugins
-
-#### `command_line`
-
-A `logger` plugin which prints to the command line. No configuration needed.
-
-#### `extension`
-
-A `name` plugin which appends the source type to the end of the file name (i.e. `txt`, `png`, etc...). No configuration needed.
-
-#### `file`
-
-A `source` plugin which provides a file to be uploaded. File is specified with the `--file {file_path}` argument. No configuration needed.
-
-#### `preserve`
-
-A `name` plugin which preserves the name of the source in the uploaded file. No configuration needed.
-
-#### `print_location`
-
-A `feedback` plugin which prints the location that sources can be accessed from. No configuration needed.
-
-#### `ssh`
-
-An `upload` plugin which uploads the sources to the server using `ssh`.
-
-##### Configuration
-| Attribute | Type | Default | Description |
-|-----------|--------|---------|------------------------------------------------------|
-| host | string | None | The hostname to ssh into |
-| path | string | None | The path of the directory to upload the sources into |
-| port | int | 22 | The port to ssh into |
-| user | string | $USER | The user to ssh as |
-
-#### `stdin`
-
-A `source` plugin which reads from stdin. No configuration needed.
-
-#### `time`
-
-A `name` plugin which adds the current unix time to the name of the of the uploaded file.
-
-##### Configuration
-| Attribute | Type | Default | Description |
-|-----------|--------|---------|------------------------------------------------------|
-| format | int | 62 | The base between 2 and 62 to represent the number in |
-
-##### `uri`
-
-A `location` plugin which constructs the resulting URI that the uploaded file can be accessed from.
-
-##### Configuration
-| Attribute | Type | Default | Description |
-|-----------|--------|---------|------------------------------------------------------|
-| protocol | string | https | The protocol of the URI |
-| host | string | None | The hostname the file can be accessed from |
-| port | int | 22 | The port the file can be accessed at |
-| path | string | None | The path the file can be accessed at |
-
-## Documentation - Plugin Development
-
-A plugin for SSHare is just a python file (`*.py`). There are currently 6 types of plugins:
-- `logger`
-- `source`
-- `name`
-- `upload`
-- `location`
-- `feedback`
-A plugin can specify be any combination of the above types. For examples of plugins see [here](src/sshare/plugins)
-and [here](https://forge.monodon.me/Gnarwhal/sshare_plugins)
-
-### General Attributes
-
-Every plugin regardless of type specifies or is provided with these attributes.
-
-#### `plugin_type`
-
-This mandatory paramater specifies what type(s) this plugin is.
-It can be either a:
-- `string` - Promotes to `{ string }`
-- `set` - A set containing each of the plugin's types.
-
-#### `activate`
-
-This optional parameter specifies what flag(s) or argument(s) must be passed for this plugin to be activated.
-It can be either a:
-- `string` - Promotes to `{ string }`
-- `set` - Promotes to `{ plugin_type: { string, ... }, plugin_type2: { string, ... }, ... }`
-- `dict` - A dictionary that maps each type the plugin is, to the flags or arguments that activate the plugin for that type
-All arguments specified in `activate` must be provided by the user for the plugin to activate.
-
-#### `config`
-
-This optional parameter specifies configuration options for the plugin. These values are what a user is allowed to change
-through `config.toml`. It is a map containing `{ option: default_value, ... }`. If there is no default value (i.e. it is mandatory
-that the user set it explicitly) for the option, it can be set to `NoDefault` provided by
-`from sshare.plugin.config import NoDefault`.
-
-While `config` is initially specified as a `dict`, when the plugin is loaded it will be converted to an object with attributes.
-For example, if a config is specified as
-```python
-config = {
- "example0": 42,
-}
-```
-it would then be accessed by `config.example` not `config["example"]`.
-
-#### `args`
-
-This optional parameter specifies arguments for the plugin. These values are also accessed from the `config` object,
-however they are provided via program arguments as opposed to being specified in `config.toml`. An option specified
-in both `config` and `args` will be loaded from the config file first and overriden by the program argument if
-provided. Arguments are of type `Argument` provided by `from sshare.plugin.config import Argument`.
-`Argument` takes `name` (optionally) and a list of `kwargs` equivalent to the option document [here](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument). As a convenience there is also `Flag` provided by
-`from sshare.plugin.config import Flag`, which is for boolean arguments which take no parameters.
-`Flag` only takes a `help=...` paramater.
-
-#### `init`
-
-If a plugin needs to do any initialisation, it can be done in the `init` method. The `init` method takes no parameters
-and returns no values.
-
-#### `logger`
-
-Logger is not specified by the plugin developer, but is available inside the plugin if needed. The logger
-has three levels: `info`, `warn` and `error`.
-
-### Type Specific Attributes
-
-#### `logger`
-- `info(str)`
-- `warn(str)`
-- `error(str)`
-
-#### `source` -> `get_source()`
-
-The `get_source` function takes no arguments and returns a source. There are currently two types of sources provided by
-`from sshare.plugin.source import (Raw | File)`.
-- `Raw` - A raw data source. It has a type, a source name, and a byte array providing the data.
-- `File` - A file or directory source. It has only the path to the file.
-
-#### `name` -> `get_name(current_name, source)`
-
-`name` plugins are chained one after the other. The first `name` plugin is provided an empty string for `current_name`.
-Each subsequent `name` plugin is provided the output of the previous `name` plugin's `get_name` function. The `source`
-parameter is the either `Raw` or `File` data source.
-
-#### `upload` -> `upload(name, source)`
-
-`upload` plugins are responsible for getting the source to the destination.
-
-#### `location` -> `get_location(name)`
-
-The `get_location` function takes in the name of a source and returns a location that the source can now be accessed from
-(e.g. a URL).
-
-#### `feedback` -> `give_feedback(location)`
-
-The `give_feedback` function takes output from `location` plugins and presents it to the user (e.g. printing to console,
-desktop notification. etc...).
-
+Refer to \[coming soon...\] for information on installing plugins
diff --git a/_test_in.py b/_test_in.py
new file mode 100755
index 0000000..572e9d1
--- /dev/null
+++ b/_test_in.py
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+import sys
+
+print("=> " + sys.stdin.read())
diff --git a/_test_out.py b/_test_out.py
new file mode 100755
index 0000000..94711d4
--- /dev/null
+++ b/_test_out.py
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+for i in range(0, 10000):
+ print(f"\033[{(i % 7) + 30}mHello World!")
+ print(f"\033[{(i % 7) + 90}mHello World!")
diff --git a/examples b/examples
index 99f2e9c..3273aa4 160000
--- a/examples
+++ b/examples
@@ -1 +1 @@
-Subproject commit 99f2e9c3d2fbab02b0582dc8dcf9ed05df7789a6
+Subproject commit 3273aa4e4c1898c484e989340a8525edb343cebe
diff --git a/pyproject.toml b/pyproject.toml
index b49baec..3fb26fb 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,7 +19,7 @@ requires-python = ">=3.11"
[project.urls]
Homepage = "https://forge.monodon.me/Gnarwhal/sshare"
-Documentation = "https://forge.monodon.me/Gnarwhal/sshare/README.md"
+Documentation = "https://forge.monodon.me/Gnarwhal/sshare/README.md#Usage"
Repository = "https://forge.monodon.me/Gnarwhal/sshare"
Issues = "https://forge.monodon.me/Gnarwhal/sshare/issues"
diff --git a/src/sshare/config_directory.py b/src/sshare/config_directory.py
index cf45ae0..022480d 100644
--- a/src/sshare/config_directory.py
+++ b/src/sshare/config_directory.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
@@ -25,12 +22,9 @@ def default_config():
return _LOCATION / "config.toml"
def plugins():
- if (_LOCATION / "plugins").is_dir():
- return [
- path for
- path in
- (_LOCATION / "plugins").iterdir()
- if path.is_file() and path.suffix == ".py"
- ]
- else:
- return []
+ return [
+ path for
+ path in
+ (_LOCATION / "plugins").iterdir()
+ if path.is_file() and path.suffix == ".py"
+ ]
diff --git a/src/sshare/logger.py b/src/sshare/logger.py
index 6f16d85..60d804c 100644
--- a/src/sshare/logger.py
+++ b/src/sshare/logger.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
diff --git a/src/sshare/main.py b/src/sshare/main.py
index f5c09e1..da4369b 100644
--- a/src/sshare/main.py
+++ b/src/sshare/main.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
@@ -43,31 +40,6 @@ def main():
arguments, _ = arg_parser.parse_known_args()
with open(arguments.config or config_directory.default_config(), mode="rb") as file:
config = tomllib.load(file)
-
- config["spec"] = config.get("spec", {})
- for spec_name, spec in config["spec"].items():
- if spec_name != "default":
- flags = []
- if spec.get("flag", {}).get("short") != None:
- flags = flags + [ f"-{spec["flag"]["short"]}" ]
- if spec.get("flag", {}).get("long") != None:
- flags = flags + [ f"--{spec["flag"]["long"]}" ]
- arg_parser.add_argument(
- *flags,
- action="store_const",
- const=True,
- default=False,
- help=spec.get(help, f"Use {spec_name} spec"),
- dest=spec_name,
- )
-
- use_spec = config["spec"].get("default", {})
- arguments, _ = arg_parser.parse_known_args()
- for spec_name, spec in config["spec"].items():
- if spec_name != "default":
- if getattr(arguments, spec_name):
- use_spec = spec
-
config["config"] = config.get("config", {})
config["flags" ] = config.get("flags", {})
@@ -97,9 +69,8 @@ def main():
logger.add(command_line)
plugins = PluginManager(
logger,
- use_spec,
config["config"],
- config["flags" ],
+ config["flags"],
arg_parser,
)
plugins.activate("logger")
diff --git a/src/sshare/plugin/__init__.py b/src/sshare/plugin/__init__.py
index 3dca581..60040ad 100644
--- a/src/sshare/plugin/__init__.py
+++ b/src/sshare/plugin/__init__.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
diff --git a/src/sshare/plugin/config.py b/src/sshare/plugin/config.py
index 7714abc..b12f099 100644
--- a/src/sshare/plugin/config.py
+++ b/src/sshare/plugin/config.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
@@ -15,6 +12,8 @@
# You should have received a copy of the GNU General Public License along with
# SSHare. If not, see .
+import argparse
+
class NoDefault: pass
def Flag(name=None, help=None):
@@ -36,6 +35,8 @@ class Argument:
self._short = None
self._long = name
+ self._is_remainder = kwargs.get("nargs", None) == argparse.REMAINDER
+
if not "default" in kwargs:
kwargs["default"] = NoDefault
kwargs["default"] = _None(kwargs["default"])
@@ -46,6 +47,8 @@ class Argument:
if self._long == None:
self._long = argument
self._kwargs["metavar"] = argument
+ if self._is_remainder:
+ self._long = self.dest()
def set_flags(self, short, long):
if short != None:
@@ -78,11 +81,13 @@ class Argument:
def add(self, arg_parser):
flags = []
- if self._short != None: flags.append(f"-{self._short}")
- if self._long != None: flags.append(f"--{self._long}")
- kwargs = self._kwargs | {
- "dest": self.dest()
- }
+ kwargs = self._kwargs
+ if self._is_remainder:
+ flags.append(self._long)
+ else:
+ if self._short != None: flags.append(f"-{self._short}")
+ if self._long != None: flags.append(f"--{self._long}")
+ kwargs["dest"] = self.dest()
arg_parser.add_argument(
*flags,
**kwargs
diff --git a/src/sshare/plugin/plugin.py b/src/sshare/plugin/plugin.py
index 5da1184..dc413c6 100644
--- a/src/sshare/plugin/plugin.py
+++ b/src/sshare/plugin/plugin.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
@@ -38,9 +35,9 @@ class PluginLoader:
in ([ "command_line" ] if command_line else []) + [
"file",
"stdin",
- "preserve",
- "time",
- "extension",
+ "wrap_command",
+ "current_time",
+ "append_type",
"ssh",
"uri",
"print_location",
@@ -64,7 +61,7 @@ class PluginLoader:
]
class PluginManager:
- def __init__(self, logger, spec, config, flags, arg_parser):
+ def __init__(self, logger, config, flags, arg_parser):
self._logger = logger
self._arg_parser = arg_parser
@@ -75,22 +72,14 @@ class PluginManager:
for type in Plugin.types():
setattr(self, type, PluginState())
- self._uninitialized = []
- uninitialized = PluginLoader.all(
+ self._uninitialized = PluginLoader.all(
command_line=False,
logger=logger,
config=config,
flags=flags,
)
- for plugin in uninitialized:
+ for plugin in self._uninitialized:
plugin.add_args(arg_parser)
- if not "name" in plugin.plugin_type:
- self._uninitialized.append(plugin)
-
- for name in spec.get("name", []):
- for plugin in uninitialized:
- if plugin.name == name:
- self._uninitialized.append(plugin)
def activate(self, activate_type=None):
args = self._arg_parser.parse_args()
diff --git a/src/sshare/plugin/source.py b/src/sshare/plugin/source.py
index 374988b..fcfc6a6 100644
--- a/src/sshare/plugin/source.py
+++ b/src/sshare/plugin/source.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
@@ -18,8 +15,7 @@
from pathlib import Path
class Raw:
- def __init__(self, name, type, data):
- self.name = name
+ def __init__(self, type, data):
self.type = type
self.data = data
diff --git a/src/sshare/plugins/extension.py b/src/sshare/plugins/append_type.py
similarity index 94%
rename from src/sshare/plugins/extension.py
rename to src/sshare/plugins/append_type.py
index 670aabf..7b97012 100644
--- a/src/sshare/plugins/extension.py
+++ b/src/sshare/plugins/append_type.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
diff --git a/src/sshare/plugins/command_line.py b/src/sshare/plugins/command_line.py
index 7d1d2c7..6b86cce 100644
--- a/src/sshare/plugins/command_line.py
+++ b/src/sshare/plugins/command_line.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
diff --git a/src/sshare/plugins/time.py b/src/sshare/plugins/current_time.py
similarity index 71%
rename from src/sshare/plugins/time.py
rename to src/sshare/plugins/current_time.py
index 62b0bac..16cdd44 100644
--- a/src/sshare/plugins/time.py
+++ b/src/sshare/plugins/current_time.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
@@ -23,13 +20,19 @@ from sshare.plugin.source import File
plugin_type = "name"
config = {
- "format": 62,
+ "base": 62,
}
-
+
+def init():
+ if not isinstance(config.base, int):
+ logger.fatal("Error: 'base' must be an integer")
+ elif config.base < 2:
+ logger.fatal("Error: 'base' cannot be less than 2")
+ elif config.base > 62:
+ logger.fatal("Error: 'base' cannot be greater than 62")
+
def get_name(name, source):
- if name != "":
- name = name + "_"
- return name + _rebase(config.format, time.time_ns())
+ return name + _rebase(config.base, time.time_ns())
def _rebase(base, number):
if number == 0:
@@ -44,8 +47,8 @@ def _rebase(base, number):
def _number_to_char(number):
if number < 10:
- return chr(number + 48) # 0-9
+ return chr(number + 48)
elif number < 36:
- return chr(number + 87) # a-z
+ return chr(number + 87)
else:
- return chr(number + 29) # A-Z
+ return chr(number + 29)
diff --git a/src/sshare/plugins/file.py b/src/sshare/plugins/file.py
index 5216bae..12cd2c4 100644
--- a/src/sshare/plugins/file.py
+++ b/src/sshare/plugins/file.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
diff --git a/src/sshare/plugins/preserve.py b/src/sshare/plugins/preserve.py
deleted file mode 100644
index 52773b0..0000000
--- a/src/sshare/plugins/preserve.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
-# This file is part of SSHare.
-#
-# SSHare is free software: you can redistribute it and/or modify it under the terms of
-# the GNU General Public License as published by the Free Software Foundation,
-# either version 3 of the License, or (at your option) any later version.
-#
-# SSHare is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# SSHare. If not, see .
-
-from sshare.plugin.source import File
-from sshare.plugin.source import Raw
-
-plugin_type = "name"
-
-def get_name(name, source):
- if name != "":
- name = name + "_"
- else:
- name = ""
- if isinstance(source, File):
- components = source.path.name.split(".")
- if components[0] == "":
- return name + "." + components[1]
- else:
- return name + components[0]
- else:
- return name + source.name
diff --git a/src/sshare/plugins/print_location.py b/src/sshare/plugins/print_location.py
index eb00620..ed2991e 100644
--- a/src/sshare/plugins/print_location.py
+++ b/src/sshare/plugins/print_location.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
diff --git a/src/sshare/plugins/ssh.py b/src/sshare/plugins/ssh.py
index a1d3e3c..2bf71cf 100644
--- a/src/sshare/plugins/ssh.py
+++ b/src/sshare/plugins/ssh.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
diff --git a/src/sshare/plugins/stdin.py b/src/sshare/plugins/stdin.py
index bfc747f..d4edc64 100644
--- a/src/sshare/plugins/stdin.py
+++ b/src/sshare/plugins/stdin.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
@@ -24,11 +21,11 @@ plugin_type = "source"
activate = { "stdin" }
config = {
- "suffix": "txt"
+ "suffix": "txt",
}
args = {
"stdin": Flag(help="Upload from stdin")
}
def get_source():
- return Raw("stdin", config.suffix, sys.stdin.buffer.read())
+ return Raw(config.suffix, sys.stdin.buffer.read())
diff --git a/src/sshare/plugins/uri.py b/src/sshare/plugins/uri.py
index 143d95b..53078b7 100644
--- a/src/sshare/plugins/uri.py
+++ b/src/sshare/plugins/uri.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
diff --git a/src/sshare/plugins/wrap_command.py b/src/sshare/plugins/wrap_command.py
new file mode 100644
index 0000000..410874b
--- /dev/null
+++ b/src/sshare/plugins/wrap_command.py
@@ -0,0 +1,50 @@
+# This file is part of SSHare.
+#
+# SSHare is free software: you can redistribute it and/or modify it under the terms of
+# the GNU General Public License as published by the Free Software Foundation,
+# either version 3 of the License, or (at your option) any later version.
+#
+# SSHare is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# SSHare. If not, see .
+
+import argparse
+import locale
+import subprocess
+
+from sshare.plugin.config import Argument
+from sshare.plugin.source import Raw
+
+plugin_type = "source"
+
+activate = { "command" }
+config = {
+ "suffix": "txt",
+}
+args = {
+ "command": Argument(
+ nargs=argparse.REMAINDER,
+ help="Upload the contents of the wrapped command",
+ )
+}
+
+def init():
+ config.command = config.command[1:]
+
+def get_source():
+ output = b""
+ with subprocess.Popen(
+ config.command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=0,
+ ) as process:
+ for line in process.stdout:
+ print(line.decode(locale.getpreferredencoding()), end="")
+ output += line
+
+ return Raw(config.suffix, output)
diff --git a/src/sshare/validator.py b/src/sshare/validator.py
index 0516afa..b003ba2 100644
--- a/src/sshare/validator.py
+++ b/src/sshare/validator.py
@@ -1,6 +1,3 @@
-# SSHare - upload files to a server
-# Copyright (c) 2024 Gnarwhal
-#
# This file is part of SSHare.
#
# SSHare is free software: you can redistribute it and/or modify it under the terms of
diff --git a/tests/test_test.py b/tests/test_test.py
new file mode 100644
index 0000000..3c71169
--- /dev/null
+++ b/tests/test_test.py
@@ -0,0 +1,4 @@
+from src.sshare.plugin.config import Argument
+
+def test_foo():
+ assert True