-
Notifications
You must be signed in to change notification settings - Fork 5
/
logbook
executable file
·305 lines (266 loc) · 8.3 KB
/
logbook
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
#!/bin/bash
#
# simple logbook aka journal aka work logger for lazy hoomans
#
set -e
PROG=${0##*/}
_err() { echo -e "$PROG:" "$@" >&2 ; exit 1; }
_default_editor() {
if [ -z "$EDITOR" ]; then
command -v ed >/dev/null && EDITOR=ed
[ -z "$EDITOR" ] && command -v nano >/dev/null && EDITOR=nano
[ -z "$EDITOR" ] && command -v vi >/dev/null && EDITOR=vi
[ -z "$EDITOR" ] && _err "EDITOR is not set"
fi
case "$EDITOR" in
"subl "*|*"/subl "|subl|*"/subl")
# set cursor to end of file in sublime
case "$EDITOR" in
*"{}"*) ;;
*) EDITOR="$EDITOR \"{}:999999\"" ;;
esac
;;
esac
echo $EDITOR
}
_default_reader() {
PAGER=${PAGER:-less}
if [ "$PAGER" = less ]; then
PAGER="$PAGER -I -K -s --tilde"
fi
echo "$PAGER"
}
LOGFILE=
LOGDIR=
LOGDIR_NAME=${LOGBOOK_LOGDIR_NAME:-.logbook}
DEFAULT_LOGDIR=${LOGBOOK_DEFAULT_LOGDIR:-~/$LOGDIR_NAME}
LOGFILE_PATTERN=${LOGBOOK_LOGFILE_PATTERN:-"%Y-%m-%d.md"}
EDIT_CMD=${LOGBOOK_EDIT_CMD:-$(_default_editor)}
READ_CMD=${LOGBOOK_READ_CMD:-$(_default_reader)}
GREP_CMD=${LOGBOOK_GREP_CMD:-grep -i -R -F --color}
USE_DEFAULT_LOGDIR=true
UTC=false
COMMAND=
_need2() { [ -z "$2" ] && _err "missing value for $1"; }
while [ $# -gt 0 ]; do case "$1" in
-h|-help|--help|help) cat << _END
Simple journaling for lazy hoomans.
Usage:
$PROG [options] [edit] # Edit today's entry
$PROG [options] edit [<entry>] # Edit today's entry or specific entry
$PROG [options] read [<entry>] # Read today's entry or specific entry
$PROG [options] add [--] <s>... # Append line <s>... to today's entry
$PROG [options] ls # List entries
$PROG [options] grep <greparg>... # Search entries
$PROG [options] logdir # Print path of log directory
$PROG [options] logfile # Print path of today's entry
Options:
-C <dir> Use <dir> as log directory (instead of searching for it.)
-f <file> Use <file> as log entry (instead of automatically selecting one.)
-P <pattern> Set and override LOGBOOK_LOGFILE_PATTERN.
-E <cmd> Set and override LOGBOOK_EDIT_CMD.
-D <dir> Set and override LOGBOOK_DEFAULT_LOGDIR.
-utc Use log-entry filenames based on UTC instead of local time.
-no-default Do not fall back to LOGBOOK_DEFAULT_LOGDIR if log dir was not found.
-h, -help Show help on stdout and exit.
Environment variables:
LOGBOOK_LOGDIR_NAME ($LOGDIR_NAME)
Name of logbook directories
LOGBOOK_DEFAULT_LOGDIR (${DEFAULT_LOGDIR/$HOME/~})
Directory to use for log entires when logbook is invoked outside of
a directory with a LOGBOOK_LOGDIR_NAME directory.
LOGBOOK_LOGFILE_PATTERN ($LOGFILE_PATTERN)
Filename pattern to use for log files, passed to the "date" program.
See \`date --help\` for info on possible patterns.
LOGBOOK_EDIT_CMD ($EDIT_CMD)
Command invoked to edit a log entry. Substitutes "{}" for the log file.
If "{}" is not present, passes the log file as an argument.
LOGBOOK_READ_CMD ($READ_CMD)
Command invoked to read a log entry. Substitutes "{}" for the log file.
If "{}" is not present, passes the log file as an argument.
LOGBOOK_GREP_CMD ($GREP_CMD)
Command invoked to search a log directory. Substitutes "{}" for the log
directory. Runs in the log directory as working directory.
Examples:
Edit today's entry:
$PROG
Append a line to today's entry:
$PROG add Bought cat food
Keep logs per week instead of per day:
export LOGBOOK_LOGFILE_PATTERN="%Y-week_%U.txt"
Edit logs in a macOS application:
export LOGBOOK_EDIT_CMD="open -a TextEdit"
_END
exit ;;
-f) _need2 "$@"; LOGFILE=$2; shift; shift ;;
-C) _need2 "$@"; LOGDIR=$2; shift; shift ;;
-P) _need2 "$@"; LOGBOOK_LOGFILE_PATTERN=$2 ; shift; shift ;;
-E) _need2 "$@"; LOGBOOK_EDIT_CMD=$2 ; shift; shift ;;
-D) _need2 "$@"; LOGBOOK_DEFAULT_LOGDIR=$2 ; shift; shift ;;
-utc) UTC=true; shift ;;
-no-default) USE_DEFAULT_LOGDIR=false; shift ;;
--) shift ; break ;;
-*) _err "unknown option: $1" ;;
*)
[ -z "$COMMAND" ] || break
COMMAND=$1; shift
[ "$COMMAND" = grep ] && break
;;
esac; done
# extract file extension from LOGFILE_PATTERN
FILEEXT=$(basename "$LOGFILE_PATTERN")
FILEEXT=${FILEEXT##*.}
[ -n "$FILEEXT" ] && FILEEXT=.$FILEEXT
_find_logdir() {
LOGDIR=$PWD
while true; do
[ -z "$LOGDIR" -o -d "$LOGDIR/$LOGDIR_NAME" ] && break
LOGDIR=${LOGDIR%/*} # shave off last path component
done
if [ -d "$LOGDIR/$LOGDIR_NAME" ]; then
LOGDIR=$LOGDIR/$LOGDIR_NAME
else
# expand tilde in DEFAULT_LOGDIR
case "$DEFAULT_LOGDIR" in
~/*) DEFAULT_LOGDIR=${DEFAULT_LOGDIR/~/$HOME} ;;
esac
if $USE_DEFAULT_LOGDIR && [ -d "$DEFAULT_LOGDIR" ]; then
LOGDIR=$DEFAULT_LOGDIR
else
cat <<EOF >&2
$PROG: No log directory found.
I looked for a directory named "$LOGDIR_NAME" in the current directory and its parents.
EOF
$USE_DEFAULT_LOGDIR &&
echo "(Default directory \"${DEFAULT_LOGDIR/$HOME/~}\" does not exist.)" >&2
exit 1
fi
fi
}
_logfile() {
local DATECMD=date
$UTC && DATECMD="$DATE_ARGS -u"
$DATECMD "+$LOGFILE_PATTERN"
}
_logfile_name() {
basename "$1" "$FILEEXT"
}
_cmd_ls() {
[ -z "$1" ] || _err "unexpected extra argument: $1"
# determine TTY size
local TERMSIZE COLS
TERMSIZE=$(stty size 2>/dev/null || true)
if [ -n "$TERMSIZE" ]; then
#ROWS=${TERMSIZE% *}
COLS=${TERMSIZE#* }
fi
COLS=${COLS:-60}
local f excerpt excerptlen namelen name files
namelen=0
files=()
for f in "$LOGDIR"/*$FILEEXT; do
files+=( "$f" )
name=$(basename "$f" "$FILEEXT")
[ ${#name} -gt $namelen ] && namelen=${#name}
done
# max length of excerpt (-3 for 2 spaces + …)
excerptlen=$(expr $COLS - $namelen - 3)
for f in "${files[@]}"; do
name=$(basename "$f" "$FILEEXT")
excerpt=$(head -n3 "$f")
excerpt=${excerpt/
/" "}
if [ ${#excerpt} -ge $excerptlen ]; then
excerpt=$(echo "$excerpt" | head -c$excerptlen)"…"
fi
printf "%-*s %s\n" $namelen "$name" "$excerpt"
done
}
_cmd_edit() {
[ -z "$2" ] || _err "unexpected extra argument: $2"
[ -n "$1" ] && LOGFILE=$LOGDIR/${1}$FILEEXT
case "$EDIT_CMD" in
*"{}"*)
EDIT_CMD=${EDIT_CMD/\{\}/"$LOGFILE"}
eval "exec $EDIT_CMD"
;;
*)
exec $EDIT_CMD "$LOGFILE"
;;
esac
}
_cmd_read() {
[ -z "$2" ] || _err "unexpected extra argument: $2"
[ -n "$1" ] && LOGFILE=$LOGDIR/${1}$FILEEXT
local NAME=$(basename "$(dirname "$LOGDIR")")
[ -n "$NAME" ] && NAME=$NAME/
NAME=$NAME$(_logfile_name "$LOGFILE")
local ARGS=()
case "$READ_CMD" in
"less "*|*"/less "|less|*"/less")
ARGS+=( "-Ps$NAME" ) ;;
esac
case "$READ_CMD" in
*"{}"*)
READ_CMD=${READ_CMD/\{\}/"$LOGFILE"}
eval "exec $READ_CMD ${ARGS[@]}"
;;
*)
exec $READ_CMD "${ARGS[@]}" "$LOGFILE"
;;
esac
}
_cmd_grep() {
cd "$LOGDIR"
case "$GREP_CMD" in
*"{}"*)
GREP_CMD=${GREP_CMD/\{\}/"$LOGDIR"}
eval "exec $GREP_CMD $@"
;;
*)
exec $GREP_CMD $@ *$FILEEXT
;;
esac
}
_cmd_append() {
[ $# -gt 0 ] || _err "no message to append"
if [ $(tail -c1 "$LOGFILE"|wc -l) = "0" ]; then
# no newline at end
echo >> "$LOGFILE"
fi
echo "$@" >> "$LOGFILE"
echo "Added message to $(_logfile_name "$LOGFILE")"
}
_cmd_logdir() {
[ -z "$1" ] || _err "unexpected extra argument: $1"
echo "$LOGDIR"
}
_cmd_logfile() {
[ -z "$1" ] || _err "unexpected extra argument: $1"
echo "$LOGFILE"
}
# sort out LOGDIR and LOGFILE
if [ -z "$LOGFILE" ]; then
[ -z "$LOGDIR" ] && _find_logdir
LOGFILE=$LOGDIR/$(_logfile)
else
LOGDIR=$(dirname "$LOGFILE")
fi
[ -e "$LOGDIR" ] || _err "log directory \"$LOGDIR\" does not exist"
[ -d "$LOGDIR" ] || _err "log directory \"$LOGDIR\" is not a directory"
# echo "LOGFILE $LOGFILE"
# echo "LOGDIR $LOGDIR"
# Create directories, in case LOGFILE_PATTERN contains subdirectories.
# Note that _find_logdir already checked that LOGDIR exists.
mkdir -pv "${LOGFILE%/*}"
# call
case "$COMMAND" in
e|ed|edit|"") _cmd_edit "$@" ;;
a|add|append) _cmd_append "$@" ;;
l|ls|list) _cmd_ls "$@" ;;
r|read|cat) _cmd_read "$@" ;;
g|grep|search) _cmd_grep "$@" ;;
logfile) _cmd_logfile "$@" ;;
logdir) _cmd_logdir "$@" ;;
*) _err "unrecognized command: $COMMAND" ;;
esac