aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2025-12-27 22:37:47 +0100
committerMattias Andrée <m@maandree.se>2025-12-27 22:37:47 +0100
commit4506c1e56ec89ad42c1a37f4b9b41204e4d991d1 (patch)
tree477d80ea90a4d827e70bb5d85781c1e366cee124
downloadgluhncheck-4506c1e56ec89ad42c1a37f4b9b41204e4d991d1.tar.gz
gluhncheck-4506c1e56ec89ad42c1a37f4b9b41204e4d991d1.tar.bz2
gluhncheck-4506c1e56ec89ad42c1a37f4b9b41204e4d991d1.tar.xz
First commit
Signed-off-by: Mattias Andrée <m@maandree.se>
-rw-r--r--.gitignore15
-rw-r--r--LICENSE15
-rw-r--r--Makefile37
-rw-r--r--README40
-rw-r--r--config.mk8
-rw-r--r--luhncheck.185
-rw-r--r--luhncheck.c81
7 files changed, 281 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..968c03a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*\#*
+*~
+*.o
+*.a
+*.lo
+*.su
+*.so
+*.so.*
+*.dll
+*.dylib
+*.gch
+*.gcov
+*.gcno
+*.gcda
+/luhncheck
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0e6be1c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+© 2025 Mattias Andrée <m@maandree.se>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2cf17d4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,37 @@
+.POSIX:
+
+CONFIGFILE = config.mk
+include $(CONFIGFILE)
+
+OBJ =\
+ luhncheck.o
+
+HDR =
+
+all: luhncheck
+$(OBJ): $(HDR)
+
+.c.o:
+ $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
+
+luhncheck: $(OBJ)
+ $(CC) -o $@ $(OBJ) $(LDFLAGS)
+
+install: luhncheck
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/bin"
+ mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1/"
+ cp -- luhncheck "$(DESTDIR)$(PREFIX)/bin/"
+ cp -- luhncheck.1 "$(DESTDIR)$(MANPREFIX)/man1/"
+
+uninstall:
+ -rm -f -- "$(DESTDIR)$(PREFIX)/bin/luhncheck"
+ -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/luhncheck.1"
+
+clean:
+ -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.gch *.gcov *.gcno *.gcda
+ -rm -f -- luhncheck
+
+.SUFFIXES:
+.SUFFIXES: .o .c
+
+.PHONY: all install uninstall clean
diff --git a/README b/README
new file mode 100644
index 0000000..08f88d5
--- /dev/null
+++ b/README
@@ -0,0 +1,40 @@
+NAME
+ luhncheck - Check number with Luhn's algorithm
+
+SYNOPSIS
+ luhncheck number ...
+
+DESCRIPTION
+ The luhncheck utility checks that a number, containing a
+ check digit, is valid according to Luhn's algorithm.
+
+OPTIONS
+ The luhncheck utility conforms to the Base Definitions volume
+ of POSIX.1-2017, Section 12.2, Utility Syntax Guidelines.
+
+ No options are supported.
+
+OPERANDS
+ The following operand is supported:
+
+ number
+ Number to check.
+
+STDOUT
+ The luhncheck utility will print one line per number, in the
+ order they are provided in the command line, to the standard
+ output. Each line will be use the format
+
+ "%s is %s\n", number, <"OK" if valid, "invalid" otherwise>.
+
+EXIT STATUS
+ The following exit values are returned:
+
+ 0 All numbers are valid.
+
+ 1 At least one number is invalid.
+
+ 2 An error occured.
+
+SEE ALSO
+ gluhncheck(1)
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..f4adf12
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,8 @@
+PREFIX = /usr
+MANPREFIX = $(PREFIX)/share/man
+
+CC = c99
+
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
+CFLAGS =
+LDFLAGS =
diff --git a/luhncheck.1 b/luhncheck.1
new file mode 100644
index 0000000..11da1e5
--- /dev/null
+++ b/luhncheck.1
@@ -0,0 +1,85 @@
+.TH LUHNCHECK 1 LUHNCHECK
+
+.SH NAME
+luhncheck \- Check number with Luhn's algorithm
+
+.SH SYNOPSIS
+.B luhncheck
+.IR number " ..."
+
+.SH DESCRIPTION
+The
+.B luhncheck
+utility checks that a number, containing a check digit,
+is valid according to Luhn's algorithm.
+
+.SH OPTIONS
+The
+.B luhncheck
+utility conforms to the Base Definitions volume of POSIX.1-2017,
+.IR "Section 12.2" ,
+.IR "Utility Syntax Guidelines" .
+.PP
+No options are supported.
+
+.SH OPERANDS
+The following operand is supported:
+.TP
+.I number
+Number to check.
+
+.SH STDIN
+Not used.
+
+.SH INPUT FILES
+None.
+
+.SH ENVIRONMENT VARIABLES
+No environment variables affect the execution of
+.BR luhncheck .
+
+.SH ASYNCHRONOUS EVENTS
+Default.
+
+.SH STDOUT
+The
+.B luhncheck
+utility will print one line per
+.IR number ,
+in the order they are provided in the command line,
+to the standard output.
+Each line will be use the format
+
+.RS
+.B \(dq%s is %s\en\(dq,
+.IB number ,
+.RI < \fB\(dqOK\(dq\fP\ if\ valid,\ \fB\(dqinvalid\(dq\fP\ otherwise >.
+.RE
+
+.SH STDERR
+The standard error is only used for diagnostic messages.
+
+.SH OUTPUT FILES
+None.
+
+.SH EXTENDED DESCRIPTION
+None.
+
+.SH EXIT STATUS
+The following exit values are returned:
+.TP
+0
+All
+.IR number s
+are valid.
+.TP
+1
+At least one
+.I number
+is invalid.
+.TP
+2
+An error occured.
+
+.SH SEE ALSO
+.BR gluhncheck (1)
diff --git a/luhncheck.c b/luhncheck.c
new file mode 100644
index 0000000..51f04ab
--- /dev/null
+++ b/luhncheck.c
@@ -0,0 +1,81 @@
+/* See LICENSE file for copyright and license details. */
+#include <libsimple-arg.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+NUSAGE(2, "number ...");
+
+
+static int
+check_luhn(const char *s)
+{
+ unsigned sum = 0;
+
+ switch (strlen(s) & 1U) {
+ for (;;) {
+ case 0:
+ if ('0' <= *s && *s <= '4')
+ sum += 2U * (unsigned)(*s - '0');
+ else if ('5' <= *s && *s <= '9')
+ sum += 2U * (unsigned)(*s - '0') - 9U;
+ else if (*s)
+ return 0;
+ else
+ break;
+ s++;
+
+ case 1:
+ if ('0' <= *s && *s <= '9')
+ sum += (unsigned)(*s - '0');
+ else if (*s)
+ return 0;
+ else
+ break;
+ s++;
+
+ sum %= 10U;
+ }
+ }
+
+ return (sum % 10U) == 0U;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int use_colour;
+ int ret = 0;
+ int r, ok;
+
+ ARGBEGIN {
+ default:
+ usage();
+ } ARGEND;
+
+ if (!argc)
+ usage();
+
+ use_colour = isatty(STDOUT_FILENO);
+
+ for (; *argv; argv++) {
+ ok = check_luhn(*argv);
+ ret |= !ok;
+ r = printf("%s%s %s%s\n", use_colour ? ok ? "\033[1;32m" : "\033[1;31m" : "",
+ *argv, ok ? "is OK" : "is invalid", use_colour ? "\033[m" : "");
+ if (r < 0) {
+ fprintf(stderr, "%s: printf: %s\n", argv0, strerror(errno));
+ exit(2);
+ }
+ }
+
+ if (fflush(stdout) || fclose(stdout)) {
+ fprintf(stderr, "%s: printf: %s\n", argv0, strerror(errno));
+ exit(2);
+ }
+
+ return ret;
+}