tfirst commit - stopwatch - simple timer for console or x root window
 (HTM) git clone git://src.adamsgaard.dk/stopwatch
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 3390af26250ccad0fd7e796c2325c94f5dbe538c
 (HTM) Author: Anders Damsgaard <anders@adamsgaard.dk>
       Date:   Wed,  4 Nov 2020 14:56:30 +0100
       
       first commit
       
       Diffstat:
         A LICENSE                             |      15 +++++++++++++++
         A Makefile                            |      63 +++++++++++++++++++++++++++++++
         A README                              |      18 ++++++++++++++++++
         A stopwatch.1                         |      45 +++++++++++++++++++++++++++++++
         A stopwatch.c                         |     105 +++++++++++++++++++++++++++++++
       
       5 files changed, 246 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/LICENSE b/LICENSE
       t@@ -0,0 +1,15 @@
       +ISC License
       +
       +Copyright (c) 2020 Anders Damsgaard <anders@adamsgaard.dk>
       +
       +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.
 (DIR) diff --git a/Makefile b/Makefile
       t@@ -0,0 +1,63 @@
       +.POSIX:
       +
       +NAME = stopwatch
       +VERSION = 0.1
       +
       +# paths
       +PREFIX = /usr/local
       +MANPREFIX = ${PREFIX}/share/man
       +DOCPREFIX = ${PREFIX}/share/doc/${NAME}
       +
       +BIN = ${NAME}
       +SRC = ${BIN:=.c}
       +MAN1 = ${BIN:=.1}
       +DOC = \
       +        LICENSE\
       +        README
       +
       +all: ${BIN}
       +
       +CFLAGS = -DVERSION=\"${VERSION}\" -I/usr/X11R6/include
       +LDFLAGS = -L/usr/X11R6/lib -lX11
       +
       +${BIN}: ${@:=.o}
       +
       +.o:
       +        ${CC} ${LDFLAGS} -o $@ $<
       +
       +.c.o:
       +        ${CC} ${CFLAGS} ${CPPFLAGS} -o $@ -c $<
       +
       +dist:
       +        rm -rf "${NAME}-${VERSION}"
       +        mkdir -p "${NAME}-${VERSION}"
       +        cp -f ${MAN1} ${DOC} ${SRC} Makefile "${NAME}-${VERSION}"
       +        tar cf - "${NAME}-${VERSION}" | gzip -c > "${NAME}-${VERSION}.tar.gz"
       +        rm -rf "${NAME}-${VERSION}"
       +
       +clean:
       +        rm -f ${BIN} ${OBJ}
       +
       +install:
       +        # installing executable files.
       +        mkdir -p "${DESTDIR}${PREFIX}/bin"
       +        cp -f ${BIN} "${DESTDIR}${PREFIX}/bin"
       +        for f in ${BIN}; do chmod 755 "${DESTDIR}${PREFIX}/bin/$$f"; done
       +        # installing documentation files.
       +        mkdir -p "${DESTDIR}${DOCPREFIX}"
       +        cp -f ${DOC} "${DESTDIR}${DOCPREFIX}"
       +        # installing manual pages for general commands: section 1.
       +        mkdir -p "${DESTDIR}${DOCPREFIX}/man1"
       +        cp -f ${MAN1} "${DESTDIR}${MANPREFIX}/man1"
       +        for m in ${MAN1}; do chmod 644 "${DESTDIR}${MANPREFIX}/man1/$$m"; done
       +
       +uninstall:
       +        # removing executable files.
       +        for f in ${BIN}; do rm -f "${DESTDIR}${PREFIX}/bin/$$f"; done
       +        # removing documentation files.
       +        for d in ${DOC}; do rm -f "${DESTDIR}${DOCPREFIX}/$$d"; done
       +        -rmdir "${DESTDIR}${DOCPREFIX}"
       +        # removing manual pages.
       +        for m in ${MAN1}; do rm -f "${DESTDIR}${MANPREFIX}/man1/$$m"; done
       +
       +.PHONY: all clean dist install uninstall
 (DIR) diff --git a/README b/README
       t@@ -0,0 +1,18 @@
       +# stopwatch
       +
       +About: A simple stopwatch which can print the output to the terminal or
       +       the X root window parameter.  The latter is used for setting the
       +       dwm(1) title bar.
       +
       +Compiling: `make && make install`
       +           (optionally prefix the install comman with sudo(1) or doas(1))
       +
       +Compatibility: Tested on OpenBSD 6.8
       +
       +Documentation: `man 1 stopwatch`
       +
       +Copyright: See LICENSE
       +
       +Author: Anders Damsgaard <anders@adamsgaard.dk>
       +        git://src.adamsgaard.dk/stopwatch
       +        https://src.adamsgaard.dk/stopwatch
 (DIR) diff --git a/stopwatch.1 b/stopwatch.1
       t@@ -0,0 +1,45 @@
       +.Dd $Mdocdate$
       +.Dt STOPWATCH 1
       +.Os
       +.Sh NAME
       +.Nm stopwatch
       +.Nd reports elapsed time after launch.
       +.Sh SYNOPSIS
       +.Nm
       +.Op Fl p Ar prefix
       +.Op Fl P Ar postfix
       +.Op Fl i Ar interval
       +.Op Fl x
       +.Sh DESCRIPTION
       +The
       +.Nm
       +prints the elapsed time to standard output by default.  The time
       +is written in the format "%S s" for elapsed durations less than one
       +minute long, "%M:%SS" for durations less than one hour, and
       +"%H:%MM:%SS" for all longer durations.
       +.Pp
       +The options are as follows:
       +.Bl -tag -width Ds
       +.It Fl p Ar prefix
       +Print the
       +.Ar prefix
       +string before the time stamp.
       +.It Fl P Ar postfix
       +Print the
       +.Ar postfix
       +string after the time stamp.
       +.It Fl i Ar interval
       +update the elapsed time with this interval in seconds.
       +.It Fl x
       +write the output to the X root window parameter, which
       +.Xr dwm 1
       +uses as status line.
       +.Sh EXIT STATUS
       +.Nm
       +exits with 0 on success, and >0 if a a runtime error occurs.
       +.Sh SEE ALSO
       +.Xr dwm 1
       +.Xr spoon 1
       +.Xr xsetroot 1
       +.Sh AUTHORS
       +.An Anders Damsgaard Aq Mt anders@adamsgaard.dk
 (DIR) diff --git a/stopwatch.c b/stopwatch.c
       t@@ -0,0 +1,105 @@
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <limits.h>
       +#include <unistd.h>
       +#include <time.h>
       +#include <string.h>
       +#include <err.h>
       +#include <X11/Xlib.h>
       +
       +void
       +usage(char *argv0)
       +{
       +        errx(1, "usage: %s [-p prefix] [-P postfix] [-i interval] [-x]", argv0);
       +}
       +
       +void
       +format_time(char *out, size_t outsiz, time_t t_elapsed, char *prefix, char *postfix)
       +{
       +        time_t h = 0, m = 0, s = 0;
       +
       +        h = t_elapsed / 3600;
       +        m = (t_elapsed % 3600) / 60;
       +        s = t_elapsed % 60;
       +
       +        if (h > 0)
       +                snprintf(out, outsiz, "%s%lld:%02lld:%02lld%s",
       +                         prefix, h, m, s, postfix);
       +        else if (m > 0)
       +                snprintf(out, outsiz, "%s%lld:%02lld%s", prefix, m, s, postfix);
       +        else
       +                snprintf(out, outsiz, "%s%lld s%s", prefix, s, postfix);
       +}
       +
       +void
       +print_loop(unsigned int interval, char *prefix, char *postfix)
       +{
       +        char buf[LINE_MAX];
       +        time_t t_start = time(NULL);
       +
       +        while (1) {
       +                format_time(buf, sizeof(buf), time(NULL) - t_start, prefix, postfix);
       +                printf("\r%s\n", buf);
       +                fflush(stdout);
       +                sleep(interval);
       +        }
       +}
       +
       +void
       +xroot_loop(unsigned int interval, char *prefix, char *postfix)
       +{
       +        Display *dpy;
       +        char buf[LINE_MAX];
       +        time_t t_start = time(NULL);
       +
       +        dpy = XOpenDisplay(NULL);
       +        if (dpy == NULL)
       +                errx(1, "cannot open display");
       +        while (1) {
       +                format_time(buf, sizeof(buf), time(NULL) - t_start, prefix, postfix);
       +                XStoreName(dpy, DefaultRootWindow(dpy), buf);
       +                XSync(dpy, False);
       +                sleep(interval);
       +        }
       +}
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        int ch, xflag = 0;
       +        unsigned int interval = 1;
       +        char prefix[LINE_MAX] = "", postfix[LINE_MAX] = "";
       +        const char *errstr;
       +
       +        while ((ch = getopt(argc, argv, "p:P:i:x")) != -1) {
       +                switch (ch) {
       +                case 'p':
       +                        strlcpy(prefix, optarg, sizeof(prefix));
       +                        break;
       +                case 'P':
       +                        strlcpy(postfix, optarg, sizeof(postfix));
       +                        break;
       +                case 'i':
       +                        interval = strtonum(optarg, 1, UINT_MAX, &errstr);
       +                        if (errstr != NULL)
       +                                errx(1, "interval is %s: %s", errstr, optarg);
       +                        break;
       +                case 'x':
       +                        xflag = 1;
       +                        break;
       +                default:
       +                        usage(argv[0]);
       +                }
       +        }
       +        argc -= optind;
       +        argv += optind;
       +        if (argc > 0)
       +                usage(argv[0]);
       +
       +        if (xflag)
       +                xroot_loop(interval, prefix, postfix);
       +        else
       +                print_loop(interval, prefix, postfix);
       +
       +        return 0;
       +}