Compare commits

..

No commits in common. "master" and "v83.4" have entirely different histories.

35 changed files with 3132 additions and 7032 deletions

14
.gitignore vendored
View file

@ -1,14 +0,0 @@
*.o
*.swp
*~
/Makefile
/config.h
/config.log
/config.status
/tts
/vers.c
/build
/autom4te.cache
/doconf
/bling_import.pl.old
/queue.h

View file

@ -1,25 +1,13 @@
# TTS - track your time.
# Copyright (c) 2012-2014 Felicity Tarnell.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely. This software is provided 'as-is', without any express or implied
# warranty.
.SUFFIXES: .c .o .d .h
VPATH = @top_srcdir@
CC = @CC@
MAKEDEPEND = @CC@ -M
CPPFLAGS = @CPPFLAGS@ -D_XOPEN_SOURCE_EXTENDED
CPPFLAGS = @CPPFLAGS@
CFLAGS = @CFLAGS@ -I@top_srcdir@ -I@top_builddir@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
INSTALL = @INSTALL@
OBJS = tts.o wide.o entry.o ui.o functions.o commands.o bindings.o \
str.o style.o wcslcpy.o
OBJS = tts.o
prefix = @prefix@
exec_prefix = @exec_prefix@
@ -35,22 +23,8 @@ install:
.c.o:
${CC} ${CPPFLAGS} ${CFLAGS} -c $<
.c.d:
$(MAKEDEPEND) $(CPPFLAGS) $(CFLAGS) $< -o $@
vers.c: vers.c.sh configure.ac
sh @top_srcdir@/vers.c.sh @top_srcdir@/configure.ac
clean:
rm -f tts ${OBJS} $(OBJS:.o=.d)
depend: $(OBJS:.o=.d)
sed '/^# Do not remove this line -- make depend needs it/,$$ d' \
<Makefile >Makefile.new
echo '# Do not remove this line -- make depend needs it' >>Makefile.new
cat *.d >> Makefile.new
mv Makefile.new Makefile
.PHONY: depend clean install libuv
# Do not remove this line -- make depend needs it
rm -f tts *.o

View file

@ -1,21 +1,8 @@
TTS - Time-tracking software
============================
TTS is a simple, text-based (curses) time-tracking application. It allows you
to track the time you spend working by client, project, etc. to allow accurate
invoicing of clients or reporting to a corporate time-tracking system. It
uses a simple text format to store data which can easily be parsed to export
data to another system automatically. Entries can be added in bulk or in real
time using a timer, and invoiced and non-billable entries are tracked.
Entries can optionally be rounded up to a minimum billing increment.
Screenshot
----------
![A screenshot of TTS in use](screenshot.png)
Installation
------------
TTS is a simple, text-based (curses) time-tracking application. For more
details, see the website at <http://loreley.flyingparchment.org.uk/~felicity/pages/tts>.
TTS has been tested on FreeBSD, NetBSD, Solaris, Cygwin and Linux, with the
following caveats:
@ -34,8 +21,7 @@ TTS uses autoconf and can be built as follows:
After starting with 'tts', type '?' for help.
Quick start
-----------
### Quick start:
* Press 'a' to add a new entry, and enter its name. The timer starts running.
* Press space to toggle the timer on and off.
@ -48,9 +34,6 @@ Quick start
start the interrupt timer. When you're done with the other thing, press 'r'
again to assign the interrupt time to a new entry.
Contact
-------
### Contact
To report problems or request features, please use the GitHub issue tracker at
<https://github.com/ftarnell/tts/issues>. This requires that you have a GitHub
account.
Send questions/comments/bugs/patches to <felicity@loreley.flyingparchment.org.uk>.

10
aclocal.m4 vendored
View file

@ -108,7 +108,7 @@
# HAVE_CURSES_ENHANCED and ax_cv_curses_enhanced are defined if the
# library supports the X/Open Enhanced Curses definition. In particular,
# the wide-character types attr_t, cchar_t and wint_t, the functions
# wattr_set() and wget_wch() and the macros WA_BOLD and _XOPEN_CURSES
# wattr_set() and wget_wch() and the macros WA_NORMAL and _XOPEN_CURSES
# are checked. The Ncurses library does NOT conform to this definition,
# although NcursesW does.
#
@ -228,7 +228,7 @@ AC_DEFUN([AX_WITH_CURSES], [
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_BOLD;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
int g = getattrs(stdscr);
@ -259,7 +259,7 @@ AC_DEFUN([AX_WITH_CURSES], [
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_BOLD;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
int g = getattrs(stdscr);
@ -290,7 +290,7 @@ AC_DEFUN([AX_WITH_CURSES], [
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_BOLD;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
int g = getattrs(stdscr);
@ -443,7 +443,7 @@ AC_DEFUN([AX_WITH_CURSES], [
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_BOLD;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
initscr();

View file

@ -1,194 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#include <stdlib.h>
#include "bindings.h"
#include "wide.h"
#include "ui.h"
binding_list_t bindings = TTS_TAILQ_HEAD_INITIALIZER(bindings);
static tkey_t keys[] = {
{ KEY_BREAK, L"<BREAK>" },
{ KEY_DOWN, L"<DOWN>" },
{ KEY_UP, L"<UP>" },
{ KEY_LEFT, L"<LEFT>" },
{ KEY_RIGHT, L"<RIGHT>" },
{ KEY_HOME, L"<HOME>" },
{ KEY_BACKSPACE, L"<BACKSPACE>" },
{ 0x7F, L"<BACKSPACE>" }, /* DEL */
{ KEY_F(0), L"<F0>" },
{ KEY_F(1), L"<F1>" },
{ KEY_F(2), L"<F2>" },
{ KEY_F(3), L"<F3>" },
{ KEY_F(4), L"<F4>" },
{ KEY_F(5), L"<F5>" },
{ KEY_F(6), L"<F6>" },
{ KEY_F(7), L"<F7>" },
{ KEY_F(8), L"<F8>" },
{ KEY_F(9), L"<F9>" },
{ KEY_F(10), L"<F10>" },
{ KEY_F(11), L"<F1l>" },
{ KEY_F(12), L"<F12>" },
{ KEY_F(13), L"<F13>" },
{ KEY_F(14), L"<F14>" },
{ KEY_F(15), L"<F15>" },
{ KEY_F(16), L"<F16>" },
{ KEY_F(17), L"<F17>" },
{ KEY_F(18), L"<F18>" },
{ KEY_F(19), L"<F19>" },
{ KEY_F(20), L"<F20>" },
{ KEY_F(21), L"<F21>" },
{ KEY_F(22), L"<F22>" },
{ KEY_F(23), L"<F23>" },
{ KEY_F(24), L"<F24>" },
{ KEY_NPAGE, L"<NEXT>" },
{ KEY_PPAGE, L"<PREV>" },
{ '\001', L"<CTRL-A>" },
{ '\002', L"<CTRL-B>" },
{ '\003', L"<CTRL-C>" },
{ '\004', L"<CTRL-D>" },
{ '\005', L"<CTRL-E>" },
{ '\006', L"<CTRL-F>" },
{ '\007', L"<CTRL-G>" },
{ '\010', L"<CTRL-H>" },
{ '\011', L"<CTRL-I>" },
{ '\011', L"<TAB>" },
{ '\012', L"<CTRL-J>" },
{ '\013', L"<CTRL-K>" },
{ '\014', L"<CTRL-L>" },
{ '\015', L"<CTRL-N>" },
{ '\016', L"<CTRL-O>" },
{ '\017', L"<CTRL-P>" },
{ '\020', L"<CTRL-Q>" },
{ '\021', L"<CTRL-R>" },
{ '\022', L"<CTRL-S>" },
{ '\023', L"<CTRL-T>" },
{ '\024', L"<CTRL-U>" },
{ '\025', L"<CTRL-V>" },
{ '\026', L"<CTRL-W>" },
{ '\027', L"<CTRL-X>" },
{ '\030', L"<CTRL-Y>" },
{ '\031', L"<CTRL-Z>" },
{ ' ', L"<SPACE>" },
{ KEY_ENTER, L"<ENTER>" },
{ KEY_BACKSPACE, L"<BACKSPACE>" },
{ KEY_DC, L"<DELETE>" }
};
/*
* Bind .keyname to run the function .funcname. If a binding for .keyname
* already exists, overwrite it.
*
* If .keyname is a single character, e.g. 'a', it is used as a key name
* directly, rather than being looked up in the key table.
*/
void
bind_key(keyname, funcname, is_macro)
const wchar_t *keyname, *funcname;
{
tkey_t *key = NULL;
function_t *func;
binding_t *binding;
wint_t code;
/* Find the key and the function */
if (wcslen(keyname) > 1) {
if ((key = find_key(keyname)) == NULL) {
errbox(L"Unknown key \"%ls\"", keyname);
return;
}
code = key->ky_code;
} else
code = *keyname;
if (!is_macro) {
if ((func = find_func(funcname)) == NULL) {
errbox(L"Unknown function \"%ls\"", funcname);
return;
}
}
/* Do we already have a binding for this key? */
TTS_TAILQ_FOREACH(binding, &bindings, bi_entries) {
if (binding->bi_code == code) {
if (is_macro) {
binding->bi_func = NULL;
binding->bi_macro = wcsdup(funcname);
} else {
free(binding->bi_macro);
binding->bi_func = func;
}
return;
}
}
/* No, add a new one */
if ((binding = calloc(1, sizeof(*binding))) == NULL)
return;
binding->bi_key = key;
binding->bi_code = code;
if (is_macro)
binding->bi_macro = wcsdup(funcname);
else
binding->bi_func = func;
TTS_TAILQ_INSERT_TAIL(&bindings, binding, bi_entries);
}
/*
* Return the tkey_t for the key called .name, or NULL if such a key doesn't
* exist.
*/
tkey_t *
find_key(name)
const wchar_t *name;
{
size_t i;
for (i = 0; i < sizeof(keys) / sizeof(*keys); i++)
if (wcscmp(name, keys[i].ky_name) == 0)
return &keys[i];
return NULL;
}
void
bind_defaults(void)
{
bind_key(L"?", L"help", 0);
bind_key(L"a", L"add", 0);
bind_key(L"A", L"add-old", 0);
bind_key(L"d", L"delete", 0);
bind_key(L"u", L"undelete", 0);
bind_key(L"q", L"quit", 0);
bind_key(L"<CTRL-C>", L"quit", 0);
bind_key(L"i", L"invoice", 0);
bind_key(L"b", L"billable", 0);
bind_key(L"m", L"mark", 0);
bind_key(L"U", L"unmarkall", 0);
bind_key(L"<SPACE>", L"startstop", 0);
bind_key(L"e", L"edit-desc", 0);
bind_key(L"\\", L"edit-time", 0);
bind_key(L"<TAB>", L"showhide-inv", 0);
bind_key(L"c", L"copy", 0);
bind_key(L"+", L"add-time", 0);
bind_key(L"-", L"sub-time", 0);
bind_key(L"/", L"search", 0);
bind_key(L"$", L"sync", 0);
bind_key(L"<UP>", L"prev", 0);
bind_key(L"<DOWN>", L"next", 0);
bind_key(L":", L"execute", 0);
bind_key(L"M", L"merge", 0);
bind_key(L"r", L"interrupt", 0);
bind_key(L"R", L"interrupt", 0);
}

View file

@ -1,40 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_BINDINGS_H
#define TTS_BINDINGS_H
#include "wide.h"
#include "functions.h"
#include "tailq.h"
typedef struct tkey {
wint_t ky_code;
const wchar_t *ky_name;
} tkey_t;
typedef struct binding {
wint_t bi_code;
tkey_t *bi_key;
function_t *bi_func;
wchar_t *bi_macro;
TTS_TAILQ_ENTRY(binding) bi_entries;
} binding_t;
typedef TTS_TAILQ_HEAD(bindlist, binding) binding_list_t;
extern binding_list_t bindings;
void bind_defaults();
tkey_t *find_key(const wchar_t *name);
void bind_key(const wchar_t *key, const wchar_t *func, int is_macro);
#endif /* !TTS_BINDINGS_H */

View file

@ -20,7 +20,6 @@
use warnings;
use strict;
use LWP::Protocol::https;
use LWP::UserAgent;
use URI::Escape qw/uri_escape/;
use POSIX qw/strftime/;
@ -90,7 +89,7 @@ while (<INF>) {
if ($res->is_success) {
my $resp = decode_json($res->content);
if (defined($resp->{description})) {
print "Failed to Bling [$desc]: " . $resp->{description} . "\n";
print "Failed to Bling [$desc]: " . $resp->description . "\n";
} else {
if ($flags eq "-") {
$flags = "i";

View file

@ -1,157 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#include <stdlib.h>
#include "commands.h"
#include "style.h"
#include "bindings.h"
#include "variable.h"
static command_t commands[] = {
{ L"bind", c_bind },
{ L"macro", c_macro },
{ L"style", c_style },
{ L"set", c_set },
{ }
};
command_t *
find_command(name)
const wchar_t *name;
{
command_t *c;
for (c = commands; c->cm_name; c++)
if (wcscmp(name, c->cm_name) == 0)
return c;
return NULL;
}
void
c_style(argc, argv)
size_t argc;
wchar_t **argv;
{
style_t *sy;
wchar_t *last, *tok;
if (argc < 3 || argc > 4) {
cmderr(L"Usage: style <item> <foreground> [background]");
return;
}
if (wcscmp(argv[1], L"header") == 0)
sy = &sy_header;
else if (wcscmp(argv[1], L"status") == 0)
sy = &sy_status;
else if (wcscmp(argv[1], L"entry") == 0)
sy = &sy_entry;
else if (wcscmp(argv[1], L"selected") == 0)
sy = &sy_selected;
else if (wcscmp(argv[1], L"running") == 0)
sy = &sy_running;
else if (wcscmp(argv[1], L"date") == 0)
sy = &sy_date;
else {
cmderr(L"Unknown style item.");
return;
}
style_clear(sy);
for (tok = wcstok(argv[2], L",", &last); tok != NULL;
tok = wcstok(NULL, L",", &last)) {
style_add(sy, tok, argv[3]);
}
apply_styles();
}
void
c_bind(argc, argv)
size_t argc;
wchar_t **argv;
{
if (argc != 3) {
cmderr(L"Usage: bind <key> <function>");
return;
}
bind_key(argv[1], argv[2], 0);
}
void
c_macro(argc, argv)
size_t argc;
wchar_t **argv;
{
if (argc != 3) {
cmderr(L"Usage: macro <key> <def>");
return;
}
bind_key(argv[1], argv[2], 1);
}
void
c_set(argc, argv)
size_t argc;
wchar_t **argv;
{
variable_t *var;
int val;
wchar_t *p = NULL;
if (argc != 3) {
cmderr(L"Usage: set <variable> <value>");
return;
}
if ((var = find_variable(argv[1])) == NULL) {
cmderr(L"Unknown variable \"%ls\".", argv[1]);
return;
}
switch (var->va_type) {
case VTYPE_BOOL:
if (wcscmp(argv[2], L"true") == 0 ||
wcscmp(argv[2], L"yes") == 0 ||
wcscmp(argv[2], L"on") == 0 ||
wcscmp(argv[2], L"1") == 0) {
val = 1;
} else if (wcscmp(argv[2], L"false") == 0 ||
wcscmp(argv[2], L"no") == 0 ||
wcscmp(argv[2], L"off") == 0 ||
wcscmp(argv[2], L"0") == 0) {
val = 0;
} else {
cmderr(L"Invalid value for boolean: \"%ls\".", argv[2]);
return;
}
*(int *)var->va_addr = val;
break;
case VTYPE_STRING:
free(*(wchar_t **)var->va_addr);
*(wchar_t **)var->va_addr = wcsdup(argv[2]);
break;
case VTYPE_INT:
val = wcstol(argv[2], &p, 0);
if (!*argv[2] || *p) {
cmderr(L"Invalid number \"%ls\"", argv[2]);
break;
}
*(int *)var->va_addr = val;
break;
}
}

View file

@ -1,33 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_COMMANDS_H
#define TTS_COMMANDS_H
#include <stdarg.h>
#include "wide.h"
typedef struct command {
const wchar_t *cm_name;
void (*cm_hdl) (size_t, wchar_t **);
} command_t;
command_t *find_command(const wchar_t *);
void c_bind (size_t, wchar_t **);
void c_style (size_t, wchar_t **);
void c_set (size_t, wchar_t **);
void c_macro (size_t, wchar_t **);
void cmderr (const wchar_t *, ...);
void vcmderr (const wchar_t *, va_list);
#endif /* !TTS_COMMANDS_H */

1558
config.guess vendored

File diff suppressed because it is too large Load diff

View file

@ -24,9 +24,6 @@
/* Define to 1 if you have the `IORegisterForSystemPower' function. */
#undef HAVE_IOREGISTERFORSYSTEMPOWER
/* Define to 1 if you have the `m' library (-lm). */
#undef HAVE_LIBM
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
@ -72,12 +69,6 @@
/* Define to 1 if you have the `use_default_colors' function. */
#undef HAVE_USE_DEFAULT_COLORS
/* Define to 1 if you have the `wcslcat' function. */
#undef HAVE_WCSLCAT
/* Define to 1 if you have the `wcslcpy' function. */
#undef HAVE_WCSLCPY
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT

1788
config.sub vendored

File diff suppressed because it is too large Load diff

229
configure vendored
View file

@ -1,8 +1,8 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for TTS D.84.1.
# Generated by GNU Autoconf 2.69 for RT/TTS T.83.4.
#
# Report bugs to <ft@le-fay.org>.
# Report bugs to <felicity@loreley.flyingparchment.org.uk>.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@ -266,11 +266,11 @@ fi
$as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
$as_echo "$0: be upgraded to zsh 4.3.4 or later."
else
$as_echo "$0: Please tell bug-autoconf@gnu.org and ft@le-fay.org
$0: about your system, including any error possibly output
$0: before this message. Then install a modern shell, or
$0: manually run the script under such a shell if you do
$0: have one."
$as_echo "$0: Please tell bug-autoconf@gnu.org and
$0: felicity@loreley.flyingparchment.org.uk about your
$0: system, including any error possibly output before this
$0: message. Then install a modern shell, or manually run
$0: the script under such a shell if you do have one."
fi
exit 1
fi
@ -578,11 +578,11 @@ MFLAGS=
MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='TTS'
PACKAGE_TARNAME='tts'
PACKAGE_VERSION='D.84.1'
PACKAGE_STRING='TTS D.84.1'
PACKAGE_BUGREPORT='ft@le-fay.org'
PACKAGE_NAME='RT/TTS'
PACKAGE_TARNAME='rt-tts'
PACKAGE_VERSION='T.83.4'
PACKAGE_STRING='RT/TTS T.83.4'
PACKAGE_BUGREPORT='felicity@loreley.flyingparchment.org.uk'
PACKAGE_URL=''
ac_unique_file="tts.c"
@ -638,14 +638,6 @@ CPPFLAGS
LDFLAGS
CFLAGS
CC
host_os
host_vendor
host_cpu
host
build_os
build_vendor
build_cpu
build
target_alias
host_alias
build_alias
@ -1240,7 +1232,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures TTS D.84.1 to adapt to many kinds of systems.
\`configure' configures RT/TTS T.83.4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1288,7 +1280,7 @@ Fine tuning of the installation directories:
--infodir=DIR info documentation [DATAROOTDIR/info]
--localedir=DIR locale-dependent data [DATAROOTDIR/locale]
--mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root [DATAROOTDIR/doc/tts]
--docdir=DIR documentation root [DATAROOTDIR/doc/rt-tts]
--htmldir=DIR html documentation [DOCDIR]
--dvidir=DIR dvi documentation [DOCDIR]
--pdfdir=DIR pdf documentation [DOCDIR]
@ -1296,16 +1288,12 @@ Fine tuning of the installation directories:
_ACEOF
cat <<\_ACEOF
System types:
--build=BUILD configure for building on BUILD [guessed]
--host=HOST cross-compile to build programs to run on HOST [BUILD]
_ACEOF
fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of TTS D.84.1:";;
short | recursive ) echo "Configuration of RT/TTS T.83.4:";;
esac
cat <<\_ACEOF
@ -1329,7 +1317,7 @@ Some influential environment variables:
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
Report bugs to <ft@le-fay.org>.
Report bugs to <felicity@loreley.flyingparchment.org.uk>.
_ACEOF
ac_status=$?
fi
@ -1392,7 +1380,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
TTS configure D.84.1
RT/TTS configure T.83.4
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@ -1664,9 +1652,9 @@ $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
( $as_echo "## ---------------------------- ##
## Report this to ft@le-fay.org ##
## ---------------------------- ##"
( $as_echo "## ------------------------------------------------------ ##
## Report this to felicity@loreley.flyingparchment.org.uk ##
## ------------------------------------------------------ ##"
) | sed "s/^/$as_me: WARNING: /" >&2
;;
esac
@ -1761,7 +1749,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by TTS $as_me D.84.1, which was
It was created by RT/TTS $as_me T.83.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@ -2113,122 +2101,6 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_config_headers="$ac_config_headers config.h"
ac_aux_dir=
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
if test -f "$ac_dir/install-sh"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/install-sh -c"
break
elif test -f "$ac_dir/install.sh"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/install.sh -c"
break
elif test -f "$ac_dir/shtool"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/shtool install -c"
break
fi
done
if test -z "$ac_aux_dir"; then
as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
fi
# These three variables are undocumented and unsupported,
# and are intended to be withdrawn in a future Autoconf release.
# They can cause serious problems if a builder's source tree is in a directory
# whose full name contains unusual characters.
ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
# Make sure we can run config.sub.
$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
$as_echo_n "checking build system type... " >&6; }
if ${ac_cv_build+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_build_alias=$build_alias
test "x$ac_build_alias" = x &&
ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
test "x$ac_build_alias" = x &&
as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
$as_echo "$ac_cv_build" >&6; }
case $ac_cv_build in
*-*-*) ;;
*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
esac
build=$ac_cv_build
ac_save_IFS=$IFS; IFS='-'
set x $ac_cv_build
shift
build_cpu=$1
build_vendor=$2
shift; shift
# Remember, the first character of IFS is used to create $*,
# except with old shells:
build_os=$*
IFS=$ac_save_IFS
case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
$as_echo_n "checking host system type... " >&6; }
if ${ac_cv_host+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "x$host_alias" = x; then
ac_cv_host=$ac_cv_build
else
ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
$as_echo "$ac_cv_host" >&6; }
case $ac_cv_host in
*-*-*) ;;
*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
esac
host=$ac_cv_host
ac_save_IFS=$IFS; IFS='-'
set x $ac_cv_host
shift
host_cpu=$1
host_vendor=$2
shift; shift
# Remember, the first character of IFS is used to create $*,
# except with old shells:
host_os=$*
IFS=$ac_save_IFS
case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
case $host_os in
darwin*)
CPPFLAGS="$CPPFLAGS -D_DARWIN_USE_64_BIT_INODE=1"
;;
linux*)
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
;;
netbsd*)
CPPFLAGS="$CPPFLAGS -D_NETBSD_SOURCE"
;;
solaris*)
CPPFLAGS="-D__EXTENSIONS__ -D_FILE_OFFSET_BITS=64"
;;
esac
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@ -3018,6 +2890,35 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_aux_dir=
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
if test -f "$ac_dir/install-sh"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/install-sh -c"
break
elif test -f "$ac_dir/install.sh"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/install.sh -c"
break
elif test -f "$ac_dir/shtool"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/shtool install -c"
break
fi
done
if test -z "$ac_aux_dir"; then
as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
fi
# These three variables are undocumented and unsupported,
# and are intended to be withdrawn in a future Autoconf release.
# They can cause serious problems if a builder's source tree is in a directory
# whose full name contains unusual characters.
ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
# incompatible versions:
@ -3219,7 +3120,7 @@ main ()
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_BOLD;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
int g = getattrs(stdscr);
@ -3283,7 +3184,7 @@ main ()
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_BOLD;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
int g = getattrs(stdscr);
@ -3347,7 +3248,7 @@ main ()
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_BOLD;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
int g = getattrs(stdscr);
@ -3711,7 +3612,7 @@ main ()
chtype a = A_BOLD;
int b = KEY_LEFT;
chtype c = COLOR_PAIR(1) & A_COLOR;
attr_t d = WA_BOLD;
attr_t d = WA_NORMAL;
cchar_t e;
wint_t f;
initscr();
@ -3870,20 +3771,14 @@ fi
LIBS=$ax_saved_LIBS
if test "x$ax_cv_curses" != xyes; then :
as_fn_error $? "XSI/SVR4 curses is required to compile TTS" "$LINENO" 5
fi
oLIBS="$LIBS"
LIBS="$LIBS $CURSES_LIB"
for ac_func in use_default_colors wcslcpy wcslcat
for ac_func in use_default_colors
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
ac_fn_c_check_func "$LINENO" "use_default_colors" "ac_cv_func_use_default_colors"
if test "x$ac_cv_func_use_default_colors" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
#define HAVE_USE_DEFAULT_COLORS 1
_ACEOF
fi
@ -4348,7 +4243,7 @@ fi
oLDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -framework IOKit"
LDFLAGS="$LDFLAGS -framework CoreFoundation -framework IOKit"
for ac_func in IORegisterForSystemPower
do :
ac_fn_c_check_func "$LINENO" "IORegisterForSystemPower" "ac_cv_func_IORegisterForSystemPower"
@ -4871,7 +4766,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by TTS $as_me D.84.1, which was
This file was extended by RT/TTS $as_me T.83.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -4927,13 +4822,13 @@ $config_files
Configuration headers:
$config_headers
Report bugs to <ft@le-fay.org>."
Report bugs to <felicity@loreley.flyingparchment.org.uk>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
TTS config.status D.84.1
RT/TTS config.status T.83.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View file

@ -1,35 +1,15 @@
AC_PREREQ([2.69])
AC_INIT([TTS], [D.84.1], [ft@le-fay.org])
AC_INIT([RT/TTS], [T.83.4], [felicity@loreley.flyingparchment.org.uk])
AC_CONFIG_SRCDIR([tts.c])
AC_CONFIG_HEADERS([config.h])
AC_CANONICAL_HOST
case $host_os in
darwin*)
CPPFLAGS="$CPPFLAGS -D_DARWIN_USE_64_BIT_INODE=1"
;;
linux*)
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
;;
netbsd*)
CPPFLAGS="$CPPFLAGS -D_NETBSD_SOURCE"
;;
solaris*)
CPPFLAGS="-D__EXTENSIONS__ -D_FILE_OFFSET_BITS=64"
;;
esac
AC_PROG_CC
AC_PROG_INSTALL
AX_WITH_CURSES
AS_IF([test "x$ax_cv_curses" != xyes], [
AC_ERROR([XSI/SVR4 curses is required to compile TTS])
])
oLIBS="$LIBS"
LIBS="$LIBS $CURSES_LIB"
AC_CHECK_FUNCS([use_default_colors wcslcpy wcslcat])
AC_CHECK_FUNCS([use_default_colors])
LIBS="$oLIBS"
AC_CHECK_HEADERS([IOKit/pwr_mgt/IOPMLib.h])
@ -37,7 +17,7 @@ AC_CHECK_HEADERS([IOKit/pwr_mgt/IOPMLib.h])
AC_CHECK_LIB(m, round)
oLDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -framework IOKit"
LDFLAGS="$LDFLAGS -framework CoreFoundation -framework IOKit"
AC_CHECK_FUNCS([IORegisterForSystemPower], [], [LDFLAGS="$oLDFLAGS"])
AC_CONFIG_FILES([Makefile])

124
entry.c
View file

@ -1,124 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include "entry.h"
#include "wide.h"
#include "tts.h"
entry_list entries = TTS_TAILQ_HEAD_INITIALIZER(entries);
entry_t *running;
entry_t *
entry_new(desc)
const wchar_t *desc;
{
entry_t *en;
if ((en = calloc(1, sizeof(*en))) == NULL)
return NULL;
if (auto_nonbillable && wcsstr(desc, auto_nonbillable))
en->en_flags.efl_nonbillable = 1;
TTS_TAILQ_INSERT_HEAD(&entries, en, en_entries);
en->en_desc = wcsdup(desc);
time(&en->en_created);
return en;
}
void
entry_start(en)
entry_t *en;
{
if (running)
entry_stop(running);
time(&en->en_started);
running = en;
}
void
entry_stop(en)
entry_t *en;
{
if (running == en)
running = NULL;
en->en_secs += time(NULL) - en->en_started;
en->en_started = 0;
}
void
entry_free(en)
entry_t *en;
{
if (en == running)
entry_stop(en);
free(en->en_desc);
}
void
entry_account(en)
entry_t *en;
{
if (!en->en_started)
return;
en->en_secs += time(NULL) - en->en_started;
time(&en->en_started);
}
/*
* Return the amount of time for the day on which the timestamp .when falls.
* If .inv is 0, sum non-invoiced entries; if 1, sum invoiced entries; if
* 2, sum billable entries; if -1, sum all entries. If .incr is non-zero,
* individual entry time will be rounded up to intervals of that many minutes.
*/
time_t
entry_time_for_day(when, inv, incr)
time_t when;
{
time_t day = time_day(when);
time_t sum = 0;
entry_t *en;
int rnd = incr * 60;
TTS_TAILQ_FOREACH(en, &entries, en_entries) {
time_t n;
if (entry_day(en) > day)
continue;
if (entry_day(en) < day)
break;
if (inv == 0 && en->en_flags.efl_invoiced)
continue;
if (inv == 1 && !en->en_flags.efl_invoiced)
continue;
if (inv == 2 && en->en_flags.efl_nonbillable)
continue;
n = en->en_secs;
if (en->en_started)
n += time(NULL) - en->en_started;
if (!n)
continue;
if (rnd)
n = (1 + round((n - 1) / rnd)) * rnd;
sum += n;
}
return sum;
}

59
entry.h
View file

@ -1,59 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_ENTRY_H
#define TTS_ENTRY_H
#include <time.h>
#include "tailq.h"
#include "wide.h"
typedef struct entry {
wchar_t *en_desc;
int en_secs;
time_t en_started;
time_t en_created;
struct {
int efl_visible:1;
int efl_invoiced:1;
int efl_marked:1;
int efl_deleted:1;
int efl_nonbillable:1;
} en_flags;
TTS_TAILQ_ENTRY(entry) en_entries;
} entry_t;
typedef TTS_TAILQ_HEAD(entrylist, entry) entry_list;
extern entry_list entries;
extern entry_t *running;
entry_t *entry_new (const wchar_t *);
void entry_start (entry_t *);
void entry_stop (entry_t *);
void entry_free (entry_t *);
void entry_account (entry_t *);
time_t entry_time_for_day (time_t, int, int);
#define time_day(t) (((t) / (60 * 60 * 24)) * (60 * 60 * 24))
#define entry_day(e) (time_day((e)->en_created))
#define time_to_hms(t, h, m, s) do { \
time_t n = t; \
h = n / (60 * 60); \
n %= (60 * 60); \
m = n / 60; \
n %= 60; \
s = n; \
} while (0)
#endif /* !TTS_ENTRY_H */

View file

@ -1,761 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#include <stdlib.h>
#include "functions.h"
#include "tts.h"
#include "entry.h"
#include "ui.h"
#include "commands.h"
#include "tts_curses.h"
#include "bindings.h"
#include "str.h"
function_t funcs[] = {
{ L"help", khelp, L"display help screen" },
{ L"add", kadd, L"add a new entry and start the timer" },
{ L"add-old", kaddold, L"add a new entry and specify its duration" },
{ L"delete", kmarkdel, L"delete the current entry" },
{ L"undelete", kundel, L"undelete the current entry" },
{ L"quit", kquit, L"exit TTS" },
{ L"invoice", kinvoiced, L"toggle current entry as invoiced" },
{ L"billable", kbillable, L"toggle current entry as billable" },
{ L"mark", kmark, L"mark the current entry" },
{ L"unmarkall", kunmarkall, L"unmark all entries" },
{ L"startstop", ktoggle, L"start or stop the timer" },
{ L"edit-desc", keddesc, L"edit the current entry's description" },
{ L"edit-time", kedtime, L"edit the current entry's duration" },
{ L"showhide-inv", ktoggleinv, L"show or hide invoiced entries" },
{ L"copy", kcopy, L"copy the current entry's description to a new entry" },
{ L"add-time", kaddtime, L"add time to the current entry" },
{ L"sub-time", kdeltime, L"subtract time from the current entry" },
{ L"search", ksearch, L"search for an entry by name" },
{ L"sync", ksync, L"purge all deleted entries" },
{ L"prev", kup, L"move to the previous entry" },
{ L"next", kdown, L"move to the next entry" },
{ L"execute", kexec, L"execute a configuration command" },
{ L"merge", kmerge, L"merge marked entries into current entry" },
{ L"interrupt", kint, L"split current entry into new entry"},
{ }
};
static int
valid_description(d)
const wchar_t *d;
{
if (!d)
return 0;
while (iswspace(*d))
d++;
if (!*d)
return 0;
return 1;
}
void
kquit()
{
entry_t *en;
int ndel = 0;
TTS_TAILQ_FOREACH(en, &entries, en_entries) {
if (en->en_flags.efl_deleted)
ndel++;
}
if (ndel) {
wchar_t s[128];
swprintf(s, wsizeof(s), L"Purge %d deleted entries?", ndel);
if (yesno(s)) {
ksync();
}
}
doexit = 1;
}
void
kadd()
{
wchar_t *name;
entry_t *en;
name = prompt(L"Description:", NULL, NULL);
if (!valid_description(name)) {
free(name);
return;
}
en = entry_new(name);
entry_start(en);
curent = en;
save();
}
void
kaddold()
{
wchar_t *name;
entry_t *en;
name = prompt(L"Description:", NULL, NULL);
if (!valid_description(name)) {
free(name);
return;
}
en = entry_new(name);
curent = en;
kedtime();
save();
}
void
ktoggle()
{
itime = 0;
if (!curent)
return;
if (curent == running) {
entry_stop(curent);
save();
return;
}
if (running)
entry_stop(running);
entry_start(curent);
save();
}
void
kundel()
{
if (!curent)
return;
curent->en_flags.efl_deleted = 0;
if (delete_advance)
cursadvance();
}
void
kmarkdel()
{
entry_t *en;
int nmarked = 0;
TTS_TAILQ_FOREACH(en, &entries, en_entries) {
if (en->en_flags.efl_marked) {
nmarked++;
en->en_flags.efl_deleted = 1;
}
}
if (nmarked)
return;
if (!curent) {
drawstatus(L"No entries to delete.");
return;
}
curent->en_flags.efl_deleted = 1;
if (delete_advance)
cursadvance();
}
void
ksync()
{
entry_t *en, *ten;
TTS_TAILQ_FOREACH_SAFE(en, &entries, en_entries, ten) {
if (!en->en_flags.efl_deleted)
continue;
if (en == curent)
curent = NULL;
TTS_TAILQ_REMOVE(&entries, en, en_entries);
entry_free(en);
}
if (curent == NULL)
curent = TTS_TAILQ_FIRST(&entries);
save();
}
void
kup()
{
entry_t *prev = curent;
if (!curent)
return;
do {
if ((prev = TTS_TAILQ_PREV(prev, entrylist, en_entries)) == NULL)
break;
} while (!showinv && prev->en_flags.efl_invoiced);
if (prev == NULL) {
drawstatus(L"Already at first entry.");
return;
}
curent = prev;
if (!curent->en_flags.efl_visible)
pagestart--;
}
void
kdown()
{
entry_t *next = curent;
if (!curent)
return;
do {
if ((next = TTS_TAILQ_NEXT(next, en_entries)) == NULL)
break;
} while (!showinv && next->en_flags.efl_invoiced);
if (next == NULL) {
drawstatus(L"Already at last entry.");
return;
}
curent = next;
if (!curent->en_flags.efl_visible)
pagestart++;
}
void
kinvoiced()
{
entry_t *en;
int anymarked = 0;
TTS_TAILQ_FOREACH(en, &entries, en_entries) {
if (!en->en_flags.efl_marked)
continue;
anymarked = 1;
en->en_flags.efl_invoiced = !en->en_flags.efl_invoiced;
en->en_flags.efl_marked = 0;
}
if (anymarked) {
save();
return;
}
if (!curent) {
drawstatus(L"No entry selected.");
return;
}
curent->en_flags.efl_invoiced = !curent->en_flags.efl_invoiced;
save();
en = curent;
if (showinv) {
if (TTS_TAILQ_NEXT(curent, en_entries) != NULL)
curent = TTS_TAILQ_NEXT(curent, en_entries);
return;
}
/*
* Try to find the next uninvoiced request to move the cursor to.
*/
for (;;) {
if ((curent = TTS_TAILQ_NEXT(curent, en_entries)) == NULL)
break; /* end of list */
if (!curent->en_flags.efl_invoiced)
return;
}
/*
* We didn't find any, so try searching backwards instead.
*/
for (curent = en;;) {
if ((curent = TTS_TAILQ_PREV(curent, entrylist, en_entries)) == NULL)
break; /* end of list */
if (!curent->en_flags.efl_invoiced)
return;
}
}
void
kbillable()
{
entry_t *en;
int anymarked = 0;
TTS_TAILQ_FOREACH(en, &entries, en_entries) {
if (!en->en_flags.efl_marked)
continue;
anymarked = 1;
en->en_flags.efl_nonbillable = !en->en_flags.efl_nonbillable;
en->en_flags.efl_marked = 0;
}
if (anymarked) {
save();
return;
}
if (!curent) {
drawstatus(L"No entry selected.");
return;
}
curent->en_flags.efl_nonbillable = !curent->en_flags.efl_nonbillable;
save();
if (bill_advance)
cursadvance();
}
void
keddesc()
{
wchar_t *new;
if (!curent) {
drawstatus(L"No entry selected.");
return;
}
if ((new = prompt(L"Description:", curent->en_desc, NULL)) == NULL)
return;
free(curent->en_desc);
curent->en_desc = new;
save();
}
void
kedtime()
{
time_t n;
if (!curent) {
drawstatus(L"No entry selected.");
return;
}
n = curent->en_secs;
if (curent->en_started)
n += time(NULL) - curent->en_started;
if ((n = prduration(L"Duration:", n)) == (time_t) -1) {
drawstatus(L"Invalid duration.");
return;
}
curent->en_secs = n;
if (curent->en_started)
time(&curent->en_started);
save();
}
void
ktoggleinv()
{
entry_t *en = curent;
showinv = !showinv;
drawstatus(L"%ls invoiced entries.", showinv ? L"Showing" : L"Hiding");
if (curent && !curent->en_flags.efl_invoiced)
return;
if (!curent) {
curent = TTS_TAILQ_FIRST(&entries);
return;
}
/*
* Try to find the next uninvoiced request to move the cursor to.
*/
for (;;) {
if ((curent = TTS_TAILQ_NEXT(curent, en_entries)) == NULL)
break; /* end of list */
if (!curent->en_flags.efl_invoiced)
return;
}
/*
* We didn't find any, so try searching backwards instead.
*/
for (curent = en;;) {
if ((curent = TTS_TAILQ_PREV(curent, entrylist, en_entries)) == NULL)
break; /* end of list */
if (!curent->en_flags.efl_invoiced)
return;
}
}
void
kcopy()
{
entry_t *en;
if (!curent) {
drawstatus(L"No entry selected.");
return;
}
en = entry_new(curent->en_desc);
curent = en;
entry_start(en);
save();
}
void
kaddtime()
{
time_t secs;
if (!curent) {
drawstatus(L"No entry selected.");
return;
}
if ((secs = prduration(L"Time to add:", 0)) == (time_t) -1) {
drawstatus(L"Invalid time format.");
return;
}
curent->en_secs += secs;
save();
}
void
kdeltime()
{
time_t secs;
if (!curent) {
drawstatus(L"No entry selected.");
return;
}
if ((secs = prduration(L"Time to subtract:", 0)) == (time_t) -1) {
drawstatus(L"Invalid time format.");
return;
}
entry_account(curent);
if (curent->en_secs - secs < 0) {
drawstatus(L"Remaining time cannot be less than zero.");
return;
}
curent->en_secs -= secs;
save();
}
void
kmerge()
{
entry_t *en, *ten;
int nmarked = 0;
wchar_t pr[128];
wchar_t *ct;
int s = 0;
if (!curent) {
drawstatus(L"No entry selected.");
return;
}
/*
* Count number of marked entries and the summed time.
*/
TTS_TAILQ_FOREACH(en, &entries, en_entries) {
if (!en->en_flags.efl_marked || en == curent)
continue;
nmarked++;
s += en->en_secs;
if (en->en_started)
s += time(NULL) - en->en_started;
}
if (nmarked == 0) {
drawstatus(L"No marked entries.");
return;
}
ct = maketime(s, time_format);
swprintf(pr, wsizeof(pr), L"Merge %d marked entries [%ls] into current entry",
nmarked, ct);
free(ct);
if (!yesno(pr))
return;
TTS_TAILQ_FOREACH_SAFE(en, &entries, en_entries, ten) {
if (!en->en_flags.efl_marked || en == curent)
continue;
if (en->en_started)
entry_stop(en);
curent->en_secs += en->en_secs;
TTS_TAILQ_REMOVE(&entries, en, en_entries);
entry_free(en);
}
save();
}
void
khelp()
{
WINDOW *hwin;
size_t nhelp = 0;
wchar_t **help;
#define HTITLE L" TTS keys "
size_t width = 0;
size_t i;
wint_t c;
binding_t *bi;
/* Count the number of bindings */
TTS_TAILQ_FOREACH(bi, &bindings, bi_entries)
nhelp++;
help = calloc(nhelp, sizeof(const wchar_t *));
i = 0;
TTS_TAILQ_FOREACH(bi, &bindings, bi_entries) {
wchar_t s[128], t[16];
if (bi->bi_key)
swprintf(t, wsizeof(t), L"%ls", bi->bi_key->ky_name);
else
swprintf(t, wsizeof(t), L"%lc", bi->bi_code);
if (bi->bi_macro) {
wchar_t *esc = escstr(bi->bi_macro);
swprintf(s, wsizeof(s), L"%-10ls execute macro: %ls",
t, esc);
free(esc);
} else
swprintf(s, wsizeof(s), L"%-10ls %ls (%ls)",
t, bi->bi_func->fn_desc, bi->bi_func->fn_name);
help[i] = wcsdup(s);
i++;
}
if (nhelp > (LINES - 6))
nhelp = LINES - 6;
for (i = 0; i < nhelp; i++)
if (wcslen(help[i]) > width)
width = wcslen(help[i]);
hwin = newwin(nhelp + 4, width + 4,
(LINES / 2) - ((nhelp + 2) / 2),
(COLS / 2) - ((width + 2) / 2));
wborder(hwin, 0, 0, 0, 0, 0, 0, 0, 0);
wattron(hwin, A_REVERSE | A_BOLD);
wmove(hwin, 0, (width / 2) - (wsizeof(HTITLE) - 1)/2);
waddwstr(hwin, HTITLE);
wattroff(hwin, A_REVERSE | A_BOLD);
for (i = 0; i < nhelp; i++) {
wmove(hwin, i + 2, 2);
waddwstr(hwin, help[i]);
}
wrefresh(hwin);
while (wget_wch(hwin, &c) == ERR
#ifdef KEY_RESIZE
|| (c == KEY_RESIZE)
#endif
)
;
delwin(hwin);
for (i = 0; i < nhelp; i++)
free(help[i]);
free(help);
}
void
kmark()
{
if (!curent) {
drawstatus(L"No entry selected.");
return;
}
curent->en_flags.efl_marked = !curent->en_flags.efl_marked;
if (mark_advance)
cursadvance();
}
void
kunmarkall()
{
entry_t *en;
TTS_TAILQ_FOREACH(en, &entries, en_entries)
en->en_flags.efl_marked = 0;
}
void
kint()
{
time_t duration;
entry_t *en;
wchar_t *name;
if (!itime) {
if (!running) {
drawstatus(L"No running entry.");
return;
}
itime = time(NULL);
return;
}
if (!running) {
drawstatus(L"No running entry.");
return;
}
name = prompt(L"Description (<ENTER> to abandon interrupt):", NULL, NULL);
if (!valid_description(name)) {
itime = 0;
free(name);
return;
}
duration = time(NULL) - itime;
itime = 0;
running->en_secs += (time(NULL) - running->en_started);
running->en_started = time(NULL);
running->en_secs -= duration;
en = entry_new(name);
en->en_created = time(NULL) - duration;
en->en_secs = duration;
save();
free(name);
}
void
ksearch()
{
static wchar_t *lastsearch;
wchar_t *term;
entry_t *start, *cur;
if (!curent) {
drawstatus(L"No entries.");
return;
}
if ((term = prompt(L"Search:", NULL, NULL)) == NULL)
return;
if (!*term) {
free(term);
if (!lastsearch)
return;
term = lastsearch;
} else {
free(lastsearch);
lastsearch = term;
}
cur = start = curent;
for (;;) {
cur = TTS_TAILQ_NEXT(cur, en_entries);
if (cur == NULL) {
drawstatus(L"Search reached last entry, continuing from top.");
cur = TTS_TAILQ_FIRST(&entries);
}
if (cur == start) {
drawstatus(L"No matches.");
break;
}
if (wcsstr(cur->en_desc, term)) {
curent = cur;
if (!showinv && cur->en_flags.efl_invoiced)
showinv = 1;
return;
}
}
}
void
kexec()
{
wchar_t *cmd;
wchar_t **args;
command_t *cmds;
size_t nargs;
if ((cmd = prompt(L":", NULL, NULL)) == NULL || !*cmd) {
free(cmd);
return;
}
nargs = tokenise(cmd, &args);
free(cmd);
if (nargs == 0) {
tokfree(&args);
return;
}
if ((cmds = find_command(args[0])) == NULL) {
drawstatus(L"Unknown command.");
tokfree(&args);
return;
}
cmds->cm_hdl(nargs, args);
tokfree(&args);
}
/*
* Return the function_t for the function called .name, or NULL if such a
* function doesn't exist.
*/
function_t *
find_func(name)
const wchar_t *name;
{
function_t *f;
for (f = funcs; f->fn_name; f++)
if (wcscmp(name, f->fn_name) == 0)
return f;
return NULL;
}

View file

@ -1,51 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_FUNCTIONS_H
#define TTS_FUNCTIONS_H
#include "wide.h"
void kadd(void);
void kaddold(void);
void kquit(void);
void kup(void);
void kdown(void);
void ktoggle(void);
void kinvoiced(void);
void kbillable(void);
void keddesc(void);
void kedtime(void);
void ktoggleinv(void);
void kcopy(void);
void kaddtime(void);
void kdeltime(void);
void khelp(void);
void kmark(void);
void kunmarkall(void);
void ksearch(void);
void kmarkdel(void);
void kundel(void);
void ksync(void);
void kexec(void);
void kmerge(void);
void kint(void);
typedef struct function {
const wchar_t *fn_name;
void (*fn_hdl) (void);
const wchar_t *fn_desc;
} function_t;
extern function_t funcs[];
function_t *find_func(const wchar_t *name);
#endif /* !TTS_FUNCTIONS_H */

611
queue.h Normal file
View file

@ -0,0 +1,611 @@
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
* FreeBSD: release/9.0.0/sys/sys/queue.h 221843 2011-05-13 15:49:23Z mdf
*/
#ifndef TTS_QUEUE_H
#define TTS_QUEUE_H
/*
* This file defines four types of data structures: singly-linked lists,
* singly-linked tail queues, lists and tail queues.
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A singly-linked tail queue is headed by a pair of pointers, one to the
* head of the list and the other to the tail of the list. The elements are
* singly linked for minimum space and pointer manipulation overhead at the
* expense of O(n) removal for arbitrary elements. New elements can be added
* to the list after an existing element, at the head of the list, or at the
* end of the list. Elements being removed from the head of the tail queue
* should use the explicit macro for this purpose for optimum efficiency.
* A singly-linked tail queue may only be traversed in the forward direction.
* Singly-linked tail queues are ideal for applications with large datasets
* and few or no removals or for implementing a FIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* For details on the use of these macros, see the queue(3) manual page.
*
*
* SLIST LIST STAILQ TAILQ
* _HEAD + + + +
* _HEAD_INITIALIZER + + + +
* _ENTRY + + + +
* _INIT + + + +
* _EMPTY + + + +
* _FIRST + + + +
* _NEXT + + + +
* _PREV - - - +
* _LAST - - + +
* _FOREACH + + + +
* _FOREACH_SAFE + + + +
* _FOREACH_REVERSE - - - +
* _FOREACH_REVERSE_SAFE - - - +
* _INSERT_HEAD + + + +
* _INSERT_BEFORE - + - +
* _INSERT_AFTER + + + +
* _INSERT_TAIL - - + +
* _CONCAT - - + +
* _REMOVE_AFTER + - + -
* _REMOVE_HEAD + - + -
* _REMOVE + + + +
* _SWAP + + + +
*
*/
#ifdef QUEUE_MACRO_DEBUG
/* Store the last 2 places the queue element or head was altered */
struct qm_trace {
char * lastfile;
int lastline;
char * prevfile;
int prevline;
};
#define TRACEBUF struct qm_trace trace;
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
#define QMD_TRACE_HEAD(head) do { \
(head)->trace.prevline = (head)->trace.lastline; \
(head)->trace.prevfile = (head)->trace.lastfile; \
(head)->trace.lastline = __LINE__; \
(head)->trace.lastfile = __FILE__; \
} while (0)
#define QMD_TRACE_ELEM(elem) do { \
(elem)->trace.prevline = (elem)->trace.lastline; \
(elem)->trace.prevfile = (elem)->trace.lastfile; \
(elem)->trace.lastline = __LINE__; \
(elem)->trace.lastfile = __FILE__; \
} while (0)
#else
#define QMD_TRACE_ELEM(elem)
#define QMD_TRACE_HEAD(head)
#define QMD_SAVELINK(name, link)
#define TRACEBUF
#define TRASHIT(x)
#endif /* QUEUE_MACRO_DEBUG */
/*
* Singly-linked List declarations.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List functions.
*/
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_FOREACH(var, head, field) \
for ((var) = SLIST_FIRST((head)); \
(var); \
(var) = SLIST_NEXT((var), field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST((head)); \
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
(var) = (tvar))
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
for ((varp) = &SLIST_FIRST((head)); \
((var) = *(varp)) != NULL; \
(varp) = &SLIST_NEXT((var), field))
#define SLIST_INIT(head) do { \
SLIST_FIRST((head)) = NULL; \
} while (0)
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
SLIST_NEXT((slistelm), field) = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
SLIST_FIRST((head)) = (elm); \
} while (0)
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_REMOVE(head, elm, type, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
if (SLIST_FIRST((head)) == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} \
else { \
struct type *curelm = SLIST_FIRST((head)); \
while (SLIST_NEXT(curelm, field) != (elm)) \
curelm = SLIST_NEXT(curelm, field); \
SLIST_REMOVE_AFTER(curelm, field); \
} \
TRASHIT(*oldnext); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
SLIST_NEXT(elm, field) = \
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
} while (0)
#define SLIST_SWAP(head1, head2, type) do { \
struct type *swap_first = SLIST_FIRST(head1); \
SLIST_FIRST(head1) = SLIST_FIRST(head2); \
SLIST_FIRST(head2) = swap_first; \
} while (0)
/*
* Singly-linked Tail queue declarations.
*/
#define STAILQ_HEAD(name, type) \
struct name { \
struct type *stqh_first;/* first element */ \
struct type **stqh_last;/* addr of last next element */ \
}
#define STAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).stqh_first }
#define STAILQ_ENTRY(type) \
struct { \
struct type *stqe_next; /* next element */ \
}
/*
* Singly-linked Tail queue functions.
*/
#define STAILQ_CONCAT(head1, head2) do { \
if (!STAILQ_EMPTY((head2))) { \
*(head1)->stqh_last = (head2)->stqh_first; \
(head1)->stqh_last = (head2)->stqh_last; \
STAILQ_INIT((head2)); \
} \
} while (0)
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
#define STAILQ_FIRST(head) ((head)->stqh_first)
#define STAILQ_FOREACH(var, head, field) \
for((var) = STAILQ_FIRST((head)); \
(var); \
(var) = STAILQ_NEXT((var), field))
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = STAILQ_FIRST((head)); \
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define STAILQ_INIT(head) do { \
STAILQ_FIRST((head)) = NULL; \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_NEXT((tqelm), field) = (elm); \
} while (0)
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_FIRST((head)) = (elm); \
} while (0)
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
STAILQ_NEXT((elm), field) = NULL; \
*(head)->stqh_last = (elm); \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#define STAILQ_LAST(head, type, field) \
(STAILQ_EMPTY((head)) ? \
NULL : \
((struct type *)(void *) \
((char *)((head)->stqh_last) - __offsetof(struct type, field))))
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
#define STAILQ_REMOVE(head, elm, type, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
if (STAILQ_FIRST((head)) == (elm)) { \
STAILQ_REMOVE_HEAD((head), field); \
} \
else { \
struct type *curelm = STAILQ_FIRST((head)); \
while (STAILQ_NEXT(curelm, field) != (elm)) \
curelm = STAILQ_NEXT(curelm, field); \
STAILQ_REMOVE_AFTER(head, curelm, field); \
} \
TRASHIT(*oldnext); \
} while (0)
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
if ((STAILQ_NEXT(elm, field) = \
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#define STAILQ_REMOVE_HEAD(head, field) do { \
if ((STAILQ_FIRST((head)) = \
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#define STAILQ_SWAP(head1, head2, type) do { \
struct type *swap_first = STAILQ_FIRST(head1); \
struct type **swap_last = (head1)->stqh_last; \
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
(head1)->stqh_last = (head2)->stqh_last; \
STAILQ_FIRST(head2) = swap_first; \
(head2)->stqh_last = swap_last; \
if (STAILQ_EMPTY(head1)) \
(head1)->stqh_last = &STAILQ_FIRST(head1); \
if (STAILQ_EMPTY(head2)) \
(head2)->stqh_last = &STAILQ_FIRST(head2); \
} while (0)
/*
* List declarations.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List functions.
*/
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_FOREACH(var, head, field) \
for ((var) = LIST_FIRST((head)); \
(var); \
(var) = LIST_NEXT((var), field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST((head)); \
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
(var) = (tvar))
#define LIST_INIT(head) do { \
LIST_FIRST((head)) = NULL; \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
QMD_LIST_CHECK_NEXT(listelm, field); \
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
LIST_NEXT((listelm), field)->field.le_prev = \
&LIST_NEXT((elm), field); \
LIST_NEXT((listelm), field) = (elm); \
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
QMD_LIST_CHECK_PREV(listelm, field); \
(elm)->field.le_prev = (listelm)->field.le_prev; \
LIST_NEXT((elm), field) = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
QMD_LIST_CHECK_HEAD((head), field); \
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
LIST_FIRST((head)) = (elm); \
(elm)->field.le_prev = &LIST_FIRST((head)); \
} while (0)
#define LIST_PREV(elm, field) ((elm)->field.le_prev ? *(elm)->field.le_prev : NULL)
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_REMOVE(elm, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.le_next); \
QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
QMD_LIST_CHECK_NEXT(elm, field); \
QMD_LIST_CHECK_PREV(elm, field); \
if (LIST_NEXT((elm), field) != NULL) \
LIST_NEXT((elm), field)->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
TRASHIT(*oldnext); \
TRASHIT(*oldprev); \
} while (0)
#define LIST_SWAP(head1, head2, type, field) do { \
struct type *swap_tmp = LIST_FIRST((head1)); \
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
LIST_FIRST((head2)) = swap_tmp; \
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
} while (0)
/*
* Tail queue declarations.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
TRACEBUF \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
TRACEBUF \
}
/*
* Tail queue functions.
*/
#if (defined(_KERNEL) && defined(INVARIANTS))
#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
if (!TAILQ_EMPTY(head) && \
TAILQ_FIRST((head))->field.tqe_prev != \
&TAILQ_FIRST((head))) \
panic("Bad tailq head %p first->prev != head", (head)); \
} while (0)
#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
if (*(head)->tqh_last != NULL) \
panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
} while (0)
#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
if (TAILQ_NEXT((elm), field) != NULL && \
TAILQ_NEXT((elm), field)->field.tqe_prev != \
&((elm)->field.tqe_next)) \
panic("Bad link elm %p next->prev != elm", (elm)); \
} while (0)
#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
if (*(elm)->field.tqe_prev != (elm)) \
panic("Bad link elm %p prev->next != elm", (elm)); \
} while (0)
#else
#define QMD_TAILQ_CHECK_HEAD(head, field)
#define QMD_TAILQ_CHECK_TAIL(head, headname)
#define QMD_TAILQ_CHECK_NEXT(elm, field)
#define QMD_TAILQ_CHECK_PREV(elm, field)
#endif /* (_KERNEL && INVARIANTS) */
#define TAILQ_CONCAT(head1, head2, field) do { \
if (!TAILQ_EMPTY(head2)) { \
*(head1)->tqh_last = (head2)->tqh_first; \
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
(head1)->tqh_last = (head2)->tqh_last; \
TAILQ_INIT((head2)); \
QMD_TRACE_HEAD(head1); \
QMD_TRACE_HEAD(head2); \
} \
} while (0)
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_FOREACH(var, head, field) \
for ((var) = TAILQ_FIRST((head)); \
(var); \
(var) = TAILQ_NEXT((var), field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST((head)); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for ((var) = TAILQ_LAST((head), headname); \
(var); \
(var) = TAILQ_PREV((var), headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST((head), headname); \
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))
#define TAILQ_INIT(head) do { \
TAILQ_FIRST((head)) = NULL; \
(head)->tqh_last = &TAILQ_FIRST((head)); \
QMD_TRACE_HEAD(head); \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
QMD_TAILQ_CHECK_NEXT(listelm, field); \
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
TAILQ_NEXT((elm), field)->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else { \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
QMD_TRACE_HEAD(head); \
} \
TAILQ_NEXT((listelm), field) = (elm); \
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
QMD_TRACE_ELEM(&(elm)->field); \
QMD_TRACE_ELEM(&listelm->field); \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
QMD_TAILQ_CHECK_PREV(listelm, field); \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
TAILQ_NEXT((elm), field) = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
QMD_TRACE_ELEM(&(elm)->field); \
QMD_TRACE_ELEM(&listelm->field); \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
QMD_TAILQ_CHECK_HEAD(head, field); \
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
TAILQ_FIRST((head))->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
TAILQ_FIRST((head)) = (elm); \
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
QMD_TRACE_HEAD(head); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
QMD_TAILQ_CHECK_TAIL(head, field); \
TAILQ_NEXT((elm), field) = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
QMD_TRACE_HEAD(head); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_REMOVE(head, elm, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
QMD_TAILQ_CHECK_NEXT(elm, field); \
QMD_TAILQ_CHECK_PREV(elm, field); \
if ((TAILQ_NEXT((elm), field)) != NULL) \
TAILQ_NEXT((elm), field)->field.tqe_prev = \
(elm)->field.tqe_prev; \
else { \
(head)->tqh_last = (elm)->field.tqe_prev; \
QMD_TRACE_HEAD(head); \
} \
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
TRASHIT(*oldnext); \
TRASHIT(*oldprev); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_SWAP(head1, head2, type, field) do { \
struct type *swap_first = (head1)->tqh_first; \
struct type **swap_last = (head1)->tqh_last; \
(head1)->tqh_first = (head2)->tqh_first; \
(head1)->tqh_last = (head2)->tqh_last; \
(head2)->tqh_first = swap_first; \
(head2)->tqh_last = swap_last; \
if ((swap_first = (head1)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head1)->tqh_first; \
else \
(head1)->tqh_last = &(head1)->tqh_first; \
if ((swap_first = (head2)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head2)->tqh_first; \
else \
(head2)->tqh_last = &(head2)->tqh_first; \
} while (0)
#endif /* !TTS_QUEUE_H */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

277
str.c
View file

@ -1,277 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#include <stddef.h>
#include <wctype.h>
#include <time.h>
#include "str.h"
#include "tts.h"
size_t
tokenise(str, res)
const wchar_t *str;
wchar_t ***res;
{
int ntoks = 0;
const wchar_t *p, *q;
wchar_t *r;
*res = NULL;
p = str;
for (;;) {
ptrdiff_t sz;
int qskip = 0;
int isbsl = 0;
/* Skip leading whitespace */
while (iswspace(*p))
p++;
/* End of string - no more arguments */
if (!*p)
break;
q = p;
if (*q == '"') {
/* Quoted string - scan for end of string */
p++;
while (*++q) {
if (!isbsl && (*q == '\\')) {
isbsl = 1;
continue;
}
if (!isbsl && (*q == '"'))
break;
isbsl = 0;
}
/* At this point, *q == '"'. If it's NUL instead, then the
* string was not terminated with a closing '"' before the end
* of the line. We could give an error here, but it seems
* more useful to just accept it.
*/
if (*q == '"')
qskip = 1;
} else {
/* Not quoted - just find the next whitespace */
while (!iswspace(*q) && *q)
q++;
}
/* Copy the argument (which is sz bytes long) into the result array */
sz = (q - p);
*res = realloc(*res, sizeof(wchar_t *) * (ntoks + 1));
(*res)[ntoks] = malloc(sizeof(wchar_t) * (sz + 1));
wmemcpy((*res)[ntoks], p, sz);
/* Handle \ escapes */
for (r = (*res)[ntoks]; r < ((*res)[ntoks] + sz);) {
if (!isbsl) {
if (*r == '\\') {
wmemmove(r, r + 1, sz - (r - (*res)[ntoks]));
sz--;
isbsl = 1;
continue;
}
r++;
continue;
}
switch (*r) {
case 't': *r = '\t'; break;
case 'n': *r = '\n'; break;
case 'r': *r = '\r'; break;
case 'v': *r = '\v'; break;
case '\\': *r = '\\'; break;
}
isbsl = 0;
r++;
}
(*res)[ntoks][sz] = 0;
ntoks++;
if (qskip)
q += qskip;
while (iswspace(*q))
q++;
/*
* q is the start of the next token (with leading whitespace); reset
* p to process the next argument.
*/
if (!*q)
break;
p = q;
}
*res = realloc(*res, sizeof(wchar_t *) * (ntoks + 1));
(*res)[ntoks] = NULL;
return ntoks;
}
void
tokfree(vec)
wchar_t ***vec;
{
wchar_t **p;
for (p = (*vec); *p; p++)
free(*p);
free(*vec);
}
time_t
parsetime(tm)
const wchar_t *tm;
{
int h = 0, m = 0, s = 0;
time_t i = 0, r = 0;
/* The empty string is not a valid duration */
if (!*tm)
return (time_t) -1;
/* Check for "hh:mm:ss" or "mm:ss" */
if (swscanf(tm, L"%d:%d:%d", &h, &m, &s) == 3)
return (h * 60 * 60) + (m * 60) + s;
if (swscanf(tm, L"%d:%d", &m, &s) == 2)
return (m * 60) + s;
/*
* The string could either be a format like 3h10m, or a simle number like 47
* (meaning seconds), which is also handled here. This is effectively an
* implementation of atoi with special meaning for 'h', 'm' and 's' characters.
*
* Note that we make no attempt to handle overflow.
*/
for (; *tm; tm++) {
switch (*tm) {
case L'h':
r += i * (60 * 60);
i = 0;
continue;
case L'm':
r += i * 60;
i = 0;
continue;
case L's':
r += i;
i = 0;
continue;
case L' ':
continue;
}
if (wcschr(L"0123456789", *tm) == NULL)
return (time_t) -1;
i *= 10;
i += *tm - L'0';
}
return r + i;
}
wchar_t *
maketime(tm, fmt)
time_t tm;
{
wchar_t res[64] = {};
wchar_t t[16];
if (fmt == TIME_HMS) {
int h, m, s;
time_to_hms(tm, h, m, s);
swprintf(t, wsizeof(t), L"%02d:%02d:%02d",
h, m, s);
return wcsdup(t);
}
if (fmt == TIME_HM) {
int h, m, s;
time_to_hms(tm, h, m, s);
swprintf(t, wsizeof(t), L"%02d:%02d", h, m);
return wcsdup(t);
}
if (tm >= (60 * 60)) {
swprintf(t, wsizeof(t), L"%2dh ", tm / (60 * 60));
wcslcat(res, t, wsizeof(res));
tm %= (60 * 60);
}
if (tm >= 60) {
swprintf(t, wsizeof(t), L"%2dm ", tm / 60);
wcslcat(res, t, wsizeof(res));
tm %= 60;
}
if (fmt == TIME_AHMS && tm) {
swprintf(t, wsizeof(t), L"%2ds ", tm);
wcslcat(res, t, wsizeof(res));
}
res[wcslen(res) - 1] = '\0';
return wcsdup(res);
}
wchar_t *
escstr(s)
const wchar_t *s;
{
wchar_t *ret, *p;
if ((ret = calloc(sizeof(wchar_t), wcslen(s) * 2 + 1)) == NULL)
return NULL;
for (p = ret; *s; s++) {
switch (*s) {
case '\\':
*p++ = L'\\';
*p++ = L'\\';
continue;
case '\n':
*p++ = L'\\';
*p++ = L'n';
continue;
case '\t':
*p++ = L'\\';
*p++ = L't';
continue;
case '\v':
*p++ = L'\\';
*p++ = L'v';
continue;
case '\r':
*p++ = L'\\';
*p++ = L'r';
continue;
case '"':
*p++ = L'\\';
*p++ = L'"';
continue;
}
*p++ = *s;
}
return ret;
}

36
str.h
View file

@ -1,36 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_STR_H
#define TTS_STR_H
#include <stdlib.h>
#include "wide.h"
#define TIME_HMS 0 /* HH:MM:SS */
#define TIME_HM 1 /* HH:MM */
#define TIME_AHMS 2 /* 1h10m37s */
#define TIME_AHM 3 /* 1h10w */
#define TIMEFMT_FOR_EDIT(f) \
( (f) == TIME_HMS ? TIME_HMS \
: (f) == TIME_HM ? TIME_HMS \
: (f) == TIME_AHMS ? TIME_AHMS \
: (f) == TIME_AHM ? TIME_AHMS \
: 0)
size_t tokenise (const wchar_t *, wchar_t ***result);
void tokfree (wchar_t ***);
time_t parsetime (const wchar_t *);
wchar_t *maketime (time_t, int format);
wchar_t *escstr (const wchar_t *);
#endif /* !TTS_STR_H */

146
style.c
View file

@ -1,146 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#include "style.h"
#include "ui.h"
#include "wide.h"
short default_fg, default_bg;
style_t sy_header = { 1, 0 },
sy_status = { 2, 0 },
sy_entry = { 3, 0 },
sy_running = { 4, 0 },
sy_selected = { 5, 0 },
sy_date = { 6, 0 };
static attrname_t attrnames[] = {
{ L"normal", 0 },
{ L"bold", WA_BOLD },
{ L"reverse", WA_REVERSE },
{ L"blink", WA_BLINK },
{ L"dim", WA_DIM },
{ L"underline", WA_UNDERLINE },
{ L"standout", WA_STANDOUT }
};
static colour_t colours[] = {
{ L"black", COLOR_BLACK },
{ L"red", COLOR_RED },
{ L"green", COLOR_GREEN },
{ L"yellow", COLOR_YELLOW },
{ L"blue", COLOR_BLUE },
{ L"magenta", COLOR_MAGENTA },
{ L"cyan", COLOR_CYAN },
{ L"white", COLOR_WHITE }
};
int
attr_find(name, result)
const wchar_t *name;
attr_t *result;
{
size_t i;
for (i = 0; i < sizeof(attrnames) / sizeof(*attrnames); i++) {
if (wcscmp(attrnames[i].an_name, name) == 0) {
*result = attrnames[i].an_value;
return 0;
}
}
return -1;
}
int
colour_find(name, result)
const wchar_t *name;
short *result;
{
size_t i;
for (i = 0; i < sizeof(colours) / sizeof(*colours); i++) {
if (wcscmp(colours[i].co_name, name) == 0) {
*result = colours[i].co_value;
return 0;
}
}
return -1;
}
void
style_clear(sy)
style_t *sy;
{
init_pair(sy->sy_pair, default_fg, default_bg);
sy->sy_attrs = 0;
}
int
style_set(sy, fg, bg)
style_t *sy;
const wchar_t *fg, *bg;
{
sy->sy_attrs = 0;
init_pair(sy->sy_pair, default_fg, default_bg);
return style_add(sy, fg, bg);
}
int
style_add(sy, fg, bg)
style_t *sy;
const wchar_t *fg, *bg;
{
attr_t at;
short colfg, colbg = default_bg;
if (colour_find(fg, &colfg) == 0) {
if (bg && (colour_find(bg, &colbg) == -1))
return -1;
init_pair(sy->sy_pair, colfg, colbg);
return 0;
}
if (attr_find(fg, &at) == -1)
return -1;
sy->sy_attrs |= at;
return 0;
}
void
apply_styles()
{
wbkgd(statwin, style_bg(sy_status));
wattr_on(statwin, style_fg(sy_status), NULL);
drawstatus(L"");
wbkgd(titwin, style_bg(sy_header));
wattr_on(titwin, style_fg(sy_header), NULL);
drawheader();
}
void
style_defaults(void)
{
init_pair(1, default_fg, default_bg);
init_pair(2, default_fg, default_bg);
init_pair(3, default_fg, default_bg);
init_pair(4, default_fg, default_bg);
init_pair(5, default_fg, default_bg);
init_pair(6, default_fg, default_bg);
style_set(&sy_header, L"reverse", NULL);
style_set(&sy_status, L"normal", NULL);
style_set(&sy_entry, L"normal", NULL);
style_set(&sy_selected, L"normal", NULL);
style_set(&sy_running, L"bold", NULL);
style_set(&sy_date, L"underline", NULL);
apply_styles();
}

54
style.h
View file

@ -1,54 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_STYLE_H
#define TTS_STYLE_H
#include "tts_curses.h"
#include "wide.h"
typedef struct style {
short sy_pair;
attr_t sy_attrs;
} style_t;
#define style_fg(s) (COLOR_PAIR((s).sy_pair) | (s).sy_attrs)
#define style_bg(s) ((wint_t) ' ' | COLOR_PAIR((s).sy_pair) | ((s).sy_attrs & ~WA_UNDERLINE))
typedef struct attrname {
const wchar_t *an_name;
attr_t an_value;
} attrname_t;
typedef struct colour {
const wchar_t *co_name;
short co_value;
} colour_t;
extern style_t sy_header,
sy_status,
sy_entry,
sy_running,
sy_selected,
sy_date;
extern short default_fg, default_bg;
int attr_find (const wchar_t *name, attr_t *result);
int colour_find (const wchar_t *name, short *result);
void style_clear (style_t *);
int style_set (style_t *, const wchar_t *fg, const wchar_t *bg);
int style_add (style_t *, const wchar_t *fg, const wchar_t *bg);
void style_defaults (void);
void apply_styles (void);
#endif /* !TTS_STYLE_H */

163
tailq.h
View file

@ -1,163 +0,0 @@
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
* FreeBSD: release/9.0.0/sys/sys/queue.h 221843 2011-05-13 15:49:23Z mdf
*/
#ifndef TTS_TAILQ_H
#define TTS_TAILQ_H
/*
* Tail queue declarations.
*/
#define TTS_TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TTS_TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TTS_TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
#define TTS_TAILQ_CONCAT(head1, head2, field) do { \
if (!TAILQ_EMPTY(head2)) { \
*(head1)->tqh_last = (head2)->tqh_first; \
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
(head1)->tqh_last = (head2)->tqh_last; \
TTS_TAILQ_INIT((head2)); \
} \
} while (0)
#define TTS_TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#define TTS_TAILQ_FIRST(head) ((head)->tqh_first)
#define TTS_TAILQ_FOREACH(var, head, field) \
for ((var) = TTS_TAILQ_FIRST((head)); \
(var); \
(var) = TTS_TAILQ_NEXT((var), field))
#define TTS_TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TTS_TAILQ_FIRST((head)); \
(var) && ((tvar) = TTS_TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define TTS_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for ((var) = TTS_TAILQ_LAST((head), headname); \
(var); \
(var) = TTS_TAILQ_PREV((var), headname, field))
#define TTS_TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)\
for ((var) = TTS_TAILQ_LAST((head), headname); \
(var) && ((tvar) = TTS_TAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))
#define TTS_TAILQ_INIT(head) do { \
TTS_TAILQ_FIRST((head)) = NULL; \
(head)->tqh_last = &TTS_TAILQ_FIRST((head)); \
} while (0)
#define TTS_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if ((TTS_TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
TTS_TAILQ_NEXT((elm), field)->field.tqe_prev = \
&TTS_TAILQ_NEXT((elm), field); \
else { \
(head)->tqh_last = &TTS_TAILQ_NEXT((elm), field); \
} \
TTS_TAILQ_NEXT((listelm), field) = (elm); \
(elm)->field.tqe_prev = &TTS_TAILQ_NEXT((listelm), field); \
} while (0)
#define TTS_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
TTS_TAILQ_NEXT((elm), field) = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &TTS_TAILQ_NEXT((elm), field); \
} while (0)
#define TTS_TAILQ_INSERT_HEAD(head, elm, field) do { \
if ((TTS_TAILQ_NEXT((elm), field) = TTS_TAILQ_FIRST((head))) != NULL) \
TTS_TAILQ_FIRST((head))->field.tqe_prev = \
&TTS_TAILQ_NEXT((elm), field); \
else \
(head)->tqh_last = &TTS_TAILQ_NEXT((elm), field); \
TTS_TAILQ_FIRST((head)) = (elm); \
(elm)->field.tqe_prev = &TTS_TAILQ_FIRST((head)); \
} while (0)
#define TTS_TAILQ_INSERT_TAIL(head, elm, field) do { \
TTS_TAILQ_NEXT((elm), field) = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &TTS_TAILQ_NEXT((elm), field); \
} while (0)
#define TTS_TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
#define TTS_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TTS_TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TTS_TAILQ_REMOVE(head, elm, field) do { \
if ((TTS_TAILQ_NEXT((elm), field)) != NULL) \
TTS_TAILQ_NEXT((elm), field)->field.tqe_prev = \
(elm)->field.tqe_prev; \
else { \
(head)->tqh_last = (elm)->field.tqe_prev; \
} \
*(elm)->field.tqe_prev = TTS_TAILQ_NEXT((elm), field); \
} while (0)
#define TTS_TAILQ_SWAP(head1, head2, type, field) do { \
struct type *swap_first = (head1)->tqh_first; \
struct type **swap_last = (head1)->tqh_last; \
(head1)->tqh_first = (head2)->tqh_first; \
(head1)->tqh_last = (head2)->tqh_last; \
(head2)->tqh_first = swap_first; \
(head2)->tqh_last = swap_last; \
if ((swap_first = (head1)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head1)->tqh_first; \
else \
(head1)->tqh_last = &(head1)->tqh_first; \
if ((swap_first = (head2)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head2)->tqh_first; \
else \
(head2)->tqh_last = &(head2)->tqh_first; \
} while (0)
#endif /* !TTS_TAILQ_H */

2747
tts.c

File diff suppressed because it is too large Load diff

78
tts.h
View file

@ -1,78 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_H
#define TTS_H
#include <signal.h>
#include "wide.h"
#include "tailq.h"
#include "entry.h"
extern char const *tts_version;
extern time_t laststatus;
/*
* Configuration options.
*/
extern int show_billable;
extern int delete_advance;
extern int mark_advance;
extern int bill_advance;
extern int bill_increment;
extern wchar_t *auto_nonbillable;
/*
* Global state.
*/
extern entry_t *curent;
extern volatile sig_atomic_t doexit;
extern int pagestart;
extern time_t itime;
int load(void);
int save(void);
/*
* Command history.
*/
#define NHIST 50
typedef struct histent {
wchar_t *he_text;
TTS_TAILQ_ENTRY(histent) he_entries;
} histent_t;
typedef TTS_TAILQ_HEAD(hentlist, histent) hentlist_t;
typedef struct history {
int hi_nents;
hentlist_t hi_ents;
} history_t;
history_t *hist_new(void);
void hist_add(history_t *, wchar_t const *);
extern history_t *searchhist;
extern history_t *prompthist;
#ifndef HAVE_WCSLCPY
size_t wcslcat(wchar_t *s1, const wchar_t *s2, size_t n);
#endif
#ifndef HAVE_WCSLCAT
size_t wcslcpy(wchar_t *s1, const wchar_t *s2, size_t n);
#endif
extern int time_format;
#endif /* !TTS_H */

View file

@ -1,30 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_CURSES_H
#define TTS_CURSES_H
#include "config.h"
#if defined HAVE_NCURSESW_CURSES_H
# include <ncursesw/curses.h>
#elif defined HAVE_NCURSESW_H
# include <ncursesw.h>
#elif defined HAVE_NCURSES_CURSES_H
# include <ncurses/curses.h>
#elif defined HAVE_NCURSES_H
# include <ncurses.h>
#elif defined HAVE_CURSES_H
# include <curses.h>
#else
# error "SVR4 or XSI compatible curses header file required"
#endif
#endif /* !TTS_CURSES_H */

View file

@ -11,15 +11,6 @@
#set mark_advance 1
#set delete_advance 1
# Select the time format used in the display; value can be from 0 to 3.
#
# 0: 10:03:37 (default)
# 1: 10:03
# 2: 10h 3m 37s
# 3: 10h 3m
#
#set time_format 0
#### Billing options
#
# If set, show billable time in each daily summary.
@ -42,7 +33,7 @@
#set bill_advance 0
#### Bindings and macros
#### Bindings
#
# Use the 'bind' command to (re)define keybindings. Type '?' while TTS is
# running for a full list of key bindings.
@ -54,17 +45,6 @@
bind j next
bind k prev
# Macros work in a similar way to bindings, except the second argument is a
# string which will be executed as if it was typed. For example, the
# following macro would add a new entry called "test", and set its timer to
# 30 minutes.
#macro t "atest\n+30m\n"
# Macros can also prompt for input from the user using $[Prompt string]; the
# $[...] will be replaced with the user's input. For example, this macro will
# prompt for the name of a new entry, then set its duration to 30m:
#macro t "a$[Description:]\n+30m\n"
#### Styling
#
# You can style UI elements with the 'style' command. Its syntax is:
@ -121,8 +101,8 @@ bind k prev
#
style header yellow,bold blue
style status yellow,bold blue
style date white,underline black
style entry white black
style selected yellow,bold red
# Use bold *and* underline, because we already bolded 'selected' above.
style running white,bold,underline black
style running bold,underline
style date underline,bold

666
ui.c
View file

@ -1,666 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#include <string.h>
#include <stdlib.h>
#include "ui.h"
#include "tts.h"
#include "style.h"
#include "str.h"
WINDOW *titwin, *statwin, *listwin;
int in_curses;
void
ui_init(void)
{
initscr();
in_curses = 1;
start_color();
#ifdef HAVE_USE_DEFAULT_COLORS
use_default_colors();
#endif
cbreak();
noecho();
nonl();
nodelay(stdscr, TRUE);
pair_content(0, &default_fg, &default_bg);
refresh();
intrflush(stdscr, TRUE);
keypad(stdscr, TRUE);
leaveok(stdscr, TRUE);
titwin = newwin(1, 0, 0, 0);
intrflush(titwin, FALSE);
keypad(titwin, TRUE);
leaveok(titwin, TRUE);
statwin = newwin(1, 0, LINES - 1, 0);
intrflush(statwin, FALSE);
keypad(statwin, TRUE);
leaveok(statwin, TRUE);
listwin = newwin(LINES - 2, 0, 1, 0);
intrflush(listwin, FALSE);
keypad(listwin, TRUE);
leaveok(listwin, TRUE);
curs_set(0);
}
/*
* Move the cursor to the next entry after an operation like mark or deleted.
* If there are no suitable entries after this one, move it backwards instead.
*/
void
cursadvance()
{
entry_t *en;
if (!curent) {
curent = TTS_TAILQ_FIRST(&entries);
return;
}
/*
* Try to find the next suitable entry to move the cursor to.
*/
for (en = TTS_TAILQ_NEXT(curent, en_entries); en; en = TTS_TAILQ_NEXT(en, en_entries)) {
if (!showinv && en->en_flags.efl_invoiced)
continue;
curent = en;
if (!curent->en_flags.efl_visible)
pagestart++;
return;
}
/*
* No entries; if the current entry is visible, stay here, otherwise
* try moving backwards instead.
*/
if (showinv || !curent->en_flags.efl_invoiced)
return;
for (en = TTS_TAILQ_PREV(curent, entrylist, en_entries); en;
en = TTS_TAILQ_PREV(en, entrylist, en_entries)) {
if (!showinv && en->en_flags.efl_invoiced)
continue;
curent = en;
if (!curent->en_flags.efl_visible)
pagestart--;
return;
}
/*
* Couldn't find any entries at all?
*/
curent = NULL;
}
void
drawheader()
{
wchar_t title[64];
swprintf(title, wsizeof(title), L"TTS %s - Type '?' for help",
tts_version);
wmove(titwin, 0, 0);
waddwstr(titwin, title);
if (itime > 0) {
wchar_t str[128], *tm;
time_t passed = time(NULL) - itime;
tm = maketime(passed, time_format);
swprintf(str, wsizeof(str), L" *** MARK INTERRUPT: %ls ***", tm);
free(tm);
wattron(titwin, A_BOLD);
waddwstr(titwin, str);
wattroff(titwin, A_BOLD);
}
wclrtoeol(titwin);
wrefresh(titwin);
}
void
vdrawstatus(msg, ap)
const wchar_t *msg;
va_list ap;
{
wchar_t s[1024];
vswprintf(s, wsizeof(s), msg, ap);
wmove(statwin, 0, 0);
waddwstr(statwin, s);
wclrtoeol(statwin);
wrefresh(statwin);
time(&laststatus);
}
void
drawstatus(const wchar_t *msg, ...)
{
va_list ap;
va_start(ap, msg);
vdrawstatus(msg, ap);
va_end(ap);
}
int
yesno(msg)
const wchar_t *msg;
{
WINDOW *pwin;
wint_t c;
pwin = newwin(1, COLS, LINES - 1, 0);
keypad(pwin, TRUE);
wattron(pwin, A_BOLD);
wattr_on(pwin, style_fg(sy_status), NULL);
wbkgd(pwin, style_bg(sy_status));
wmove(pwin, 0, 0);
waddwstr(pwin, msg);
waddwstr(pwin, L" [y/N]? ");
wattroff(pwin, A_BOLD);
while (input_char(pwin, &c) == ERR
#ifdef KEY_RESIZE
|| (c == KEY_RESIZE)
#endif
)
;
delwin(pwin);
wtouchln(statwin, 0, 1, 1);
wrefresh(statwin);
return (c == 'Y' || c == 'y') ? 1 : 0;
}
wchar_t *
prompt(msg, def, hist)
const wchar_t *msg, *def;
history_t *hist;
{
WINDOW *pwin;
wchar_t input[256];
size_t pos = 0;
histent_t *histpos = NULL;
if (hist == NULL)
hist = prompthist;
memset(input, 0, sizeof(input));
if (def) {
wcsncpy(input, def, wsizeof(input) - 1);
pos = wcslen(input);
}
pwin = newwin(1, COLS, LINES - 1, 0);
keypad(pwin, TRUE);
wattr_on(pwin, style_fg(sy_status), NULL);
wbkgd(pwin, style_bg(sy_status));
wattron(pwin, A_BOLD);
wmove(pwin, 0, 0);
waddwstr(pwin, msg);
wattroff(pwin, A_BOLD);
curs_set(1);
for (;;) {
wint_t c;
int i, inpos;
wmove(pwin, 0, wcslen(msg) + 1);
for (i = 0; i < wcslen(input); i++) {
if (input[i] == L'\t')
waddwstr(pwin, L" ");
else {
wchar_t s[] = { input[i], '\0' };
waddwstr(pwin, s);
}
}
i--;
wclrtoeol(pwin);
for (i = 0, inpos = 0; i < wcslen(input) && i < pos; i++)
if (input[i] == L'\t')
inpos += 8;
else
inpos++;
wmove(pwin, 0, wcslen(msg) + 1 + inpos);
wrefresh(pwin);
if (input_char(pwin, &c) == ERR)
continue;
switch (c) {
case '\n':
case '\r':
goto end;
case KEY_BACKSPACE:
case 0x7F:
case 0x08:
if (pos) {
if (pos == wcslen(input))
input[--pos] = 0;
else {
int i = wcslen(input);
pos--;
wmemcpy(input + pos, input + pos + 1, wcslen(input) - pos);
input[i] = 0;
}
}
break;
case KEY_DC:
if (pos < wcslen(input)) {
int i = wcslen(input);
wmemcpy(input + pos, input + pos + 1, wcslen(input) - pos);
input[i] = 0;
}
break;
case KEY_LEFT:
if (pos)
pos--;
break;
case KEY_RIGHT:
if (pos < wcslen(input))
pos++;
break;
case KEY_HOME:
case 0x01: /* ^A */
pos = 0;
break;
case KEY_END:
case 0x05: /* ^E */
pos = wcslen(input);
break;
case 0x07: /* ^G */
case 0x1B: /* ESC */
curs_set(0);
delwin(pwin);
return NULL;
case 0x15: /* ^U */
input[0] = 0;
pos = 0;
break;
#ifdef KEY_RESIZE
case KEY_RESIZE:
break;
#endif
case KEY_UP:
if (histpos == NULL) {
if ((histpos = TTS_TAILQ_LAST(&hist->hi_ents, hentlist)) == NULL) {
beep();
break;
}
} else {
if (TTS_TAILQ_PREV(histpos, hentlist, he_entries) == NULL) {
beep();
break;
} else
histpos = TTS_TAILQ_PREV(histpos, hentlist, he_entries);
}
wcsncpy(input, histpos->he_text, wsizeof(input) - 1);
pos = wcslen(input);
break;
case KEY_DOWN:
if (histpos == NULL) {
beep();
break;
}
if (TTS_TAILQ_NEXT(histpos, he_entries) == NULL) {
beep();
break;
} else
histpos = TTS_TAILQ_NEXT(histpos, he_entries);
wcsncpy(input, histpos->he_text, wsizeof(input) - 1);
pos = wcslen(input);
break;
default:
if (pos != wcslen(input)) {
wmemmove(input + pos + 1, input + pos, wcslen(input) - pos);
input[pos++] = c;
} else {
input[pos++] = c;
input[pos] = 0;
}
break;
}
}
end: ;
curs_set(0);
delwin(pwin);
wtouchln(statwin, 0, 1, 1);
wrefresh(statwin);
hist_add(hist, input);
return wcsdup(input);
}
void
errbox(const wchar_t *msg, ...)
{
va_list ap;
va_start(ap, msg);
verrbox(msg, ap);
va_end(ap);
}
void
verrbox(msg, ap)
const wchar_t *msg;
va_list ap;
{
wchar_t text[4096];
WINDOW *ewin;
#define ETITLE L" Error "
#define ECONT L" <OK> "
int width;
wint_t c;
vswprintf(text, wsizeof(text), msg, ap);
width = wcslen(text);
ewin = newwin(6, width + 4,
(LINES / 2) - ((1 + 2)/ 2),
(COLS / 2) - ((width + 2) / 2));
leaveok(ewin, TRUE);
wborder(ewin, 0, 0, 0, 0, 0, 0, 0, 0);
wattron(ewin, A_REVERSE | A_BOLD);
wmove(ewin, 0, (width / 2) - (wsizeof(ETITLE) - 1)/2);
waddwstr(ewin, ETITLE);
wattroff(ewin, A_REVERSE | A_BOLD);
wmove(ewin, 2, 2);
waddwstr(ewin, text);
wattron(ewin, A_REVERSE | A_BOLD);
wmove(ewin, 4, (width / 2) - ((wsizeof(ECONT) - 1) / 2));
waddwstr(ewin, ECONT);
wattroff(ewin, A_REVERSE | A_BOLD);
for (;;) {
if (wget_wch(ewin, &c) == ERR)
continue;
if (c == '\r')
break;
}
delwin(ewin);
}
void
drawentries()
{
int i, nlines;
int cline = 0;
time_t lastday = 0;
entry_t *en;
chtype oldbg;
getmaxyx(listwin, nlines, i);
TTS_TAILQ_FOREACH(en, &entries, en_entries)
en->en_flags.efl_visible = 0;
en = TTS_TAILQ_FIRST(&entries);
for (i = 0; i < pagestart; i++)
if ((en = TTS_TAILQ_NEXT(en, en_entries)) == NULL)
return;
for (; en; en = TTS_TAILQ_NEXT(en, en_entries)) {
time_t n;
wchar_t flags[10], stime[16], *p;
attr_t attrs = 0;
wchar_t *etime;
if (!showinv && en->en_flags.efl_invoiced)
continue;
oldbg = getbkgd(listwin);
if (lastday != entry_day(en)) {
struct tm *lt;
wchar_t lbl[128];
wchar_t *itime = maketime(entry_time_for_day(entry_day(en), 1, 0), time_format),
*ntime = maketime(entry_time_for_day(entry_day(en), 0, 0), time_format),
*btime = maketime(entry_time_for_day(entry_day(en), 2, bill_increment), time_format),
*ttime = maketime(entry_time_for_day(entry_day(en), 1, 0) +
entry_time_for_day(entry_day(en), 0, 0), time_format);
wchar_t hdrtext[256];
int n = 0;
oldbg = getbkgd(listwin);
wbkgdset(listwin, style_bg(sy_entry));
wattr_on(listwin, style_fg(sy_entry), NULL);
wmove(listwin, cline, 0);
wclrtoeol(listwin);
wbkgdset(listwin, oldbg);
wattr_off(listwin, style_fg(sy_entry), NULL);
if (++cline >= nlines)
break;
lastday = entry_day(en);
lt = localtime(&lastday);
wcsftime(lbl, wsizeof(lbl), L"%A, %d %B %Y", lt);
swprintf(hdrtext, wsizeof(hdrtext), L"%-30ls [", lbl);
if (*itime) {
wcslcat(hdrtext, L"I:", wsizeof(hdrtext));
wcslcat(hdrtext, itime, wsizeof(hdrtext));
n++;
}
free(itime);
if (*ntime) {
if (n++)
wcslcat(hdrtext, L" ", wsizeof(hdrtext));
wcslcat(hdrtext, L"N:", wsizeof(hdrtext));
wcslcat(hdrtext, ntime, wsizeof(hdrtext));
}
free(ntime);
if (*ttime) {
if (n++)
wcslcat(hdrtext, L" ", wsizeof(hdrtext));
wcslcat(hdrtext, L"T:", wsizeof(hdrtext));
wcslcat(hdrtext, ttime, wsizeof(hdrtext));
}
free(ttime);
if (*btime) {
if (n++)
wcslcat(hdrtext, L" ", wsizeof(hdrtext));
wcslcat(hdrtext, L"B:", wsizeof(hdrtext));
wcslcat(hdrtext, btime, wsizeof(hdrtext));
}
free(btime);
wcslcat(hdrtext, L"]", wsizeof(hdrtext));
wattr_on(listwin, style_fg(sy_date), NULL);
wbkgdset(listwin, style_bg(sy_date));
wmove(listwin, cline, 0);
waddwstr(listwin, hdrtext);
wclrtoeol(listwin);
wattr_off(listwin, style_fg(sy_date), NULL);
wbkgdset(listwin, oldbg);
if (++cline >= nlines) {
wbkgdset(listwin, oldbg);
wattr_off(listwin, style_fg(sy_date), NULL);
break;
}
oldbg = getbkgd(listwin);
wbkgdset(listwin, style_bg(sy_entry));
wattr_on(listwin, style_fg(sy_entry), NULL);
wmove(listwin, cline, 0);
wclrtoeol(listwin);
wbkgdset(listwin, oldbg);
wattr_off(listwin, style_fg(sy_entry), NULL);
if (++cline >= nlines)
break;
}
en->en_flags.efl_visible = 1;
wmove(listwin, cline, 0);
attrs = style_fg(sy_entry);
if (en->en_started && en == curent)
attrs = style_fg(sy_selected) |
(style_fg(sy_running) & (
WA_STANDOUT | WA_UNDERLINE |
WA_REVERSE | WA_BLINK | WA_DIM |
WA_BOLD));
else if (en->en_started)
attrs = style_fg(sy_running);
else if (en == curent)
attrs = style_fg(sy_selected);
wbkgdset(listwin, ' ' | (attrs & ~WA_UNDERLINE));
wattr_on(listwin, attrs, NULL);
if (en == curent) {
waddwstr(listwin, L" -> ");
} else
waddwstr(listwin, L" ");
n = en->en_secs;
if (en->en_started)
n += time(NULL) - en->en_started;
etime = maketime(n, time_format);
swprintf(stime, wsizeof(stime), L"%8ls%c ",
*etime ? etime : L"0m", (itime && (en == running)) ? '*' : ' ');
free(etime);
waddwstr(listwin, stime);
memset(flags, 0, sizeof(flags));
p = flags;
if (en->en_flags.efl_marked)
*p++ = 'M';
else
*p++ = ' ';
if (en->en_flags.efl_invoiced)
*p++ = 'I';
else
*p++ = ' ';
if (!en->en_flags.efl_nonbillable)
*p++ = 'B';
else
*p++ = ' ';
if (en->en_flags.efl_deleted)
*p++ = 'D';
else
*p++ = ' ';
if (*flags) {
wchar_t s[10];
swprintf(s, wsizeof(s), L"%-5ls ", flags);
waddwstr(listwin, s);
} else
waddwstr(listwin, L" ");
waddwstr(listwin, en->en_desc);
wclrtoeol(listwin);
wbkgdset(listwin, oldbg);
wattr_off(listwin, attrs, NULL);
if (++cline >= nlines)
return;
}
oldbg = getbkgd(listwin);
wattr_on(listwin, style_fg(sy_entry), NULL);
wbkgdset(listwin, style_bg(sy_entry));
for (; cline < nlines; cline++) {
wmove(listwin, cline, 0);
wclrtoeol(listwin);
}
wattr_off(listwin, style_fg(sy_entry), NULL);
wbkgdset(listwin, oldbg);
}
time_t
prduration(pr, def)
wchar_t *pr;
time_t def;
{
wchar_t *defstr = NULL;
wchar_t *tstr;
time_t ret;
defstr = maketime(def, TIMEFMT_FOR_EDIT(time_format));
if ((tstr = prompt(pr, defstr, NULL)) == NULL) {
free(defstr);
return -1;
}
free(defstr);
if (!*tstr) {
drawstatus(L"No duration entered");
free(tstr);
return -1;
}
if ((ret = parsetime(tstr)) == (time_t) -1) {
free(tstr);
drawstatus(L"Invalid time format.");
return -1;
}
free(tstr);
return ret;
}

38
ui.h
View file

@ -1,38 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_UI_H
#define TTS_UI_H
#include <stdarg.h>
#include "tts.h"
#include "tts_curses.h"
#include "wide.h"
extern WINDOW *titwin, *statwin, *listwin;
extern int in_curses;
extern int showinv;
void ui_init (void);
void cursadvance (void);
void drawstatus (const wchar_t *msg, ...);
void vdrawstatus (const wchar_t *msg, va_list);
void drawheader (void);
void drawentries (void);
wchar_t *prompt (wchar_t const *, wchar_t const *, history_t *);
time_t prduration (wchar_t *prompt, time_t def);
int yesno (wchar_t const *);
void errbox (wchar_t const *, ...);
void verrbox (wchar_t const *, va_list);
#endif /* !TTS_UI_H */

View file

@ -1,28 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_VARIABLE_H
#define TTS_VARIABLE_H
#include "wide.h"
typedef struct variable {
wchar_t const *va_name;
int va_type;
void *va_addr;
} variable_t;
#define VTYPE_INT 1
#define VTYPE_BOOL 2
#define VTYPE_STRING 3
variable_t *find_variable(const wchar_t *name);
#endif /* !TTS_VARIABLE_H */

View file

@ -1,99 +0,0 @@
/* $NetBSD: strlcpy.c,v 1.3 2007/06/04 18:19:27 christos Exp $ */
/* $OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and 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 TODD C. MILLER DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER 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.
*/
#include <sys/types.h>
#include <assert.h>
#include <string.h>
#include <wchar.h>
#include "config.h"
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns wcslen(src); if retval >= siz, truncation occurred.
*/
#ifndef HAVE_WCSLCPY
size_t
wcslcpy(dst, src, siz)
wchar_t *dst;
wchar_t const *src;
size_t siz;
{
wchar_t *d = dst;
const wchar_t *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
break;
} while (--n != 0);
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
#endif
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= wcslen(dst)).
* Returns wcslen(src) + MIN(siz, wcslen(initial dst)).
* If retval >= siz, truncation occurred.
*/
#ifndef HAVE_WCSLCAT
size_t
wcslcat(wchar_t *dst, const wchar_t *src, size_t siz)
{
wchar_t *d = dst;
const wchar_t *s = src;
size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + wcslen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}
#endif

26
wide.c
View file

@ -1,26 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#include "wide.h"
#include "tts_curses.h"
#ifdef NEED_TTS_WGETCH
static int
tts_wgetch(win, d)
WINDOW *win;
int *d;
{
int c;
if ((c = wgetch(win)) == ERR)
return ERR;
*d = c;
return OK;
}
#endif /* !NEED_TTS_WGETCH */

24
wide.h
View file

@ -1,24 +0,0 @@
/*
* TTS - track your time.
* Copyright (c) 2012-2014 Felicity Tarnell.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely. This software is provided 'as-is', without any express or implied
* warranty.
*/
#ifndef TTS_WIDE_H
#define TTS_WIDE_H
#include <wchar.h>
#include "config.h"
#include "tts_curses.h"
#define wsizeof(s) (sizeof(s) / sizeof(wchar_t))
int input_char (WINDOW *, wint_t *);
void input_macro (wchar_t *);
#endif /* !TTS_WIDE_H */