diff options
author | Thomas Voss <thomas.voss@humanwave.nl> | 2025-09-05 15:46:26 +0200 |
---|---|---|
committer | Thomas Voss <thomas.voss@humanwave.nl> | 2025-09-05 15:46:26 +0200 |
commit | df78b72fe58251bb31c8bc5ab34460e44220383d (patch) | |
tree | e2fa5436c73d9f11ccf6e5cdce3450e3472c2a1f |
Genesis commit
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 21 | ||||
-rw-r--r-- | main.c | 184 | ||||
-rw-r--r-- | po/messages.pot | 38 | ||||
-rw-r--r-- | po/sv/LC_MESSAGES/messages.po | 40 |
5 files changed, 285 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10af64e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +tiktok +*.mo diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e5e7cb4 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +CC = cc +CFLAGS = -Wall -Wextra -Wpedantic -std=c23 \ + -I$$(brew --prefix gettext)/include \ + -L$$(brew --prefix gettext)/lib \ + -lintl + +all: tiktok + +tiktok: main.c + $(CC) $(CFLAGS) -o $@ $< + +extract: + xgettext --from-code=UTF-8 -k_ -o po/messages.pot main.c + find po -name '*.po' -exec msgmerge {} po/messages.pot -o {} \; + +translations: + find po -name '*.po' | while read -r file; do msgfmt "$$file" -o "$${file%po}mo"; done + +clean: + rm tiktok + find po -name '*.mo' -delete @@ -0,0 +1,184 @@ +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <getopt.h> +#include <libgen.h> +#include <locale.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <libintl.h> + +#define _(x) gettext(x) + +#define NSEC_PER_SEC UINT64_C(1'000'000'000) + +#if __APPLE__ +#define TIMER_ABSTIME (1) + +int clock_nanosleep(clockid_t, int, const struct timespec *, struct timespec *); +#endif + +time_t syncs(time_t), syncm(time_t), synch(time_t); + +static void +usage(const char *argv0, int code) +{ + fprintf(stderr, _("Usage: %s [-h] [-i interval] [format]\n"), argv0); + exit(code); +} + +int +main(int argc, char **argv) +{ + setlocale(LC_ALL, ""); + bindtextdomain("messages", "./po"); + textdomain("messages"); + + char interval = 's'; + const char *argv0 = basename(argv[0]); + const char *dfmt = "%c", *optstr = "hi:"; + static struct option longopts[] = { + {"help", no_argument, nullptr, 'h'}, + {"interval", required_argument, nullptr, 'i'}, + {nullptr, 0, nullptr, 0 }, + }; + + for (;;) { + int opt = getopt_long(argc, argv, optstr, longopts, nullptr); + if (opt == -1) + break; + + switch (opt) { + case 'h': + usage(argv0, EXIT_SUCCESS); + case 'i': + if (strlen(optarg) == 1) { + interval = *optarg; + if (interval == 's' || interval == 'm' || interval == 'h') + break; + } + errx(EXIT_FAILURE, + _("invalid interval ‘%s’\nRead the %s(1) manual page for valid intervals"), + optarg, argv0); + default: + usage(argv0, EXIT_FAILURE); + } + } + + argc -= optind; + argv += optind; + + if (argc > 1) + usage(argv0, EXIT_FAILURE); + if (argc != 0) + dfmt = argv[0]; + + time_t (*sync)(time_t) = + interval == 's' ? syncs + : interval == 'm' ? syncm + : interval == 'h' ? synch + : /* default */ syncs; + + for (;;) { + struct timespec before, after; + if (clock_gettime(CLOCK_REALTIME, &before) == -1) + warn(_("failed to get the time")); + + time_t now = time(NULL); + struct tm *tm = localtime(&now); + + char buf[256]; + size_t n = strftime(buf, sizeof(buf), dfmt, tm); + if (n == 0) + warnx(_("buffer too small")); + puts(buf); + + if (clock_gettime(CLOCK_REALTIME, &after) == -1) + warn(_("failed to get the time")); + + /* Duration of the clock formatting and printing */ + struct timespec Δ = { + after.tv_sec - before.tv_sec, + after.tv_nsec - before.tv_nsec, + }; + if (Δ.tv_nsec < 0) { + Δ.tv_nsec += NSEC_PER_SEC; + Δ.tv_sec--; + } + + struct timespec rqtp = { + .tv_sec = sync(after.tv_sec), + .tv_nsec = 0, + }; + + int rv; + do + rv = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &rqtp, nullptr); + while (rv == -1 && errno == EINTR); + } + + /* We should never get here */ + return EXIT_FAILURE; +} + +time_t +syncs(time_t n) +{ + return n + 1; +} + +time_t +syncm(time_t n) +{ + return n + 60 - n%60; +} + +time_t +synch(time_t n) +{ + return n + 3600 - n%3600; +} + +#if __APPLE__ + +int +clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *req, + struct timespec *rem) +{ + /* The rest not implemented */ + assert(flags == TIMER_ABSTIME); + + struct timespec now, Δ; + + if (clock_gettime(clock_id, &now) != 0) + return errno; + + Δ.tv_sec = req->tv_sec - now.tv_sec; + Δ.tv_nsec = req->tv_nsec - now.tv_nsec; + + if (Δ.tv_sec < 0) { + Δ.tv_sec = 0; + Δ.tv_nsec = 0; + goto out; + } + + if (Δ.tv_nsec < 0) { + if (Δ.tv_sec == 0) { + Δ.tv_sec = 0; + Δ.tv_nsec = 0; + goto out; + } + + Δ.tv_sec--; + Δ.tv_nsec += NSEC_PER_SEC; + } + +out: + return nanosleep(&Δ, rem); +} + +#endif diff --git a/po/messages.pot b/po/messages.pot new file mode 100644 index 0000000..01929bf --- /dev/null +++ b/po/messages.pot @@ -0,0 +1,38 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-09-05 15:43+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: main.c:30 +#, c-format +msgid "Usage: %s [-h] [-i interval] [format]\n" +msgstr "" + +#: main.c:65 +#, c-format +msgid "" +"invalid interval ‘%s’\n" +"Read the %s(1) manual page for valid intervals" +msgstr "" + +#: main.c:89 main.c:101 +msgid "failed to get the time" +msgstr "" + +#: main.c:97 +msgid "buffer too small" +msgstr "" diff --git a/po/sv/LC_MESSAGES/messages.po b/po/sv/LC_MESSAGES/messages.po new file mode 100644 index 0000000..be09d24 --- /dev/null +++ b/po/sv/LC_MESSAGES/messages.po @@ -0,0 +1,40 @@ +# Swedish translations for PACKAGE package. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Thomas Voß <mail@thomasvoss.com>, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-09-05 15:43+0200\n" +"PO-Revision-Date: 2025-09-05 15:18+0200\n" +"Last-Translator: Thomas Voss <mail@thomasvoss.com>\n" +"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n" +"Language: sv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: main.c:30 +#, c-format +msgid "Usage: %s [-h] [-i interval] [format]\n" +msgstr "Användning: %s [-h] [-i intervall] [format]\n" + +#: main.c:65 +#, c-format +msgid "" +"invalid interval ‘%s’\n" +"Read the %s(1) manual page for valid intervals" +msgstr "" +"ogiltigt intervall ”%s”\n" +"Läs %s(1)-manualsidan för giltiga intervall" + +#: main.c:89 main.c:101 +msgid "failed to get the time" +msgstr "misslyckades med att få tiden" + +#: main.c:97 +msgid "buffer too small" +msgstr "bufferten är för liten" |