logo

utils-extra

Collection of extra tools for Unixes
commit: 38794d2599aea207de65b4d1b40f726eba5e16ba
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Tue, 26 Sep 2023 17:18:19 +0200

Import from utils

Diffstat:

A.gitignore14++++++++++++++
A.reuse/dep56++++++
AKyuafile7+++++++
ALICENSES/CC0-1.0.txt121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ALICENSES/MPL-2.0.txt373+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AMakefile61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/args.120++++++++++++++++++++
Acmd/args.c20++++++++++++++++++++
Acmd/del.122++++++++++++++++++++++
Acmd/del.c24++++++++++++++++++++++++
Acmd/errno.122++++++++++++++++++++++
Acmd/errno.c42++++++++++++++++++++++++++++++++++++++++++
Acmd/humanize.131+++++++++++++++++++++++++++++++
Acmd/humanize.c144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/lolcat.136++++++++++++++++++++++++++++++++++++
Acmd/lolcat.c113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/mdate.c22++++++++++++++++++++++
Acmd/memsys.c37+++++++++++++++++++++++++++++++++++++
Acmd/pat.129+++++++++++++++++++++++++++++
Acmd/pat.c89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/sizeof.c64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/sname.125+++++++++++++++++++++++++
Acmd/sname.c38++++++++++++++++++++++++++++++++++++++
Acmd/xcd.c259+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfigure223+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest-cmd/Kyuafile20++++++++++++++++++++
Atest-cmd/args47+++++++++++++++++++++++++++++++++++++++++++++++
Atest-cmd/date93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest-cmd/del57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest-cmd/errno51+++++++++++++++++++++++++++++++++++++++++++++++++++
Atest-cmd/humanize107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest-cmd/inputs/all_bytes0
Atest-cmd/lolcat92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest-cmd/mdate13+++++++++++++
Atest-cmd/memsys48++++++++++++++++++++++++++++++++++++++++++++++++
Atest-cmd/mkdir45+++++++++++++++++++++++++++++++++++++++++++++
Atest-cmd/outputs/lolcat/all_bytes0
Atest-cmd/outputs/lolcat/all_bytes.l12++++++++++++
Atest-cmd/outputs/xcd/all_bytes19+++++++++++++++++++
Atest-cmd/outputs/xcd/null4++++
Atest-cmd/pat78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest-cmd/shellcheck14++++++++++++++
Atest-cmd/sizeof26++++++++++++++++++++++++++
Atest-cmd/sname33+++++++++++++++++++++++++++++++++
Atest-cmd/xcd61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest_functions.sh12++++++++++++
46 files changed, 2674 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +/config.mk +/target_filter +/cmd/* +!/cmd/*.c +!/cmd/*.ha +!/cmd/*.1 +*.t.err +*.o + +# Kyua +/html/ diff --git a/.reuse/dep5 b/.reuse/dep5 @@ -0,0 +1,6 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Source: https://hacktivis.me/git/utils + +Files: test-cmd/inputs/* test-cmd/outputs/* +Copyright: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +License: CC0-1.0 diff --git a/Kyuafile b/Kyuafile @@ -0,0 +1,7 @@ +-- SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +-- SPDX-License-Identifier: MPL-2.0 +syntax(2) + +test_suite("utils-extra") + +include("test-cmd/Kyuafile") diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/LICENSES/MPL-2.0.txt b/LICENSES/MPL-2.0.txt @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/Makefile b/Makefile @@ -0,0 +1,61 @@ +# SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +include config.mk + +all: $(EXE) + +.c: + rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno} + $(CC) -std=c99 $(CFLAGS) -o $@ $< $(LDFLAGS) + +.c.o: + rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno} + $(CC) -std=c99 $(CFLAGS) -c -o $@ $< + +.PHONY: check +check: all + MALLOC_CHECK_=3 POSIX_ME_HARDER=1 POSIXLY_CORRECT=1 kyua test || (kyua report --verbose --results-filter=broken,failed; false) + +.PHONY: lint +lint: + $(SHELLCHECK) ./configure test_all.sh test_functions.sh ./sh/* + SHELLCHECK=${SHELLCHECK} ./test-cmd/shellcheck + ${FLAWFINDER} --error-level=4 . + $(MANDOC) -Tlint -Wunsupp,error,warning $(MAN1) + $(REUSE) lint + +clean: + rm -fr $(EXE) + rm -fr ${EXE:=.c.gcov} ${EXE:=.gcda} ${EXE:=.gcno} + +install: script-install cmd-install + +script-install: + mkdir -p $(DESTDIR)$(SHELLDIR)/ $(DESTDIR)$(PERLDIR)/ + cp -r sh/* $(DESTDIR)$(SHELLDIR)/ + cp -r perl/* $(DESTDIR)$(PERLDIR)/ + +cmd-install: + mkdir -p ${DESTDIR}${BINDIR}/ + cp -p ${EXE} ${DESTDIR}${BINDIR}/ + chown 0:0 ${DESTDIR}${BINDIR}/memsys + chmod 4755 ${DESTDIR}${BINDIR}/memsys + mkdir -p ${DESTDIR}${MANDIR}/man1 + cp -p ${MAN1} ${DESTDIR}${MANDIR}/man1 + +.PHONY: coverage +coverage: + $(GCOV) -b $(EXE) + +C_SOURCES = cmd/*.c lib/*.h lib/*.c +format: $(C_SOURCES) + clang-format -style=file -assume-filename=.clang-format -i $(C_SOURCES) + +cmd/lolcat: cmd/lolcat.c Makefile + rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno} + $(CC) -std=c99 $(CFLAGS) -o $@ cmd/lolcat.c -lm $(LDFLAGS) + +cmd/xcd: cmd/xcd.c Makefile + rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno} + $(CC) -std=c99 $(CFLAGS) -o $@ cmd/xcd.c -lm $(LDFLAGS) diff --git a/cmd/args.1 b/cmd/args.1 @@ -0,0 +1,20 @@ +.\" Collection of Unix tools, comparable to coreutils +.\" Copyright 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: MPL-2.0 +.Dd 2022-02-13 +.Dt ARGS 1 +.Os +.Sh NAME +.Nm args +.Nd dump all passed arguments +.Sh SYNOPSIS +.Nm +.Op Ar arguments +.Sh DESCRIPTION +.Nm +will print out all the arguments passed to it. +This is intended as a tool for shell debugging. +.Sh EXIT STATUS +.Ex -std +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me diff --git a/cmd/args.c b/cmd/args.c @@ -0,0 +1,20 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +// printf() +#include <stdio.h> + +int +main(int argc, char *argv[]) +{ + printf("argc: %i\n", argc); + + for(int i = 0; i < argc; i++) + { + printf("argv[%d]: \"%s\"\n", i, argv[i]); + } + + return 0; +} diff --git a/cmd/del.1 b/cmd/del.1 @@ -0,0 +1,22 @@ +.\" Collection of Unix tools, comparable to coreutils +.\" Copyright 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: MPL-2.0 +.Dd 2021-05-13 +.Dt DEL 1 +.Os +.Sh NAME +.Nm del +.Nd delete any file +.Sh SYNOPSIS +.Nm +.Ar +.Sh DESCRIPTION +Simpler replacement to +.Xr rm 1 , +notably treating directories like any other file. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr remove 3 +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me diff --git a/cmd/del.c b/cmd/del.c @@ -0,0 +1,24 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +#include <errno.h> /* errno */ +#include <stdio.h> /* remove() */ +#include <string.h> /* strerror() */ + +int +main(int argc, char *argv[]) +{ + for(int i = 1; i < argc; i++) + { + if(remove(argv[i]) < 0) + { + // TODO: interact on write protection to force deletion + fprintf(stderr, "del: remove(%s) error: %s\n", argv[i], strerror(errno)); + return 1; + } + } + + return 0; +} diff --git a/cmd/errno.1 b/cmd/errno.1 @@ -0,0 +1,22 @@ +.\" Collection of Unix tools, comparable to coreutils +.\" Copyright 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: MPL-2.0 +.Dd 2022-04-19 +.Dt ENV 1 +.Os +.Sh NAME +.Nm errno +.Nd print errno message for a value +.Sh SYNOPSIS +.Nm +.Ar value +.Sh DESCRIPTION +.Nm +shall print the errno message associated with +.Ar value . +.Sh EXIT STATUS +.Ex -std +.Sh STANDARDS +No applicable one known. +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me diff --git a/cmd/errno.c b/cmd/errno.c @@ -0,0 +1,42 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#include <errno.h> // errno +#include <stdio.h> // puts, perror +#include <stdlib.h> // strtol +#include <string.h> // strerror + +int +main(int argc, char *argv[]) +{ + if(argc != 2) + { + puts("usage: errno <number>"); + return 1; + } + + errno = 0; + + int err = (int)(strtol(argv[1], NULL, 10)); + if(errno != 0) + { + perror("errno: strtol"); + return 1; + } + + errno = 0; + char *msg = strerror(err); + if(errno != 0) + { + perror("errno: strerror"); + return 1; + } + + if(puts(msg) < 0) + { + return 1; + } + + return 0; +} diff --git a/cmd/humanize.1 b/cmd/humanize.1 @@ -0,0 +1,31 @@ +.\" Collection of Unix tools, comparable to coreutils +.\" Copyright 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: MPL-2.0 +.Dd 2023-06-05 +.Dt HUMANIZE 1 +.Os +.Sh NAME +.Nm humanize +.Nd format numbers into human readable form +.Sh SYNOPSIS +.Nm +.Op Fl dbt +.Ar number ... +.Sh DESCRIPTION +Takes each +.Ar number +and format it in a human readable form, one per line. +It supports the following options: +.Bl -tag -width Ds +.It Fl b +Divide by 1024, typically used for bytes. +.It Fl d +Divide by 1000, this is the default but made explicit in case of changes without breaking scripts. +.It Fl t +.Ar number +represents time duration in seconds. +.El +.Sh EXIT STATUS +.Ex -std +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me diff --git a/cmd/humanize.c b/cmd/humanize.c @@ -0,0 +1,144 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L + +#include <err.h> // errx +#include <errno.h> // EINVAL, ERANGE +#include <limits.h> // LLONG_MIN, LLONG_MAX +#include <stdbool.h> // bool +#include <stdio.h> // fprintf, perror, sscanf +#include <stdlib.h> // strtonum +#include <unistd.h> // opt*, getopt + +void +dtosi(double num, char *buf, size_t bufsize, bool iec) +{ +#define PFX 11 + char *si_prefixes[PFX] = {"", "k", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"}; + char *iec_prefixes[PFX] = { + "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"}; + + int div = iec ? 1024 : 1000; + char **prefixes = iec ? iec_prefixes : si_prefixes; + + unsigned quotient = 0; + + while(num > div && quotient < PFX) + { + num /= div; + quotient += 1; + } + + snprintf(buf, bufsize, "%g %s", num, prefixes[quotient]); +} + +static void +usage() +{ + fprintf(stderr, "Usage: humanize [-bdt] number\n"); +} + +int +main(int argc, char *argv[]) +{ + // default to -d + bool iec = false, time = false; + + int c = -1; + while((c = getopt(argc, argv, ":bdt")) != -1) + { + switch(c) + { + case 'b': + iec = true; + break; + case 'd': + iec = false; + break; + case 't': + time = true; + break; + case ':': + fprintf(stderr, "humanize: Error: Missing operand for option: '-%c'\n", optopt); + usage(); + return 1; + case '?': + fprintf(stderr, "humanize: Error: Unrecognised option: '-%c'\n", optopt); + usage(); + return 1; + } + } + + argc -= optind; + argv += optind; + + if(argc < 1) + { + usage(); + return 1; + } + + for(int argi = 0; argi < argc; argi++) + { + const char *errstr = NULL; + char buf[32] = ""; + + if(time) + { + int year = 0, month = 0, mday = 0, hour = 0, min = 0, sec = 0; + long int epoch = 0; + + if(sscanf(argv[argi], "%ld", &epoch) < 1) + { + perror("humanize: sscanf"); + return 1; + } + + year = epoch / 31556926; // round(year) + epoch %= 31556926; + month = epoch / 2629743; // year/12 + epoch %= 2629743; + mday = epoch / 86400; + epoch %= 86400; + + hour = epoch / 3600; + epoch %= 3600; + min = epoch / 60; + epoch %= 60; + sec = epoch; + + if(year > 0) printf("%d年 ", year); + if(month > 0) printf("%d月 ", month); + if(mday > 0) printf("%d日 ", mday); + if(hour > 0) printf("%dh ", hour); + if(min > 0) printf("%dm ", min); + if(sec > 0) printf("%ds", sec); + printf("\n"); + + continue; + } + + errno = 0; + long long n = strtoll(argv[argi], NULL, 10); + if(n == LLONG_MIN) + { + errx(1, "%s is too small", argv[argi]); + } + if(n == LLONG_MAX) + { + errx(1, "%s is too large", argv[argi]); + } + if(errno != 0) + { + perror("humanize: strtoll"); + } + + dtosi(n, buf, 32, iec); + + printf("%s\n", buf); + } + + return 0; +} diff --git a/cmd/lolcat.1 b/cmd/lolcat.1 @@ -0,0 +1,36 @@ +.\" Collection of Unix tools, comparable to coreutils +.\" Copyright 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: MPL-2.0 +.Dd 2021-05-15 +.Dt LOLCAT 1 +.Os +.Sh NAME +.Nm lolcat +.Nd a truecolor rainbow filter +.Sh SYNOPSIS +.Nm +.Ar +.Sh DESCRIPTION +.Nm +takes each character from it's standard input and prints it with a truecolor escape sequence, making the text output rainbow colored given enough characters. +.Pp +.Nm +also replaces escape to +.Sq ^[ +in bold, this allows to strip escape codes easily. +.Sh KNOWN ISSUES & LIMITATIONS +.Bl -bullet +.It +Frequence is not adjustable yet +.It +.Nm +puts escape codes even when stdout isn't a tty, this is expected, do not call +.Nm +otherwise +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Lk https://github.com/busyloop/lolcat "Original lolcat program, in ruby" +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me diff --git a/cmd/lolcat.c b/cmd/lolcat.c @@ -0,0 +1,113 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +#include <errno.h> /* errno */ +#include <math.h> /* sin() */ +#include <stdio.h> /* fgetc(), fputc(), printf(), fopen(), fclose() */ +#include <string.h> /* strerror() */ + +void +rainbow(double freq, int i) +{ + double red, green, blue; + double pi = 3.14159; + + red = sin(freq * i + 0) * 127 + 128; + green = sin(freq * i + 2 * pi / 3) * 127 + 128; + blue = sin(freq * i + 4 * pi / 3) * 127 + 128; + + printf("[38;2;%02d;%02d;%02dm", (int)red, (int)green, (int)blue); +} + +int +concat(FILE *stream) +{ + double freq = 0.1; + int i = 0; + + int c; + errno = 0; + while((c = fgetc(stream)) != EOF) + { + rainbow(freq, i); + i++; + if(c == '') + { + printf("^["); + continue; + } + + if(fputc(c, stdout) == EOF) + { + fprintf(stderr, "\nlolcat: Write error: %s\n", strerror(errno)); + return 1; + } + } + + if(c == EOF && errno != 0) + { + fprintf(stderr, "\nlolcat: Read error: %s\n", strerror(errno)); + return 1; + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + int ret = 0; + + if(argc <= 1) + { + ret = concat(stdin); + goto end; + } + + for(int argi = 1; argi < argc; argi++) + { + if(strncmp(argv[argi], "-", 2) == 0) + { + ret = concat(stdin); + if(ret != 0) + { + goto end; + } + } + else if(strncmp(argv[argi], "--", 3) == 0) + { + continue; + } + else + { + FILE *file = fopen(argv[argi], "r"); + + if(!file) + { + fprintf(stderr, "\nlolcat: Error opening ‘%s’: %s\n", argv[argi], strerror(errno)); + ret = 1; + goto end; + } + else + { + ret = concat(file); + if(ret != 0) + { + goto end; + } + + ret = fclose(file); + if(ret != 0) + { + goto end; + } + } + } + } + +end: + printf(""); + return ret; +} diff --git a/cmd/mdate.c b/cmd/mdate.c @@ -0,0 +1,22 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +#include <stdio.h> /* printf */ +#include <time.h> /* time */ + +// 3600*24.5 +#define cycle 88200 + +int +main(void) +{ + time_t now = time(NULL); + time_t date_now = now / cycle; + time_t time_now = now % cycle; + + printf("%lX,%05lX\n", date_now, time_now); + + return 0; +} diff --git a/cmd/memsys.c b/cmd/memsys.c @@ -0,0 +1,37 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +#include <fcntl.h> /* open() */ +#include <stdio.h> /* perror() */ +#include <unistd.h> /* close(), write() */ + +#define SYS_POWER_STATE "/sys/power/state" + +int +main(void) +{ + int fd, err = 0; + char *entry = "mem"; + size_t entry_size = 3; + + fd = open(SYS_POWER_STATE, O_WRONLY); + + if(fd == -1) + { + perror("memsys: open(\"" SYS_POWER_STATE "\")"); + err++; + } + else + { + if(write(fd, entry, entry_size) < (ssize_t)entry_size) + { + perror("memsys: write(fd, \"mem\", 3)"); + err++; + } + close(fd); + } + + return err; +} diff --git a/cmd/pat.1 b/cmd/pat.1 @@ -0,0 +1,29 @@ +.\" Collection of Unix tools, comparable to coreutils +.\" Copyright 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: MPL-2.0 +.Dd 2023-02-22 +.Dt PAT 1 +.Os +.Sh NAME +.Nm pat +.Nd print concatenated files +.Sh SYNOPSIS +.Nm +.Op Ar files ... +.Sh DESCRIPTION +.Nm +writes the number of files to be read, then in sequence: +prints their filename; then reads the +.Ar file +and writes it on the standard output. +If no +.Ar file +is given, +.Nm +uses standard input as one. +.Sh EXIT STATUS +.Ex -std +.Sh STANDARDS +None applicable. +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me diff --git a/cmd/pat.c b/cmd/pat.c @@ -0,0 +1,89 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +#include <errno.h> /* errno */ +#include <fcntl.h> /* open(), O_RDONLY */ +#include <stdio.h> /* fprintf(), fwrite(), BUFSIZ */ +#include <string.h> /* strerror(), strncmp() */ +#include <unistd.h> /* read(), close() */ + +int files = 1; + +int +concat(int fd, const char *fdname) +{ + ssize_t c; + char buf[BUFSIZ]; + + // File Number as an esccape/containment solution + printf("\n### File %d << %s >> ###\n", files++, fdname); + + while((c = read(fd, buf, sizeof(buf))) > 0) + { + if(fwrite(buf, (size_t)c, 1, stdout) < 0) + { + fprintf(stderr, "pat: Error writing: %s\n", strerror(errno)); + return 1; + } + } + + if(c < 0) + { + fprintf(stderr, "pat: Error reading ‘%s’: %s\n", fdname, strerror(errno)); + return 1; + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + if(argc <= 1) + { + printf("### 1 Files ###"); + return concat(0, "<stdin>"); + } + + // no \n, concat starts with one + printf("### %d Files ###", argc - 1); + + for(int argi = 1; argi < argc; argi++) + { + if(strncmp(argv[argi], "-", 2) == 0) + { + if(concat(0, "<stdin>") != 0) + { + return 1; + } + } + else if(strncmp(argv[argi], "--", 3) == 0) + { + continue; + } + else + { + int fd = open(argv[argi], O_RDONLY); + if(fd < 0) + { + fprintf(stderr, "pat: Error opening ‘%s’: %s\n", argv[argi], strerror(errno)); + return 1; + } + + if(concat(fd, argv[argi]) != 0) + { + return 1; + } + + if(close(fd) < 0) + { + fprintf(stderr, "pat: Error closing ‘%s’: %s\n", argv[argi], strerror(errno)); + return 1; + } + } + } + + return 0; +} diff --git a/cmd/sizeof.c b/cmd/sizeof.c @@ -0,0 +1,64 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#define FORMAT_M "sizeof(%s) == %d bytes; %d bits; (MIN:MAX) == (%d:%d)\n" + +static void +print_size(size_t size, char *type) +{ + int ret = printf("sizeof(%s) == %zd bytes; %zd bits\n", type, size, size * CHAR_BIT); + if(ret < 0) + { + exit(1); + } +} + +int +main(void) +{ + int c, ret; + + ret = printf("CHAR_BIT == %d\n", CHAR_BIT); + if(ret < 0) + { + exit(1); + } + + c = sizeof(int); + /* flawfinder: ignore. Not given by user but by a macro */ + ret = printf(FORMAT_M, "int", c, c * CHAR_BIT, INT_MIN, INT_MAX); + if(ret < 0) + { + exit(1); + } + c = sizeof(char); + /* flawfinder: ignore. Not given by user but by a macro */ + ret = printf(FORMAT_M, "char", c, c * CHAR_BIT, CHAR_MIN, CHAR_MAX); + if(ret < 0) + { + exit(1); + } + + print_size(sizeof(uint8_t), "uint8_t"); + print_size(sizeof(short), "short"); + print_size(sizeof(long), "long"); + print_size(sizeof(long long), "long long"); + print_size(sizeof(float), "float"); + print_size(sizeof(double), "double"); + print_size(sizeof(long double), "long double"); + print_size(sizeof(double long), "double long"); + print_size(sizeof(char[BUFSIZ]), "char[BUFSIZ]"); + print_size(sizeof(char[256]), "char[256]"); + print_size(sizeof(char[32]), "char[32]"); + print_size(sizeof(char[2]), "char[2]"); + print_size(sizeof('a'), "'a'"); + + return 0; +} diff --git a/cmd/sname.1 b/cmd/sname.1 @@ -0,0 +1,25 @@ +.\" Collection of Unix tools, comparable to coreutils +.\" Copyright 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: MPL-2.0 +.Dd 2021-04-05 +.Dt INAME 1 +.Os +.Sh NAME +.Nm sname +.Nd Structured output of system name and info +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +is equivalent to +.Xr uname 1 +except the output is a sorted tab-separated key-value list, which allows to be extracted and extended. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr look 1 , +.Xr uname 3 +.Sh STANDARDS +No applicable one known. +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me diff --git a/cmd/sname.c b/cmd/sname.c @@ -0,0 +1,38 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +// uname(2) isn't in POSIX +#define _GNU_SOURCE +#include <stdio.h> // printf(), perror() +#include <stdlib.h> // exit() +#include <sys/utsname.h> // utsname, uname() + +void +print_kv(char *key, char *value) +{ + if(printf("%s %s\n", key, value) < 0) + { + perror("sname: printf"); + exit(1); + } +} + +int +main() +{ + struct utsname name; + if(uname(&name) != 0) + { + perror("uname"); + return 1; + } + + print_kv("machine", name.machine); + print_kv("nodename", name.nodename); + print_kv("release", name.release); + print_kv("sysname", name.sysname); + print_kv("version", name.version); + + return 0; +} diff --git a/cmd/xcd.c b/cmd/xcd.c @@ -0,0 +1,259 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +#include <ctype.h> /* isprint() */ +#include <errno.h> /* errno */ +#include <math.h> /* sin() */ +#include <stdint.h> /* uint8_t */ +#include <stdio.h> /* printf(), fread(), fopen(), fclose() */ +#include <string.h> /* memset(), strerror() */ + +struct rgb +{ + double red, green, blue; +}; + +static struct rgb +rgb_char(unsigned char i) +{ + double freq = 0.018; + + if(i == 0) + { + return (struct rgb){64, 64, 64}; + } + else + { + struct rgb color; + double pi = 3.14159; + + color.red = sin(freq * i + 0 * pi / 3) * 127 + 128; + color.green = sin(freq * i + 2 * pi / 3) * 127 + 128; + color.blue = sin(freq * i + 4 * pi / 3) * 127 + 128; + + return color; + } +} + +static int +print_hex_rgb(unsigned char c) +{ + struct rgb color = rgb_char(c); + + int ret = printf("[38;2;%d;%d;%dm%02hhx ", (int)color.red, (int)color.green, (int)color.blue, c); + + return (ret <= 0) ? 1 : 0; +} + +static int +print_xcd_reset() +{ + int ret = printf(""); + + return (ret <= 0) ? 1 : 0; +} + +static int +print_plain_rgb(char *line, size_t len) +{ + if(print_xcd_reset() != 0) + { + return 1; + } + + if(printf(" >") <= 0) + { + return 1; + } + + for(size_t i = 0; i < len; i++) + { + struct rgb color = rgb_char((unsigned char)line[i]); + int ret = 0; + + ret = printf("[38;2;%d;%d;%dm%c", + (int)color.red, + (int)color.green, + (int)color.blue, + isprint(line[i]) ? line[i] : '.'); + + if(ret <= 0) + { + return 1; + } + } + + if(print_xcd_reset() != 0) + { + return 1; + } + + if(printf("<") <= 0) + { + return 1; + } + + return 0; +} + +#define WIDTH 16 + +int +concat(FILE *stream) +{ + int cols = 0; + char line[WIDTH]; + char c; + unsigned int bytes = 0; + struct rgb pos_rgb; + int ret = 0; + errno = 0; + + memset(&line, 0, WIDTH); + + if(print_xcd_reset() != 0) + { + goto werr; + } + + pos_rgb = rgb_char((unsigned char)bytes); + + ret = printf( + "[38;2;%d;%d;%dm0x%06x ", (int)pos_rgb.red, (int)pos_rgb.green, (int)pos_rgb.blue, bytes); + if(ret <= 0) + { + goto werr; + } + + while(fread(&c, 1, 1, stream) > 0) + { + if(cols >= WIDTH) + { + print_plain_rgb(line, (size_t)cols); + memset(&line, 0, WIDTH); + + pos_rgb = rgb_char((unsigned char)bytes); + ret = printf("\n[38;2;%d;%d;%dm0x%06x ", + (int)pos_rgb.red, + (int)pos_rgb.green, + (int)pos_rgb.blue, + bytes); + if (ret <= 0) + { + goto werr; + } + + cols = 0; + } + + ret = print_hex_rgb((unsigned char)c); + if(ret != 0) + { + goto werr; + } + + line[cols] = c; + + cols++; + bytes++; + } + + // Fill the rest of the hex space with spaces + for(; cols < WIDTH; cols++) + { + ret = printf(" "); + + if(ret <= 0) + { + goto werr; + } + } + + if(print_xcd_reset() != 0) + { + goto werr; + } + + ret = print_plain_rgb(line, (size_t)cols); + if(ret != 0) + { + goto werr; + } + + pos_rgb = rgb_char((unsigned char)bytes); + ret = printf( + "\n[38;2;%d;%d;%dm0x%06x\n", (int)pos_rgb.red, (int)pos_rgb.green, (int)pos_rgb.blue, bytes); + if(ret <= 0) + { + goto werr; + } + + if(print_xcd_reset() != 0) + { + goto werr; + } + + return 0; + +werr: + fprintf(stderr, "\nxcd: Write error: %s\n", strerror(errno)); + return 1; +} + +int +main(int argc, char *argv[]) +{ + int err = 0; + + if(argc <= 1) + { + if(concat(stdin) != 0) + { + err = 1; + goto cleanup; + } + } + else + { + for(int argi = 1; (err == 0) && (argi < argc); argi++) + { + if(strncmp(argv[argi], "-", 2) == 0) + { + if(concat(stdin) != 0) + { + err = 1; + goto cleanup; + } + } + else + { + FILE *file = fopen(argv[argi], "r"); + if(!file) + { + fprintf(stderr, "\nxcd: Error opening ‘%s’: %s\n", argv[argi], strerror(errno)); + err = 1; + goto cleanup; + } + else + { + err += concat(file); + err += fclose(file); + + if(err != 0) + { + fprintf(stderr, "\nxcd: Error closing ‘%s’: %s\n", argv[argi], strerror(errno)); + err = 1; + goto cleanup; + } + } + } + } + } + +cleanup: + printf(""); + + return err; +} diff --git a/configure b/configure @@ -0,0 +1,223 @@ +#!/bin/sh +# SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +DEPS="" + +usage() { +cat <<END +Usage: [variables] configure [variables] + +Variables: + PREFIX=DIR + BINDIR=DIR + SHELLDIR=DIR + PERLDIR=DIR + MANDIR=DIR + + PKGCONFIG=BIN + MSGFMT=BIN + CC=BIN + MAKE=BIN + MANDOC=BIN + SHELLCHECK=BIN + FLAWFINDER=BIN + GCOV=BIN + REUSE=BIN + + CFLAGS=OPTIONS + LDFLAGS=OPTIONS + EXTRA_CFLAGS=OPTIONS + +Variables are set in the following order: Default, Environment, Arguments + +Dependencies: See the README.md file +END +} + +is_ok() { + status="$?" + + if test $status -eq 0; then + printf " OK\n" + else + printf " FAIL\n" + fi + + return $status +} + +or_die() { + is_ok || exit 1 +} + +pkg_config_check() { + printf 'Checking: %s %s ...' "${PKGCONFIG}" "$*" + "${PKGCONFIG}" "$@" + is_ok +} + +gen_targets() { + printf 'EXE = ' + printf '%s\n ' cmd/*.c | sed 's;.c$;;' | tr -d '\n' + echo + + printf 'MAN1 = ' + printf '%s\n ' cmd/*.1 | tr -d '\n' + echo +} + +check_cmd() { + var="$1" + full_cmd="$2" + + # shellcheck disable=SC2086 + set -- $full_cmd + cmd="$1" + + printf 'Checking $%s = %s command existance ...' "$var" "$full_cmd" + command -v "$cmd" >/dev/null ; is_ok +} + +## User configuration + +# defaults +PREFIX="${PREFIX:-/usr/local}" + +PKGCONFIG="${PKGCONFIG:-pkg-config}" +MSGFMT="${MSGFMT:-msgfmt}" +CC="${CC:-cc}" +MAKE="${MAKE:-make}" +GCOV="${GCOV:-gcov}" +# -DDEBUG: Otherwise assert() does nothing, fine to be removed in production +CFLAGS="${CFLAGS:--g -O2 -DDEBUG}" +MANDOC="${MANDOC:-mandoc}" +SHELLCHECK="${SHELLCHECK:-shellcheck}" +FLAWFINDER="${FLAWFINDER:-flawfinder}" +REUSE="${REUSE:-reuse}" + +# Also allow variables through arguments +for i; do + case "$i" in + -h|--help) + usage + exit 1 + ;; + -*) + printf "Unknown argument ‘%s’\n" "${i}" + usage + exit 1 + ;; + *=*) + # shellcheck disable=SC2163 + export "$i" + shift + ;; + *) + printf "Unknown argument ‘%s’\n" "${i}" + usage + exit 1 + ;; + esac +done + +# Fallback definitions for dirs, based on $PREFIX +BINDIR="${BINDIR:-${PREFIX}/bin}" +SHELLDIR="${SHELLDIR:-${BINDIR}}" +PERLDIR="${PERLDIR:-${BINDIR}}" +MANDIR="${MANDIR:-${PREFIX}/share/man}" + +# Add some extra CFLAGS +CFLAGS="${CFLAGS} ${EXTRA_CFLAGS}" + +printf 'Pruning old configurations ...' +rm -f config.mk ; or_die + +## System checks +# commands +check_cmd PKGCONFIG "$PKGCONFIG" || exit 1 +check_cmd CC "$CC" || exit 1 +check_cmd MAKE "$MAKE" || exit 1 + +if check_cmd GCOV "$GCOV" +then + : +else + echo 'Notice: "coverage" target will fail' + GCOV="false" +fi + +if check_cmd MANDOC "$MANDOC" +then + : +else + echo 'Notice: Linting depending on mandoc disabled' + MANDOC="true" +fi + +if check_cmd SHELLCHECK "$SHELLCHECK" +then + : +else + echo 'Notice: Linting depending on shellcheck disabled' + SHELLCHECK="true" +fi + +if check_cmd FLAWFINDER "$FLAWFINDER" +then + : +else + echo 'Notice: Linting depending on flawfinder disabled' + FLAWFINDER="true" +fi + +if check_cmd REUSE "$REUSE" +then + : +else + echo 'Notice: Copyright linting depending on reuse disabled' + REUSE="true" +fi + +echo + +# pkg-config +for dep in ${DEPS} +do + pkg_config_check --exists "$dep" || exit 1 +done + +echo + +## Configuration write + +printf 'Writing to config.mk ...' +cat >config.mk <<EOF +# Autogenerated by ./configure +PREFIX = ${PREFIX} +BINDIR = ${BINDIR} +SHELLDIR = ${SHELLDIR} +PERLDIR = ${PERLDIR} +MANDIR = ${MANDIR} + +PKGCONFIG = ${PKGCONFIG} +CC = ${CC} +MAKE = ${MAKE} +MANDOC = ${MANDOC} +SHELLCHECK = ${SHELLCHECK} +FLAWFINDER = ${FLAWFINDER} +MSGFMT = ${MSGFMT} +DBG = ${DBG} +GCOV = ${GCOV} +REUSE = ${REUSE} + +CFLAGS = ${CFLAGS} +LDFLAGS = ${LDFLAGS} +EOF +is_ok + +gen_targets >> config.mk ; or_die + +echo + +echo 'Done, you can now run make' diff --git a/test-cmd/Kyuafile b/test-cmd/Kyuafile @@ -0,0 +1,20 @@ +-- SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +-- SPDX-License-Identifier: MPL-2.0 +syntax(2) + +test_suite("utils") + +basedir = fs.dirname(fs.dirname(current_kyuafile())) + +-- 9,$|LC_ALL=C.UTF-8 sort +-- atf_test_program{name="pat", required_files=basedir.."/cmd/pat", timeout=1} +atf_test_program{name="args", required_files=basedir.."/cmd/args", timeout=1} +atf_test_program{name="del", required_files=basedir.."/cmd/del", timeout=1} +atf_test_program{name="errno", required_files=basedir.."/cmd/errno", timeout=1} +atf_test_program{name="humanize", required_files=basedir.."/cmd/humanize", timeout=1} +atf_test_program{name="lolcat", required_files=basedir.."/cmd/lolcat", timeout=1} +atf_test_program{name="mdate", required_files=basedir.."/cmd/mdate", timeout=1} +atf_test_program{name="memsys", required_files=basedir.."/cmd/memsys", timeout=1} +atf_test_program{name="sizeof", required_files=basedir.."/cmd/sizeof", timeout=1} +atf_test_program{name="sname", required_files=basedir.."/cmd/sname", timeout=1} +atf_test_program{name="xcd", required_files=basedir.."/cmd/xcd", timeout=1} diff --git a/test-cmd/args b/test-cmd/args @@ -0,0 +1,47 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + + +atf_test_case noargs +noargs_body() { + atf_check -o 'inline:argc: 1 +argv[0]: "../cmd/args" +' ../cmd/args +} + +atf_test_case onearg +onearg_body() { + atf_check -o 'inline:argc: 2 +argv[0]: "../cmd/args" +argv[1]: "a" +' ../cmd/args a +} + +atf_test_case twoargs +twoargs_body() { + atf_check -o 'inline:argc: 3 +argv[0]: "../cmd/args" +argv[1]: "a" +argv[2]: "b c" +' ../cmd/args a 'b c' +} + +atf_test_case options +options_body() { + atf_check -o 'inline:argc: 5 +argv[0]: "../cmd/args" +argv[1]: "-1" +argv[2]: "+2" +argv[3]: "--3" +argv[4]: "/4" +' ../cmd/args -1 +2 --3 /4 +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + atf_add_test_case noargs + atf_add_test_case onearg + atf_add_test_case twoargs + atf_add_test_case options +} diff --git a/test-cmd/date b/test-cmd/date @@ -0,0 +1,93 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case noargs +noargs_body() { + atf_check -o not-empty ../cmd/date +} + +atf_test_case badarg +badarg_body() { + atf_check -s 'exit:1' -e "inline:date: Error: Unrecognised option: '-x'\ndate [-uR][-d datetime] [+format]\n" ../cmd/date -x +} + +atf_test_case epoch +epoch_body() { + atf_check -o "match:^[0-9]+$" ../cmd/date '+%s' + atf_check -o "inline:1155544496\n" ../cmd/date -uR -d @1155544496 '+%s' +} + +atf_test_case rfc3339 +rfc3339_body() { + atf_check -o "match:^[0-9]{4}\-[0-9]{2}\-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\+[0-9]{4}$" ../cmd/date '+%FT%T%z' + atf_check -o "match:^2006\-08\-14T08:34:56[+\-]00:?00$" ../cmd/date -uR -d @1155544496 '+%FT%T%z' +} + +atf_test_case rfc5322 +rfc5322_body() { + atf_check -o "match:^Mon, 14 Aug 2006 08:34:56 [+\-]00:?00$" ../cmd/date -uR -d @1155544496 +} + +atf_test_case empty +empty_body() { + atf_check -o 'inline:\n' ../cmd/date '+' +} + +atf_test_case echolike +echolike_body() { + atf_check -o 'inline:hello world\n' ../cmd/date '+hello world' +} + +atf_test_case devfull +devfull_body() { + has_glibc && atf_expect_fail "glibc ignoring write errors for puts()" + [ "$(uname -s)" = "NetBSD" ] && atf_expect_fail "NetBSD ignoring write errors for puts()" + [ "$(uname -s)" = "FreeBSD" ] && atf_expect_fail "FreeBSD ignoring write errors for puts()" + + atf_check -s exit:1 -e 'inline:date: puts: No space left on device\n' sh -c '../cmd/date >/dev/full' +} + +atf_test_case utc +utc_body() { + atf_check -o "match:^[0-9]+$" ../cmd/date -u '+%s' +} + +atf_test_case timestamp +timestamp_body() { + atf_check -o "inline:1970-01-01T00:00:00\n" ../cmd/date -u -d @0 '+%FT%T' + atf_check -o "inline:1970-01-01T00:01:09\n" ../cmd/date -u -d @69 '+%FT%T' + atf_check -o "inline:1969-12-31T23:58:51\n" ../cmd/date -u -d @-69 '+%FT%T' + + atf_check -s 'exit:1' -e "inline:date: Error: Missing operand for option: '-d'\ndate [-uR][-d datetime] [+format]\n" ../cmd/date -u -d + + # 36893488147419103232 = 2^65 + atf_check -s 'exit:1' -e not-empty ../cmd/date -u -d @36893488147419103232 +} + +atf_test_case isodate +isodate_body() { + atf_check -o "inline:0\n" ../cmd/date -u -d "1970-01-01T00:00:00Z" '+%s' + atf_check -o "inline:69\n" ../cmd/date -u -d "1970-01-01T00:01:09Z" '+%s' + atf_check -o "inline:-69\n" ../cmd/date -u -d "1969-12-31T23:58:51Z" '+%s' +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + + . ../test_functions.sh + + atf_add_test_case noargs + atf_add_test_case badarg + atf_add_test_case empty + atf_add_test_case echolike + atf_add_test_case devfull + + atf_add_test_case epoch + atf_add_test_case rfc3339 + atf_add_test_case rfc5322 + atf_add_test_case utc + + atf_add_test_case timestamp + atf_add_test_case isodate +} diff --git a/test-cmd/del b/test-cmd/del @@ -0,0 +1,57 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case basic cleanup +basic_body() { + touch inputs/del-test || atf_fail "touching del-test" + atf_check ../cmd/del inputs/del-test + + mkdir -p inputs/del-test.d || atf_fail "mkdir del-test.d" + atf_check ../cmd/del inputs/del-test.d +} +basic_cleanup() { + rm -fr inputs/del-test inputs/del-test.d || atf_fail "rm inputs/del-test{,.d}" +} + +atf_test_case nopermf cleanup +nopermf_body() { + touch inputs/chmod_000 || atf_fail "touching chmod_000" + chmod 0000 inputs/chmod_000 || atf_fail "chmod 0000 chmod_000" + + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:del: Error opening ‘inputs/chmod_000’: Permission denied\n' ../cmd/del inputs/chmod_000 +} +nopermf_cleanup() { + rm -fr inputs/chmod_000 inputs/chmod_000.d || atf_fail "rm chmod_000{,.d}" +} + +atf_test_case nopermd cleanup +nopermd_body() { + mkdir -p inputs/chmod_000.d || atf_fail "mkdir chmod_000.d" + chmod 0000 inputs/chmod_000.d || atf_fail "chmod 0000 chmod_000.d" + + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:del: Error opening ‘inputs/chmod_000’: Permission denied\n' ../cmd/del inputs/chmod_000.d +} +nopermd_cleanup() { + rm -fr inputs/chmod_000 inputs/chmod_000.d || atf_fail "rm chmod_000{,.d}" +} + +atf_test_case enoent +enoent_body() { + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:del: remove(/var/empty/e/no/ent) error: No such file or directory\n' ../cmd/del /var/empty/e/no/ent +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + atf_add_test_case basic + + # None of the supported implementations of remove(3) seem to have checks + # based on file mode + #atf_add_test_case nopermf + #atf_add_test_case nopermd + + atf_add_test_case enoent +} diff --git a/test-cmd/errno b/test-cmd/errno @@ -0,0 +1,51 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case simple +simple_body() { + atf_check -o "inline:Operation not permitted\n" ../cmd/errno 1 +} + +atf_test_case noargs +noargs_body() { + atf_check -s exit:1 -o "inline:usage: errno <number>\n" ../cmd/errno +} + +atf_test_case devfull +devfull_body() { + has_glibc && atf_expect_fail "glibc ignoring write errors for puts()" + [ "$(uname -s)" = "NetBSD" ] && atf_expect_fail "NetBSD ignoring write errors for puts()" + [ "$(uname -s)" = "FreeBSD" ] && atf_expect_fail "FreeBSD ignoring write errors for puts()" + + atf_check -s exit:1 sh -c '../cmd/errno 1 >/dev/full' +} + +atf_test_case einval +einval_body() { + if has_glibc; then + atf_check -s exit:0 -o "inline:Unknown error -1\n" ../cmd/errno -1 + elif has_musl; then + atf_check -s exit:0 -o "inline:No error information\n" ../cmd/errno -1 + else + atf_check -s exit:1 -e "inline:errno: strerror: Invalid argument\n" ../cmd/errno -1 + fi +} + +atf_test_case erange +erange_body() { + # 36893488147419103232 = 2^65 + atf_check -s 'exit:1' -e not-empty ../cmd/errno 36893488147419103232 +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + + . ../test_functions.sh + + atf_add_test_case simple + atf_add_test_case noargs + atf_add_test_case devfull + atf_add_test_case einval + atf_add_test_case erange +} diff --git a/test-cmd/humanize b/test-cmd/humanize @@ -0,0 +1,107 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case one +one_body() { + atf_check -o "inline:1 \n" ../cmd/humanize 1 + atf_check -o "inline:1 \n" ../cmd/humanize -d 1 + atf_check -o "inline:1 B\n" ../cmd/humanize -b 1 +} + +atf_test_case two +two_body() { + atf_check -o "inline:12 \n" ../cmd/humanize 12 + atf_check -o "inline:12 \n" ../cmd/humanize -d 12 + atf_check -o "inline:12 B\n" ../cmd/humanize -b 12 +} + +atf_test_case three +three_body() { + atf_check -o "inline:123 \n" ../cmd/humanize 123 + atf_check -o "inline:123 \n" ../cmd/humanize -d 123 + atf_check -o "inline:123 B\n" ../cmd/humanize -b 123 +} + +# 1 234 +atf_test_case four +four_body() { + atf_check -o "inline:1.234 k\n" ../cmd/humanize 1234 + atf_check -o "inline:1.234 k\n" ../cmd/humanize -d 1234 + atf_check -o "inline:1.20508 KiB\n" ../cmd/humanize -b 1234 +} + +# 12 345 +atf_test_case five +five_body() { + atf_check -o "inline:12.345 k\n" ../cmd/humanize 12345 + atf_check -o "inline:12.345 k\n" ../cmd/humanize -d 12345 + atf_check -o "inline:12.0557 KiB\n" ../cmd/humanize -b 12345 +} + +# 123 456 +atf_test_case six +six_body() { + atf_check -o "inline:123.456 k\n" ../cmd/humanize 123456 + atf_check -o "inline:123.456 k\n" ../cmd/humanize -d 123456 + atf_check -o "inline:120.562 KiB\n" ../cmd/humanize -b 123456 +} + +# 1234 4567 +atf_test_case seven +seven_body() { + atf_check -o "inline:1.23457 M\n" ../cmd/humanize 1234567 + atf_check -o "inline:1.23457 M\n" ../cmd/humanize -d 1234567 + atf_check -o "inline:1.17737 MiB\n" ../cmd/humanize -b 1234567 +} + +atf_test_case noarg +noarg_body() { + atf_check -s exit:1 -e 'inline:Usage: humanize [-bdt] number\n' ../cmd/humanize +} + +atf_test_case badflag +badflag_body() { + atf_check -s exit:1 -e "inline:humanize: Error: Unrecognised option: '-f'\n"'Usage: humanize [-bdt] number\n' ../cmd/humanize -f + atf_check -s exit:1 -e "inline:humanize: Error: Unrecognised option: '-f'\n"'Usage: humanize [-bdt] number\n' ../cmd/humanize -f 123 +} + +atf_test_case duration +duration_body() { + atf_check -o "inline:1s\n" ../cmd/humanize -t 1 + atf_check -o "inline:1m \n" ../cmd/humanize -t 60 + atf_check -o "inline:1h \n" ../cmd/humanize -t 3600 + atf_check -o "inline:1日 \n" ../cmd/humanize -t 86400 + atf_check -o "inline:1月 \n" ../cmd/humanize -t 2629743 + atf_check -o "inline:1年 \n" ../cmd/humanize -t 31556926 + atf_check -o "inline:1年 1月 1日 1h 1m 1s\n" ../cmd/humanize -t 34276730 +} + +atf_test_case limits +limits_body() { + atf_check -o 'inline:9.22337 E\n' ../cmd/humanize 9223372036854775806 + atf_check -s exit:1 -e 'inline:humanize: 9223372036854775808 is too large\n' ../cmd/humanize 9223372036854775808 + + # Not as great as it should but at least not a lie + atf_check -o 'inline:-9.22337e+18 \n' ../cmd/humanize -- -9223372036854775807 + + atf_check -s exit:1 -e 'inline:humanize: -9223372036854775809 is too small\n' ../cmd/humanize -- -9223372036854775809 +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + + atf_add_test_case noarg + atf_add_test_case badflag + atf_add_test_case limits + + atf_add_test_case one + atf_add_test_case two + atf_add_test_case three + atf_add_test_case four + atf_add_test_case five + atf_add_test_case six + atf_add_test_case seven + + atf_add_test_case duration +} diff --git a/test-cmd/inputs/all_bytes b/test-cmd/inputs/all_bytes Binary files differ. diff --git a/test-cmd/lolcat b/test-cmd/lolcat @@ -0,0 +1,92 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case allfile +allfile_body() { + atf_check -o file:outputs/lolcat/all_bytes ../cmd/lolcat inputs/all_bytes +} + +atf_test_case allinput +allinput_body() { + atf_check -o file:outputs/lolcat/all_bytes ../cmd/lolcat <inputs/all_bytes +} + +atf_test_case alldashinput +alldashinput_body() { + atf_check -o file:outputs/lolcat/all_bytes ../cmd/lolcat - <inputs/all_bytes +} + +atf_test_case devnull +devnull_body() { + if [ "$(uname -s)" = "FreeBSD" ] || [ "$(uname -s)" = "NetBSD" ]; then + # FIXME + atf_check -s exit:1 -e 'inline:\nlolcat: Read error: Inappropriate ioctl for device\n' -o 'inline:' ../cmd/lolcat /dev/null + atf_check -s exit:1 -e 'inline:\nlolcat: Read error: Inappropriate ioctl for device\n' -o 'inline:' ../cmd/lolcat </dev/null + atf_check -s exit:1 -e 'inline:\nlolcat: Read error: Inappropriate ioctl for device\n' -o 'inline:' ../cmd/lolcat - </dev/null + else + atf_check -o 'inline:' ../cmd/lolcat /dev/null + atf_check -o 'inline:' ../cmd/lolcat </dev/null + atf_check -o 'inline:' ../cmd/lolcat - </dev/null + fi +} + +atf_test_case noperm cleanup +noperm_body() { + touch inputs/chmod_000 || atf_fail "touching chmod_000" + chmod 0000 inputs/chmod_000 || atf_fail "chmod 0000 chmod_000" + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:\nlolcat: Error opening ‘inputs/chmod_000’: Permission denied\n' -o 'inline:' ../cmd/lolcat inputs/chmod_000 +} +noperm_cleanup() { + chmod 0600 inputs/chmod_000 || atf_fail "chmod 0600 chmod_000" + rm inputs/chmod_000 || atf_fail "rm chmod_000" +} + +atf_test_case devfull +devfull_body() { + has_glibc && atf_expect_fail "glibc ignoring write errors for fputc()" + [ "$(uname -s)" = "FreeBSD" ] && atf_expect_fail "FreeBSD badly handling write errors" + [ "$(uname -s)" = "NetBSD" ] && atf_expect_fail "NetBSD badly handling write errors" + + atf_check -s exit:1 -e 'inline:\nlolcat: Write error: No space left on device\n' sh -c '../cmd/lolcat inputs/all_bytes >/dev/full' + atf_check -s exit:1 -e 'inline:\nlolcat: Write error: No space left on device\n' sh -c '../cmd/lolcat <inputs/all_bytes >/dev/full' + atf_check -s exit:1 -e 'inline:\nlolcat: Write error: No space left on device\n' sh -c '../cmd/lolcat - <inputs/all_bytes >/dev/full' +} + +atf_test_case readslash +readslash_body() { + [ "$(uname -s)" = "NetBSD" ] && atf_skip "NetBSD allows to read directories" + + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:\nlolcat: Read error: Is a directory\n' -o 'inline:' ../cmd/lolcat ./ +} + +atf_test_case enoent +enoent_body() { + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:\nlolcat: Error opening ‘/var/empty/e/no/ent’: No such file or directory\n' -o 'inline:' ../cmd/lolcat /var/empty/e/no/ent +} + +atf_test_case doubledash +doubledash_body() { + atf_check -o file:outputs/lolcat/all_bytes -- ../cmd/lolcat -- inputs/all_bytes + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:\nlolcat: Error opening ‘---’: No such file or directory\n' -o 'inline:' -- ../cmd/lolcat --- inputs/all_bytes +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + + . ../test_functions.sh + + atf_add_test_case allfile + atf_add_test_case allinput + atf_add_test_case alldashinput + atf_add_test_case devnull + atf_add_test_case noperm + atf_add_test_case devfull + atf_add_test_case readslash + atf_add_test_case enoent + atf_add_test_case doubledash +} diff --git a/test-cmd/mdate b/test-cmd/mdate @@ -0,0 +1,13 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case basic +basic_body() { + atf_check -o "match:^[0-9A-F]+,[0-9A-F]+$" ../cmd/mdate +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + atf_add_test_case basic +} diff --git a/test-cmd/memsys b/test-cmd/memsys @@ -0,0 +1,48 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + + +atf_test_case basic cleanup +basic_body() { + command -v "${BWRAP:-bwrap}" >/dev/null 2>/dev/null || atf_skip "${BWRAP:-bwrap} command not found" + [ -n "${NO_BWRAP}" ] && atf_skip "'NO_BWRAP' set" + + atf_check touch ./tmp-memsys + atf_check -- "${BWRAP:-bwrap}" --bind / / --bind ./tmp-memsys /sys/power/state ../cmd/memsys + atf_check -o 'inline:mem' cat tmp-memsys +} +basic_cleanup() { + rm -f tmp-memsys +} + +atf_test_case chmod_000 cleanup +chmod_000_body() { + command -v "${BWRAP:-bwrap}" >/dev/null 2>/dev/null || atf_skip "${BWRAP:-bwrap} command not found" + [ -n "${NO_BWRAP}" ] && atf_skip "'NO_BWRAP' set" + + atf_check touch ./tmp-memsys.000 + atf_check chmod 000 ./tmp-memsys.000 + atf_check -s exit:1 -e 'inline:memsys: open("/sys/power/state"): Permission denied\n' -- "${BWRAP:-bwrap}" --bind / / --bind ./tmp-memsys.000 /sys/power/state ../cmd/memsys + atf_check chmod 600 ./tmp-memsys.000 + atf_check -o empty cat tmp-memsys.000 +} +chmod_000_cleanup() { + rm -f tmp-memsys.000 +} + +atf_test_case devfull +devfull_body() { + command -v "${BWRAP:-bwrap}" >/dev/null 2>/dev/null || atf_skip "${BWRAP:-bwrap} command not found" + [ -n "${NO_BWRAP}" ] && atf_skip "'NO_BWRAP' set" + + atf_check -s exit:1 -e 'inline:memsys: open("/sys/power/state"): Permission denied\n' -- "${BWRAP:-bwrap}" --bind / / --bind /dev/full /sys/power/state ../cmd/memsys +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + + atf_add_test_case basic + atf_add_test_case chmod_000 + atf_add_test_case devfull +} diff --git a/test-cmd/mkdir b/test-cmd/mkdir @@ -0,0 +1,45 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case empty +empty_body() { + atf_check -s exit:1 -e 'inline:mkdir: missing operands\nUsage: mkdir [-p] [-m mode] [directories...]\n' ../cmd/mkdir +} + +atf_test_case foo cleanup +foo_body() { + atf_check rm -fr mkdir-foo + atf_check ../cmd/mkdir mkdir-foo + atf_check test -d mkdir-foo +} +foo_cleanup() { + rm -fr mkdir-foo +} + +atf_test_case p_flag cleanup +p_flag_body() { + atf_check rm -fr mkdir-p_flag/1/2/3 + atf_check -s exit:1 -e 'inline:mkdir: No such file or directory\n' ../cmd/mkdir mkdir-p_flag/1/2/3 + atf_check ../cmd/mkdir -p mkdir-p_flag/1/2/3 + atf_check test -d mkdir-p_flag/1/2/3 +} +p_flag_cleanup() { + rm -fr mkdir-p_flag/1/2/3 +} + +atf_test_case devnull +devnull_body() { + atf_check -s exit:1 -e 'inline:mkdir: File exists\n' ../cmd/mkdir /dev/null + atf_check -s exit:1 -e 'inline:mkdir: File exists\n' ../cmd/mkdir -p /dev/null + atf_check -s exit:1 -e 'inline:mkdir: Not a directory\n' ../cmd/mkdir /dev/null/no + atf_check -s exit:1 -e 'inline:mkdir: Not a directory\n' ../cmd/mkdir -p /dev/null/no +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + atf_add_test_case empty + atf_add_test_case foo + #atf_add_test_case p_flag + atf_add_test_case devnull +} diff --git a/test-cmd/outputs/lolcat/all_bytes b/test-cmd/outputs/lolcat/all_bytes Binary files differ. diff --git a/test-cmd/outputs/lolcat/all_bytes.l b/test-cmd/outputs/lolcat/all_bytes.l @@ -0,0 +1,12 @@ +\000\001\002\003\004\005\006\a\b\t$ +\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\ +\035\036\037 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX\ +YZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204\205\ +\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\ +\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\ +\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\ +\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\ +\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\ +\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\ +\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\ +\375\376\377$ diff --git a/test-cmd/outputs/xcd/all_bytes b/test-cmd/outputs/xcd/all_bytes @@ -0,0 +1,18 @@ +0x000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f  >................< +0x000010 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f  >................< +0x000020 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f  > !"#$%&'()*+,-./< +0x000030 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f  >0123456789:;<=>?< +0x000040 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  >@ABCDEFGHIJKLMNO< +0x000050 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f  >PQRSTUVWXYZ[\]^_< +0x000060 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f  >`abcdefghijklmno< +0x000070 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f  >pqrstuvwxyz{|}~.< +0x000080 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f  >................< +0x000090 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f  >................< +0x0000a0 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af  >................< +0x0000b0 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf  >................< +0x0000c0 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf  >................< +0x0000d0 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df  >................< +0x0000e0 e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef  >................< +0x0000f0 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff  >................< +0x000100 + +\ No newline at end of file diff --git a/test-cmd/outputs/xcd/null b/test-cmd/outputs/xcd/null @@ -0,0 +1,3 @@ +0x000000  >................< +0x000000 + +\ No newline at end of file diff --git a/test-cmd/pat b/test-cmd/pat @@ -0,0 +1,78 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case allfile +allfile_body() { + atf_check -o file:outputs/pat/all_bytes ../cmd/pat outputs/pat/all_bytes +} + +atf_test_case allinput +allinput_body() { + atf_check -o file:outputs/pat/all_bytes ../cmd/pat <outputs/pat/all_bytes +} + +atf_test_case alldashinput +alldashinput_body() { + atf_check -o file:outputs/pat/all_bytes ../cmd/pat - <outputs/pat/all_bytes +} + +atf_test_case devnull +devnull_body() { + atf_check ../cmd/pat /dev/null + atf_check ../cmd/pat </dev/null + atf_check ../cmd/pat - </dev/null +} + +atf_test_case noperm cleanup +noperm_body() { + touch inputs/chmod_000 || atf_fail "touching chmod_000" + chmod 0000 inputs/chmod_000 || atf_fail "chmod 0000 chmod_000" + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:pat: Error opening ‘inputs/chmod_000’: Permission denied\n' ../cmd/pat inputs/chmod_000 +} +noperm_cleanup() { + chmod 0600 inputs/chmod_000 || atf_fail "chmod 0600 chmod_000" + rm inputs/chmod_000 || atf_fail "rm chmod_000" +} + +atf_test_case devfull +devfull_body() { + atf_check -s exit:1 -e 'inline:pat: Error writing: No space left on device\n' sh -c '../cmd/pat outputs/pat/all_bytes >/dev/full' + atf_check -s exit:1 -e 'inline:pat: Error writing: No space left on device\n' sh -c '../cmd/pat <outputs/pat/all_bytes >/dev/full' + atf_check -s exit:1 -e 'inline:pat: Error writing: No space left on device\n' sh -c '../cmd/pat - <outputs/pat/all_bytes >/dev/full' +} + +atf_test_case readslash +readslash_body() { + [ "$(uname -s)" = "NetBSD" ] && atf_skip "NetBSD allows to read directories" + + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:pat: Error reading ‘/’: Is a directory\n' ../cmd/pat / +} + +atf_test_case enoent +enoent_body() { + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:pat: Error opening ‘/var/empty/e/no/ent’: No such file or directory\n' ../cmd/pat /var/empty/e/no/ent +} + +atf_test_case doubledash +doubledash_body() { + atf_check -o file:outputs/pat/all_bytes -- ../cmd/pat -- outputs/pat/all_bytes + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:pat: Error opening ‘---’: No such file or directory\n' -o empty -- ../cmd/pat --- outputs/pat/all_bytes +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + atf_add_test_case allfile + atf_add_test_case allinput + atf_add_test_case alldashinput + atf_add_test_case devnull + atf_add_test_case noperm + atf_add_test_case devfull + atf_add_test_case readslash + atf_add_test_case enoent + atf_add_test_case doubledash +} diff --git a/test-cmd/shellcheck b/test-cmd/shellcheck @@ -0,0 +1,14 @@ +#!/bin/sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +set -ex + +SHELLCHECK="${SHELLCHECK:-shellcheck}" + +${SHELLCHECK} "$0" + +cd "$(dirname "$0")" + +# shellcheck disable=SC2046 +${SHELLCHECK} --shell=sh -x $(grep -v -- '--' ./Kyuafile | grep atf_test_program | sed -E 's;^atf_test_program\{name="([^"]*)".*;./\1;') diff --git a/test-cmd/sizeof b/test-cmd/sizeof @@ -0,0 +1,26 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case empty +empty_body() { + atf_check -o not-empty ../cmd/sizeof +} + +atf_test_case devfull +devfull_body() { + has_glibc && atf_expect_fail "glibc ignoring write errors for printf()" + [ "$(uname -s)" = "FreeBSD" ] && atf_skip "FreeBSD ignoring write errors for printf()" + [ "$(uname -s)" = "NetBSD" ] && atf_skip "NetBSD ignoring write errors for printf()" + + atf_check -s exit:1 sh -c '../cmd/sizeof >/dev/full' +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + + . ../test_functions.sh + + atf_add_test_case empty + atf_add_test_case devfull +} diff --git a/test-cmd/sname b/test-cmd/sname @@ -0,0 +1,33 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case generic +generic_body() { + # sed is because of FreeBSD + atf_check -o "inline:machine $(uname -m) +nodename $(uname -n) +release $(uname -r) +sysname $(uname -s) +version $(uname -v | sed 's; *$;;') +" \ + ../cmd/sname +} + +atf_test_case devfull +devfull_body() { + has_glibc && atf_expect_fail "glibc ignoring write errors for puts()" + [ "$(uname -s)" = "NetBSD" ] && atf_expect_fail "NetBSD ignoring write errors for puts()" + [ "$(uname -s)" = "FreeBSD" ] && atf_expect_fail "FreeBSD ignoring write errors for puts()" + + atf_check -s exit:1 -e 'inline:sname: printf: No space left on device\n' sh -c '../cmd/sname >/dev/full' +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + + . ../test_functions.sh + + atf_add_test_case generic + atf_add_test_case devfull +} diff --git a/test-cmd/xcd b/test-cmd/xcd @@ -0,0 +1,61 @@ +#!/usr/bin/env atf-sh +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +atf_test_case openfile +openfile_body() { + atf_check -o file:outputs/xcd/all_bytes ../cmd/xcd inputs/all_bytes +} + +atf_test_case stdinput +stdinput_body() { + atf_check -o file:outputs/xcd/all_bytes ../cmd/xcd <inputs/all_bytes + atf_check -o file:outputs/xcd/all_bytes ../cmd/xcd - <inputs/all_bytes +} + +atf_test_case nullfile +nullfile_body() { + atf_check -o file:outputs/xcd/null ../cmd/xcd /dev/null +} + +atf_test_case nullinput +nullinput_body() { + atf_check -o file:outputs/xcd/null ../cmd/xcd </dev/null +} + +atf_test_case noperm cleanup +noperm_body() { + touch inputs/chmod_000 || atf_fail "touching chmod_000" + chmod 0000 inputs/chmod_000 || atf_fail "chmod 0000 chmod_000" + # shellcheck disable=SC1112 + atf_check -s exit:1 -e 'inline:\nxcd: Error opening ‘inputs/chmod_000’: Permission denied\n' -o 'inline:' ../cmd/xcd inputs/chmod_000 +} +noperm_cleanup() { + chmod 0600 inputs/chmod_000 || atf_fail "chmod 0600 chmod_000" + rm inputs/chmod_000 || atf_fail "rm chmod_000" +} + +atf_test_case devfull +devfull_body() { + has_glibc && atf_expect_fail "glibc ignoring write errors for printf()" + [ "$(uname -s)" = "NetBSD" ] && atf_expect_fail "NetBSD ignoring write errors for printf()" + [ "$(uname -s)" = "FreeBSD" ] && atf_expect_fail "FreeBSD ignoring write errors for printf()" + + # shellcheck disable=SC1112 + atf_check -s 'exit:1' -e 'inline:\nxcd: Write error: No space left on device\n\nxcd: Error closing ‘inputs/all_bytes’: No space left on device\n' sh -c '../cmd/xcd inputs/all_bytes >/dev/full' + atf_check -s 'exit:1' -e 'inline:\nxcd: Write error: No space left on device\n' sh -c '../cmd/xcd <inputs/all_bytes >/dev/full' + atf_check -s 'exit:1' -e 'inline:\nxcd: Write error: No space left on device\n' sh -c '../cmd/xcd - <inputs/all_bytes >/dev/full' +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" || exit 1 + + . ../test_functions.sh + + atf_add_test_case openfile + atf_add_test_case stdinput + atf_add_test_case nullfile + atf_add_test_case nullinput + atf_add_test_case noperm + atf_add_test_case devfull +} diff --git a/test_functions.sh b/test_functions.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + +has_glibc() { + test -f /usr/include/features.h && grep -q '#define\W__GLIBC__' /usr/include/features.h +} + +has_musl() { + command -v ldd && ldd 2>&1 | grep -q musl +}