Add nix-based build environment
This commit is contained in:
parent
3c0f0f157c
commit
2b00252635
3 changed files with 501 additions and 422 deletions
26
flake.lock
Normal file
26
flake.lock
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1696078222,
|
||||||
|
"narHash": "sha256-QBqNSFSPitmvpF2SPqTXbPKo7Wr3kv7cY4zjhCTsHRU=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "e4f393e78df6fe1986f24b018e60264e89b5c8de",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
53
flake.nix
Normal file
53
flake.nix
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
{
|
||||||
|
description = "Nix notes.sh dev environment";
|
||||||
|
|
||||||
|
# Flake inputs
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Flake outputs
|
||||||
|
outputs = { self, nixpkgs }:
|
||||||
|
let
|
||||||
|
# Systems supported
|
||||||
|
allSystems = [
|
||||||
|
"x86_64-linux" # 64-bit Intel/AMD Linux
|
||||||
|
"aarch64-linux" # 64-bit ARM Linux
|
||||||
|
"x86_64-darwin" # 64-bit Intel macOS
|
||||||
|
"aarch64-darwin" # 64-bit ARM macOS
|
||||||
|
];
|
||||||
|
|
||||||
|
# Helper to provide system-specific attributes
|
||||||
|
forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f {
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
});
|
||||||
|
|
||||||
|
make_package = pkgs: pkgs.stdenv.mkDerivation {
|
||||||
|
name = "notes-sh";
|
||||||
|
src = self;
|
||||||
|
nativeBuildInputs = [pkgs.makeWrapper];
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp notes.sh $out/bin
|
||||||
|
'';
|
||||||
|
|
||||||
|
postFixup = ''
|
||||||
|
wrapProgram $out/bin/notes.sh \
|
||||||
|
--prefix PATH : ${pkgs.lib.makeBinPath (with pkgs; [ gawk findutils gnused coreutils])}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells = forAllSystems ({ pkgs }: {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
fzf
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
packages = forAllSystems({ pkgs }: rec {notes-sh = make_package pkgs; default=notes-sh;});
|
||||||
|
overlays.default = final: prev: {
|
||||||
|
notes-sh = make_package prev;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
844
notes.sh
844
notes.sh
|
@ -4,13 +4,13 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2021, Konstantin Nazarov <mail@knazarov.com>
|
# Copyright (c) 2021, Konstantin Nazarov <mail@knazarov.com>
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
# modification, are permitted provided that the following conditions are met:
|
# modification, are permitted provided that the following conditions are met:
|
||||||
#
|
#
|
||||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
# list of conditions and the following disclaimer.
|
# list of conditions and the following disclaimer.
|
||||||
#
|
#
|
||||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
# and/or other materials provided with the distribution.
|
# and/or other materials provided with the distribution.
|
||||||
|
@ -35,17 +35,17 @@ CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/notes.sh"
|
||||||
EDITOR="${EDITOR:-vim}"
|
EDITOR="${EDITOR:-vim}"
|
||||||
|
|
||||||
if [ -n "$NOTES_SH_BASEDIR" ]; then
|
if [ -n "$NOTES_SH_BASEDIR" ]; then
|
||||||
BASEDIR="$NOTES_SH_BASEDIR"
|
BASEDIR="$NOTES_SH_BASEDIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
if [ ! -d "$BASEDIR" ]; then
|
if [ ! -d "$BASEDIR" ]; then
|
||||||
mkdir -p "$BASEDIR"/{tmp,new,cur}
|
mkdir -p "$BASEDIR"/{tmp,new,cur}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
echo "$@" 1>&2;
|
echo "$@" 1>&2;
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid()
|
uuid()
|
||||||
|
@ -81,528 +81,528 @@ uuid()
|
||||||
}
|
}
|
||||||
|
|
||||||
utc_timestamp() {
|
utc_timestamp() {
|
||||||
date -u +"%Y-%m-%dT%H:%M:%SZ"
|
date -u +"%Y-%m-%dT%H:%M:%SZ"
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_boundary()
|
gen_boundary()
|
||||||
{
|
{
|
||||||
for (( N=0; N < 32; ++N ))
|
for (( N=0; N < 32; ++N ))
|
||||||
do
|
do
|
||||||
B=$(( RANDOM%255 ))
|
B=$(( RANDOM%255 ))
|
||||||
printf '%02x' $B
|
printf '%02x' $B
|
||||||
done
|
done
|
||||||
echo
|
echo
|
||||||
}
|
}
|
||||||
|
|
||||||
yesno() {
|
yesno() {
|
||||||
PROMPT="$1"
|
PROMPT="$1"
|
||||||
DEFAULT="$2"
|
DEFAULT="$2"
|
||||||
|
|
||||||
if [[ "$DEFAULT" == "y" ]]; then
|
if [[ "$DEFAULT" == "y" ]]; then
|
||||||
read -p "$PROMPT [Y/n] " -r CHOICE
|
read -p "$PROMPT [Y/n] " -r CHOICE
|
||||||
if [[ "$CHOICE" =~ ^[Nn]$ ]]; then
|
if [[ "$CHOICE" =~ ^[Nn]$ ]]; then
|
||||||
echo "n"
|
echo "n"
|
||||||
else
|
else
|
||||||
echo "y"
|
echo "y"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
read -p "$PROMPT [y/N] " -r CHOICE
|
read -p "$PROMPT [y/N] " -r CHOICE
|
||||||
if [[ "$CHOICE" =~ ^[Yy]$ ]]; then
|
if [[ "$CHOICE" =~ ^[Yy]$ ]]; then
|
||||||
echo "y"
|
echo "y"
|
||||||
else
|
else
|
||||||
echo "n"
|
echo "n"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
get_headers() {
|
get_headers() {
|
||||||
HEADERS_FILE="$1"
|
HEADERS_FILE="$1"
|
||||||
|
|
||||||
FILTER="\
|
FILTER="\
|
||||||
{ \
|
{ \
|
||||||
if (\$0~/^$/) {exit} \
|
if (\$0~/^$/) {exit} \
|
||||||
if (\$0!~/^[^ ]*: .*$/) {exit} \
|
if (\$0!~/^[^ ]*: .*$/) {exit} \
|
||||||
print \$0 \
|
print \$0 \
|
||||||
} \
|
} \
|
||||||
"
|
"
|
||||||
awk "$FILTER" "$HEADERS_FILE"
|
awk "$FILTER" "$HEADERS_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
get_body() {
|
get_body() {
|
||||||
BODY_FILE="$1"
|
BODY_FILE="$1"
|
||||||
|
|
||||||
FILTER="\
|
FILTER="\
|
||||||
{ \
|
{ \
|
||||||
if (body==1) {\
|
if (body==1) {\
|
||||||
print \$0;
|
print \$0;
|
||||||
}\
|
}\
|
||||||
if (\$0~/^$/) {body=1} \
|
if (\$0~/^$/) {body=1} \
|
||||||
if (\$0!~/^[^ ]*: .*$/ && body != 1) {\
|
if (\$0!~/^[^ ]*: .*$/ && body != 1) {\
|
||||||
body=1;
|
body=1;
|
||||||
print \$0;
|
print \$0;
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
"
|
"
|
||||||
awk "$FILTER" "$BODY_FILE"
|
awk "$FILTER" "$BODY_FILE"
|
||||||
}
|
}
|
||||||
get_header() {
|
get_header() {
|
||||||
FILE="$1"
|
FILE="$1"
|
||||||
HEADER="$2"
|
HEADER="$2"
|
||||||
grep -m1 "^${HEADER}: " < "$FILE" | sed -n "s/^${HEADER}: \(.*\)$/\1/p"
|
grep -m1 "^${HEADER}: " < "$FILE" | sed -n "s/^${HEADER}: \(.*\)$/\1/p"
|
||||||
}
|
}
|
||||||
|
|
||||||
find_file_by_id() {
|
find_file_by_id() {
|
||||||
ID="$1"
|
ID="$1"
|
||||||
|
|
||||||
{ grep -l -r -m1 "^X-Note-Id: $ID$" "$BASEDIR" || true; } | sort| head -1
|
{ grep -l -r -m1 "^X-Note-Id: $ID$" "$BASEDIR" || true; } | sort| head -1
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_find_file_by_id() {
|
assert_find_file_by_id() {
|
||||||
FILE="$(find_file_by_id "$1")"
|
FILE="$(find_file_by_id "$1")"
|
||||||
|
|
||||||
if [ ! -f "$FILE" ]; then
|
if [ ! -f "$FILE" ]; then
|
||||||
die "Note with ID <$ID> not found"
|
die "Note with ID <$ID> not found"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$FILE"
|
echo "$FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
find_files_by_id() {
|
find_files_by_id() {
|
||||||
ID="$1"
|
ID="$1"
|
||||||
grep -l -r -m 1 "^X-Note-Id: $ID$" "$BASEDIR" || true
|
grep -l -r -m 1 "^X-Note-Id: $ID$" "$BASEDIR" || true
|
||||||
}
|
}
|
||||||
|
|
||||||
get_part() {
|
get_part() {
|
||||||
FILE="$1"
|
FILE="$1"
|
||||||
BOUNDARY="$2"
|
BOUNDARY="$2"
|
||||||
NUM="$3"
|
NUM="$3"
|
||||||
FILTER="\
|
FILTER="\
|
||||||
BEGIN { \
|
BEGIN { \
|
||||||
rec=0; \
|
rec=0; \
|
||||||
body=0; \
|
body=0; \
|
||||||
} \
|
} \
|
||||||
{ \
|
{ \
|
||||||
if (\$0==\"--$BOUNDARY\" || \$0==\"--$BOUNDARY--\") { \
|
if (\$0==\"--$BOUNDARY\" || \$0==\"--$BOUNDARY--\") { \
|
||||||
if (body == 0) { \
|
if (body == 0) { \
|
||||||
body=1; \
|
body=1; \
|
||||||
} \
|
} \
|
||||||
rec=rec+1; \
|
rec=rec+1; \
|
||||||
} \
|
} \
|
||||||
else if (body == 1 && rec==$NUM) { \
|
else if (body == 1 && rec==$NUM) { \
|
||||||
print \$0 \
|
print \$0 \
|
||||||
} \
|
} \
|
||||||
}"
|
}"
|
||||||
|
|
||||||
awk "$FILTER" "$FILE"
|
awk "$FILTER" "$FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
unpack_part() {
|
unpack_part() {
|
||||||
FILE="$1"
|
FILE="$1"
|
||||||
DIR="$2"
|
DIR="$2"
|
||||||
MIME_TYPE=$(get_header "$FILE" Content-Type)
|
MIME_TYPE=$(get_header "$FILE" Content-Type)
|
||||||
DISPOSITION=$(get_header "$FILE" Content-Disposition)
|
DISPOSITION=$(get_header "$FILE" Content-Disposition)
|
||||||
|
|
||||||
if [[ $DISPOSITION == *"attachment"* ]]; then
|
if [[ $DISPOSITION == *"attachment"* ]]; then
|
||||||
ENCODING=$(get_header "$FILE" Content-Transfer-Encoding)
|
ENCODING=$(get_header "$FILE" Content-Transfer-Encoding)
|
||||||
FILENAME=$(echo "$DISPOSITION" | \
|
FILENAME=$(echo "$DISPOSITION" | \
|
||||||
sed -n 's/^.*filename="\{0,1\}\([^"]*\)"\{0,1\}$/\1/p')
|
sed -n 's/^.*filename="\{0,1\}\([^"]*\)"\{0,1\}$/\1/p')
|
||||||
FILTER="\
|
FILTER="\
|
||||||
{ \
|
{ \
|
||||||
if (body==1) {print \$0}\
|
if (body==1) {print \$0}\
|
||||||
if (\$0~/^$/) {body=1} \
|
if (\$0~/^$/) {body=1} \
|
||||||
} \
|
} \
|
||||||
"
|
"
|
||||||
|
|
||||||
if [[ $ENCODING == *"base64"* ]]; then
|
if [[ $ENCODING == *"base64"* ]]; then
|
||||||
awk "$FILTER" "$FILE" | base64 --decode >> "$DIR/$FILENAME"
|
awk "$FILTER" "$FILE" | base64 --decode >> "$DIR/$FILENAME"
|
||||||
else
|
else
|
||||||
awk "$FILTER" "$FILE" >> "$DIR/$FILENAME"
|
awk "$FILTER" "$FILE" >> "$DIR/$FILENAME"
|
||||||
fi
|
fi
|
||||||
elif [[ $MIME_TYPE == *"text/plain"* ]]; then
|
elif [[ $MIME_TYPE == *"text/plain"* ]]; then
|
||||||
FILTER="\
|
FILTER="\
|
||||||
{ \
|
{ \
|
||||||
if (body==1) {print \$0}\
|
if (body==1) {print \$0}\
|
||||||
if (\$0~/^$/) {body=1} \
|
if (\$0~/^$/) {body=1} \
|
||||||
} \
|
} \
|
||||||
"
|
"
|
||||||
awk "$FILTER" "$FILE" >> "$DIR/note.md"
|
awk "$FILTER" "$FILE" >> "$DIR/note.md"
|
||||||
elif [[ $MIME_TYPE == *"multipart/mixed"* ]]; then
|
elif [[ $MIME_TYPE == *"multipart/mixed"* ]]; then
|
||||||
BOUNDARY=$(echo "$MIME_TYPE" | sed -n 's/^.*boundary="\(.*\)"$/\1/p')
|
BOUNDARY=$(echo "$MIME_TYPE" | sed -n 's/^.*boundary="\(.*\)"$/\1/p')
|
||||||
i=1
|
i=1
|
||||||
while true; do
|
while true; do
|
||||||
TMP=$(mktemp "$DIR/tmp.XXXXXXXX")
|
TMP=$(mktemp "$DIR/tmp.XXXXXXXX")
|
||||||
get_part "$FILE" "$BOUNDARY" "$i" > "$TMP"
|
get_part "$FILE" "$BOUNDARY" "$i" > "$TMP"
|
||||||
((i++))
|
((i++))
|
||||||
if [ ! -s "$TMP" ]; then
|
if [ ! -s "$TMP" ]; then
|
||||||
rm "$TMP"
|
rm "$TMP"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
#cat "$TMP"
|
#cat "$TMP"
|
||||||
(unpack_part "$TMP" "$DIR")
|
(unpack_part "$TMP" "$DIR")
|
||||||
rm "$TMP"
|
rm "$TMP"
|
||||||
done
|
done
|
||||||
elif [[ $MIME_TYPE == *"multipart/related"* ]]; then
|
elif [[ $MIME_TYPE == *"multipart/related"* ]]; then
|
||||||
echo "multipart/related not yet supported"
|
echo "multipart/related not yet supported"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unpack_mime() {
|
unpack_mime() {
|
||||||
FILE="$1"
|
FILE="$1"
|
||||||
DIR="$2"
|
DIR="$2"
|
||||||
|
|
||||||
get_headers "$FILE" | grep -v "^Content-Type\|^Content-Disposition\|^Date\|^MIME-Version" >> "$DIR/note.md"
|
get_headers "$FILE" | grep -v "^Content-Type\|^Content-Disposition\|^Date\|^MIME-Version" >> "$DIR/note.md"
|
||||||
echo "" >> "$DIR/note.md"
|
echo "" >> "$DIR/note.md"
|
||||||
|
|
||||||
TMP=$(mktemp "$DIR/tmp.XXXXXXXX")
|
TMP=$(mktemp "$DIR/tmp.XXXXXXXX")
|
||||||
cat "$FILE" > "$TMP"
|
cat "$FILE" > "$TMP"
|
||||||
|
|
||||||
(unpack_part "$TMP" "$DIR")
|
(unpack_part "$TMP" "$DIR")
|
||||||
rm "$TMP"
|
rm "$TMP"
|
||||||
}
|
}
|
||||||
|
|
||||||
pack_part() {
|
pack_part() {
|
||||||
PART_FILE="$1"
|
PART_FILE="$1"
|
||||||
CONTENT_TYPE="$(file -b --mime-type "$PART_FILE")"
|
CONTENT_TYPE="$(file -b --mime-type "$PART_FILE")"
|
||||||
echo "Content-Disposition: attachment; filename=\"$(basename "$PART_FILE")\""
|
echo "Content-Disposition: attachment; filename=\"$(basename "$PART_FILE")\""
|
||||||
|
|
||||||
if [[ "$CONTENT_TYPE" =~ "text/" ]]; then
|
if [[ "$CONTENT_TYPE" =~ "text/" ]]; then
|
||||||
echo "Content-Type: text/plain"
|
echo "Content-Type: text/plain"
|
||||||
echo
|
echo
|
||||||
cat "$PART_FILE"
|
cat "$PART_FILE"
|
||||||
else
|
else
|
||||||
echo "Content-Type: $CONTENT_TYPE"
|
echo "Content-Type: $CONTENT_TYPE"
|
||||||
echo "Content-Transfer-Encoding: base64"
|
echo "Content-Transfer-Encoding: base64"
|
||||||
echo
|
echo
|
||||||
base64 | fold -w 76 < "$PART_FILE"
|
base64 | fold -w 76 < "$PART_FILE"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
pack_mime() {
|
pack_mime() {
|
||||||
DIR="$1"
|
DIR="$1"
|
||||||
FILE="$2"
|
FILE="$2"
|
||||||
FILE_COUNT="$(find "$DIR/" -type f | wc -l | tr -d " ")"
|
FILE_COUNT="$(find "$DIR/" -type f | wc -l | tr -d " ")"
|
||||||
MIME_TIMESTAMP=$(LC_ALL="en_US.UTF-8" date "+$DATE_FORMAT")
|
MIME_TIMESTAMP=$(LC_ALL="en_US.UTF-8" date "+$DATE_FORMAT")
|
||||||
|
|
||||||
if [[ "$FILE_COUNT" == "1" ]]; then
|
if [[ "$FILE_COUNT" == "1" ]]; then
|
||||||
{
|
{
|
||||||
echo "MIME-Version: 1.0"
|
echo "MIME-Version: 1.0"
|
||||||
echo "Date: $MIME_TIMESTAMP"
|
echo "Date: $MIME_TIMESTAMP"
|
||||||
echo "Content-Type: text/plain; charset=utf-8"
|
echo "Content-Type: text/plain; charset=utf-8"
|
||||||
echo "Content-Disposition: inline"
|
echo "Content-Disposition: inline"
|
||||||
cat "$DIR/note.md"
|
cat "$DIR/note.md"
|
||||||
} >> "$FILE"
|
} >> "$FILE"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BOUNDARY="$(gen_boundary)"
|
BOUNDARY="$(gen_boundary)"
|
||||||
{
|
{
|
||||||
echo "MIME-Version: 1.0"
|
echo "MIME-Version: 1.0"
|
||||||
echo "Date: $MIME_TIMESTAMP"
|
echo "Date: $MIME_TIMESTAMP"
|
||||||
echo "Content-Type: multipart/mixed; boundary=\"$BOUNDARY\""
|
echo "Content-Type: multipart/mixed; boundary=\"$BOUNDARY\""
|
||||||
get_headers "$DIR/note.md"
|
get_headers "$DIR/note.md"
|
||||||
echo
|
echo
|
||||||
echo "--$BOUNDARY"
|
echo "--$BOUNDARY"
|
||||||
echo "Content-Type: text/plain; charset=utf-8"
|
echo "Content-Type: text/plain; charset=utf-8"
|
||||||
echo "Content-Disposition: inline"
|
echo "Content-Disposition: inline"
|
||||||
echo
|
echo
|
||||||
get_body "$DIR/note.md"
|
get_body "$DIR/note.md"
|
||||||
} >> "$FILE"
|
} >> "$FILE"
|
||||||
|
|
||||||
|
|
||||||
find "$DIR/" -type f ! -name 'note.md' | while read -r FN
|
find "$DIR/" -type f ! -name 'note.md' | while read -r FN
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
echo "--$BOUNDARY"
|
echo "--$BOUNDARY"
|
||||||
pack_part "$FN"
|
pack_part "$FN"
|
||||||
} >> "$FILE"
|
} >> "$FILE"
|
||||||
done
|
done
|
||||||
echo "--$BOUNDARY--" >> "$FILE"
|
echo "--$BOUNDARY--" >> "$FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
input_note() {
|
input_note() {
|
||||||
INP="$1"
|
INP="$1"
|
||||||
OUTP="$2"
|
OUTP="$2"
|
||||||
|
|
||||||
DIR="$(mktemp -d)"
|
DIR="$(mktemp -d)"
|
||||||
ENTRY_FILE="$DIR/note.md"
|
ENTRY_FILE="$DIR/note.md"
|
||||||
MIME_TIMESTAMP=$(LC_ALL="en_US.UTF-8" date "+$DATE_FORMAT")
|
MIME_TIMESTAMP=$(LC_ALL="en_US.UTF-8" date "+$DATE_FORMAT")
|
||||||
UTC_TIMESTAMP=$(utc_timestamp)
|
UTC_TIMESTAMP=$(utc_timestamp)
|
||||||
|
|
||||||
if [ -n "$INP" ] && [ ! -f "$INP" ] && [ ! -d "$INP" ]; then
|
if [ -n "$INP" ] && [ ! -f "$INP" ] && [ ! -d "$INP" ]; then
|
||||||
die "File or directory doesn't exist: $INP"
|
die "File or directory doesn't exist: $INP"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f "$INP" ]; then
|
if [ -f "$INP" ]; then
|
||||||
{
|
{
|
||||||
get_headers "$INP"
|
get_headers "$INP"
|
||||||
echo ""
|
echo ""
|
||||||
get_body "$INP"
|
get_body "$INP"
|
||||||
} >> "$DIR/note.md"
|
} >> "$DIR/note.md"
|
||||||
elif [ -d "$INP" ]; then
|
elif [ -d "$INP" ]; then
|
||||||
if [ ! -f "$INP/note.md" ]; then
|
if [ ! -f "$INP/note.md" ]; then
|
||||||
die "File doesn't exist: $INP/note.md"
|
die "File doesn't exist: $INP/note.md"
|
||||||
fi
|
fi
|
||||||
cp -n "$INP"/* "$DIR/" || true
|
cp -n "$INP"/* "$DIR/" || true
|
||||||
elif [ -t 0 ]; then
|
elif [ -t 0 ]; then
|
||||||
cat > "$ENTRY_FILE" <<- EOF
|
cat > "$ENTRY_FILE" <<- EOF
|
||||||
X-Date: $UTC_TIMESTAMP
|
X-Date: $UTC_TIMESTAMP
|
||||||
X-Note-Id: $(uuid)
|
X-Note-Id: $(uuid)
|
||||||
Subject:
|
Subject:
|
||||||
EOF
|
EOF
|
||||||
OLD_DIR="$(pwd)"
|
OLD_DIR="$(pwd)"
|
||||||
cd "$DIR"
|
cd "$DIR"
|
||||||
"$EDITOR" "$ENTRY_FILE"
|
$EDITOR "$ENTRY_FILE"
|
||||||
cd "$OLD_DIR"
|
cd "$OLD_DIR"
|
||||||
else
|
else
|
||||||
while read -r line ; do
|
while read -r line ; do
|
||||||
echo "$line" >> "$ENTRY_FILE"
|
echo "$line" >> "$ENTRY_FILE"
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
MERGED_ENTRY_FILE="$(mktemp)"
|
MERGED_ENTRY_FILE="$(mktemp)"
|
||||||
|
|
||||||
HEADERS=$(get_headers "$ENTRY_FILE")
|
HEADERS=$(get_headers "$ENTRY_FILE")
|
||||||
{
|
{
|
||||||
echo "$HEADERS" | grep -q "^X-Date:" || echo "X-Date: $UTC_TIMESTAMP"
|
echo "$HEADERS" | grep -q "^X-Date:" || echo "X-Date: $UTC_TIMESTAMP"
|
||||||
echo "$HEADERS" | grep -q "^X-Note-Id:" || echo "X-Note-Id: $(uuid)"
|
echo "$HEADERS" | grep -q "^X-Note-Id:" || echo "X-Note-Id: $(uuid)"
|
||||||
echo "$HEADERS"
|
echo "$HEADERS"
|
||||||
echo "$HEADERS" | grep -q "^Subject:" || echo "Subject: "
|
echo "$HEADERS" | grep -q "^Subject:" || echo "Subject: "
|
||||||
echo ""
|
echo ""
|
||||||
get_body "$ENTRY_FILE"
|
get_body "$ENTRY_FILE"
|
||||||
} > "$MERGED_ENTRY_FILE"
|
} > "$MERGED_ENTRY_FILE"
|
||||||
|
|
||||||
|
|
||||||
mv "$MERGED_ENTRY_FILE" "$ENTRY_FILE"
|
mv "$MERGED_ENTRY_FILE" "$ENTRY_FILE"
|
||||||
|
|
||||||
pack_mime "$DIR" "$OUTP"
|
pack_mime "$DIR" "$OUTP"
|
||||||
|
|
||||||
rm -rf "$DIR"
|
rm -rf "$DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_notes_by_id() {
|
remove_notes_by_id() {
|
||||||
ID="$1"
|
ID="$1"
|
||||||
|
|
||||||
find_files_by_id "$ID" | while read -r FN
|
find_files_by_id "$ID" | while read -r FN
|
||||||
do
|
do
|
||||||
rm "$FN"
|
rm "$FN"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
notes_equal() {
|
notes_equal() {
|
||||||
NOTE1="$1"
|
NOTE1="$1"
|
||||||
NOTE2="$2"
|
NOTE2="$2"
|
||||||
|
|
||||||
MIME_TYPE1=$(get_header "$NOTE1" Content-Type)
|
MIME_TYPE1=$(get_header "$NOTE1" Content-Type)
|
||||||
MIME_TYPE2=$(get_header "$NOTE2" Content-Type)
|
MIME_TYPE2=$(get_header "$NOTE2" Content-Type)
|
||||||
BOUNDARY1=$(echo "$MIME_TYPE1" | sed -n 's/^.*boundary="\(.*\)"$/\1/p')
|
BOUNDARY1=$(echo "$MIME_TYPE1" | sed -n 's/^.*boundary="\(.*\)"$/\1/p')
|
||||||
BOUNDARY2=$(echo "$MIME_TYPE2" | sed -n 's/^.*boundary="\(.*\)"$/\1/p')
|
BOUNDARY2=$(echo "$MIME_TYPE2" | sed -n 's/^.*boundary="\(.*\)"$/\1/p')
|
||||||
|
|
||||||
FILTER1="^Date:"
|
FILTER1="^Date:"
|
||||||
FILTER2="^Date:"
|
FILTER2="^Date:"
|
||||||
|
|
||||||
if [ -n "$BOUNDARY1" ]; then
|
if [ -n "$BOUNDARY1" ]; then
|
||||||
FILTER1="^Date:\|$BOUNDARY1"
|
FILTER1="^Date:\|$BOUNDARY1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$BOUNDARY2" ]; then
|
if [ -n "$BOUNDARY2" ]; then
|
||||||
FILTER2="^Date:\|$BOUNDARY2"
|
FILTER2="^Date:\|$BOUNDARY2"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
NOTE1_S="$(mktemp)"
|
NOTE1_S="$(mktemp)"
|
||||||
|
|
||||||
grep -v "$FILTER1" "$NOTE1" > "$NOTE1_S"
|
grep -v "$FILTER1" "$NOTE1" > "$NOTE1_S"
|
||||||
|
|
||||||
if grep -v "$FILTER2" "$NOTE2" | cmp -s "$NOTE1_S"; then
|
if grep -v "$FILTER2" "$NOTE2" | cmp -s "$NOTE1_S"; then
|
||||||
rm "$NOTE1_S"
|
rm "$NOTE1_S"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
rm "$NOTE1_S"
|
rm "$NOTE1_S"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
new_entry() {
|
new_entry() {
|
||||||
OUTP="$(mktemp)"
|
OUTP="$(mktemp)"
|
||||||
input_note "$1" "$OUTP"
|
input_note "$1" "$OUTP"
|
||||||
|
|
||||||
if [ ! -s "$OUTP" ]; then
|
if [ ! -s "$OUTP" ]; then
|
||||||
rm "$OUTP"
|
rm "$OUTP"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
NOTE_ID="$(get_header "$OUTP" X-Note-Id)"
|
NOTE_ID="$(get_header "$OUTP" X-Note-Id)"
|
||||||
|
|
||||||
OLD_NOTE="$(find_file_by_id "$NOTE_ID")"
|
OLD_NOTE="$(find_file_by_id "$NOTE_ID")"
|
||||||
if [ -n "$OLD_NOTE" ] && notes_equal "$OLD_NOTE" "$OUTP"; then
|
if [ -n "$OLD_NOTE" ] && notes_equal "$OLD_NOTE" "$OUTP"; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
remove_notes_by_id "$NOTE_ID"
|
remove_notes_by_id "$NOTE_ID"
|
||||||
|
|
||||||
UNIX_TIMESTAMP=$(date "+%s")
|
UNIX_TIMESTAMP=$(date "+%s")
|
||||||
HOSTNAME=$(hostname -s)
|
HOSTNAME=$(hostname -s)
|
||||||
FILENAME="$UNIX_TIMESTAMP.${PID}_1.${HOSTNAME}:2,S"
|
FILENAME="$UNIX_TIMESTAMP.${PID}_1.${HOSTNAME}:2,S"
|
||||||
mv "$OUTP" "$BASEDIR/cur/$FILENAME"
|
mv "$OUTP" "$BASEDIR/cur/$FILENAME"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
edit_entry() {
|
edit_entry() {
|
||||||
ID="$1"
|
ID="$1"
|
||||||
FILENAME="$(assert_find_file_by_id "$ID")"
|
FILENAME="$(assert_find_file_by_id "$ID")"
|
||||||
|
|
||||||
DIR="$CACHE_DIR/$ID"
|
DIR="$CACHE_DIR/$ID"
|
||||||
|
|
||||||
if [ -d "$DIR" ] && [ -f "$DIR/note.md" ]; then
|
if [ -d "$DIR" ] && [ -f "$DIR/note.md" ]; then
|
||||||
RESUME_EDITING=$(yesno "Unsaved changes found for this note. Resume editing?" y)
|
RESUME_EDITING=$(yesno "Unsaved changes found for this note. Resume editing?" y)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$RESUME_EDITING" != "y" ]; then
|
if [ "$RESUME_EDITING" != "y" ]; then
|
||||||
rm -rf "$DIR"
|
rm -rf "$DIR"
|
||||||
mkdir -p "$DIR"
|
mkdir -p "$DIR"
|
||||||
unpack_mime "$FILENAME" "$DIR"
|
unpack_mime "$FILENAME" "$DIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
OLD_DIR="$(pwd)"
|
OLD_DIR="$(pwd)"
|
||||||
cd "$DIR"
|
cd "$DIR"
|
||||||
if ! "$EDITOR" "$DIR/note.md"; then
|
if ! $EDITOR "$DIR/note.md"; then
|
||||||
die "Editor returned non-zero exit code. Leaving the note untouched."
|
die "Editor returned non-zero exit code. Leaving the note untouched."
|
||||||
fi
|
fi
|
||||||
cd "$OLD_DIR"
|
cd "$OLD_DIR"
|
||||||
|
|
||||||
UNIX_TIMESTAMP=$(date "+%s")
|
UNIX_TIMESTAMP=$(date "+%s")
|
||||||
HOSTNAME=$(hostname -s)
|
HOSTNAME=$(hostname -s)
|
||||||
|
|
||||||
RESULT=$(mktemp)
|
RESULT=$(mktemp)
|
||||||
pack_mime "$DIR" "$RESULT"
|
pack_mime "$DIR" "$RESULT"
|
||||||
|
|
||||||
if ! notes_equal "$RESULT" "$FILENAME"; then
|
if ! notes_equal "$RESULT" "$FILENAME"; then
|
||||||
DEST_FILENAME="$UNIX_TIMESTAMP.${PID}_1.${HOSTNAME}:2,S"
|
DEST_FILENAME="$UNIX_TIMESTAMP.${PID}_1.${HOSTNAME}:2,S"
|
||||||
mv "$RESULT" "$BASEDIR/cur/$DEST_FILENAME"
|
mv "$RESULT" "$BASEDIR/cur/$DEST_FILENAME"
|
||||||
rm "$FILENAME"
|
rm "$FILENAME"
|
||||||
fi
|
fi
|
||||||
rm -rf "$DIR"
|
rm -rf "$DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
list_entries() {
|
list_entries() {
|
||||||
FILTER="\
|
FILTER="\
|
||||||
BEGIN { \
|
BEGIN { \
|
||||||
message_id=0; \
|
message_id=0; \
|
||||||
subject=0; \
|
subject=0; \
|
||||||
date=0; \
|
date=0; \
|
||||||
} \
|
} \
|
||||||
match(\$0, /^X-Note-Id: .*$/) { \
|
match(\$0, /^X-Note-Id: .*$/) { \
|
||||||
if (message_id != 0) { \
|
if (message_id != 0) { \
|
||||||
if (subject !=0 && date != 0)\
|
if (subject !=0 && date != 0)\
|
||||||
print date, message_id, subject; \
|
print date, message_id, subject; \
|
||||||
subject = 0; \
|
subject = 0; \
|
||||||
date = 0; \
|
date = 0; \
|
||||||
};\
|
};\
|
||||||
message_id = substr(\$0, 12, RLENGTH-11); \
|
message_id = substr(\$0, 12, RLENGTH-11); \
|
||||||
} \
|
} \
|
||||||
match(\$0, /^Subject: .*$/) { \
|
match(\$0, /^Subject: .*$/) { \
|
||||||
if (subject != 0) { \
|
if (subject != 0) { \
|
||||||
if (message_id != 0 && date != 0)\
|
if (message_id != 0 && date != 0)\
|
||||||
print date, message_id, subject; \
|
print date, message_id, subject; \
|
||||||
message_id = 0; \
|
message_id = 0; \
|
||||||
date = 0; \
|
date = 0; \
|
||||||
}; \
|
}; \
|
||||||
subject = substr(\$0, 10, RLENGTH-9); \
|
subject = substr(\$0, 10, RLENGTH-9); \
|
||||||
} \
|
} \
|
||||||
match(\$0, /^X-Date: .*$/) { \
|
match(\$0, /^X-Date: .*$/) { \
|
||||||
if (date != 0) { \
|
if (date != 0) { \
|
||||||
if (message_id != 0 && subject != 0)\
|
if (message_id != 0 && subject != 0)\
|
||||||
print date, message_id, subject; \
|
print date, message_id, subject; \
|
||||||
subject = 0; \
|
subject = 0; \
|
||||||
message_id = 0; \
|
message_id = 0; \
|
||||||
}; \
|
}; \
|
||||||
date = substr(\$0, 9, RLENGTH-8); \
|
date = substr(\$0, 9, RLENGTH-8); \
|
||||||
} \
|
} \
|
||||||
END { \
|
END { \
|
||||||
if (message_id != 0 && subject != 0 && date != 0)\
|
if (message_id != 0 && subject != 0 && date != 0)\
|
||||||
print date, message_id, subject;\
|
print date, message_id, subject;\
|
||||||
}\
|
}\
|
||||||
"
|
"
|
||||||
|
|
||||||
grep -r -h "^Subject:\|^X-Note-Id:\|^X-Date:" "$BASEDIR" | awk "$FILTER" | sort | cut -d " " -f "2-"
|
grep -r -h "^Subject:\|^X-Note-Id:\|^X-Date:" "$BASEDIR" | awk "$FILTER" | sort | cut -d " " -f "2-"
|
||||||
}
|
}
|
||||||
|
|
||||||
export_note() {
|
export_note() {
|
||||||
ID="$1"
|
ID="$1"
|
||||||
FILENAME="$(assert_find_file_by_id "$ID")"
|
FILENAME="$(assert_find_file_by_id "$ID")"
|
||||||
|
|
||||||
DIR="$2"
|
DIR="$2"
|
||||||
if [ -z "$DIR" ]; then
|
if [ -z "$DIR" ]; then
|
||||||
DIR="$(mktemp -d)"
|
DIR="$(mktemp -d)"
|
||||||
unpack_mime "$FILENAME" "$DIR"
|
unpack_mime "$FILENAME" "$DIR"
|
||||||
|
|
||||||
cat "$DIR/note.md"
|
cat "$DIR/note.md"
|
||||||
rm -rf "$DIR"
|
rm -rf "$DIR"
|
||||||
else
|
else
|
||||||
unpack_mime "$FILENAME" "$DIR"
|
unpack_mime "$FILENAME" "$DIR"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
get_raw_graph() {
|
get_raw_graph() {
|
||||||
UUID_RE="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
|
UUID_RE="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
|
||||||
FILTER="\
|
FILTER="\
|
||||||
BEGIN { \
|
BEGIN { \
|
||||||
message_id=0; \
|
message_id=0; \
|
||||||
subject=0; \
|
subject=0; \
|
||||||
} \
|
} \
|
||||||
match(\$0, /^X-Note-Id: .*$/) { \
|
match(\$0, /^X-Note-Id: .*$/) { \
|
||||||
if (message_id != 0) { \
|
if (message_id != 0) { \
|
||||||
if (subject !=0)\
|
if (subject !=0)\
|
||||||
print \"node\", message_id, subject; \
|
print \"node\", message_id, subject; \
|
||||||
subject = 0 \
|
subject = 0 \
|
||||||
};\
|
};\
|
||||||
message_id = substr(\$0, 12, RLENGTH-11) \
|
message_id = substr(\$0, 12, RLENGTH-11) \
|
||||||
} \
|
} \
|
||||||
match(\$0, /^Subject: .*$/) { \
|
match(\$0, /^Subject: .*$/) { \
|
||||||
if (subject != 0) { \
|
if (subject != 0) { \
|
||||||
if (message_id != 0)\
|
if (message_id != 0)\
|
||||||
print \"node\", message_id, subject; \
|
print \"node\", message_id, subject; \
|
||||||
message_id = 0 \
|
message_id = 0 \
|
||||||
}; \
|
}; \
|
||||||
subject = substr(\$0, 10, RLENGTH-9) \
|
subject = substr(\$0, 10, RLENGTH-9) \
|
||||||
} \
|
} \
|
||||||
match(\$0, /^note:\/\/.*$/) { \
|
match(\$0, /^note:\/\/.*$/) { \
|
||||||
link_to = substr(\$0, 8, RLENGTH-7); \
|
link_to = substr(\$0, 8, RLENGTH-7); \
|
||||||
if (subject != 0 && message_id != 0) { \
|
if (subject != 0 && message_id != 0) { \
|
||||||
print \"link\", message_id, link_to; \
|
print \"link\", message_id, link_to; \
|
||||||
}; \
|
}; \
|
||||||
} \
|
} \
|
||||||
END { \
|
END { \
|
||||||
if (message_id != 0 && subject != 0)\
|
if (message_id != 0 && subject != 0)\
|
||||||
print \"node\", message_id, subject;\
|
print \"node\", message_id, subject;\
|
||||||
}\
|
}\
|
||||||
"
|
"
|
||||||
grep -E -i -o -r -h "^X-Note-Id: $UUID_RE|^Subject.*$|note://$UUID_RE" \
|
grep -E -i -o -r -h "^X-Note-Id: $UUID_RE|^Subject.*$|note://$UUID_RE" \
|
||||||
"$BASEDIR"/cur | awk "$FILTER"
|
"$BASEDIR"/cur | awk "$FILTER"
|
||||||
}
|
}
|
||||||
|
|
||||||
get_graph() {
|
get_graph() {
|
||||||
UUIDLEN=36
|
UUIDLEN=36
|
||||||
FILTER="\
|
FILTER="\
|
||||||
BEGIN { \
|
BEGIN { \
|
||||||
print \"graph notes {\" \
|
print \"graph notes {\" \
|
||||||
} \
|
} \
|
||||||
{\
|
{\
|
||||||
if (\$1 == \"node\") {\
|
if (\$1 == \"node\") {\
|
||||||
printf \" \\\"%s\\\" \", \$2;\
|
printf \" \\\"%s\\\" \", \$2;\
|
||||||
printf \"[label=\\\"%s\\\"]\", substr(\$0, $UUIDLEN + 7, length(\$0) - $UUIDLEN - 5);\
|
printf \"[label=\\\"%s\\\"]\", substr(\$0, $UUIDLEN + 7, length(\$0) - $UUIDLEN - 5);\
|
||||||
printf \";\\n\";\
|
printf \";\\n\";\
|
||||||
}\
|
}\
|
||||||
if (\$1 == \"link\") {\
|
if (\$1 == \"link\") {\
|
||||||
printf \" \\\"%s\\\" -- \\\"%s\\\";\\n\", \$2, \$3;\
|
printf \" \\\"%s\\\" -- \\\"%s\\\";\\n\", \$2, \$3;\
|
||||||
}\
|
}\
|
||||||
}\
|
}\
|
||||||
END { \
|
END { \
|
||||||
print \"}\" \
|
print \"}\" \
|
||||||
} \
|
} \
|
||||||
"
|
"
|
||||||
get_raw_graph | sort -r | sed 's/"/\\"/g' | awk "$FILTER"
|
get_raw_graph | sort -r | sed 's/"/\\"/g' | awk "$FILTER"
|
||||||
}
|
}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
|
@ -627,18 +627,18 @@ while (( "$#" )); do
|
||||||
edit_entry "$2"
|
edit_entry "$2"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
-E|--export)
|
-E|--export)
|
||||||
if [ -z "$2" ]; then
|
if [ -z "$2" ]; then
|
||||||
echo "Misssing arguments for $1"
|
echo "Misssing arguments for $1"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
export_note "$2" "$3"
|
export_note "$2" "$3"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
-g|--graph)
|
-g|--graph)
|
||||||
get_graph
|
get_graph
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
|
|
Loading…
Reference in a new issue