1 Commits

Author SHA1 Message Date
fa8154fc21 build(make): follow GNU install dir conventions 2025-01-25 13:33:26 +01:00
7 changed files with 73 additions and 234 deletions

View File

@@ -12,11 +12,4 @@ Initial release.
- Edit, list todo notes by any date format understood by `date(1)`
- Write custom template programs to generate content for newly created notes
## v0.2.0
### Added
- Builtin templates for the markdown and xit formats (`.md` and `.xit`
extensions); the templates carry over open tasks from previous agenda notes
[changelog.md]: https://changelog.md/

View File

@@ -1,24 +1,45 @@
PREFIX := /usr/local
SHELL = /bin/sh
./agenda.1.gz: ./agenda.1
gzip -fk ./agenda.1
# See the following link regarding GNU Makefile conventions.
# https://www.gnu.org/software/make/manual/html_node/Makefile-Conventions.html
# This seems stupid. Isn't install a coreutil anyways?
# INSTALL := install
# INSTALL_PROGRAM := $(INSTALL)
# INSTALL_DATA := ${INSTALL} -m 644
# Variables for installation directories.
# https://www.gnu.org/software/make/manual/html_node/Directory-Variables.html
DESTDIR :=
prefix := /usr/local
exec_prefix := $(prefix)
bindir := $(exec_prefix)/bin
datarootdir := $(prefix)/share
datadir := $(datarootdir)
# libdir := $(prefix)/lib
mandir := $(datarootdir)/man
man1dir := $(mandir)/man1
.PHONY: all
all: ./agenda.1.gz
.PHONY: install
install: ./agenda.1.gz
install -m 755 -D ./agenda $(PREFIX)/bin/agenda
mkdir -p $(PREFIX)/share/agenda/templates
install -m 755 ./templates/* $(PREFIX)/share/agenda/templates
install -m 644 -D ./agenda.1.gz $(PREFIX)/share/man/man1/agenda.1.gz
install -m 644 -D ./completion.bash $(PREFIX)/share/bash-completion/completions/agenda
install -m 755 -D ./agenda $(DESTDIR)$(bindir)/agenda
install -m 644 -D ./agenda.1.gz $(DESTDIR)$(man1dir)/agenda.1.gz
install -m 644 -D ./completion.bash $(DESTDIR)$(datarootdir)/bash-completion/completions/agenda
.PHONY: uninstall
uninstall:
$(RM) $(PREFIX)/bin/agenda
$(RM) $(PREFIX)/share/agenda/templates/*
$(RM) $(PREFIX)/share/man/man1/agenda.1.gz
$(RM) $(PREFIX)/share/bash-completion/completions/agenda
rmdir $(PREFIX)/share/agenda/templates $(PREFIX)/share/agenda
rm -f $(DESTDIR)$(bindir)/agenda
rm -f $(DESTDIR)$(man1dir)/agenda.1.gz
rm -f $(DESTDIR)$(datarootdir)/bash-completion/completions/agenda
.PHONY: clean
clean:
$(RM) ./agenda.1.gz
rm -f ./agenda.1.gz
./agenda.1.gz: ./agenda.1
gzip -fk ./agenda.1

93
agenda
View File

@@ -17,9 +17,6 @@ AGENDA_EDITOR=${AGENDA_EDITOR:-${VISUAL:-${EDITOR:-vi}}}
#
AGENDA_DIR=${AGENDA_DIR:-}
# Location of additional files of an agenda installation.
AGENDA_DATA_DIRS=${AGENDA_DATA_DIRS:-}
# The default `-e` option.
AGENDA_EXTENSION=${AGENDA_EXTENSION:-"md"}
@@ -27,10 +24,7 @@ AGENDA_EXTENSION=${AGENDA_EXTENSION:-"md"}
AGENDA_DEFAULT=${AGENDA_DEFAULT:-"agenda"}
# Command that provides the initial contents of new agenda files.
#
# If this is null AGENDA_PATH is searched for an executable in the template
# subdirectory with the same name as AGENDA_EXTENSION.
AGENDA_TEMPLATE=${AGENDA_TEMPLATE:-}
AGENDA_TEMPLATE=${AGENDA_TEMPLATE:-"__agenda_default_template"}
__usage() {
fold -s <<-USAGE
@@ -62,15 +56,7 @@ __main() {
"--version") __version && return ;;
esac
if [ -z "$AGENDA_DATA_DIRS" ]; then
# FIXME: As per spec only absolute paths should be accepted!
AGENDA_DATA_DIRS="${XDG_CONFIG_HOME:-"$HOME/.config"}/agenda"
AGENDA_DATA_DIRS="$AGENDA_DATA_DIRS:/usr/local/share/agenda"
AGENDA_DATA_DIRS="$AGENDA_DATA_DIRS:/usr/share/agenda"
fi
if [ -z "$AGENDA_DIR" ]; then
# FIXME: As per spec only absolute paths should be accepted!
AGENDA_DIR="${XDG_DATA_HOME:-"$HOME/.local/share"}/agenda"
fi
@@ -305,7 +291,6 @@ __list() {
fi
done < <(
# Sort the paths by basename.
# TODO: Inline the prefix functions and explain this.
find "$dir" -type f \
| __agenda_sort_prefix \
| sort \
@@ -327,41 +312,27 @@ __agenda_create() {
tmp_err=$(mktemp)
if [ "$backlog" = "no" ]; then
# Search for a template program if the AGENDA_TEMPLATE variable is empty.
if [ -z "$AGENDA_TEMPLATE" ]; then
AGENDA_TEMPLATE=$(
__agenda_get_template_path "$AGENDA_EXTENSION" ||
__agenda_get_template_path "default" ||
true
)
# Capture exit code of the template command.
local code
set +e
"$AGENDA_TEMPLATE" > "$tmp" 2> "$tmp_err"
code=$?
set -e
if [ $code -eq 127 ]; then
echo "${0##*/}: cannot create '$file': template '$AGENDA_TEMPLATE' does not exist" >&2
return 127
fi
# If a template program is set, attempt to use it to create the new agenda
# note file.
if [ -n "$AGENDA_TEMPLATE" ]; then
local code
set +e
"$AGENDA_TEMPLATE" > "$tmp" 2> "$tmp_err"
code=$?
set -e
if [ -s "$tmp_err" ]; then
while read -r REPLY; do
printf "agenda: $AGENDA_TEMPLATE: %s\n" "${REPLY#"${0}: line"*": "}" >&2
done < "$tmp_err"
fi
if [ $code -eq 127 ]; then
echo "${0##*/}: cannot create '$file': template '$AGENDA_TEMPLATE' does not exist" >&2
return 127
fi
if [ -s "$tmp_err" ]; then
while read -r REPLY; do
printf "agenda: $AGENDA_TEMPLATE: %s\n" "${REPLY#"${0}: line"*": "}" >&2
done < "$tmp_err"
fi
if [ $code -gt 0 ]; then
echo "${0##*/}: cannot create '$file': template error" >&2
return $code
fi
else
touch "$tmp"
if [ $code -gt 0 ]; then
echo "${0##*/}: cannot create '$file': template error" >&2
return $code
fi
fi
@@ -369,6 +340,19 @@ __agenda_create() {
mv "$tmp" "$file"
}
__agenda_default_template() {
case $AGENDA_EXTENSION in
"md")
printf "# %s %s\n\n- [ ] " "$AGENDA_NAME" "$AGENDA_DATE"
;;
"txt"|"xit"|"")
printf "%s %s\n[ ] " "$AGENDA_NAME" "$AGENDA_DATE"
;;
*)
;;
esac
}
__agenda_last() {
local file=$1 name=$2
@@ -391,19 +375,6 @@ __agenda_last() {
echo "${last:-}"
}
__agenda_get_template_path() {
local template=$1
local IFS=":" path
for path in $AGENDA_DATA_DIRS; do
if [ -x "$path/templates/$template" ]; then
AGENDA_TEMPLATE="$path/templates/$template"
return
fi
done
return 1
}
# https://stackoverflow.com/a/49035906
__slugify() {
echo "$1" \

View File

@@ -1,4 +1,4 @@
.TH AGENDA 1 "2024-06-07" "agenda 0.2.0"
.TH AGENDA 1 "2024-06-07" "agenda 0.1.0"
\#=============================================================================
.SH NAME
agenda \- Manage daily tasks and notes in any plain text format.
@@ -107,26 +107,7 @@ The editor process has access to certain environment variables.
\fBAGENDA_DIR\fP
.RS 4
The home of all agenda note files read and written by agenda. If this is empty
\fBXDG_DATA_HOME\fP\fI/agenda\fP will be used.
.PP
.RE
\#
\fBAGENDA_DATA_DIRS\fP
.RS 4
Colon delimited Paths to additional files that come with an agenda installation
and custom overrides. Currently only used for template programs (located in the
\fItemplate/\fP sub-directory). If a file is searched in these paths the first file
that is found is used.
.nr PI 2n
If this variable is not set or empty it is set to include (in order)
.IP \[bu]
\fBXDG_CONFIG_HOME\fP\fI/agenda\fP
.IP \[bu]
\fI/usr/local/share/agenda\fP
.IP \[bu]
\fI/usr/share/agenda\fP
`${XDG_DATA_HOME:-"$HOME/.local/share"}/agenda` will be used.
.PP
.RE
\#
@@ -146,14 +127,10 @@ The default agenda name if the \fI-t\fP option is omitted. This falls back to
\#
\fBAGENDA_TEMPLATE\fP
.RS 4
If this variable is not empty the value is interpreted as a command and
executed when creating a new agenda note with the \fI-c\fP flag. Any std output
of the command is written to the new file. The template process has access to
certain environment variables.
.PP
If this variable is unset or empty \fBAGENDA_DATA_DIRS\fP is searched for an
executable in the \fItemplates\fP sub-directory that is named either like the
corresponding extension or "default".
If this is not empty the value is interpreted as a command and executed when
creating a new agenda note with the \fI-c\fP flag. Any std output of the
command is written to the new file. The template process has access to certain
environment variables.
.sp 1
The following example will copy the last agenda note to
the new agenda note file.
@@ -176,10 +153,6 @@ Note however that this example is specific to \fBbash(1)\fP, since only bash
can export functions this way. In general you should write your template as a
script, save it, and set it as executable.
.PP
To set/override a template for a certain extension (e.g. markdown), save the
program to one of the \fBAGENDA_DATA_DIRS\fP, e.g.
\fI\fBXDG_CONFIG_HOME\fP/agenda/templates/md\fP.
.PP
\#-----------------------------------------------------------------------------
.SS "Templates and editor"
The template command and editor processes have access to certain environment
@@ -215,21 +188,6 @@ The file path of the agenda note.
The last agenda file. Can be empty if no previous agenda note exists.
.PP
.RE
.SS "Other"
\#
\fBXDG_CONFIG_HOME\fP
.RS 4
The default user configuration path. If this is not set the default of
\fBHOME\fP\fI/.config\fP is assumed.
.PP
.RE
\#
\fBXDG_DATA_HOME\fP
.RS 4
The default user data path. If this is not set the default of
\fBHOME\fP\fI/.local/share\fP is assumed.
.PP
.RE
\#=============================================================================
.SH "SEE ALSO"
\fBdate(1)\fP

View File

@@ -1,6 +0,0 @@
#!/bin/sh
set -eu
type agenda >/dev/null
# Capitalize the title.
echo "$AGENDA_NAME $AGENDA_DATE" | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}'

View File

@@ -1,42 +0,0 @@
#!/bin/sh
set -eu
type agenda >/dev/null
if [ "${AGENDA_EXTENSION:-}" != "md" ]; then
exit
fi
# Capitalize the title.
echo "$AGENDA_NAME $AGENDA_DATE" | awk '{print "# "toupper(substr($0,0,1))tolower(substr($0,2))}'
echo
if [ -n "${AGENDA_IS_BACKLOG:-}" ]; then
exit
fi
# Carry over open tasks from the last agenda markdown file.
if [ -e "$AGENDA_LAST" ] && [ "${AGENDA_LAST#*.}" = "md" ]; then
while IFS= read -r REPLY; do
case ${state:-} in
"open_task")
case $REPLY in
"- [ ] "*|" "*)
# Append indented lines to open tasks.
echo "$REPLY"
;;
*)
unset state
;;
esac
;;
*)
case $REPLY in
"[ ] "*|"[@] "*|"[?] "*)
state="open_task"
echo "$REPLY"
;;
esac
;;
esac
done < "$AGENDA_LAST"
fi

View File

@@ -1,56 +0,0 @@
#!/bin/sh
set -eu
type agenda >/dev/null
if [ "${AGENDA_EXTENSION:-}" != "xit" ]; then
exit
fi
# Capitalize the title.
echo "$AGENDA_NAME $AGENDA_DATE" | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}'
if [ -n "${AGENDA_IS_BACKLOG:-}" ]; then
exit
fi
# Carry over open tasks from the last agenda xit file.
if [ -e "$AGENDA_LAST" ] && [ "${AGENDA_LAST#*.}" = "xit" ]; then
line=0
while IFS= read -r REPLY; do
line=$(( line + 1 ))
case ${state:-} in
"open_task")
case $REPLY in
"[ ] "*|"[@] "*|"[?] "*|" "*)
# Append indented lines to open tasks.
echo "$REPLY"
;;
*)
unset state
;;
esac
;;
*)
case $REPLY in
"[ ] "*|"[@] "*|"[?] "*)
# Carry over group heading with open tasks.
if [ -n "${header:-}" ]; then
echo
echo "$header"
unset header
fi
state="open_task"
echo "$REPLY"
;;
[a-z]*|[A-Z]*)
# Skip the first heading (assumed to be the generated
# title of the agenda note).
if [ $line -gt 1 ]; then
header=$REPLY
fi
;;
esac
;;
esac
done < "$AGENDA_LAST"
fi