├── LICENSE ├── Makefile ├── README.md ├── config.mk ├── snore.1 └── snore.c /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2016-2024 Claudio Alessi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # snore - sleep with feedback 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | APPNAME=snore 7 | SRC = ${APPNAME}.c 8 | OBJ = ${SRC:.c=.o} 9 | 10 | all: options ${APPNAME} 11 | 12 | options: 13 | @echo ${APPNAME} build options: 14 | @echo "CFLAGS = ${CFLAGS}" 15 | @echo "LDFLAGS = ${LDFLAGS}" 16 | @echo "CC = ${CC}" 17 | 18 | .c.o: 19 | @echo CC $< 20 | @${CC} -c ${CFLAGS} $< 21 | 22 | ${OBJ}: config.mk 23 | 24 | ${APPNAME}: ${OBJ} 25 | @echo CC -o $@ 26 | @${CC} -o $@ ${OBJ} ${LDFLAGS} 27 | 28 | clean: 29 | @echo cleaning 30 | @rm -f ${APPNAME} ${OBJ} ${APPNAME}-${VERSION}.tar.gz 31 | 32 | dist: clean 33 | @echo creating dist tarball 34 | @mkdir -p ${APPNAME}-${VERSION} 35 | @cp -R LICENSE Makefile README.md config.mk \ 36 | ${APPNAME}.1 ${SRC} ${APPNAME}-${VERSION} 37 | @tar -cf ${APPNAME}-${VERSION}.tar ${APPNAME}-${VERSION} 38 | @gzip ${APPNAME}-${VERSION}.tar 39 | @rm -rf ${APPNAME}-${VERSION} 40 | 41 | install: all 42 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin 43 | @mkdir -p ${DESTDIR}${PREFIX}/bin 44 | @cp -f ${APPNAME} ${DESTDIR}${PREFIX}/bin 45 | @chmod 755 ${DESTDIR}${PREFIX}/bin/${APPNAME} 46 | @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 47 | @mkdir -p ${DESTDIR}${MANPREFIX}/man1 48 | @sed "s/VERSION/${VERSION}/g" < ${APPNAME}.1 > ${DESTDIR}${MANPREFIX}/man1/${APPNAME}.1 49 | @chmod 644 ${DESTDIR}${MANPREFIX}/man1/${APPNAME}.1 50 | 51 | uninstall: 52 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin 53 | @rm -f ${DESTDIR}${PREFIX}/bin/${APPNAME} 54 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 55 | @rm -f ${DESTDIR}${MANPREFIX}/man1/${APPNAME}.1 56 | 57 | .PHONY: all options clean dist install uninstall 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | snore - sleep with feedback 2 | =========================== 3 | 4 | SYNOPSIS 5 | 6 | snore [-v] [NUMBER[SUFFIX]...] 7 | 8 | snore pause for NUMBER seconds. SUFFIX may be 's' for seconds (default), 'm' 9 | for minutes, 'h' for hours or 'd' for days. Given two or more arguments, pause 10 | for the amount of time specified by the sum of their values. A visual feedback 11 | is given by printing the flowing of time in both ascending and descending 12 | order. If no arguments are given, snore pauses for 1d (one day). 13 | 14 | SHORTCUTS 15 | 16 | Enter or Ctrl-j output the split time 17 | 18 | Ctrl-s pause execution 19 | 20 | Ctrl-q resume execution 21 | 22 | Ctrl-c stop and exit 23 | 24 | 25 | Installation 26 | ------------ 27 | Edit config.mk to match your local setup (snore is installed into the 28 | /usr/local namespace by default). 29 | 30 | Afterwards enter the following command to build and install snore (if 31 | necessary as root): 32 | 33 | make clean install 34 | 35 | Status 36 | ------ 37 | snore is considered complete and no further development is expected to happen. 38 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # snore - sleep with feedback 2 | VERSION = 0.3.1 3 | 4 | # Customize below to fit your system 5 | 6 | # paths 7 | PREFIX = /usr/local 8 | MANPREFIX = ${PREFIX}/share/man 9 | 10 | # includes and libs 11 | INCS = 12 | LIBS = 13 | 14 | # flags 15 | CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" 16 | #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} 17 | CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} 18 | LDFLAGS = ${LIBS} 19 | 20 | # Solaris 21 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" 22 | #LDFLAGS = ${LIBS} 23 | 24 | # compiler and linker 25 | CC = cc 26 | -------------------------------------------------------------------------------- /snore.1: -------------------------------------------------------------------------------- 1 | .TH SNORE 1 snore\-VERSION 2 | .SH NAME 3 | snore \- sleep with feedback 4 | .SH SYNOPSIS 5 | .B snore 6 | .RB [ \-v ] 7 | .RI [ NUMBER [ SUFFIX ]... ] 8 | .SH DESCRIPTION 9 | .B snore 10 | pause for NUMBER seconds. SUFFIX may be 's' for seconds (default), 'm' for 11 | minutes, 'h' for hours or 'd' for days. Given two or more arguments, pause for 12 | the amount of time specified by the sum of their values. A visual feedback is 13 | given by printing the flowing of time in both ascending and descending order. 14 | If no arguments are given, 15 | .B snore 16 | pauses for 1d (one day). 17 | .SH SHORTCUTS 18 | .TP 19 | .B Enter or Ctrl-j 20 | output the split time 21 | .TP 22 | .B Ctrl-s 23 | pause 24 | .TP 25 | .B Ctrl-q 26 | resume 27 | .TP 28 | .B Ctrl-c 29 | stop and exit 30 | .SH AUTHORS 31 | See the LICENSE file for the authors. 32 | .SH LICENSE 33 | See the LICENSE file for the terms of redistribution. 34 | .SH SEE ALSO 35 | .BR sleep (1), 36 | .BR nanosleep (2) 37 | -------------------------------------------------------------------------------- /snore.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE for license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define BILLION 1000000000.0; 11 | #define TICK 10000 12 | #define CLEAR "\33[2K\r" 13 | #define LENGTH(X) (sizeof X / sizeof X[0]) 14 | #define ISCHR(c) (c >= 'a' && c <= 'z') 15 | 16 | typedef struct symbol_t { 17 | char sym; 18 | int mult; 19 | int precision; 20 | } Symbol; 21 | 22 | void die(const char *errstr, ...); 23 | int sleepu(double usec); 24 | double time_to_sec(char *s); 25 | void time_print(double tm); 26 | 27 | /* must be in ascending order */ 28 | static Symbol symbols[] = { 29 | /* symbol multiplier precision */ 30 | { 's', 1, 3 }, /* first is default (if no suffix) */ 31 | { 'm', 60, 0 }, 32 | { 'h', 3600, 0 }, 33 | { 'd', 86400, 0 }, /* last is default (if no arguments) */ 34 | }; 35 | 36 | void 37 | die(const char *errstr, ...) { 38 | va_list ap; 39 | 40 | va_start(ap, errstr); 41 | vfprintf(stderr, errstr, ap); 42 | va_end(ap); 43 | exit(1); 44 | } 45 | 46 | int 47 | sleepu(double usec) { 48 | struct timespec req, rem; 49 | int r; 50 | 51 | req.tv_sec = 0; 52 | req.tv_nsec = usec * 1000; 53 | while((r = nanosleep(&req, &rem)) == -1 && errno == EINTR) 54 | req = rem; 55 | return r; 56 | } 57 | 58 | double 59 | time_to_sec(char *s) { 60 | double calculated = 0.0, part; 61 | char *parse_end, *string_end; 62 | int j; 63 | 64 | string_end = s + strlen(s); 65 | while(s < string_end) { 66 | part = strtod(s, &parse_end); 67 | if(parse_end == s) { 68 | /* error parsing float */ 69 | return -1; 70 | } 71 | s = parse_end; 72 | if(s < string_end && ISCHR(s[0])) { 73 | for(j = 0; j < LENGTH(symbols); ++j) { 74 | if(s[0] == symbols[j].sym) { 75 | part *= symbols[j].mult; 76 | if(part >= INT_MAX) 77 | return -1; 78 | s++; 79 | break; 80 | } 81 | } 82 | } 83 | calculated += part; 84 | } 85 | return calculated; 86 | } 87 | 88 | void 89 | time_print(double tm) { 90 | double piece; 91 | int i; 92 | 93 | for(i = LENGTH(symbols) - 1; i >= 0; --i) { 94 | piece = tm / symbols[i].mult; 95 | if(!symbols[i].precision) 96 | piece = (int)piece; 97 | printf("%.*f%c%s", symbols[i].precision, piece, symbols[i].sym, i ? " " : ""); 98 | tm -= piece * symbols[i].mult; 99 | } 100 | } 101 | 102 | int 103 | main(int argc, char *argv[]) { 104 | struct timespec start, current; 105 | double endtm = 0, tm; 106 | int i; 107 | 108 | clock_gettime(CLOCK_REALTIME, &start); 109 | if(argc == 2 && !strcmp("-v", argv[1])) 110 | die("snore-"VERSION"\n"); 111 | if(argc == 1) { 112 | endtm = symbols[LENGTH(symbols) - 1].mult; 113 | } 114 | else { 115 | for(i = 1; i < argc; ++i) { 116 | tm = time_to_sec(argv[i]); 117 | if(tm < 0) 118 | die("%s: wrong time\n", argv[i]); 119 | endtm += tm; 120 | if(endtm >= INT_MAX) 121 | die("%s: time too large\n", argv[0]); 122 | } 123 | } 124 | 125 | for(tm = 0; tm < endtm; ) { 126 | time_print(tm); /* ascending */ 127 | printf(" | "); 128 | time_print(endtm - tm); /* descending */ 129 | fflush(stdout); 130 | sleepu(TICK); 131 | printf(CLEAR); 132 | clock_gettime(CLOCK_REALTIME, ¤t); 133 | tm = (current.tv_sec - start.tv_sec) + (current.tv_nsec - start.tv_nsec) / BILLION; 134 | } 135 | time_print(tm); 136 | printf("\n"); 137 | return 0; 138 | } 139 | --------------------------------------------------------------------------------