refactor(history): commit development branch
new development branch from root commit
Marc Beninca 3 weeks ago 94 files (+4804, -0)
Changed files
@@ -0,0 +1,18 @@+on: [push]+jobs:+ job:+ container:+ image: ${{vars.DOCKER}}debian:bookworm+ steps:+ - name: spcd+ env:+ SPCD: ${{vars.SPCD}}+ SPCD_SSH_HOSTS: ${{vars.SPCD_SSH_HOSTS}}+ SPCD_SSH_KEY: ${{secrets.SPCD_SSH_KEY}}+ SPCD_TXT_LOCALE: ${{vars.SPCD_TXT_LOCALE}}+ run: ${{vars.SPCD}}++ #- run: spcd-check-project+ - run: spcd-build-project+ - run: spcd-browse-workspace+ - run: spcd-synchronize
ADDED
.gitignore
ADDED
.gitignore
@@ -0,0 +1,5 @@+__pycache__+/tmp+/.venv+/.vscode+/dist
ADDED
.shellcheckrc
ADDED
.shellcheckrc
@@ -0,0 +1,4 @@+disable=3043+enable=all+external-sources=true+shell=sh
ADDED
build.py
ADDED
build.py
@@ -0,0 +1,11 @@+#! /usr/bin/env python3+"""Dummy build."""++from pathlib import Path++from rwx.fs import make_directory, write++if __name__ == "__main__":+ out = Path(__file__).parent / "out" / "web"+ make_directory(out)+ write(out / "index.html", "rwx.rwx.work")
ADDED
license.md
ADDED
license.md
@@ -0,0 +1,660 @@+# GNU AFFERO GENERAL PUBLIC LICENSE++Version 3, 19 November 2007++Copyright (C) 2007 Free Software Foundation, Inc.+<https://fsf.org/>++Everyone is permitted to copy and distribute verbatim copies of this+license document, but changing it is not allowed.++## Preamble++The GNU Affero General Public License is a free, copyleft license for+software and other kinds of works, specifically designed to ensure+cooperation with the community in the case of network server software.++The licenses for most software and other practical works are designed+to take away your freedom to share and change the works. By contrast,+our General Public Licenses are intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains+free software for all its users.++When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+them if you wish), that you receive source code or can get it if you+want it, that you can change the software or use pieces of it in new+free programs, and that you know you can do these things.++Developers that use our General Public Licenses protect your rights+with two steps: (1) assert copyright on the software, and (2) offer+you this License which gives you legal permission to copy, distribute+and/or modify the software.++A secondary benefit of defending all users' freedom is that+improvements made in alternate versions of the program, if they+receive widespread use, become available for other developers to+incorporate. Many developers of free software are heartened and+encouraged by the resulting cooperation. However, in the case of+software used on network servers, this result may fail to come about.+The GNU General Public License permits making a modified version and+letting the public access it on a server without ever releasing its+source code to the public.++The GNU Affero General Public License is designed specifically to+ensure that, in such cases, the modified source code becomes available+to the community. It requires the operator of a network server to+provide the source code of the modified version running there to the+users of that server. Therefore, public use of a modified version, on+a publicly accessible server, gives the public access to the source+code of the modified version.++An older license, called the Affero General Public License and+published by Affero, was designed to accomplish similar goals. This is+a different license, not a version of the Affero GPL, but Affero has+released a new version of the Affero GPL which permits relicensing+under this license.++The precise terms and conditions for copying, distribution and+modification follow.++## TERMS AND CONDITIONS++### 0. Definitions.++"This License" refers to version 3 of the GNU Affero General Public+License.++"Copyright" also means copyright-like laws that apply to other kinds+of works, such as semiconductor masks.++"The Program" refers to any copyrightable work licensed under this+License. Each licensee is addressed as "you". "Licensees" and+"recipients" may be individuals or organizations.++To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of+an exact copy. The resulting work is called a "modified version" of+the earlier work or a work "based on" the earlier work.++A "covered work" means either the unmodified Program or a work based+on the Program.++To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy. Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++To "convey" a work means any kind of propagation that enables other+parties to make or receive copies. Mere interaction with a user+through a computer network, with no transfer of a copy, is not+conveying.++An interactive user interface displays "Appropriate Legal Notices" to+the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License. If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++### 1. Source Code.++The "source code" for a work means the preferred form of the work for+making modifications to it. "Object code" means any non-source form of+a work.++A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form. A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities. However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work. For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++The Corresponding Source need not include anything that users can+regenerate automatically from other parts of the Corresponding Source.++The Corresponding Source for a work in source code form is that same+work.++### 2. Basic Permissions.++All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met. This License explicitly affirms your unlimited+permission to run the unmodified Program. The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work. This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++You may make, run and propagate covered works that you do not convey,+without conditions so long as your license otherwise remains in force.+You may convey covered works to others for the sole purpose of having+them make modifications exclusively for you, or provide you with+facilities for running those works, provided that you comply with the+terms of this License in conveying all material for which you do not+control copyright. Those thus making or running the covered works for+you must do so exclusively on your behalf, under your direction and+control, on terms that prohibit them from making any copies of your+copyrighted material outside their relationship with you.++Conveying under any other circumstances is permitted solely under the+conditions stated below. Sublicensing is not allowed; section 10 makes+it unnecessary.++### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.++No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such+circumvention is effected by exercising rights under this License with+respect to the covered work, and you disclaim any intention to limit+operation or modification of the work as a means of enforcing, against+the work's users, your or third parties' legal rights to forbid+circumvention of technological measures.++### 4. Conveying Verbatim Copies.++You may convey verbatim copies of the Program's source code as you+receive it, in any medium, provided that you conspicuously and+appropriately publish on each copy an appropriate copyright notice;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++### 5. Conveying Modified Source Versions.++You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these+conditions:++- a) The work must carry prominent notices stating that you modified+ it, and giving a relevant date.+- b) The work must carry prominent notices stating that it is+ released under this License and any conditions added under+ section 7. This requirement modifies the requirement in section 4+ to "keep intact all notices".+- c) You must license the entire work, as a whole, under this+ License to anyone who comes into possession of a copy. This+ License will therefore apply, along with any applicable section 7+ additional terms, to the whole of the work, and all its parts,+ regardless of how they are packaged. This License gives no+ permission to license the work in any other way, but it does not+ invalidate such permission if you have separately received it.+- d) If the work has interactive user interfaces, each must display+ Appropriate Legal Notices; however, if the Program has interactive+ interfaces that do not display Appropriate Legal Notices, your+ work need not make them do so.++A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit. Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++### 6. Conveying Non-Source Forms.++You may convey a covered work in object code form under the terms of+sections 4 and 5, provided that you also convey the machine-readable+Corresponding Source under the terms of this License, in one of these+ways:++- a) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by the+ Corresponding Source fixed on a durable physical medium+ customarily used for software interchange.+- b) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by a+ written offer, valid for at least three years and valid for as+ long as you offer spare parts or customer support for that product+ model, to give anyone who possesses the object code either (1) a+ copy of the Corresponding Source for all the software in the+ product that is covered by this License, on a durable physical+ medium customarily used for software interchange, for a price no+ more than your reasonable cost of physically performing this+ conveying of source, or (2) access to copy the Corresponding+ Source from a network server at no charge.+- c) Convey individual copies of the object code with a copy of the+ written offer to provide the Corresponding Source. This+ alternative is allowed only occasionally and noncommercially, and+ only if you received the object code with such an offer, in accord+ with subsection 6b.+- d) Convey the object code by offering access from a designated+ place (gratis or for a charge), and offer equivalent access to the+ Corresponding Source in the same way through the same place at no+ further charge. You need not require recipients to copy the+ Corresponding Source along with the object code. If the place to+ copy the object code is a network server, the Corresponding Source+ may be on a different server (operated by you or a third party)+ that supports equivalent copying facilities, provided you maintain+ clear directions next to the object code saying where to find the+ Corresponding Source. Regardless of what server hosts the+ Corresponding Source, you remain obligated to ensure that it is+ available for as long as needed to satisfy these requirements.+- e) Convey the object code using peer-to-peer transmission,+ provided you inform other peers where the object code and+ Corresponding Source of the work are being offered to the general+ public at no charge under subsection 6d.++A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal,+family, or household purposes, or (2) anything designed or sold for+incorporation into a dwelling. In determining whether a product is a+consumer product, doubtful cases shall be resolved in favor of+coverage. For a particular product received by a particular user,+"normally used" refers to a typical or common use of that class of+product, regardless of the status of the particular user or of the way+in which the particular user actually uses, or expects or is expected+to use, the product. A product is a consumer product regardless of+whether the product has substantial commercial, industrial or+non-consumer uses, unless such uses represent the only significant+mode of use of the product.++"Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to+install and execute modified versions of a covered work in that User+Product from a modified version of its Corresponding Source. The+information must suffice to ensure that the continued functioning of+the modified object code is in no case prevented or interfered with+solely because modification has been made.++If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information. But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or+updates for a work that has been modified or installed by the+recipient, or for the User Product in which it has been modified or+installed. Access to a network may be denied when the modification+itself materially and adversely affects the operation of the network+or violates the rules and protocols for communication across the+network.++Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++### 7. Additional Terms.++"Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law. If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it. (Additional permissions may be written to require their own+removal in certain cases when you modify the work.) You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders+of that material) supplement the terms of this License with terms:++- a) Disclaiming warranty or limiting liability differently from the+ terms of sections 15 and 16 of this License; or+- b) Requiring preservation of specified reasonable legal notices or+ author attributions in that material or in the Appropriate Legal+ Notices displayed by works containing it; or+- c) Prohibiting misrepresentation of the origin of that material,+ or requiring that modified versions of such material be marked in+ reasonable ways as different from the original version; or+- d) Limiting the use for publicity purposes of names of licensors+ or authors of the material; or+- e) Declining to grant rights under trademark law for use of some+ trade names, trademarks, or service marks; or+- f) Requiring indemnification of licensors and authors of that+ material by anyone who conveys the material (or modified versions+ of it) with contractual assumptions of liability to the recipient,+ for any liability that these contractual assumptions directly+ impose on those licensors and authors.++All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10. If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term. If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions; the+above requirements apply either way.++### 8. Termination.++You may not propagate or modify a covered work except as expressly+provided under this License. Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++However, if you cease all violation of this License, then your license+from a particular copyright holder is reinstated (a) provisionally,+unless and until the copyright holder explicitly and finally+terminates your license, and (b) permanently, if the copyright holder+fails to notify you of the violation by some reasonable means prior to+60 days after the cessation.++Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License. If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++### 9. Acceptance Not Required for Having Copies.++You are not required to accept this License in order to receive or run+a copy of the Program. Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance. However,+nothing other than this License grants you permission to propagate or+modify any covered work. These actions infringe copyright if you do+not accept this License. Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++### 10. Automatic Licensing of Downstream Recipients.++Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License. You are not responsible+for enforcing compliance by third parties with this License.++An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations. If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License. For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++### 11. Patents.++A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based. The+work thus licensed is called the contributor's "contributor version".++A contributor's "essential patent claims" are all patent claims owned+or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version. For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement). To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients. "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++A patent license is "discriminatory" if it does not include within the+scope of its coverage, prohibits the exercise of, or is conditioned on+the non-exercise of one or more of the rights that are specifically+granted under this License. You may not convey a covered work if you+are a party to an arrangement with a third party that is in the+business of distributing software, under which you make payment to the+third party based on the extent of your activity of conveying the+work, and under which the third party grants, to any of the parties+who would receive the covered work from you, a discriminatory patent+license (a) in connection with copies of the covered work conveyed by+you (or copies made from those copies), or (b) primarily for and in+connection with specific products or compilations that contain the+covered work, unless you entered into that arrangement, or that patent+license was granted, prior to 28 March 2007.++Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++### 12. No Surrender of Others' Freedom.++If conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot convey a+covered work so as to satisfy simultaneously your obligations under+this License and any other pertinent obligations, then as a+consequence you may not convey it at all. For example, if you agree to+terms that obligate you to collect a royalty for further conveying+from those to whom you convey the Program, the only way you could+satisfy both those terms and this License would be to refrain entirely+from conveying the Program.++### 13. Remote Network Interaction; Use with the GNU General Public License.++Notwithstanding any other provision of this License, if you modify the+Program, your modified version must prominently offer all users+interacting with it remotely through a computer network (if your+version supports such interaction) an opportunity to receive the+Corresponding Source of your version by providing access to the+Corresponding Source from a network server at no charge, through some+standard or customary means of facilitating copying of software. This+Corresponding Source shall include the Corresponding Source for any+work covered by version 3 of the GNU General Public License that is+incorporated pursuant to the following paragraph.++Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU General Public License into a single+combined work, and to convey the resulting work. The terms of this+License will continue to apply to the part which is the covered work,+but the work with which it is combined will remain governed by version+3 of the GNU General Public License.++### 14. Revised Versions of this License.++The Free Software Foundation may publish revised and/or new versions+of the GNU Affero General Public License from time to time. Such new+versions will be similar in spirit to the present version, but may+differ in detail to address new problems or concerns.++Each version is given a distinguishing version number. If the Program+specifies that a certain numbered version of the GNU Affero General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation. If the Program does not specify a version number of the+GNU Affero General Public License, you may choose any version ever+published by the Free Software Foundation.++If the Program specifies that a proxy can decide which future versions+of the GNU Affero General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++Later license versions may give you additional or different+permissions. However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++### 15. Disclaimer of Warranty.++THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT+WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR+CORRECTION.++### 16. Limitation of Liability.++IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR+CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT+NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR+LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM+TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER+PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.++### 17. Interpretation of Sections 15 and 16.++If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++END OF TERMS AND CONDITIONS++## How to Apply These Terms to Your New Programs++If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these+terms.++To do so, attach the following notices to the program. It is safest to+attach them to the start of each source file to most effectively state+the exclusion of warranty; and each file should have at least the+"copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU Affero General Public License as+ published by the Free Software Foundation, either version 3 of the+ License, or (at your option) any later version.++ This program 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 Affero General Public License for more details.++ You should have received a copy of the GNU Affero General Public License+ along with this program. If not, see <https://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper+mail.++If your software can interact with users remotely through a computer+network, you should also make sure that it provides a way for users to+get its source. For example, if your program is a web application, its+interface could display a "Source" link that leads users to an archive+of the code. There are many ways you could offer source, and different+solutions will be better for different programs; see section 13 for+the specific requirements.++You should also get your employer (if you work as a programmer) or+school, if any, to sign a "copyright disclaimer" for the program, if+necessary. For more information on this, and how to apply and follow+the GNU AGPL, see <https://www.gnu.org/licenses/>.
ADDED
pyproject.toml
ADDED
pyproject.toml
@@ -0,0 +1,32 @@+[build-system]+requires = ["hatchling"]+build-backend = "hatchling.build"++[project]+authors = [+ { name = "Marc Beninca", email = "git@marc.beninca.link" },+]+maintainers = [+ { name = "Marc Beninca", email = "git@marc.beninca.link" },+]+classifiers = [+ "Programming Language :: Python :: 3",+ "License :: OSI Approved :: GNU Affero General Public License v3",+ "Operating System :: OS Independent",+]+dependencies = []+description = "Read Write eXecute"+dynamic = ["version"]+keywords = []+license-files = ["license.md"]+name = "rwx"+readme = "readme.md"+requires-python = ">= 3.11"++[project.scripts]+# command = "package.module:function"++[project.urls]++[tool.hatch.version]+path = "rwx/__init__.py"
ADDED
readme.md
ADDED
readme.md
@@ -0,0 +1,74 @@+# Read Write eXecute++A tiny framework to read, write & execute things.++---++## Why++---++## How++---++## What++---++## Who++### By++* [Marc Beninca](https://marc.beninca.link)++### For++* myself++---++## Where++### Chat++* [Discord](https://discord.com/channels/983145051985154108/1255894474895134761)+* [IRC](ircs://irc.libera.chat/##rwx)++### Forge++* [Repository](https://forge.rwx.work/rwx.work/rwx)+* [RSS](https://forge.rwx.work/rwx.work/rwx.rss)+* [Workflows](https://forge.rwx.work/rwx.work/rwx/actions)++### Deployment++* [Site](https://rwx.rwx.work)++---++## When++### Task stack++#### Python++* character constants for box drawing+* common __str__ function+* parse pyproject.toml to write commands+* write classes for+ * steps bars to log+ * system commands to run+ * with single call of subprocess.run+ * or alternate subprocess method?++#### Shell++* git switch signing commits & tags+* shellcheck & shfmt+* python tools+* log+* hetzner+* apt+ * apt-file search | grep+* ffmpeg
ADDED
rwx/__init__.py
ADDED
rwx/__init__.py
@@ -0,0 +1,33 @@+"""Read Write eXecute."""++__version__ = "0.0.1"++from os import linesep+++class Object:+ """Root object."""++ def __repr__(self) -> str:+ """Return machine-readable state.++ :return: state+ :rtype: str+ """+ name = self.__class__.__name__+ attributes = [+ f"{k}={v!r}" for k, v in vars(self).items() if not k.startswith("_")+ ]+ arguments = ", ".join(attributes)+ return f"{name}({arguments})"++ def __str__(self) -> str:+ """Return human-readable state.++ :return: state+ :rtype: str+ """+ attributes = [+ f"{k} = {v}" for k, v in vars(self).items() if not k.startswith("_")+ ]+ return linesep.join(attributes)
ADDED
rwx/__main__.py
ADDED
rwx/__main__.py
@@ -0,0 +1,19 @@+#! /usr/bin/env python3++"""Entry point."""++from pathlib import Path++from rwx import fs++if __name__ == "__main__":+ file_path: Path = Path(__file__).resolve()+ root_path: Path = file_path.parent+ directory_path: Path = root_path / "tmp"+ file_path = directory_path / "file"++ fs.wipe(directory_path)+ fs.make_directory(directory_path)+ fs.write(file_path, "Martine écrit beaucoup.")+ fs.empty_file(file_path)+ fs.write(file_path, "Martine écrit moins.")
ADDED
rwx/arg/__init__.py
ADDED
rwx/arg/__init__.py
@@ -0,0 +1,13 @@+"""Handle system arguments."""++import sys+++def split() -> tuple[str, list[str]]:+ """Split command & actual arguments.++ :return: both+ :rtype: tuple[str, list[str]]+ """+ command, *arguments = sys.argv+ return command, arguments
ADDED
rwx/cmd/__init__.py
ADDED
rwx/cmd/__init__.py
@@ -0,0 +1,22 @@+"""Handle system commands & packages."""++commands: list[str] = []+packages: list[str] = []+++def need(command: str) -> None:+ """Assert package dependency for a command.++ :param command: name of the requested command+ :type command: str+ """+ package: str | None+ match command:+ case "debootstrap":+ package = "debootstrap"+ case "mksquashfs" | "unsquashfs":+ package = "squashfs-tools"+ case _:+ package = None+ if package and package not in packages:+ packages.append(package)
@@ -0,0 +1,26 @@+"""Wrap SquashFS commands."""++from pathlib import Path++from rwx import cmd, ps++cmd.need("mksquashfs")+++def mksquashfs(input_root: Path, output_file: Path) -> None:+ """Make a SquashFS bootable image file.++ :param input_root: ?+ :type input_root: Path+ :param output_file: ?+ :type output_file: Path+ """+ ps.run(+ "mksquashfs",+ str(input_root),+ str(output_file),+ "-comp",+ "zstd",+ "-Xcompression-level",+ str(18),+ )
ADDED
rwx/deb/__init__.py
ADDED
rwx/deb/__init__.py
@@ -0,0 +1,31 @@+"""Wrap Debian commands."""++from pathlib import Path++from rwx import cmd, ps++cmd.need("debootstrap")++BOOTSTRAP_ARCHITECTURE = "amd64"+BOOTSTRAP_VARIANT = "minbase"+++def bootstrap(root_path: Path, suite: str, mirror_location: str) -> None:+ """Boostrap a base operating filesystem.++ :param root_path: target output path+ :type root_path: Path+ :param suite: target distribution name+ :type suite: str+ :param mirror_location: source input repository+ :type mirror_location: str+ """+ command = (+ "debootstrap",+ ("--arch", BOOTSTRAP_ARCHITECTURE),+ ("--variant", BOOTSTRAP_VARIANT),+ suite,+ str(root_path),+ mirror_location,+ )+ ps.run(*command)
ADDED
rwx/err/__init__.py
ADDED
rwx/err/__init__.py
@@ -0,0 +1,7 @@+"""Handle errors."""++from rwx import Object+++class Error(Object, Exception):+ """Parent class for all errors."""
ADDED
rwx/fs/__init__.py
ADDED
rwx/fs/__init__.py
@@ -0,0 +1,159 @@+"""Operations involving FileSystems."""++import os+import shutil+from pathlib import Path++import tomllib++from rwx import ps++CHARSET = "UTF-8"+++def create_image(file_path: Path, size_bytes: int) -> None:+ """Create a virtual device image file.++ :param file_path: target image file+ :type file_path: Path+ :param size_bytes: virtual volume+ :type size_bytes: int+ """+ ps.run(+ ("qemu-img", "create"),+ ("-f", "qcow2"),+ (str(file_path), str(size_bytes)),+ )+++def empty_file(path: Path) -> None:+ """Empty the file at provided path.++ :param path: target file to empty+ :type path: Path+ """+ write(path, "")+++def get_mount_uuid(path: Path) -> str:+ """Return the filesystem UUID of a mountpoint path.++ :param path: mountpoint path+ :type path: Path+ :rtype: str+ """+ return ps.run_line(+ "findmnt",+ "--noheadings",+ ("--output", "UUID"),+ str(path),+ )+++def get_path_mount(path: Path) -> Path:+ """Return the mountpoint path of an arbitrary path.++ :param path: arbitrary path+ :type path: Path+ :rtype: Path+ """+ return Path(+ ps.run_line(+ "stat",+ ("--format", "%m"),+ str(path),+ ),+ )+++def get_path_uuid(path: Path) -> str:+ """Return the filesystem UUID of an arbitrary path.++ :param path: arbitrary path+ :type path: Path+ :rtype: str+ """+ return get_mount_uuid(get_path_mount(path))+++def make_directory(path: Path) -> None:+ """Make a directory (and its parents) from a path.++ :param path: directory to create+ :type path: Path+ """+ path.mkdir(exist_ok=True, parents=True)+++def read_file_bytes(file_path: Path) -> bytes:+ """Read whole file bytes.++ :param file_path: source input file+ :type file_path: Path+ :rtype: bytes+ """+ with file_path.open("br") as file_object:+ return file_object.read()+++def read_file_dict(file_path: Path, charset: str = CHARSET) -> dict:+ """Read whole file as toml object.++ :param file_path: source input file+ :type file_path: Path+ :param charset: charset to use for decoding input+ :type charset: str+ :rtype: dict+ """+ text = read_file_text(file_path, charset)+ return tomllib.loads(text)+++def read_file_lines(file_path: Path, charset: str = CHARSET) -> list[str]:+ """Read whole file lines.++ :param file_path: source input file+ :type file_path: Path+ :param charset: charset to use for decoding input+ :type charset: str+ :rtype: list[str]+ """+ return read_file_text(file_path, charset).split(os.linesep)+++def read_file_text(file_path: Path, charset: str = CHARSET) -> str:+ """Read whole file text.++ :param file_path: source input file+ :type file_path: Path+ :param charset: charset to use for decoding input+ :type charset: str+ :rtype: str+ """+ return read_file_bytes(file_path).decode(charset)+++def wipe(path: Path) -> None:+ """Wipe provided path, whether directory or file.++ :param path: target path+ :type path: Path+ """+ try:+ path.unlink(missing_ok=True)+ except IsADirectoryError:+ shutil.rmtree(path)+++def write(file_path: Path, text: str, charset: str = CHARSET) -> None:+ """Write text into a file.++ :param file_path: target file path+ :type file_path: Path+ :param text: content to write+ :type text: str+ :param charset: charset to use for encoding ouput+ :type charset: str+ """+ with file_path.open(encoding=charset, mode="w") as file_object:+ file_object.write(text)
ADDED
rwx/grub/__init__.py
ADDED
rwx/grub/__init__.py
@@ -0,0 +1,54 @@+"""Wrap GRUB commands."""++from __future__ import annotations++from rwx import cmd, ps++cmd.need("grub-mkimage")++COMPRESSION = "xz"+ENV_BYTES = 1024+ENV_COMMENT = "#"+ENV_HEADER = f"""{ENV_COMMENT} GRUB Environment Block+"""+MODULES = {+ "i386-pc": [+ ("biosdisk",),+ ("ntldr",),+ ],+}+++def make_image(+ image_format: str,+ image_path: str,+ modules: list[str],+ memdisk_path: str,+ pubkey_path: str | None = None,+) -> None:+ """Make a binary bootable image.++ :param image_format: output format (x86_64-efi, i386-pc, arm64-efi)+ :type image_format: str+ :param image_path: output file+ :type image_path: str+ :param modules: modules to embed+ :type modules: list[str]+ :param memdisk_path: archive to include+ :type memdisk_path: str+ :param pubkey_path: extra public key to add+ :type pubkey_path: str | None+ """+ args: list[str | tuple[str, ...]] = [+ "grub-mkimage",+ ("--compress", COMPRESSION),+ ("--format", image_format),+ ("--output", image_path),+ ("--memdisk", memdisk_path),+ ]+ if pubkey_path:+ args.append(("--pubkey", pubkey_path))+ args.extend(modules)+ if extra_modules := MODULES.get(image_format):+ args.extend(extra_modules)+ ps.run(*args)
ADDED
rwx/log/__init__.py
ADDED
rwx/log/__init__.py
@@ -0,0 +1,51 @@+"""Handle logging."""++import logging+import sys+++def get_file_logger(name: str) -> logging.Logger:+ """Return a file logger.++ :param name: arbitrary name+ :type name: str+ :rtype: logging.Logger+ """+ # formatter+ items = [+ "%(name)s: %(asctime)s",+ "%(levelname)s",+ "%(filename)s:%(lineno)s",+ "%(process)d >>> %(message)s",+ ]+ template = " | ".join(items)+ formatter = logging.Formatter(template)+ # handler+ out_handler = logging.StreamHandler(stream=sys.stdout)+ out_handler.setFormatter(formatter)+ out_handler.setLevel(logging.INFO)+ # logger+ logger = logging.getLogger(name)+ logger.addHandler(out_handler)+ logger.setLevel(logging.INFO)+ return logger+++def get_stream_logger(level: int) -> logging.Logger:+ """Return a stream logger.++ :param level: filtering level+ :type level: int+ :rtype: logging.Logger+ """+ # handler+ out_handler = logging.StreamHandler(stream=sys.stdout)+ out_handler.setLevel(level)+ # logger+ logger = logging.getLogger()+ logger.addHandler(out_handler)+ logger.setLevel(level)+ return logger+++stream = get_stream_logger(logging.INFO)
ADDED
rwx/os/__init__.py
ADDED
rwx/os/__init__.py
@@ -0,0 +1,20 @@+"""Control Operating Systems."""++from os import sep+from pathlib import Path++from .abstract import OS+from .debian import Debian+++def from_path(path: Path) -> OS:+ """Initialize from an already existing path.++ :param path: source root directory+ :type path: Path+ :rtype: OS+ """+ return Debian(path)+++up = from_path(Path(sep))
ADDED
rwx/os/abstract.py
ADDED
rwx/os/abstract.py
@@ -0,0 +1,26 @@+"""Abstract Operating System."""++from abc import ABC, abstractmethod+from pathlib import Path++from rwx import Object+++class OS(Object, ABC):+ """Operating System."""++ def __init__(self, path: Path) -> None:+ """Set root.++ :param path: root directory+ :type path: Path+ """+ self.root = path+ self.name = self.get_name()++ @abstractmethod+ def get_name(self) -> str:+ """Return mandatory name.++ :rtype: str+ """
ADDED
rwx/os/debian.py
ADDED
rwx/os/debian.py
@@ -0,0 +1,14 @@+"""Debian operating system."""++from .abstract import OS+++class Debian(OS):+ """Debian operating system."""++ def get_name(self) -> str:+ """Return name.++ :rtype: str+ """+ return "Debian"
ADDED
rwx/os/pm/__init__.py
ADDED
rwx/os/pm/__init__.py
@@ -0,0 +1,29 @@+"""Package Manager."""++from abc import ABC, abstractmethod++from rwx import Object+from rwx.ps import Command+++class PM(Object, ABC):+ """Package Manager."""++ def __init__(self) -> None:+ """Set commands."""+ self.clean = self.get_clean_command()+ self.install = self.get_install_command()++ @abstractmethod+ def get_clean_command(self) -> Command:+ """Command to clean packages cache.++ :rtype: Command+ """++ @abstractmethod+ def get_install_command(self) -> Command:+ """Command to install package(s).++ :rtype: Command+ """
ADDED
rwx/os/pm/apt.py
ADDED
rwx/os/pm/apt.py
@@ -0,0 +1,22 @@+"""Advanced Package Tool."""++from rwx.os.pm import PM+from rwx.ps import Command+++class APT(PM):+ """Advanced Package Tool."""++ def get_clean_command(self) -> Command:+ """Return clean command.++ :rtype: Command+ """+ return Command()++ def get_install_command(self) -> Command:+ """Return install command.++ :rtype: Command+ """+ return Command()
ADDED
rwx/prj/__init__.py
ADDED
rwx/prj/__init__.py
@@ -0,0 +1,20 @@+"""Handle projects."""++from pathlib import Path++from rwx import Object+++class Project(Object):+ """Parent class for any type of project."""++ def __init__(self, file: Path) -> None:+ """Set file, root & name.++ :param file: root reference file+ :type file: Path+ """+ self.raw = file+ self.file = self.raw.resolve()+ self.root: Path = self.file.parent+ self.name: str = self.root.name
ADDED
rwx/prj/sphinx.py
ADDED
rwx/prj/sphinx.py
@@ -0,0 +1,39 @@+"""Project consisting only of a Sphinx documentation."""++from typing import TYPE_CHECKING++from sphinx.cmd.build import build_main++from rwx.fs import wipe+from rwx.prj import Project++if TYPE_CHECKING:+ from pathlib import Path+++class SphinxProject(Project):+ """Child class for a project based on Sphinx."""++ def build(self) -> None:+ """Build the project."""+ output_root: Path = self.root / "out"+ wipe(output_root)+ arguments: list[str] = [+ "-E",+ "-j",+ "2",+ "-b",+ "html",+ "-D",+ f"project={self.name}",+ "-D",+ "master_doc=index",+ "-D",+ "html_theme=sphinx_rtd_theme",+ "-c",+ str(self.root),+ # "-C",+ str(self.root / self.name),+ str(output_root / "web"),+ ]+ build_main(arguments)
ADDED
rwx/ps/__init__.py
ADDED
rwx/ps/__init__.py
@@ -0,0 +1,85 @@+"""Handle processes."""++from __future__ import annotations++import subprocess++from rwx import Object, txt+++class Command(Object):+ """Command to run."""++ def __init__(self, *arguments: str | tuple[str, ...]) -> None:+ """Set raw & flat arguments.++ :param *arguments: single argument or grouped ones+ :type *arguments: str | tuple[str, ...]+ """+ self.raw = arguments+ self.flat: list[str] = []+++def get_tuples_args(*items: str | tuple[str, ...]) -> list[str]:+ """Turn arguments tuples into an arguments list.++ :param *items: single item or grouped ones+ :type *items: str | tuple[str, ...]+ :rtype: list[str]+ """+ args: list[str] = []+ for item in items:+ match item:+ case str():+ args.append(item)+ case tuple():+ args.extend(item)+ return args+++def run(*items: str | tuple[str, ...]) -> subprocess.CompletedProcess:+ """Run from a list of arguments tuples.++ :param *items: single item or grouped ones+ :type *items: str | tuple[str, ...]+ :rtype: subprocess.CompletedProcess+ """+ return subprocess.run(+ get_tuples_args(*items),+ capture_output=False,+ check=True,+ )+++def run_line(*items: str | tuple[str, ...], charset: str = txt.CHARSET) -> str:+ """Run and return output line.++ :param *items: single item or grouped ones+ :type *items: str | tuple[str, ...]+ :param charset: charset to use for decoding binary output+ :type charset: str+ :rtype: str+ """+ line, *_ = run_lines(*items, charset=charset)+ return line+++def run_lines(+ *items: str | tuple[str, ...],+ charset: str = txt.CHARSET,+) -> list[str]:+ """Run and return output lines.++ :param *items: single item or grouped ones+ :type *items: str | tuple[str, ...]+ :param charset: charset to use for decoding binary output+ :type charset: str+ :rtype: list[str]+ """+ process = subprocess.run(+ get_tuples_args(*items),+ capture_output=True,+ check=True,+ )+ string = process.stdout.decode(charset)+ return string.rstrip().splitlines()
ADDED
rwx/py.typed
ADDED
rwx/py.typed
@@ -0,0 +1,1 @@+"""Configure FreeTube."""
@@ -0,0 +1,29 @@+"""FreeTube channels."""++from rwx import Object+++class Channel(Object):+ """FreeTube channel."""++ def __init__(self, uid: str, name: str) -> None:+ """Set uid & name.++ :param uid: unique identifier+ :type uid: str+ :param name: label+ :type name: str+ """+ self.uid = uid+ self.name = name++ def to_db(self) -> str:+ """Return identifier as db.++ :rtype: str+ """+ return f"""\+{{\+"id":"{self.uid}"\+}}\+"""
ADDED
rwx/sw/freetube/db.py
ADDED
rwx/sw/freetube/db.py
@@ -0,0 +1,21 @@+"""Output FreeTube db."""+++def to_db(value: object) -> str:+ """Render value as string.++ :param value: value to render+ :type value: object+ :rtype: str+ """+ match value:+ case bool():+ text = str(value).lower()+ case dict():+ sub = ",".join([f'"{i}":{to_db(v)}' for i, v in value.items()])+ text = f"{{{sub}}}"+ case float() | str():+ text = f'"{value}"'+ case _:+ text = str(value)+ return text
@@ -0,0 +1,24 @@+"""FreeTube languages."""++from typing import TYPE_CHECKING++from rwx import Object++if TYPE_CHECKING:+ from .playlists import Playlist+++class Language(Object):+ """FreeTube language."""++ def __init__(self, uid: str, name: str) -> None:+ """Set uid & name.++ :param uid: identifier+ :type uid: str+ :param name: label+ :type name: str+ """+ self.uid = uid+ self.name = name+ self.playlist: Playlist
@@ -0,0 +1,47 @@+"""FreeTube playlists."""++from rwx import Object++from .videos import Video+++class Playlist(Object):+ """FreeTube playlist."""++ def __init__(self, uid: str, name: str) -> None:+ """Set uid & name.++ :param uid: identifier+ :type uid: str+ :param name: label+ :type name: str+ """+ self.uid = uid+ self.name = name+ self.videos: list[Video] = []++ def add(self, video: Video) -> None:+ """Add video.++ :param video: video to add+ :type video: Video+ """+ self.videos.append(video)++ def to_db(self) -> str:+ """Return identifier, name & videos.++ :rtype: str+ """+ videos = ",".join([video.to_db() for video in self.videos])+ return f"""\+{{\+"_id":"{self.uid}"\+,\+"playlistName":"{self.name}"\+,\+"protected":true\+,\+"videos":[{videos}]\+}}\+"""
@@ -0,0 +1,45 @@+"""FreeTube profiles."""++from rwx import Object++from .channels import Channel+++class Profile(Object):+ """FreeTube profile."""++ def __init__(self, uid: str, name: str) -> None:+ """Set uid & name.++ :param uid: unique identifier+ :type uid: str+ :param name: label+ :type name: str+ """+ self.id = uid+ self.name = name+ self.channels: list[Channel] = []++ def add(self, channel: Channel) -> None:+ """Add channel.++ :param channel: channel to add+ :type channel: Channel+ """+ self.channels.append(channel)++ def to_db(self) -> str:+ """Return identifier, name & channels.++ :rtype: str+ """+ channels = ",".join([channel.to_db() for channel in self.channels])+ return f"""\+{{\+"_id":"{self.id}"\+,\+"name":"{self.name}"\+,\+"subscriptions":[{channels}]\+}}\+"""
@@ -0,0 +1,33 @@+"""FreeTube settings."""++from rwx import Object++from .db import to_db+++class Setting(Object):+ """FreeTube setting."""++ def __init__(self, uid: str, value: object) -> None:+ """Set uid & value.++ :param uid: unique identifier+ :type uid: str+ :param value: value+ :type value: object+ """+ self.uid = uid+ self.value = value++ def to_db(self) -> str:+ """Return uid & value as db string.++ :rtype: str+ """+ return f"""\+{{\+"_id":"{self.uid}"\+,\+"value":{to_db(self.value)}\+}}\+"""
@@ -0,0 +1,33 @@+"""FreeTube videos."""++from rwx import Object+++class Video(Object):+ """FreeTube video."""++ def __init__(self, uid: str, name: str) -> None:+ """Set id & name.++ :param uid: identifier+ :type uid: str+ :param name: label+ :type name: str+ """+ self.uid = uid+ self.name = name++ def to_db(self) -> str:+ """Return identifier, zero length & title.++ :rtype: str+ """+ return f"""\+{{\+"videoId":"{self.uid}"\+,\+"lengthSeconds":0\+,\+"title":"{self.name}"\+}}\+"""
ADDED
rwx/txt/__init__.py
ADDED
rwx/txt/__init__.py
@@ -0,0 +1,3 @@+"""Handle text."""++CHARSET = "UTF-8"
ADDED
sh/alias/apt.sh
ADDED
sh/alias/apt.sh
@@ -0,0 +1,95 @@+# show package information+acl() { a__apt_cache_list "${@}"; }+a__apt_cache_list() {+ apt-cache \+ show \+ "${@}"+}++# package versions policy+acp() { a__apt_cache_policy "${@}"; }+a__apt_cache_policy() {+ apt-cache \+ policy \+ "${@}"+}++# search package+acs() { a__apt_cache_search "${@}"; }+a__apt_cache_search() {+ apt-cache \+ search \+ "${@}"+}++#+agap() { a__apt_get_auto_purge "${@}"; }+a__apt_get_auto_purge() {+ apt-get \+ autopurge \+ "${@}"+}++#+agar() { a__apt_get_auto_remove "${@}"; }+a__apt_get_auto_remove() {+ apt-get \+ autoremove \+ "${@}"+}++# clean packages cache+agc() { a__apt_get_clean "${@}"; }+a__apt_get_clean() {+ apt-get \+ clean \+ "${@}"+}++# upgrade allowing package installation or removal+agfu() { a__apt_get_full_upgrade "${@}"; }+a__apt_get_full_upgrade() {+ apt-get \+ full-upgrade \+ "${@}"+}++# install packages+agi() { a__apt_get_install "${@}"; }+a__apt_get_install() {+ apt-get \+ install \+ "${@}"+}++#+agp() { a__apt_get_purge "${@}"; }+a__apt_get_purge() {+ apt-get \+ purge \+ "${@}"+}++#+agr() { a__apt_get_remove "${@}"; }+a__apt_get_remove() {+ apt-get \+ remove \+ "${@}"+}++# update packages catalog+agud() { a__apt_get_up_date "${@}"; }+a__apt_get_up_date() {+ apt-get \+ update \+ "${@}"+}++# upgrade forbidding package installation or removal+agug() { a__apt_get_up_grade "${@}"; }+a__apt_get_up_grade() {+ apt-get \+ upgrade \+ "${@}"+}
ADDED
sh/alias/batcat.sh
ADDED
sh/alias/batcat.sh
@@ -0,0 +1,5 @@+b() { a__bat "${@}"; }+a__bat() {+ batcat \+ "${@}"+}
ADDED
sh/alias/btrfs.sh
ADDED
sh/alias/btrfs.sh
@@ -0,0 +1,73 @@+bfdf() { a__btrfs_filesystem_d_f "${@}"; }+a__btrfs_filesystem_d_f() {+ btrfs \+ filesystem \+ df \+ "${@}"+}++bfdu() { a__btrfs_filesystem_d_u "${@}"; }+a__btrfs_filesystem_d_u() {+ btrfs \+ filesystem \+ du \+ --summarize \+ "${@}"+}++bfu() { a__btrfs_filesystem_usage "${@}"; }+a__btrfs_filesystem_usage() {+ btrfs \+ filesystem \+ usage \+ "${@}"+}++bpg() { a__btrfs_property_get "${@}"; }+a__btrfs_property_get() {+ btrfs \+ property \+ get \+ "${@}"+}++bsc() { a__btrfs_subvolume_create "${@}"; }+a__btrfs_subvolume_create() {+ btrfs \+ subvolume \+ create \+ "${@}"+}++bsd() { a__btrfs_subvolume_delete "${@}"; }+a__btrfs_subvolume_delete() {+ btrfs \+ subvolume \+ delete \+ "${@}"+}++bsl() { a__btrfs_subvolume_list "${@}"; }+a__btrfs_subvolume_list() {+ if [ -n "${1}" ]; then+ btrfs subvolume list "${1}" |+ cut --delimiter " " --fields 9 |+ sort+ fi+}++bss() { a__btrfs_subvolume_snapshot "${@}"; }+a__btrfs_subvolume_snapshot() {+ btrfs \+ subvolume \+ snapshot \+ "${@}"+}++bssr() { a__btrfs_subvolume_snapshot_r "${@}"; }+a__btrfs_subvolume_snapshot_r() {+ btrfs \+ subvolume \+ snapshot -r \+ "${@}"+}
ADDED
sh/alias/byobu.sh
ADDED
sh/alias/byobu.sh
@@ -0,0 +1,27 @@+bb() { a__byo_bu "${@}"; }+a__byo_bu() {+ byobu \+ "${@}"+}++bba() { a__byo_bu_attach "${@}"; }+a__byo_bu_attach() {+ byobu \+ attach-session \+ "${@}"+}++bbl() { a__byo_bu_ls "${@}"; }+a__byo_bu_ls() {+ byobu \+ ls \+ "${@}"+}++bbnd() { a__byo_bu_new_detach "${@}"; }+a__byo_bu_new_detach() {+ byobu \+ new-session \+ -d \+ "${@}"+}
ADDED
sh/alias/chmod.sh
ADDED
sh/alias/chmod.sh
@@ -0,0 +1,15 @@+# change mode to directory+cmd() { a__change_mode_directory "${@}"; }+a__change_mode_directory() {+ chmod \+ "755" \+ "${@}"+}++# change mode to file+cmf() { a__change_mode_file "${@}"; }+a__change_mode_file() {+ chmod \+ "644" \+ "${@}"+}
ADDED
sh/alias/chown.sh
ADDED
sh/alias/chown.sh
@@ -0,0 +1,15 @@+# change owner to root+cor() { a__change_owner_root "${@}"; }+a__change_owner_root() {+ chown \+ "0:0" \+ "${@}"+}++# change owner to user+cou() { a__change_owner_user "${@}"; }+a__change_owner_user() {+ chown \+ "1000:1000" \+ "${@}"+}
ADDED
sh/alias/clear.sh
ADDED
sh/alias/clear.sh
@@ -0,0 +1,6 @@+# clear terminal+c() { a__clear "${@}"; }+a__clear() {+ clear \+ "${@}"+}
ADDED
sh/alias/cp.sh
ADDED
sh/alias/cp.sh
@@ -0,0 +1,7 @@+# copy interactively+cpi() { a__co_py_interactive "${@}"; }+a__co_py_interactive() {+ cp \+ --interactive \+ "${@}"+}
ADDED
sh/alias/emacs.sh
ADDED
sh/alias/emacs.sh
@@ -0,0 +1,5 @@+em() { a__e_macs "${@}"; }+a__e_macs() {+ emacs \+ "${@}"+}
ADDED
sh/alias/evince.sh
ADDED
sh/alias/evince.sh
@@ -0,0 +1,5 @@+ev() { a__e_vince "${@}"; }+a__e_vince() {+ evince \+ "${@}"+}
ADDED
sh/alias/git.sh
ADDED
sh/alias/git.sh
@@ -0,0 +1,548 @@+RWX_GIT_LOG_FORMAT="\+%C(auto)%h%d+S %C(red)%GS+A %C(green)%an %ae+ %C(green)%ai+C %C(blue)%cn %ce+ %C(blue)%ci+%B"++# add to index+ga() { a__git_add "${@}"; }+a__git_add() {+ git \+ add \+ "${@}"+}++# add all to index+gaa() { a__git_add_all "${@}"; }+a__git_add_all() {+ git \+ add \+ --all \+ "${@}"+}++# add parts of all to index+gaap() { a__git_add_all_patch "${@}"; }+a__git_add_all_patch() {+ git \+ add \+ --all \+ --patch \+ "${@}"+}++# add parts to index+gap() { a__git_add_patch "${@}"; }+a__git_add_patch() {+ git \+ add \+ --patch \+ "${@}"+}++# create a branch+gb() { a__git_branch "${@}"; }+a__git_branch() {+ git \+ branch \+ "${@}"+}++# delete a branch+gbd() { a__git_branch_delete "${@}"; }+a__git_branch_delete() {+ git \+ branch \+ --delete \+ "${@}"+}++# force a branch deletion+gbdf() { a__git_branch_delete_force "${@}"; }+a__git_branch_delete_force() {+ git \+ branch \+ --delete \+ --force \+ "${@}"+}++# list branches+gbl() { a__git_branch_list "${@}"; }+a__git_branch_list() {+ git \+ branch \+ --all \+ --list \+ --verbose \+ --verbose \+ "${@}"+}++# set the link to a remote branch from a local branch+gbsu() { a__git_branch_set_upstream "${@}"; }+a__git_branch_set_upstream() {+ git \+ branch \+ --set-upstream-to \+ "${@}"+}++# switch to a branch or checkout file(s) from a commit+gc() { a__git_checkout "${@}"; }+a__git_checkout() {+ git \+ checkout \+ "${@}"+}++# checkout an orphan branch+gco() { a__git_checkout_orphan "${@}"; }+a__git_checkout_orphan() {+ git \+ checkout \+ --orphan \+ "${@}"+}++# pick a commit+gcp() { a__git_cherry_pick "${@}"; }+a__git_cherry_pick() {+ git \+ cherry-pick \+ "${@}"+}++# abort the commit pick+gcpa() { a__git_cherry_pick_abort "${@}"; }+a__git_cherry_pick_abort() {+ git \+ cherry-pick \+ --abort \+ "${@}"+}++# continue the commit pick+gcpc() { a__git_cherry_pick_continue "${@}"; }+a__git_cherry_pick_continue() {+ git \+ cherry-pick \+ --continue \+ "${@}"+}++# clean untracked files+gcf() { a__git_clean_force "${@}"; }+a__git_clean_force() {+ git \+ clean \+ -d \+ --force \+ "${@}"+}++# redo the last commit with a different message+gcam() { a__git_commit_amend_message "${@}"; }+a__git_commit_amend_message() {+ git \+ commit \+ --amend \+ --message \+ "${@}"+}++# make a root commit+gcem() { a__git_commit_empty_message "${@}"; }+a__git_commit_empty_message() {+ git \+ commit \+ --allow-empty \+ --allow-empty-message \+ --message \+ "${@}"+}++# commit the index+gcm() { a__git_commit_message "${@}"; }+a__git_commit_message() {+ git \+ commit \+ --message \+ "${@}"+}++# configure the user email+gcue() { a__git_config_user_email "${@}"; }+a__git_config_user_email() {+ git \+ config \+ "user.email" \+ "${@}"+}++# configure the user name+gcun() { a__git_config_user_name "${@}"; }+a__git_config_user_name() {+ git \+ config \+ "user.name" \+ "${@}"+}++# differences from last or between commits+gd() { a__git_diff "${@}"; }+a__git_diff() {+ git \+ diff \+ "${@}"+}++# display what is indexed in cache+gdc() { a__git_diff_cached "${@}"; }+a__git_diff_cached() {+ git \+ diff \+ --cached \+ "${@}"+}++# indexed character-level differences+gdcw() { a__git_diff_cached_word "${@}"; }+a__git_diff_cached_word() {+ git \+ diff \+ --cached \+ --word-diff-regex "." \+ "${@}"+}++# differences via external tool+gdt() { a__git_diff_tool "${@}"; }+a__git_diff_tool() {+ git \+ difftool \+ --dir-diff \+ "${@}"+}++# character-level differences+gdw() { a__git_diff_word "${@}"; }+a__git_diff_word() {+ git \+ diff \+ --word-diff-regex "." \+ "${@}"+}++# fetch from the remote repository+gf() { a__git_fetch "${@}"; }+a__git_fetch() {+ rwx_gpg_agent_update &&+ git \+ fetch \+ --tags \+ --verbose \+ "${@}"+}++# fetch from remote repository and prune local orphan branches+gfp() { a__git_fetch_prune "${@}"; }+a__git_fetch_prune() {+ a__git_fetch \+ --prune \+ "${@}"+}++# garbage collect all orphan commits+ggc() { a__git_garbage_collect "${@}"; }+a__git_garbage_collect() {+ git \+ reflog \+ expire \+ --all \+ --expire "all" &&+ git \+ gc \+ --aggressive \+ --prune="now"+}++# initialize a new repository+gi() { a__git_init "${@}"; }+a__git_init() {+ git \+ init \+ "${@}"+}++# initialize a new bare repository+gib() { a__git_init_bare "${@}"; }+a__git_init_bare() {+ git \+ init \+ --bare \+ "${@}"+}++# log history+gl() { a__git_log "${@}"; }+a__git_log() {+ git \+ log \+ --abbrev=8 \+ --abbrev-commit \+ --format="${RWX_GIT_LOG_FORMAT}" \+ --graph \+ "${@}"+}++# log all history+gla() { a__git_log_all "${@}"; }+a__git_log_all() {+ a__git_log \+ --all \+ "${@}"+}++# log all history with patches+glap() { a__git_log_all_patch "${@}"; }+a__git_log_all_patch() {+ a__git_log \+ --all \+ --patch \+ "${@}"+}++# log history with patches+glp() { a__git_log_patch "${@}"; }+a__git_log_patch() {+ a__git_log \+ --patch \+ "${@}"+}++# fast-forward merge to remote branch+gm() { a__git_merge "${@}"; }+a__git_merge() {+ git \+ merge \+ --ff-only \+ "${@}"+}++# abort the current merge commit+gma() { a__git_merge_abort "${@}"; }+a__git_merge_abort() {+ git \+ merge \+ --abort \+ "${@}"+}++# do a merge commit+gmc() { a__git_merge_commit "${@}"; }+a__git_merge_commit() {+ git \+ merge \+ --no-ff \+ --message \+ "${@}"+}++# squash a branch and index its modifications+gms() { a__git_merge_squash "${@}"; }+a__git_merge_squash() {+ git \+ merge \+ --squash \+ "${@}"+}++# merge via external tool+gmt() { a__git_merge_tool "${@}"; }+a__git_merge_tool() {+ git \+ mergetool \+ "${@}"+}++# push to the remote repository+gp() { a__git_push "${@}"; }+a__git_push() {+ rwx_gpg_agent_update &&+ git \+ push \+ --tags \+ --verbose \+ "${@}"+}++# delete from the remote repository+gpd() { a__git_push_delete "${@}"; }+a__git_push_delete() {+ git \+ push \+ --delete \+ "${@}"+}++# force the push to the remote repository+gpf() { a__git_push_force "${@}"; }+a__git_push_force() {+ a__git_push \+ --force \+ "${@}"+}++# rebase current branch onto another+grb() { a__git_re_base "${@}"; }+a__git_re_base() {+ git \+ rebase \+ "${@}"+}++# abort current rebase+grba() { a__git_re_base_abort "${@}"; }+a__git_re_base_abort() {+ git \+ rebase \+ --abort \+ "${@}"+}++# continue current rebase+grbc() { a__git_re_base_continue "${@}"; }+a__git_re_base_continue() {+ git \+ rebase \+ --continue \+ "${@}"+}++# force rebase without fast-forward+grbf() { a__git_re_base_force "${@}"; }+a__git_re_base_force() {+ git \+ rebase \+ --force-rebase \+ "${@}"+}++# rebase interactively+grbi() { a__git_re_base_interactive "${@}"; }+a__git_re_base_interactive() {+ git \+ rebase \+ --interactive \+ "${@}"+}++# add a new remote repository+grma() { a__git_re_mote_add "${@}"; }+a__git_re_mote_add() {+ git \+ remote \+ add \+ "${@}"+}++# list remote repositories+grml() { a__git_re_mote_list "${@}"; }+a__git_re_mote_list() {+ git \+ remote \+ --verbose \+ "${@}"+}++# set the location of a remote repository+grmsu() { a__git_re_mote_set_upstream "${@}"; }+a__git_re_mote_set_upstream() {+ git \+ remote \+ set-url \+ "${@}"+}++# show connection to a remote repository+grms() { a__git_re_mote_show "${@}"; }+a__git_re_mote_show() {+ git \+ remote \+ show \+ "${@}"+}++# remove and add removal to index+grm() { a__git_re_move "${@}"; }+a__git_re_move() {+ git \+ rm \+ "${@}"+}++# remove file(s) from index or move current branch pointer+grs() { a__git_re_set "${@}"; }+a__git_re_set() {+ git \+ reset \+ "${@}"+}++# wipe modifications or reset current branch to another commit+grsh() { a__git_re_set_hard "${@}"; }+a__git_re_set_hard() {+ git \+ reset \+ --hard \+ "${@}"+}++# show a commit+gsc() { a__git_show_commit "${@}"; }+a__git_show_commit() {+ git \+ show \+ "${@}"+}++# current state of repository+gs() { a__git_status "${@}"; }+a__git_status() {+ git \+ status \+ --untracked-files="all" \+ "${@}"+}++# tag a commit+gt() { a__git_tag "${@}"; }+a__git_tag() {+ git \+ tag \+ "${@}"+}++# delete a tag+gtd() { a__git_tag_delete "${@}"; }+a__git_tag_delete() {+ git \+ tag \+ --delete \+ "${@}"+}++# update head ref+gurh() { a__git_update_ref_head "${@}"; }+a__git_update_ref_head() {+ if [ -n "${2}" ]; then+ git \+ update-ref \+ "refs/heads/${1}" \+ "${2}"+ fi+}
ADDED
sh/alias/gpg.sh
ADDED
sh/alias/gpg.sh
@@ -0,0 +1,12 @@+# turn gpg agent off+gak() { a__gpg_agent_kill "${@}"; }+a__gpg_agent_kill() {+ gpgconf \+ --kill "gpg-agent"+}++# bind gpg agent to current tty+gau() { a__gpg_agent_update "${@}"; }+a__gpg_agent_update() {+ rwx_gpg_agent_update+}
ADDED
sh/alias/grep.sh
ADDED
sh/alias/grep.sh
@@ -0,0 +1,9 @@+# grep from current directory with regex+g() { a__grep "${@}"; }+a__grep() {+ grep \+ --directories "recurse" \+ --line-number \+ --regexp \+ "${@}"+}
ADDED
sh/alias/kill.sh
ADDED
sh/alias/kill.sh
@@ -0,0 +1,14 @@+# kill a process by id+k() { a__kill "${@}"; }+a__kill() {+ kill \+ "${@}"+}++# force kill a process by id+kf() { a__kill_force "${@}"; }+a__kill_force() {+ kill \+ -9 \+ "${@}"+}
ADDED
sh/alias/killall.sh
ADDED
sh/alias/killall.sh
@@ -0,0 +1,14 @@+# kill all instances of a process by name+ka() { a__kill_all "${@}"; }+a__kill_all() {+ killall \+ "${@}"+}++# force kill all instances of a process by name+kaf() { a__kill_all_force "${@}"; }+a__kill_all_force() {+ killall \+ -9 \+ "${@}"+}
ADDED
sh/alias/ls.sh
ADDED
sh/alias/ls.sh
@@ -0,0 +1,32 @@+export LS_COLORS="\+di=0;94\+"++# list current directory’s entries+l() { a__ls "${@}"; }+a__ls() {+ ls \+ --all \+ --color \+ -l \+ -p \+ --time-style "+" \+ "${@}"+}++# list timestamps+lt() { a__ls_time "${@}"; }+a__ls_time() {+ a__ls \+ --time-style "+%Y%m%d-%H%M%S%-:::z" \+ "${@}"+}++# list timestamps recent last+ltr() { a__ls_time_reverse "${@}"; }+a__ls_time_reverse() {+ a__ls_time \+ --reverse \+ -t \+ "${@}"+}
ADDED
sh/alias/lsblk.sh
ADDED
sh/alias/lsblk.sh
@@ -0,0 +1,31 @@+# list block devices+lb() { a__list_block "${@}"; }+a__list_block() {+ a__list_block_output \+ "SIZE" \+ "TYPE" \+ "FSTYPE" \+ "LABEL" \+ "MOUNTPOINTS" \+ "${@}"+}++# base arguments+lbne() { a__list_block_no_empty "${@}"; }+a__list_block_no_empty() {+ lsblk \+ --noempty \+ "${@}"+}++# output arguments+lbo() { a__list_block_output "${@}"; }+a__list_block_output() {+ local argument+ local arguments="NAME"+ for argument in "${@}"; do+ arguments="${arguments},${argument}"+ done+ a__list_block_no_empty \+ --output "${arguments}"+}
ADDED
sh/alias/mkdir.sh
ADDED
sh/alias/mkdir.sh
@@ -0,0 +1,14 @@+# make a directory+md() { a__make_directory "${@}"; }+a__make_directory() {+ mkdir \+ "${@}"+}++# make a directory after making its parents+mdp() { a__make_directory_parents "${@}"; }+a__make_directory_parents() {+ mkdir \+ --parents \+ "${@}"+}
ADDED
sh/alias/mount.sh
ADDED
sh/alias/mount.sh
@@ -0,0 +1,5 @@+m() { a__mount "${@}"; }+a__mount() {+ mount \+ "${@}"+}
ADDED
sh/alias/mv.sh
ADDED
sh/alias/mv.sh
@@ -0,0 +1,7 @@+# move interactively+mvi() { a__mo_ve_interactive "${@}"; }+a__mo_ve_interactive() {+ mv \+ --interactive \+ "${@}"+}
ADDED
sh/alias/nano.sh
ADDED
sh/alias/nano.sh
@@ -0,0 +1,5 @@+nn() { a__na_no "${@}"; }+a__na_no() {+ nano \+ "${@}"+}
ADDED
sh/alias/newsboat.sh
ADDED
sh/alias/newsboat.sh
@@ -0,0 +1,5 @@+nb() { a__news_boat "${@}"; }+a__news_boat() {+ newsboat \+ "${@}"+}
ADDED
sh/alias/overlay.sh
ADDED
sh/alias/overlay.sh
@@ -0,0 +1,140 @@+obm() { a__overlay_bind_mount "${@}"; }+a__overlay_bind_mount() {+ local directory+ for directory in "dev" "dev/pts" "proc" "sys"; do+ if ! mount --bind "/${directory}" "overlay/mount/${directory}"; then+ rwx_log_error "Unable to bind mount directory: ${directory}"+ return 1+ fi+ done+}++obu() { a__overlay_bind_unmount "${@}"; }+a__overlay_bind_unmount() {+ local directory+ for directory in "sys" "proc" "dev/pts" "dev"; do+ if ! umount --lazy "overlay/mount/${directory}"; then+ rwx_log_error "Unable to bind unmount directory: ${directory}"+ return 1+ fi+ done+}++ocr() { a__overlay_command_root "${@}"; }+a__overlay_command_root() {+ chroot \+ "overlay/mount" "${@}"+}++ocu() { a__overlay_command_user "${@}"; }+a__overlay_command_user() {+ chroot \+ --userspec "1000:1000" \+ "overlay/mount" "${@}"+}++omm() { a__overlay_mirror_mount "${@}"; }+a__overlay_mirror_mount() {+ mount --make-rslave --rbind "/deb" "overlay/mount/deb"+}++omu() { a__overlay_mirror_unmount "${@}"; }+a__overlay_mirror_unmount() {+ umount --recursive "overlay/mount/deb"+}++orm() { a__overlay_root_mount "${@}"; }+a__overlay_root_mount() {+ local root="${1}"+ if [ -z "${root}" ]; then+ rwx_log_error "No root target directory"+ return 1+ fi+ root="$(realpath "${root}")"+ if ! mkdir "overlay"; then+ rwx_log_error "Unable to make overlay directory"+ return 2+ fi+ (+ if ! cd "overlay"; then+ rwx_log_error "Unable to move into overlay directory"+ return 3+ fi+ local directory+ for directory in "lower" "upper" "work" "mount"; do+ if ! mkdir --parents "${directory}"; then+ rwx_log_error "Unable to make directory: ${directory}"+ return 4+ fi+ done+ local file="${root}/filesystem.squashfs"+ if ! mount "${file}" "lower"; then+ rwx_log_error "Unable to lower mount: ${file}"+ return 5+ fi+ if ! mount \+ -o "lowerdir=lower,upperdir=upper,workdir=work" \+ -t "overlay" \+ "overlay" "mount"; then+ rwx_log_error "Unable to overlay mount"+ return 6+ fi+ )+}++ors() { a__overlay_root_squash "${@}"; }+a__overlay_root_squash() {+ local directory="${1}"+ local file+ local level="${2}"+ if [ -n "${directory}" ]; then+ if mkdir "${directory}"; then+ [ -n "${level}" ] || level="18"+ for file in "vmlinuz" "initrd.img"; do+ cp "overlay/mount/${file}" "${directory}"+ done+ mksquashfs \+ "overlay/mount" "${directory}/filesystem.squashfs" \+ -noappend \+ -comp "zstd" -Xcompression-level "${level}"+ chown --recursive 1000:1000 "${directory}"+ fi+ fi+}++oru() { a__overlay_root_unmount "${@}"; }+a__overlay_root_unmount() {+ (+ if ! cd "overlay"; then+ rwx_log_error "Unable to move into overlay directory"+ return 1+ fi+ if ! umount "mount"; then+ rwx_log_error "Unable to unmount mount directory"+ return 2+ fi+ if ! rmdir "mount"; then+ rwx_log_error "Unable to remove mount directory"+ return 3+ fi+ local directory+ for directory in "upper" "work"; do+ if ! rm --force --recursive "${directory}"; then+ rwx_log_error "Unable to remove directory: ${directory}"+ return 4+ fi+ done+ if ! umount "lower"; then+ rwx_log_error "Unable to unmount lower directory"+ return 5+ fi+ if ! rmdir "lower"; then+ rwx_log_error "Unable to remove lower directory"+ return 6+ fi+ )+ if ! rmdir "overlay"; then+ rwx_log_error "Unable to remove overlay directory"+ return 7+ fi+}
ADDED
sh/alias/pass.sh
ADDED
sh/alias/pass.sh
@@ -0,0 +1,14 @@+# display pass entry’s content+p() { a__pass "${@}"; }+a__pass() {+ pass \+ "${@}"+}++# copy passphrase into clipboard+pc() { a__pass_clip "${@}"; }+a__pass_clip() {+ pass \+ --clip \+ "${@}"+}
ADDED
sh/alias/pgrep.sh
ADDED
sh/alias/pgrep.sh
@@ -0,0 +1,7 @@+# look for a string in processes names+pg() { a__proc_grep "${@}"; }+a__proc_grep() {+ pgrep \+ --list-full \+ "${@}"+}
ADDED
sh/alias/pwgen.sh
ADDED
sh/alias/pwgen.sh
@@ -0,0 +1,17 @@+# generate passwords+pwg() { a__pass_word_gen "${@}"; }+a__pass_word_gen() {+ pwgen \+ -1 \+ --num-passwords 1048576 \+ --secure \+ "${@}"+}++# generate passwords with symbols+pwgs() { a__pass_word_gen_symbols "${@}"; }+a__pass_word_gen_symbols() {+ a__pass_word_gen \+ --symbols \+ "${@}"+}
ADDED
sh/alias/rm.sh
ADDED
sh/alias/rm.sh
@@ -0,0 +1,7 @@+# remove interactively+rmi() { a__re_move_interactive "${@}"; }+a__re_move_interactive() {+ rm \+ --interactive \+ "${@}"+}
ADDED
sh/alias/rsync.sh
ADDED
sh/alias/rsync.sh
@@ -0,0 +1,27 @@+# synchronize+rs() { a__r_sync "${@}"; }+a__r_sync() {+ rsync \+ --archive \+ --no-inc-recursive \+ --partial \+ --progress \+ --verbose \+ "${@}"+}++# synchronize and delete after+rsda() { a__r_sync_delete_after "${@}"; }+a__r_sync_delete_after() {+ a__r_sync \+ --delete-after \+ "${@}"+}++# synchronize and delete before+rsdb() { a__r_sync_delete_before "${@}"; }+a__r_sync_delete_before() {+ a__r_sync \+ --delete-before \+ "${@}"+}
ADDED
sh/alias/shell.sh
ADDED
sh/alias/shell.sh
@@ -0,0 +1,29 @@+# shorten alias+a() {+ alias \+ "${@}"+}++# swap directory (current ↔ previous)+sd() {+ cd \+ - ||+ return+}++# exit terminal+x() {+ exit \+ "${@}"+}++[ "${RWX_SHELL}" = "bash" ] || return++# shellcheck disable=SC3033+..() {+ cd ..+}+# shellcheck disable=SC3033+...() {+ cd ../..+}
ADDED
sh/alias/tar.sh
ADDED
sh/alias/tar.sh
@@ -0,0 +1,31 @@+tc() { a__tar_create "${@}"; }+a__tar_create() {+ a__tar_verbose \+ --create \+ --auto-compress \+ --file \+ "${@}"+}++tl() { a__tar_list "${@}"; }+a__tar_list() {+ a__tar_verbose \+ --list \+ --file \+ "${@}"+}++tv() { a__tar_verbose "${@}"; }+a__tar_verbose() {+ tar \+ --verbose \+ "${@}"+}++tx() { a__tar_xtract "${@}"; }+a__tar_xtract() {+ a__tar_verbose \+ --extract \+ --file \+ "${@}"+}
ADDED
sh/alias/tmux.sh
ADDED
sh/alias/tmux.sh
@@ -0,0 +1,5 @@+tm() { a__t_mux "${@}"; }+a__t_mux() {+ tmux \+ "${@}"+}
ADDED
sh/alias/tree.sh
ADDED
sh/alias/tree.sh
@@ -0,0 +1,12 @@+t() { a__tree "${@}"; }+a__tree() {+ tree \+ "${@}"+}++ta() { a__tree_all "${@}"; }+a__tree_all() {+ tree \+ -a \+ "${@}"+}
ADDED
sh/cryptsetup.sh
ADDED
sh/cryptsetup.sh
@@ -0,0 +1,6 @@+_rwx_cmd_cs() { rwx_crypt_setup "${@}"; }++rwx_crypt_setup() {+ local action="${1}"+ echo "cs: ${action}"+}
ADDED
sh/debian.sh
ADDED
sh/debian.sh
@@ -0,0 +1,76 @@+RWX_DEBIAN_CODENAME="$(+ grep "VERSION_CODENAME" "/etc/os-release" |+ cut --delimiter "=" --fields "2"+)"++rwx_apt_clean() {+ apt-get \+ clean+}++rwx_apt_conf_write() {+ printf "\+Acquire::AllowInsecureRepositories False;+Acquire::AllowWeakRepositories False;+Acquire::AllowDowngradeToInsecureRepositories False;+Acquire::Check-Valid-Until True;+APT::Install-Recommends False;+APT::Install-Suggests False;+APT::Get::Show-Versions True;+Dir::Etc::SourceParts \"\";+Dpkg::Progress True;+" >"/etc/apt/apt.conf.d/apt.conf"+}++rwx_apt_install_backports() {+ rwx_apt_install_target "${RWX_DEBIAN_CODENAME}-backports" "${@}"+}++rwx_apt_install_release() {+ rwx_apt_install_target "${RWX_DEBIAN_CODENAME}" "${@}"+}++rwx_apt_install_target() {+ local target="${1}"+ shift+ local package+ for package in "${@}"; do+ rwx_log "" \+ "${package} ← ${target}"+ apt-get \+ install \+ --assume-yes \+ --target-release "${target}" \+ "${package}"+ rwx_apt_clean+ done+}++rwx_apt_sources_write() {+ printf "%s" "\+deb https://deb.debian.org/debian \+${RWX_DEBIAN_CODENAME} main non-free-firmware contrib non-free+deb https://deb.debian.org/debian \+${RWX_DEBIAN_CODENAME}-backports main non-free-firmware contrib non-free+deb https://deb.debian.org/debian \+${RWX_DEBIAN_CODENAME}-updates main non-free-firmware contrib non-free+deb https://deb.debian.org/debian-security \+${RWX_DEBIAN_CODENAME}-security main non-free-firmware contrib non-free+" >"/etc/apt/sources.list"+}++rwx_apt_update() {+ apt-get \+ update+}++rwx_apt_upgrade() {+ apt-get \+ upgrade \+ --assume-yes+ rwx_apt_clean+}++rwx_debian_frontend_disable() {+ export DEBIAN_FRONTEND="noninteractive"+}
ADDED
sh/ffmpeg.sh
ADDED
sh/ffmpeg.sh
@@ -0,0 +1,194 @@+# ╭────────┬─────────┬───────╮+# │ ffmpeg │ devices │ reset │+# ╰────────┴─────────┴───────╯++_rwx_cmd_rwx_ffmpeg_devices_reset() { rwx_ffmpeg_devices_reset "${@}"; }+rwx_ffmpeg_devices_reset() {+ local module="uvcvideo"+ modprobe --remove "${module}" &&+ modprobe "${module}"+}++# ╭────────┬────────┬─────────╮+# │ ffmpeg │ device │ formats │+# ╰────────┴────────┴─────────╯++rwx_ffmpeg_device_formats() {+ local device="${1}"+ [ -n "${device}" ] || device="/dev/video0"+ ffmpeg \+ -f "v4l2" \+ -list_formats "all" \+ -i "${device}"+}++# ╭────────┬───────╮+# │ ffmpeg │ input │+# ╰────────┴───────╯++rwx_ffmpeg_input_blue_yeti() {+ local device="alsa_input.\+usb-Generic_Blue_Microphones_2051BAB04XY8-00.analog-stereo"+ set -- \+ -f "pulse" \+ -i "${device}" \+ -ac "2" \+ -ar "48000"+ local argument+ for argument in "${@}"; do echo "${argument}"; done+}++rwx_ffmpeg_input_dell_precision() {+ local device="alsa_input.\+pci-0000_00_1f.3.analog-stereo"+ set -- \+ -f "pulse" \+ -i "${device}" \+ -ac "2" \+ -ar "48000"+ local argument+ for argument in "${@}"; do echo "${argument}"; done+}++rwx_ffmpeg_input_file() {+ local file="${1}"+ local from="${2}"+ local to="${3}"+ [ -n "${file}" ] || return+ set -- \+ -i "${file}"+ if [ -n "${to}" ]; then+ set -- "${@}" \+ -ss "${from}" \+ -to "${to}"+ fi+ local argument+ for argument in "${@}"; do echo "${argument}"; done+}++rwx_ffmpeg_input_hdmi() {+ local device="${1}"+ [ -n "${device}" ] || device="/dev/video0"+ set -- \+ -f "v4l2" \+ -video_size "1920x1080" \+ -framerate "60" \+ -input_format "yuyv422" \+ -i "${device}"+ local argument+ for argument in "${@}"; do echo "${argument}"; done+}++# ╭────────┬────────╮+# │ ffmpeg │ output │+# ╰────────┴────────╯++rwx_ffmpeg_output_audio_fast() {+ set -- \+ -codec:a "flac" \+ -compression_level "0"+ local argument+ for argument in "${@}"; do echo "${argument}"; done+}++rwx_ffmpeg_output_audio_slow() {+ set -- \+ -codec:a "libopus" \+ -b:a "128k"+ local argument+ for argument in "${@}"; do echo "${argument}"; done+}++rwx_ffmpeg_output_file() {+ local file="${1}"+ [ -n "${file}" ] || return+ set -- \+ -y "${file}"+ local argument+ for argument in "${@}"; do echo "${argument}"; done+}++rwx_ffmpeg_output_video_fast() {+ set -- \+ -codec:v "libx264" \+ -preset "ultrafast" \+ -crf "0"+ local argument+ for argument in "${@}"; do echo "${argument}"; done+}++rwx_ffmpeg_output_video_slow() {+ local crf="${1}"+ local codec="${2}"+ [ -n "${codec}" ] || codec="libx264"+ if [ -z "${crm}" ]; then+ case "${codec}" in+ "libx264") crf="23" ;;+ "libx265") crf="28" ;;+ *) ;;+ esac+ fi+ set -- \+ -codec:v "${codec}" \+ -preset "veryslow" \+ -crf "${crf}" \+ -movflags "+faststart" \+ -pix_fmt "yuv420p"+ local argument+ for argument in "${@}"; do echo "${argument}"; done+}++# ╭────────┬────────╮+# │ ffmpeg │ record │+# ╰────────┴────────╯++rwx_ffmpeg_record_hdmi_precision() {+ local file="${1}"+ [ -n "${file}" ] || return+ # LATER alternative+ # shellcheck disable=SC2046,SC2312+ set -- \+ $(rwx_ffmpeg_input_hdmi) \+ $(rwx_ffmpeg_input_dell_precision) \+ $(rwx_ffmpeg_output_video_fast) \+ $(rwx_ffmpeg_output_audio_fast) \+ $(rwx_ffmpeg_output_file "${file}")+ echo "${@}"+ ffmpeg "${@}"+}++rwx_ffmpeg_record_hdmi_yeti() {+ local file="${1}"+ [ -n "${file}" ] || return+ # LATER alternative+ # shellcheck disable=SC2046,SC2312+ set -- \+ $(rwx_ffmpeg_input_hdmi) \+ $(rwx_ffmpeg_input_blue_yeti) \+ $(rwx_ffmpeg_output_video_fast) \+ $(rwx_ffmpeg_output_audio_fast) \+ $(rwx_ffmpeg_output_file "${file}")+ echo "${@}"+ ffmpeg "${@}"+}++# ╭────────┬────────╮+# │ ffmpeg │ reduce │+# ╰────────┴────────╯++rwx_ffmpeg_reduce() {+ local input="${1}"+ local output="${2}"+ local from="${3}"+ local to="${4}"+ [ -n "${output}" ] || return+ # LATER alternative+ # shellcheck disable=SC2046,SC2312+ set -- \+ $(rwx_ffmpeg_input_file "${input}" "${from}" "${to}") \+ $(rwx_ffmpeg_output_video_slow) \+ $(rwx_ffmpeg_output_audio_slow) \+ $(rwx_ffmpeg_output_file "${output}")+ echo "${@}"+ ffmpeg "${@}"+}
ADDED
sh/fs.sh
ADDED
sh/fs.sh
@@ -0,0 +1,121 @@+rwx_fs_make_btrfs() {+ local device="${1}"+ local label="${2}"+ local uuid="${3}"+ if [ -b "${device}" ]; then+ set -- \+ --force \+ --checksum "sha256"+ if [ -n "${label}" ]; then+ set -- "${@}" \+ --label "${label}"+ fi+ if [ -n "${uuid}" ]; then+ set -- "${@}" \+ --uuid "${uuid}"+ fi+ mkfs.btrfs "${@}" "${device}"+ fi+}++rwx_fs_make_btrfs_swap() {+ local path="${1}"+ local size="${2}"+ local uuid="${3}"+ if [ -n "${path}" ]; then+ set -- filesystem mkswapfile+ if [ -n "${size}" ]; then+ set -- "${@}" \+ --size "${size}"+ fi+ if [ -n "${uuid}" ]; then+ set -- "${@}" \+ --uuid "${uuid}"+ fi+ btrfs "${@}" "${path}"+ fi+}++rwx_fs_make_fat() {+ local device="${1}"+ local name="${2}"+ local volid="${3}"+ if [ -b "${device}" ]; then+ set -- \+ -F 32 \+ -S 4096+ if [ -n "${name}" ]; then+ set -- "${@}" \+ -n "${name}"+ fi+ if [ -n "${volid}" ]; then+ set -- "${@}" \+ -i "${volid}"+ fi+ mkfs.fat "${@}" "${device}"+ fi+}++rwx_fs_raid_create() {+ if [ -n "${4}" ]; then+ local name="${1}"+ local uuid="${2}"+ shift 2+ mdadm \+ --create "/dev/md/${name}" \+ --level 0 \+ --metadata 1 \+ --name "md:${name}" \+ --raid-devices ${#} \+ --uuid "${uuid}" \+ "${@}"+ fi+}++rwx_fs_wipe() {+ local device="${1}"+ local buffer="${2}"+ local count="${3}"+ if [ -b "${device}" ]; then+ set -- \+ status="progress" \+ if="/dev/zero" \+ of="${device}"+ if [ -n "${buffer}" ]; then+ set -- "${@}" \+ bs="${buffer}"+ fi+ if [ -n "${count}" ]; then+ set -- "${@}" \+ count="${count}"+ fi+ dd "${@}"+ fi+}++rwx_fs_luks_format() {+ local passphrase="${1}"+ local device="${2}"+ local label="${3}"+ local uuid="${4}"+ if [ -b "${device}" ]; then+ set -- \+ --batch-mode \+ --cipher "aes-xts-plain64" \+ --hash "sha512" \+ --iter-time 4096 \+ --key-size 512 \+ --pbkdf "argon2id" \+ --type "luks2" \+ --use-random \+ --verbose+ if [ -n "${label}" ]; then+ set -- "${@}" --label "${label}"+ fi+ if [ -n "${uuid}" ]; then+ set -- "${@}" --uuid "${uuid}"+ fi+ echo "${passphrase}" |+ cryptsetup "${@}" luksFormat "${device}"+ fi+}
ADDED
sh/gnome.sh
ADDED
sh/gnome.sh
@@ -0,0 +1,71 @@+# ╭───────┬────────────╮+# │ gnome │ background │+# ╰───────┴────────────╯++rwx_gnome_background_black() {+ rwx_gnome_set_background "color-shading-type" "solid"+ rwx_gnome_set_background "primary-color" "#000000"+}++rwx_gnome_background_white() {+ rwx_gnome_set_background "color-shading-type" "solid"+ rwx_gnome_set_background "primary-color" "#ffffff"+}++rwx_gnome_background_win3() {+ rwx_gnome_set_background "color-shading-type" "vertical"+ rwx_gnome_set_background "primary-color" "#000000"+ rwx_gnome_set_background "secondary-color" "#0000ff"+}++# ╭───────┬───────╮+# │ gnome │ proxy │+# ╰───────┴───────╯++rwx_gnome_proxy() {+ local value+ case "${1}" in+ "on") value="manual" ;;+ *) value="none" ;;+ esac+ gsettings set "org.gnome.system.proxy" "mode" "${value}"+}++# ╭───────┬─────╮+# │ gnome │ set │+# ╰───────┴─────╯++rwx_gnome_set() {+ local group="${1}"+ local key="${2}"+ local value="${3}"+ [ -n "${value}" ] || return+ gsettings set "${group}" "${key}" "${value}"+}++rwx_gnome_set_background() {+ local key="${1}"+ local value="${2}"+ [ -n "${value}" ] || return+ rwx_gnome_set "org.gnome.desktop.background" "${key}" "${value}"+}++# ╭───────┬────────────╮+# │ gnome │ workspaces │+# ╰───────┴────────────╯++rwx_gnome_workspaces_primary() {+ local bool+ local group="org.gnome.mutter"+ local name="workspaces-only-on-primary"+ local var="${group}/${name}"+ # get+ bool="$(gsettings get "${group}" "${name}")"+ rwx_log_debug "${var}: ${bool}"+ # not+ bool="$(rwx_not "${bool}")"+ rwx_log_debug "bool: ${bool}"+ # set+ gsettings set "${group}" "${name}" "${bool}"+ rwx_log_info "${var}: ${bool}"+}
ADDED
sh/gpg.sh
ADDED
sh/gpg.sh
@@ -0,0 +1,19 @@+# bind gpg agent to current tty+rwx_gpg_agent_update() {+ gpg-connect-agent \+ updatestartuptty \+ /bye+}++rwx_gpg_ssh_auth_sock() {+ local user_id+ user_id=$(id --user)+ if [ "${user_id}" -ne 0 ]; then+ if [ -f "${HOME}/.gnupg/gpg-agent.conf" ]; then+ SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"+ export SSH_AUTH_SOCK+ fi+ fi+}++rwx_gpg_ssh_auth_sock
ADDED
sh/lint/gitlint.sh
ADDED
sh/lint/gitlint.sh
@@ -0,0 +1,6 @@+rwx_gitlint() {+ local path="${1}"+ gitlint \+ --target "${path}" \+ "lint"+}
ADDED
sh/lint/lint.sh
ADDED
sh/lint/lint.sh
@@ -0,0 +1,78 @@+# lint code+rwx_lint() {+ local path="${1}"+ [ -n "${path}" ] || return 1+ rwx_lint_clean "${path}"+ rwx_lint_tasks "${path}"+ set \+ "python" \+ "shell"+ local code+ for code in "${@}"; do+ rwx_log "" "${code}"+ "rwx_lint_${code}" "${path}"+ done+ rwx_lint_clean "${path}"+}++# clean+rwx_lint_clean() {+ local path="${1}"+ [ -n "${path}" ] || return 1+ rwx_log "" "clean" ""+ py3clean "${path}"+ set \+ "mypy" \+ "ruff"+ local tool+ for tool in "${@}"; do+ rwx_remove "${path}/.${tool}_cache"+ done+}++# lint python code+rwx_lint_python() {+ local path="${1}"+ local action+ set \+ "pylint" \+ "pydoclint" \+ "mypy" \+ "ruff"+ for action in "${@}"; do+ rwx_log "" "${action}"+ "rwx_${action}" "${path}"+ done+}++# lint shell code+rwx_lint_shell() {+ local path="${1}"+ local action+ set \+ "shellcheck" \+ "shfmt"+ for action in "${@}"; do+ rwx_log "" "${action}"+ "rwx_${action}" "${path}"+ done+}++# lint code tasks+rwx_lint_tasks() {+ local path="${1}"+ local type+ set \+ "LATER" \+ "TODO" \+ "FIXME"+ for type in "${@}"; do+ rwx_log "" "${type}"+ grep \+ --after "1" \+ --directories "recurse" \+ --line-number \+ " ${type}" \+ "${path}"+ done+}
ADDED
sh/lint/mypy.sh
ADDED
sh/lint/mypy.sh
@@ -0,0 +1,4 @@+rwx_mypy() {+ local path="${1}"+ mypy "${path}"+}
ADDED
sh/lint/pydoclint.sh
ADDED
sh/lint/pydoclint.sh
@@ -0,0 +1,9 @@+rwx_pydoclint() {+ local path="${1}"+ pydoclint \+ --allow-init-docstring True \+ --quiet \+ --skip-checking-short-docstrings False \+ --style "sphinx" \+ "${path}"+}
ADDED
sh/lint/pylint.sh
ADDED
sh/lint/pylint.sh
@@ -0,0 +1,6 @@+rwx_pylint() {+ local path="${1}"+ pylint \+ --enable-all-extensions \+ "${path}/**/*.py"+}
ADDED
sh/lint/ruff.sh
ADDED
sh/lint/ruff.sh
@@ -0,0 +1,28 @@+rwx_ruff() {+ local path="${1}"+ local action+ set \+ "check" \+ "format"+ for action in "${@}"; do+ "rwx_ruff_${action}" "${path}"+ done+}++rwx_ruff_check() {+ local path="${1}"+ ruff check \+ --ignore "D203,D213" \+ --isolated \+ --select "ALL" \+ "${path}"+}++rwx_ruff_format() {+ local path="${1}"+ ruff format \+ --diff \+ --isolated \+ --line-length "80" \+ "${path}"+}
ADDED
sh/lint/shellcheck.sh
ADDED
sh/lint/shellcheck.sh
@@ -0,0 +1,34 @@+rwx_shellcheck() {+ local root="${1}"+ local file module modules path+ file="$(mktemp)"+ modules="$(rwx_find_shell "${root}")"+ rwx_ifs_set+ for module in ${modules}; do+ path="${root}/${module}"+ echo ". \"${path}\"" >>"${file}"+ done+ rwx_ifs_unset+ rwx_shellcheck_file "${file}"+ rwx_remove "${file}"+}++rwx_shellcheck_file() {+ local file="${1}"+ shellcheck \+ --check-sourced \+ --enable "all" \+ --exclude "3043" \+ --external-sources \+ --shell "dash" \+ "${file}"+}++rwx_shellcheck_write() {+ rwx_file_write ".shellcheckrc" "\+disable=3043+enable=all+external-sources=true+shell=sh+"+}
ADDED
sh/lint/shfmt.sh
ADDED
sh/lint/shfmt.sh
@@ -0,0 +1,4 @@+rwx_shfmt() {+ local path="${1}"+ shfmt --diff "${path}"+}
ADDED
sh/live.sh
ADDED
sh/live.sh
@@ -0,0 +1,6 @@+# remount read-only medium in read-write+rwx_live_medium_remount() {+ mount \+ -o "remount,rw" \+ "/usr/lib/live/mount/medium"+}
ADDED
sh/log/log.sh
ADDED
sh/log/log.sh
@@ -0,0 +1,68 @@+RWX_LOG_LEVEL_FATAL=0+RWX_LOG_LEVEL_ERROR=1+RWX_LOG_LEVEL_WARN=2+RWX_LOG_LEVEL_INFO=3+RWX_LOG_LEVEL_DEBUG=4+RWX_LOG_LEVEL_TRACE=5++RWX_LOG_LEVEL=${RWX_LOG_LEVEL_INFO}++rwx_log() { rwx_log_info "${@}"; }++rwx_log_debug() {+ if [ "${RWX_LOG_LEVEL}" -ge "${RWX_LOG_LEVEL_DEBUG}" ]; then+ _rwx_log "[DEBUG]" "${@}"+ fi+}++rwx_log_error() {+ local code="${1}"+ shift+ [ -n "${code}" ] || rwx_log_fatal 1 "No error code"+ if [ "${RWX_LOG_LEVEL}" -ge "${RWX_LOG_LEVEL_ERROR}" ]; then+ _rwx_log "[ERROR]" "${@}" >&2+ return "${code}"+ fi+}++rwx_log_fatal() {+ local code="${1}"+ shift+ [ -n "${code}" ] || rwx_log_fatal 1 "No error code"+ if [ "${RWX_LOG_LEVEL}" -ge "${RWX_LOG_LEVEL_FATAL}" ]; then+ _rwx_log "[FATAL]" "${@}" >&2+ exit "${code}"+ fi+}++rwx_log_info() {+ if [ "${RWX_LOG_LEVEL}" -ge "${RWX_LOG_LEVEL_INFO}" ]; then+ _rwx_log "" "${@}"+ fi+}++rwx_log_trace() {+ if [ "${RWX_LOG_LEVEL}" -ge "${RWX_LOG_LEVEL_TRACE}" ]; then+ _rwx_log "[TRACE]" "${@}"+ fi+}++rwx_log_warn() {+ if [ "${RWX_LOG_LEVEL}" -ge "${RWX_LOG_LEVEL_WARN}" ]; then+ _rwx_log "[ WARN]" "${@}"+ fi+}++_rwx_log() {+ local prefix="${1}"+ shift+ [ ${#} -gt 0 ] || set -- ""+ local line+ for line in "${@}"; do+ if [ -n "${prefix}" ]; then+ __rwx_log "${prefix} ${line}"+ else+ __rwx_log "${line}"+ fi+ done+}
ADDED
sh/log/step.sh
ADDED
sh/log/step.sh
@@ -0,0 +1,25 @@+# ╭───────────────╮+# │ __ = internal │+# ╰───────────────╯++# __RWX_BAR_TOP+# __RWX_BAR_MIDDLE+# __RWX_BAR_BOTTOM++# __RWX_STEP_LEVEL+# __RWX_STEP_level_INDEX+# __RWX_STEP_level_LABEL++# ╭─────────────╮+# │ _ = private │+# ╰─────────────╯++_RWX_BOX_DOWN_AND_HORIZONTAL="┬"+_RWX_BOX_DOWN_AND_LEFT="╮"+_RWX_BOX_DOWN_AND_RIGHT="╭"+_RWX_BOX_HORIZONTAL="─"+_RWX_BOX_LEFT="╴"+_RWX_BOX_UP_AND_HORIZONTAL="┴"+_RWX_BOX_UP_AND_LEFT="╯"+_RWX_BOX_UP_AND_RIGHT="╰"+_RWX_BOX_VERTICAL="│"
ADDED
sh/main.sh
ADDED
sh/main.sh
@@ -0,0 +1,144 @@+#! /usr/bin/env sh++# ╭──────┬───────────╮+# │ main │ constants │+# ╰──────┴───────────╯++RWX_MAIN_NAME="main.sh"+RWX_SELF_NAME="rwx"++RWX_SELF_COMMAND="_${RWX_SELF_NAME}_cmd_"++# ╭──────┬───────────╮+# │ main │ variables │+# ╰──────┴───────────╯++RWX_COMMAND_ARGUMENT="${0}"+RWX_SHELL="$(cat "/proc/${$}/comm")"++RWX_COMMAND_NAME="$(basename "${RWX_COMMAND_ARGUMENT}" |+ sed "s|^-||")"+case "${RWX_COMMAND_NAME}" in+"bash" | "dash" | "sh") unset RWX_COMMAND_NAME ;;+*) ;;+esac+RWX_ROOT_SYSTEM="/usr/local/lib/${RWX_SELF_NAME}"+RWX_SELF_USER="${HOME}/${RWX_SELF_NAME}"++RWX_MAIN_PATH="${RWX_ROOT_SYSTEM}/${RWX_MAIN_NAME}"++# ╭──────┬───────╮+# │ main │ shell │+# ╰──────┴───────╯++# test if active shell is in interactive mode+rwx_shell_interactive() {+ case "${-}" in+ *i*) ;;+ *) return 1 ;;+ esac+}++# ╭──────┬─────╮+# │ main │ log │+# ╰──────┴─────╯++__rwx_log() {+ if rwx_shell_interactive; then+ [ ${#} -gt 0 ] || set -- ""+ local line+ for line in "${@}"; do+ echo "${line}"+ done+ fi+}++# ╭──────┬──────╮+# │ main │ find │+# ╰──────┴──────╯++# find directory’s files by extension+rwx_find_extension() {+ local extension="${1}"+ local root="${2}"+ local file="${3}"+ set -- \+ "${root}" \+ -name "*.${extension}" \+ -type "f"+ [ -n "${file}" ] &&+ set -- "${@}" \+ -not \+ -name "${file}"+ find "${@}" \+ -printf "%P\n" |+ sort+}++# find directory’s sh files+rwx_find_shell() {+ rwx_find_extension "sh" "${@}"+}++# ╭──────┬─────╮+# │ main │ ifs │+# ╰──────┴─────╯++rwx_ifs_set() {+ _RWX_IFS="${IFS}"+ IFS="+"+}++rwx_ifs_unset() {+ IFS="${_RWX_IFS}"+ unset RWX_IFS+}++# ╭──────┬────────╮+# │ main │ source │+# ╰──────┴────────╯++rwx_source() {+ local path="${1}"+ [ -d "${path}" ] ||+ return 1+ local count module+ count=0+ __rwx_log "" \+ ". ${path}"+ rwx_ifs_set+ for module in $(rwx_find_shell "${path}" "${RWX_MAIN_NAME}"); do+ count=$((count + 1))+ __rwx_log "$(printf "%02d" "${count}") ${module%.sh}"+ module="${path}/${module}"+ # shellcheck disable=SC1090+ . "${module}"+ done+ rwx_ifs_unset+}++# ╭──────╮+# │ main │+# ╰──────╯++# run initial steps+rwx_main() {+ # system root+ if ! rwx_source "${RWX_ROOT_SYSTEM}"; then+ __rwx_log "Not a directory: ${RWX_ROOT_SYSTEM}"+ return 1+ fi+ # user root+ rwx_source "${RWX_SELF_USER}"+ # context / command+ if [ -n "${RWX_COMMAND_NAME}" ]; then+ "${RWX_SELF_COMMAND}${RWX_COMMAND_NAME}" "${@}"+ # context / shell+ else+ rwx_self_init+ fi+}++# run main function+rwx_main "${@}"
ADDED
sh/python.sh
ADDED
sh/python.sh
@@ -0,0 +1,14 @@+# ╭────────╮+# │ python │+# ╰────────╯++# ╭────────┬──────╮+# │ python │ venv │+# ╰────────┴──────╯++rwx_python_venv() {+ local path="${1}"+ [ -d "${path}" ] || return 1+ export VIRTUAL_ENV="${path}" && \+ export PATH="${VIRTUAL_ENV}/bin:${PATH}"+}
ADDED
sh/rescue/common.sh
ADDED
sh/rescue/common.sh
@@ -0,0 +1,94 @@+rwx_rescue_configure() {+ local hostname="${1}"+ # apt / conf+ rwx_apt_conf_write+ # apt / sources+ rwx_apt_sources_write+ # bash / rc+ main_link_bashrc+ mv "${HOME}/.bashrc" "${HOME}/.bashrc.old"+ # host name+ hostname "${hostname}"+ # locales+ printf "\+en_US.UTF-8 UTF-8+fr_FR.UTF-8 UTF-8+" >"/etc/locale.gen"+ # generate locales+ locale-gen+ # update catalog+ rwx_apt_update+ # disable frontend+ rwx_debian_frontend_disable+ # install backports+ rwx_apt_install_backports "tmux"+ # install packages+ rwx_apt_install_release "apt-file" "mosh" "screen" "byobu"+ # update catalog+ rwx_apt_update+}++rwx_rescue_install() {+ # update catalog+ rwx_apt_update+ # disable frontend+ rwx_debian_frontend_disable+ # upgrade packages+ rwx_apt_upgrade+ # install packages+ rwx_apt_install_release \+ "man-db" \+ "dmidecode" "efibootmgr" "lshw" "pciutils" "usbutils" \+ "parted" "mdadm" "cryptsetup-bin" "lvm2" \+ "btrfs-progs" "dosfstools" "duperemove" "squashfs-tools" \+ "git" "micro" "nano" "python3" "rsync" "vim" \+ "exa" "lf" "ncdu" "nnn" "ranger" "tree" \+ "file" "htop" "iotop" "ipcalc" "libdigest-sha3-perl" "lsof"+ # install backports+ rwx_apt_install_backports \+ "grub-pc-bin" \+ \+ "grub-efi-amd64-bin"+}++rwx_rescue_upload() {+ local host="${1}"+ local hostname="${2}"+ if [ -n "${hostname}" ]; then+ local user="root"+ #+ local user_host="${user}@${host}"+ # remove fingerprints+ ssh-keygen -R "${host}"+ # copy ssh id+ ssh-copy-id \+ -o "StrictHostKeyChecking=accept-new" \+ "${user_host}"+ # upload root+ rsync --delete --recursive \+ "$(dirname "${ENV}")" "${user_host}:/etc"+ # call setup+ # TODO variable+ ssh "${user_host}" -- \+ ". \"${ENV}\" ; rwx_rescue_configure \"${hostname}\""+ # create session+ ssh "${user_host}" -- byobu new-session -d+ # send keys+ ssh "${user_host}" -- byobu send-keys "rwx_rescue_install" "C-m"+ # attach session+ mosh "${user_host}" -- byobu attach-session+ else+ echo "host & hostname"+ return 1+ fi+}++rwx_rescue_wipe_1_zero() {+ rwx_fs_wipe "/dev/mapper/crypt" "512M"+}++rwx_rescue_wipe_3_close() {+ umount "/media/boot"+ umount "/media/crypt" &&+ cryptsetup luksClose "crypt"+}
ADDED
sh/rescue/hetzner.sh
ADDED
sh/rescue/hetzner.sh
@@ -0,0 +1,129 @@+rwx_rescue_wipe_0_init_hetzner_8_8() {+ local device+ set \+ "/dev/sda" \+ "/dev/sdb"+ local members+ local number+ local passphrase+ # read passphrase+ passphrase="$(rwx_read_passphrase)"+ # warn+ rwx_warn_wipe "${@}"+ #+ number=0+ for device in "${@}"; do+ number=$((number + 1))+ echo+ echo "#${number}: ${device}"+ #+ parted --script "${device}" \+ mktable gpt \+ unit "mib" \+ mkpart "crypt-${number}" 33282 7630885 \+ mkpart "boot-${number}" 514 33282 \+ mkpart "esp-${number}" 2 514 \+ set 3 esp on \+ mkpart "bios-${number}" 1 2 \+ set 4 bios_grub on+ done+ #+ number=0+ for device in "${@}"; do+ number=$((number + 1))+ echo+ echo "#${number}: ${device}4"+ # wipe bios+ rwx_fs_wipe "${device}4"+ done+ #+ number=0+ for device in "${@}"; do+ number=$((number + 1))+ echo+ echo "#${number}: ${device}3"+ # format esp+ rwx_fs_wipe "${device}3" "1M"+ rwx_fs_make_fat "${device}3" "esp-${number}" "0000000${number}"+ # mount esp+ mkdir --parents "/media/esp/${number}"+ mount "${device}3" "/media/esp/${number}"+ done+ #+ number=0+ for device in "${@}"; do+ number=$((number + 1))+ echo+ echo "#${number}: ${device}2"+ # wipe boot+ rwx_fs_wipe "${device}2" "1G" 1+ done+ #+ members=""+ for device in "${@}"; do+ members="${members} ${device}2"+ done+ # LATER alternative+ # shellcheck disable=SC2086+ rwx_fs_raid_create \+ "boot" "00000000:00000000:00000000:00000002" ${members}+ #+ rwx_fs_make_btrfs "/dev/md/boot" "boot" \+ "00000000-0000-0000-0000-00000000000b"+ # mount boot+ mkdir --parents "/media/boot"+ mount \+ --options "autodefrag,compress-force=zstd" \+ "/dev/md/boot" "/media/boot"+ #+ number=0+ for device in "${@}"; do+ number=$((number + 1))+ echo+ echo "#${number}: ${device}1"+ # wipe crypt head+ rwx_fs_wipe "${device}1" "1G" 1+ done+ #+ members=""+ for device in "${@}"; do+ members="${members} ${device}1"+ done+ # LATER alternative+ # shellcheck disable=SC2086+ rwx_fs_raid_create \+ "crypt" "00000000:00000000:00000000:00000001" ${members}+ # encrypt+ rwx_fs_luks_format "${passphrase}" "/dev/md/crypt"+ # open+ echo "${passphrase}" |+ cryptsetup luksOpen "/dev/md/crypt" "crypt"+ # passphrase+ unset passphrase+}++rwx_rescue_wipe_2_make_hetzner_8_8() {+ local passphrase+ # close+ cryptsetup luksClose "crypt"+ # read passphrase+ passphrase="$(rwx_read_passphrase)"+ # encrypt+ rwx_fs_luks_format "${passphrase}" "/dev/md/crypt"+ # open+ echo "${passphrase}" |+ cryptsetup luksOpen "/dev/md/crypt" "crypt"+ # passphrase+ unset passphrase+ # format crypt+ rwx_fs_make_btrfs "/dev/mapper/crypt" "crypt" \+ "00000000-0000-0000-0000-00000000000c"+ # mount crypt+ mkdir --parents "/media/crypt"+ mount \+ --options "autodefrag,compress-force=zstd" \+ "/dev/mapper/crypt" "/media/crypt"+ # make swap file+ rwx_fs_make_btrfs_swap "/media/crypt/swap" "64g" \+ "00000000-0000-0000-0000-000000000005"+}
ADDED
sh/rescue/ovh.sh
ADDED
sh/rescue/ovh.sh
@@ -0,0 +1,71 @@+rwx_rescue_wipe_0_init_ovh_vle2() {+ local device="/dev/sdb"+ local passphrase+ # read passphrase+ passphrase="$(rwx_read_passphrase)"+ # warn+ rwx_warn_wipe "${device}"+ #+ parted --script "${device}" \+ mktable gpt \+ unit "mib" \+ mkpart "crypt" 4610 40959 \+ mkpart "boot" 514 4610 \+ mkpart "esp" 2 514 \+ set 3 esp on \+ mkpart bios 1 2 \+ set 4 bios_grub on+ # bios / wipe+ rwx_fs_wipe "${device}4"+ # esp / wipe+ rwx_fs_wipe "${device}3" "1M"+ # esp / format+ rwx_fs_make_fat "${device}3" "esp" "00000001"+ # esp / mount+ mkdir --parents "/media/esp"+ mount "${device}3" "/media/esp"+ # boot / wipe+ rwx_fs_wipe "${device}2" "1G" 1+ # boot / format+ rwx_fs_make_btrfs "${device}2" "boot" \+ "00000000-0000-0000-0000-00000000000b"+ # boot / mount+ mkdir --parents "/media/boot"+ mount --options "autodefrag,compress-force=zstd" \+ "${device}2" "/media/boot"+ # crypt / wipe+ rwx_fs_wipe "${device}1" "1G" 1+ # crypt / encrypt+ rwx_fs_luks_format "${passphrase}" "${device}1"+ # crypt / open+ echo "${passphrase}" |+ cryptsetup luksOpen "${device}1" "crypt"+ # passphrase+ unset passphrase+}++rwx_rescue_wipe_2_make_ovh_vle2() {+ local device="/dev/sdb"+ local passphrase+ # crypt / close+ cryptsetup luksClose "crypt"+ # read passphrase+ passphrase="$(rwx_read_passphrase)"+ # crypt / encrypt+ rwx_fs_luks_format "${passphrase}" "${device}1"+ # crypt / open+ echo "${passphrase}" |+ cryptsetup luksOpen "${device}1" "crypt"+ # passphrase+ unset passphrase+ # crypt / format+ rwx_fs_make_btrfs "/dev/mapper/crypt" "crypt" \+ "00000000-0000-0000-0000-00000000000c"+ # crypt / mount+ mkdir --parents "/media/crypt"+ mount --options "autodefrag,compress-force=zstd" \+ "/dev/mapper/crypt" "/media/crypt"+ # crypt / swap+ rwx_fs_make_btrfs_swap "/media/crypt/swap" "4g" \+ "00000000-0000-0000-0000-000000000005"+}
ADDED
sh/self.sh
ADDED
sh/self.sh
@@ -0,0 +1,162 @@+# meta doc+rwx_doc() {+ local name="${1}"+ [ -n "${name}" ] || return+ local doc line module+ rwx_ifs_set+ for module in $(rwx_find_shell "${RWX_ROOT_SYSTEM}"); do+ while read -r line; do+ case "${line}" in+ "#"*) doc="${doc}${line}" ;;+ "${name}() {")+ echo "${doc}"+ return+ ;;+ *) doc="" ;;+ esac+ done <"${RWX_ROOT_SYSTEM}/${module}"+ done+ rwx_ifs_unset+}++# ╭──────┬───────╮+# │ self │ check │+# ╰──────┴───────╯++# check source code+rwx_self_check() {+ # check format+ rwx_log+ rwx_shfmt "${RWX_ROOT_SYSTEM}"+ # check syntax+ rwx_log+ rwx_shellcheck "${RWX_ROOT_SYSTEM}"+}++# ╭──────┬──────────╮+# │ self │ commands │+# ╰──────┴──────────╯++# get commands from root+rwx_self_commands() {+ grep \+ --directories "recurse" \+ --no-filename \+ "^${RWX_SELF_COMMAND}" "${RWX_ROOT_SYSTEM}" |+ cut --delimiter "(" --fields 1 |+ sed "s|^${RWX_SELF_COMMAND}||"+}++# ╭──────┬───────────╮+# │ self │ functions │+# ╰──────┴───────────╯++# get functions from root+rwx_self_functions() {+ grep \+ --directories "recurse" \+ --no-filename \+ "()" "${RWX_ROOT_SYSTEM}" |+ cut --delimiter "(" --fields 1+}++# ╭──────┬──────╮+# │ self │ help │+# ╰──────┴──────╯++# output help message+rwx_self_help() {+ rwx_log \+ "rwx_… = functions" \+ " a__… = aliases" \+ " u__… = user"+}++# ╭──────┬──────╮+# │ self │ init │+# ╰──────┴──────╯++rwx_self_init() {+ # run interactive extras+ if rwx_shell_interactive; then+ # help+ rwx_log+ rwx_self_help+ fi+}++# ╭──────┬─────────╮+# │ self │ install │+# ╰──────┴─────────╯++_rwx_cmd_rwx_install() { rwx_self_install "${@}"; }+rwx_self_install() {+ local target="${1}"+ local command file root+ # code+ if [ -n "${target}" ]; then+ root="${target}${RWX_ROOT_SYSTEM}"+ rwx_remove "${root}"+ cp --recursive "${RWX_ROOT_SYSTEM}" "${root}"+ fi+ # commands+ root="${target}/usr/local/bin"+ for command in $(rwx_self_commands); do+ file="${root}/${command}"+ rwx_remove "${file}"+ rwx_link "${file}" "${RWX_MAIN_PATH}"+ done+ # sh+ file="${target}/etc/profile.d/${RWX_SELF_NAME}.sh"+ rwx_remove "${file}"+ rwx_file_write "${file}" "\+export ENV=\"${RWX_MAIN_PATH}\"+"+ # bash+ file="${target}/etc/bash.bashrc"+ rwx_remove "${file}"+ rwx_link "${file}" "${RWX_MAIN_PATH}"+}++# ╭──────┬────────╮+# │ self │ subset │+# ╰──────┴────────╯++rwx_self_subset() {+ local argument path+ for argument in "${@}"; do+ path="${RWX_ROOT_SYSTEM}/${argument}"+ if [ -d "${path}" ]; then+ local file+ for file in $(rwx_find_shell "${path}"); do+ echo "${argument}/${file}"+ done+ elif [ -f "${path}" ]; then+ echo "${argument}"+ fi+ done+}++# ╭──────┬───────╮+# │ self │ write │+# ╰──────┴───────╯++rwx_self_write() {+ local target="${1}"+ if [ -n "${target}" ]; then+ shift+ local file text+ text="#! /usr/bin/env sh+"+ rwx_ifs_set+ for file in $(rwx_self_subset "${@}"); do+ text="${text}+$(cat "${RWX_ROOT_SYSTEM}/${file}")+"+ done+ rwx_ifs_unset+ rwx_file_write "${target}" "${text}"+ rwx_shfmt "${target}"+ rwx_shellcheck_file "${target}"+ fi+}
ADDED
sh/shell.sh
ADDED
sh/shell.sh
@@ -0,0 +1,116 @@+_rwx_shell_color() {+ local code="${1}"+ case "${RWX_SHELL}" in+ "bash")+ printf "\x01\e[0"+ if [ -n "${code}" ]; then+ printf "%s" ";${code}"+ fi+ printf "m\x02"+ ;;+ *)+ printf "\033["+ if [ -n "${code}" ]; then+ printf "%s" "${code}"+ else+ printf "0"+ fi+ printf "m"+ ;;+ esac+}+RWX_COLOR_BROWN="$(_rwx_shell_color 33)"+RWX_COLOR_CYAN="$(_rwx_shell_color 36)"+RWX_COLOR_DEFAULT="$(_rwx_shell_color)"+RWX_COLOR_GREEN="$(_rwx_shell_color 31)"+RWX_COLOR_MAGENTA="$(_rwx_shell_color 35)"+RWX_COLOR_RED="$(_rwx_shell_color 32)"++rwx_shell_configure() {+ [ -n "${ENV}" ] || ENV="${RWX_MAIN_PATH}"+ export ENV+ # prompt+ PS1="\$(rwx_shell_prompt \${?})"+ PS2="├ "+ # specific+ case "${RWX_SHELL}" in+ "bash")+ # completion+ local root="/usr/share/bash-completion"+ local file="bash_completion"+ local path="${root}/${file}"+ # shellcheck disable=SC1090+ [ -f "${path}" ] && . "${path}"+ root="${root}/completions"+ if [ -d "${root}" ]; then+ set \+ "git" \+ "tar"+ for file in "${@}"; do+ path="${root}/${file}"+ # shellcheck disable=SC1090+ [ -f "${path}" ] && . "${path}"+ done+ fi+ # history+ HISTCONTROL="ignorespace"+ HISTSIZE=-1+ HISTTIMEFORMAT="%Y%m%d %H%M%S "+ ;;+ *) ;;+ esac+}+rwx_shell_configure++rwx_shell_prompt() {+ local date host id+ local code="${1}"+ date="$(date +%H:%M:%S)"+ local git+ host="$(hostname)"+ id="$(id --user)"+ local path="${PWD}"+ local user="${USER}"+ local view="╰ "+ # code+ if [ "${code}" -ne 0 ]; then+ view="${view}${RWX_COLOR_GREEN}"+ else+ view="${view}${RWX_COLOR_RED}"+ fi+ view="${view}${code}"+ # date+ view="${view}${RWX_COLOR_DEFAULT} @ "+ view="${view}${RWX_COLOR_BROWN}${date}"+ # git+ if command -v "__git_ps1" >"/dev/null"; then+ git="$(__git_ps1)"+ if [ -n "${git}" ]; then+ view="${view}${RWX_COLOR_DEFAULT} –${RWX_COLOR_MAGENTA}${git}"+ fi+ fi+ # new+ view="${view}\\n"+ # path+ view="${view}${RWX_COLOR_CYAN}${path}"+ # new+ view="${view}\\n"+ # frame+ view="${view}${RWX_COLOR_DEFAULT}╭ "+ # user+ if [ "${id}" -eq 0 ]; then+ view="${view}${RWX_COLOR_GREEN}"+ else+ view="${view}${RWX_COLOR_RED}"+ fi+ view="${view}${user}"+ # host+ view="${view}${RWX_COLOR_DEFAULT} @ "+ view="${view}${RWX_COLOR_BROWN}${host}"+ # new+ view="${view}\\n"+ # prompt+ view="${view}${RWX_COLOR_DEFAULT}${PS2}"+ printf "%b" "${view}"+}
ADDED
sh/tmux.sh
ADDED
sh/tmux.sh
@@ -0,0 +1,264 @@+# ╭──────┬───────╮+# │ tmux │ setup │+# ╰──────┴───────╯++rwx_tmux_setup() {+ local file+ file="${HOME}/.tmux.conf"+ rwx_file_write "${file}" "\+# ╭────────╮+# │ option │+# ╰────────╯++# empty name for windows+set-option -g automatic-rename-format '#{pane_current_command}'+set-option -g automatic-rename on++# first index number+set-option -g base-index 1++# display duration+set-option -g display-time 1536++# extend history limit+set-option -g history-limit 1048576++# style for messages+set-option -g message-style bg=red,fg=white++# activity monitoring+set-window-option -g monitor-activity on++# silence monitoring+set-window-option -g monitor-silence 0++# enable mouse actions+set-option -g mouse on++# prefix with ^B or F12+set-option -g prefix C-b+set-option -g prefix2 F12++# renumber windows after closing one+set-option -g renumber-windows on++# enable title+set-option -g set-titles on++# set title to working directory+set-option -g set-titles-string '\+#{session_name}\+ - \+#{window_index}∕#{session_windows} #{window_name}\+ - \+#{pane_index}∕#{window_panes} #{pane_current_command}\+'++# ╭────────┬──────╮+# │ option │ pane │+# ╰────────┴──────╯++# first index number+set-option -g pane-base-index 1++# ╭────────┬──────┬────────╮+# │ option │ pane │ border │+# ╰────────┴──────┴────────╯++# active style+set-option -g pane-active-border-style fg=green++# regular style+set-option -g pane-border-style fg=blue++# ╭────────┬────────╮+# │ option │ status │+# ╰────────┴────────╯++# status lines+set-option -g status 3++# background color+set-option -g status-bg '#0D0D0D'++# foreground color+set-option -g status-fg white++# line 1+set-option -g status-format[0] '\+#{W:\+#[bg=##202020] #[bg=##303030]\+#{?window_zoomed_flag,#[fg=magenta][, }\+#[fg=yellow]#{window_index}\+#{?window_zoomed_flag,#[fg=magenta]], }\+ \+#{?window_active,#[fg=green],\+#{?window_activity_flag,#[fg=red],#[fg=blue]}}\+#{window_name}\+#[bg=##303030] #[bg=##202020] \+#[bg=default] \+}\+#[align=right]\+#[bg=##202020] #[bg=##303030] \+#[fg=yellow]%H:%M:%S\+#[bg=##303030] #[bg=##202020]\+#{?client_prefix,#[fg=green]p, }\+'++# line 2+set-option -g status-format[1] '\+#{S:\+#[bg=##202020] #[bg=##303030] \+#{?session_many_attached,#[fg=red],\+#{?session_attached,#[fg=magenta],#[fg=blue]}}\+#{session_name}\+#[bg=##303030] #[bg=##202020] \+#[bg=default] \+}\+#[fg=yellow]→ #[fg=green]#{session_name} \+#[align=right]\+#[bg=##202020] #[bg=##303030] \+#[fg=yellow]%Y-%m-%d\+#[bg=##303030] #[bg=##202020] \+'++# line 3+set-option -g status-format[2] '\+#[fg=cyan]#{pane_current_path}\+#[align=right]\+#[bg=##202020] #[bg=##303030] \+#[fg=yellow]#{host}\+#[bg=##303030] #[bg=##202020] \+'++# line 4+set-option -g status-format[3] '\+#{P:\+#[bg=##202020] #[bg=##303030] \+#[fg=yellow]#{pane_index}\+ \+#{?pane_active,#[fg=green],#[fg=blue]}\+#{pane_current_command}\+#[bg=##303030] #[bg=##202020] \+#[bg=default] \+}\+#[align=right]\+#[bg=##202020] #[bg=##303030] \+#{?uid,#[fg=green],#[fg=red]}\+#{user}\+#[bg=##303030] #[bg=##202020] \+'++# line 5+set-option -g status-format[4] '\+#{P:\+#[bg=##202020] #[bg=##303030] \+#[fg=yellow]#{pane_index}\+ \+#{?pane_active,#[fg=green],#[fg=blue]}\+#{pane_width}×#{pane_height}\+#[bg=##303030] #[bg=##202020] \+#[bg=default] \+}\+#[align=right]\+#[bg=##202020] #[bg=##303030] \+#[fg=green]#{window_width}×#{window_height}\+#[bg=##303030] #[bg=##202020] \+'++# refresh period+set-option -g status-interval 1++# bar location+set-option -g status-position bottom++# ╭─────╮+# │ key │+# ╰─────╯++# detach client+bind-key -n F6 detach-client++# new window+bind-key -n F2 new-window++# select pane+bind-key -n C-S-Down select-pane -D+bind-key -n C-S-Left select-pane -L+bind-key -n C-S-Right select-pane -R+bind-key -n C-S-Up select-pane -U++# status lines+bind-key -n C-F10 set-option -g status off+bind-key -n C-F1 set-option -g status on+bind-key -n C-F2 set-option -g status 2+bind-key -n C-F3 set-option -g status 3+bind-key -n C-F4 set-option -g status 4+bind-key -n C-F5 set-option -g status 5++# switch session+bind-key -n M-Down switch-client -n+bind-key -n M-Up switch-client -p++# switch window+bind-key -n M-Left previous-window+bind-key -n M-Right next-window++# ╭─────┬────────╮+# │ key │ prefix │+# ╰─────┴────────╯++# rename+bind-key C-s command-prompt { rename-session '%%' }+bind-key C-w command-prompt { rename-window '%%' }++# split window+bind-key h split-window -h+bind-key v split-window -v++# toggle mouse+bind-key t set-option -g mouse \\; display-message 'mouse = #{mouse}'++# reload configuration+bind-key r source-file ${file} \\; display-message 'source-file ${file}'++# swap window+bind-key M-Left swap-window -t -1+bind-key M-Right swap-window -t +1++# ╭─────────────╮+# │ default │+# ╭───────────┬─────────┼─────┬───────┤+# │ -n │ F12 │ -n │ C-b │+# ╭───────────────────┼───────────┼─────────┼─────┼───────┤+# │ command-prompt │ │ │ │ : │+# │ copy-mode │ │ │ │ PPage │+# │ detach-client │ │ │ │ d │+# │ new-session │ │ │ │ │+# │ new-window │ F2 │ │ │ c │+# │ next-window │ M-Right │ │ │ n │+# │ previous-window │ M-Left │ │ │ p │+# │ rename-session │ │ C-s │ │ │+# │ rename-window │ │ C-w │ │ │+# │ resize-pane -Z │ │ │ │ z │+# │ select-pane -D │ C-S-Down │ │ │ │+# │ select-pane -L │ C-S-Left │ │ │ │+# │ select-pane -R │ C-S-Right │ │ │ │+# │ select-pane -U │ C-S-Up │ │ │ │+# │ set -g mouse │ │ t │ │ │+# │ set -g status off │ C-F10 │ │ │ │+# │ set -g status on │ C-F1 │ │ │ │+# │ set -g status 2 │ C-F2 │ │ │ │+# │ set -g status 3 │ C-F3 │ │ │ │+# │ set -g status 4 │ C-F4 │ │ │ │+# │ set -g status 5 │ C-F5 │ │ │ │+# │ source-file │ │ r │ │ │+# │ split-window -h │ │ h │ │ % │+# │ split-window -v │ │ v │ │ \" │+# │ swap-window -t -1 │ │ M-Left │ │ │+# │ swap-window -t +1 │ │ M-Right │ │ │+# │ switch-client -n │ M-Down │ │ │ │+# │ switch-client -p │ M-Up │ │ │ │+# ╰───────────────────┴───────────┴─────────┴─────┴───────╯+"+}
ADDED
sh/util.sh
ADDED
sh/util.sh
@@ -0,0 +1,76 @@+rwx_file_append() {+ local file="${1}"+ local text="${2}"+ if [ -n "${file}" ]; then+ printf "%s" "${text}" >>"${file}"+ fi+}++rwx_file_empty() {+ local file="${1}"+ if [ -n "${file}" ]; then+ rwx_file_write "${file}" ""+ fi+}++rwx_file_write() {+ local file="${1}"+ local text="${2}"+ if [ -n "${file}" ]; then+ printf "%s" "${text}" >"${file}"+ fi+}++rwx_link() {+ local link="${1}"+ local target="${2}"+ ln \+ --symbolic \+ "${target}" \+ "${link}"+}++rwx_list_block_devices() {+ lsblk \+ --noempty \+ --output "NAME,SIZE,TYPE,FSTYPE,LABEL,MOUNTPOINTS"+}++rwx_not() {+ case "${1}" in+ "false") echo "true" ;;+ "true") echo "false" ;;+ *) ;;+ esac+}++rwx_read_passphrase() {+ rwx_read_secret "PassPhrase: "+}++rwx_read_secret() {+ local prompt="${1}"+ local secret+ printf "%s" "${prompt}" 1>&2+ stty -echo+ read -r secret+ stty echo+ echo >&2+ echo "${secret}"+ unset secret+}++rwx_remove() {+ rm \+ --force \+ --recursive \+ "${@}"+}++rwx_warn_wipe() {+ local tmp+ rwx_list_block_devices+ printf "%s" "WIPE ${*} /?\\ OR CANCEL /!\\"+ read -r tmp+ rwx_log_trace "${tmp}"+}