aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitchell Riedstra <mitch@riedstra.dev>2023-01-27 21:15:20 -0500
committerMitchell Riedstra <mitch@riedstra.dev>2023-01-27 21:15:20 -0500
commit2cd48b9a51565a2b5831f62306ca88f061425b40 (patch)
treedfdf95a129b1a8c276d814858ca6c7a8d5acbc54
downloadpm-2cd48b9a51565a2b5831f62306ca88f061425b40.tar.gz
pm-2cd48b9a51565a2b5831f62306ca88f061425b40.tar.xz
Initial
-rw-r--r--.gitignore2
-rw-r--r--Makefile7
-rwxr-xr-xpm248
-rw-r--r--readme.md61
-rw-r--r--strftime.c65
5 files changed, 383 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..50152f1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+ses.vim
+strftime
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..dbfbcff
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+PREFIX ?= /usr/local
+
+all: strftime
+
+install: strftime
+ install -m 755 strftime $(PREFIX)/bin/strftime
+ install -m 755 pm $(PREFIX)/bin/pm
diff --git a/pm b/pm
new file mode 100755
index 0000000..6993f9f
--- /dev/null
+++ b/pm
@@ -0,0 +1,248 @@
+#!/bin/sh
+# Copyright (c) 2023 Mitchell Riedstra
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+set -e
+PASTE_MANAGER_FILE=~/.pastes
+PASTE_MANAGER_LINES="${PASTE_MANAGER_LINES:-30}"
+PASTE_MANAGER_X_SELECTION="${PASTE_MANAGER_X_SELECTION:-clipboard}"
+
+# Pretty much anything that implements a demu like interface should work here,
+# the only option called elsewhere is `-p`
+_menu="dmenu -l $PASTE_MANAGER_LINES -i $DMENU_FLAGS"
+
+# This awk script controls how the output is formatted, which, will probably
+# be quite unwieldly if you don't have a fuzzy finder patch in dmenu.
+#shellcheck disable=SC2016
+awkPrettyPrint='
+BEGIN{
+ FS="::";
+}
+{
+ # "strftime \"%H:%M:%S %m.%d.%y\" " $2 | getline created;
+ "strftime \"%H:%M:%S %m.%d.%y\" " $3 | getline updated;
+ printf("ID: %s Updated %s Name: %-60s Content: %s\n",
+ $1, updated, $4, $5);
+}'
+
+genid() {
+ { dd if=/dev/urandom 2>/dev/null | tr -C -d 'a-zA-Z0-9'; echo; } \
+ | fold -w 8 | sed 1q
+}
+
+getFifo() {
+ _p="$(mktemp)"
+ rm -f "$_p"
+ sleep 0.1
+ mkfifo "$_p"
+ echo "$_p"
+}
+
+tmpdir() {
+ _t=$(mktemp)
+ rm -f "$_t"
+ mkdir "$_t"
+ echo "$_t"
+}
+
+_clip() {
+case $(uname) in
+Darwin)
+ pbcopy
+;;
+*)
+ xclip -selection "$PASTE_MANAGER_X_SELECTION"
+:;
+esac
+}
+
+insert() {
+ exec <&-
+
+ $_menu -p "Enter name: " > "$_fifo" &
+ name="$(tr -d '\n' < "$_fifo")"
+ [ -z "$name" ] && exit 1
+
+ $_menu -p "Enter content: " > "$_fifo" &
+ content="$(tr -d '\n' < "$_fifo")"
+ [ -z "$content" ] && exit 1
+
+ printf "%s::%s::%s::%s::%s\n" \
+ "$(genid)" "$(date +%s)" "$(date +%s)" "$name" "$content" \
+ >> "$PASTE_MANAGER_FILE"
+}
+
+sel() {
+ awk "$awkPrettyPrint" < "$PASTE_MANAGER_FILE" \
+ | $_menu -p "$@" \
+ | sed -re 's/^ID: ([^ ]*).*$/\1/'
+}
+
+selectName() {
+ id="$1"; shift
+ awk -F:: -v "id=$id" '{
+ if (id == $1) {
+ print $4
+ }
+ }' < "$PASTE_MANAGER_FILE" \
+ | sed 1q \
+ | tr -d '\n'
+}
+
+selectContent() {
+ id="$1"; shift
+ awk -F:: -v "id=$id" '{
+ if (id == $1) {
+ print $5
+ }
+ }' < "$PASTE_MANAGER_FILE" \
+ | sed 1q \
+ | tr -d '\n'
+}
+
+copy() {
+ sel "Will copy content" > "$_fifo" &
+ read -r id < "$_fifo"
+ selectContent "$id" \
+ | _clip
+}
+
+copyName() {
+ sel "Will copy entry name" > "$_fifo" &
+ read -r id < "$_fifo"
+ selectName "$id" \
+ | _clip
+}
+
+_type() {
+ sel "Will type content" > "$_fifo" &
+ read -r id < "$_fifo"
+ selectContent "$id" \
+ | xdotool type --delay 1ms --clearmodifiers --file -
+}
+
+typeName() {
+ sel "Will type entry name" > "$_fifo" &
+ read -r id < "$_fifo"
+ selectName "$id" \
+ | xdotool type --delay 1ms --clearmodifiers --file -
+}
+
+rename() {
+ sel "Item to rename" > "$_fifo" &
+ read -r id < "$_fifo"
+ item="$(grep "^$id" "$PASTE_MANAGER_FILE" | awk "$awkPrettyPrint" )"
+ name="$(selectName "$id")"
+ $_menu -p "Currently: ( $name ) New Name" > "$_fifo" &
+ read -r name < "$_fifo"
+ cp "$PASTE_MANAGER_FILE" "$PASTE_MANAGER_FILE".bak
+ awk -F:: -v id="$id" -v name="$name" '
+ BEGIN { OFS="::"; }
+ {
+ if ( $1 == id ) {
+ "date +%s" | getline now;
+ $3 = now;
+ $4 = name;
+ }
+ print $0;
+ }'< "$PASTE_MANAGER_FILE".bak > "$PASTE_MANAGER_FILE"
+}
+
+update() {
+ sel "Item to update" > "$_fifo" &
+ read -r id < "$_fifo"
+ item="$(grep "^$id" "$PASTE_MANAGER_FILE" | awk "$awkPrettyPrint" )"
+ output="$(selectContent "$id")"
+ $_menu -p "Currently: ( $output ) New Content" > "$_fifo" &
+ read -r content < "$_fifo"
+ cp "$PASTE_MANAGER_FILE" "$PASTE_MANAGER_FILE".bak
+ awk -F:: -v id="$id" -v content="$content" '
+ BEGIN { OFS="::"; }
+ {
+ if ( $1 == id ) {
+ "date +%s" | getline now;
+ $3 = now;
+ $5 = content;
+ }
+ print $0;
+ }'< "$PASTE_MANAGER_FILE".bak > "$PASTE_MANAGER_FILE"
+}
+
+delete() {
+ sel "Item to delete" > "$_fifo" &
+ read -r id < "$_fifo"
+ item="$(grep "^$id" "$PASTE_MANAGER_FILE" | awk "$awkPrettyPrint" )"
+ echo Item: "$item"'
+yes
+no' | $_menu -p "Are you sure?" > "$_fifo" &
+ read -r resp < "$_fifo"
+ if [ "$resp" = "yes" ] ; then
+ cp "$PASTE_MANAGER_FILE" "$PASTE_MANAGER_FILE".bak
+ sed '/^'"$id"/d < "$PASTE_MANAGER_FILE".bak > "$PASTE_MANAGER_FILE"
+ fi
+}
+
+actions='insert
+type
+copy
+typeName
+copyName
+delete
+rename
+update'
+
+menu() {
+ echo "$actions" | $_menu -p "Command:" \
+ | {
+ read -r action;
+ runAction "$action";
+ }
+}
+
+runAction() {
+action="${1:-menu}"
+case $1 in
+ insert) action=insert; shift ;;
+ type) action=_type; shift ;;
+ typeName) action=typeName; shift ;;
+ copy) action=copy; shift ;;
+ copyName) action=copyName; shift ;;
+ delete) action=delete; shift ;;
+ rename) action=rename; shift ;;
+ update) action=update; shift ;;
+ menu) action=menu; shift ;;
+ *) help ;;
+esac
+
+$action "$@"
+}
+
+help() {
+cat <<EOF
+$0 [action]
+
+Where action is one of the following:
+
+ menu
+$(echo "$actions" | sed -e's/^/\t/g')
+
+EOF
+exit 1
+}
+
+_fifo="$(getFifo)"
+
+action="${1:-menu}"
+runAction "$action"
+
+rm -f "$_fifo"
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..4fdee24
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,61 @@
+# paste manager
+
+A simple script to create a menu driven database for storing little bits of
+information. The database is a double colon delimited plain text file
+queried with standard unix tools.
+
+This is *NOT* for passwords. Do *NOT* use it for them. See my own
+[`dpw`](https://git.riedstra.dev/mitch/dpw/about/) or
+[pass](https://www.passwordstore.org/) for a solution there.
+
+Date formatting is a bit of a pain, so there's a `strftime` command line utility
+that's bundled, yes `gawk` provides `strftime` but that's not on every system.
+
+Works best with `dmenu` fuzzy finder patch. Utilizes list mode by default.
+
+Menu format can easily be customized by editing the script near the
+top of the `pm` shell script.
+
+Database format is fairly simple:
+
+```
+ID::Created::Updated::Name::Content
+```
+
+It's a menu driven application with no CLI options, users are encouraged to
+write programs that directly talk to the database, or extend it to suit their
+needs.
+
+
+## Installation
+
+```
+# make install
+```
+
+You'll need `dmenu` installed. You may also wish to bind a hotkey to run the new
+`pm` command
+
+
+## Configuration
+
+Environment variables:
+
+`PASTE_MANAGER_FILE` can override the file location
+`PASTE_MANAGER_LINES` overrides the number of lines displayed by `dmenu`
+`PASTE_MANAGER_X_SELECTION` sets the clipboard selection, you probably want to
+leave it set to clipboard, the default.
+
+
+Edit the source code for further tweaks. The one people may have the most
+interest in is the format sent to `dmenu`, that's controlled via the `awk` script
+in the `awkPrettyPrint` variable near the top.
+
+## Bugs
+
+Probably. This was written in just a few hours. Email me if you find any, I'll
+fix them.
+
+## Not-bugs
+
+Multi line snippets are entirely unsupported.
diff --git a/strftime.c b/strftime.c
new file mode 100644
index 0000000..70d171b
--- /dev/null
+++ b/strftime.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2023 Mitchell Riedstra
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * A fairly basic interface to `strftime`, since dates are apparently difficult
+ * to handle with regular command line utilites and the POSIX spec for `date`
+ * is rather lacking.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+
+
+void
+help()
+{
+ puts("strftime <fmt> [timestamp]");
+ exit(1);
+}
+
+int
+main(int argc, char **argv) {
+ time_t t = time(NULL);
+ char **a = argv+1;
+ struct tm *tm;
+ char *fmt;
+ char out[256] = {0};
+
+ if (!*a) {
+ help();
+ }
+
+ fmt = *a;
+ a++;
+
+ if (*a) {
+ errno = 0;
+ t = strtol(*a, NULL, 10);
+ if (errno != 0) {
+ perror("strtol");
+ exit(1);
+ }
+ }
+
+ tm = localtime(&t);
+
+ if (strftime(out, 256, fmt, tm) == 0) {
+ perror("strftime");
+ exit(1);
+ }
+
+ puts(out);
+}