diff options
| author | Mitchell Riedstra <mitch@riedstra.dev> | 2023-01-27 21:15:20 -0500 |
|---|---|---|
| committer | Mitchell Riedstra <mitch@riedstra.dev> | 2023-01-27 21:15:20 -0500 |
| commit | 2cd48b9a51565a2b5831f62306ca88f061425b40 (patch) | |
| tree | dfdf95a129b1a8c276d814858ca6c7a8d5acbc54 | |
| download | pm-2cd48b9a51565a2b5831f62306ca88f061425b40.tar.gz pm-2cd48b9a51565a2b5831f62306ca88f061425b40.tar.xz | |
Initial
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 7 | ||||
| -rwxr-xr-x | pm | 248 | ||||
| -rw-r--r-- | readme.md | 61 | ||||
| -rw-r--r-- | strftime.c | 65 |
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 @@ -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); +} |
