#!/bin/sh
# Flip-file shell script version 1 by Ben Collver
# Roughly based on FLIP.BAS.
#
# <
gopher://tilde.pink/0/~bencollver/log/
# 2025-07-14-occ-freebasic-qbasic-and-wm-tweaks/flip.bas>
cardfile="FLIP.DAT"
exported="flip.tsv"
# maximum number of cards
mx=30
# maximum line length (80 - 9 - 3)
il=68
# current card number
card=1
# number of last card in file
last=0
# flag for deleted card
delmsg="' CARD DELETED '"
type mktemp >/dev/null 2>&1
if [ $? -eq 127 ]
then
mktemp()
{
o=0
s=$(date +%s)
while [ $o -lt 100 ]
do
t=$(echo $(($o + $s)) | md5sum | cut -c 1-6)
f="/tmp/tmp.$t"
if [ ! -f "$f" ]
then
break
fi
o=$(($o+1))
done
if [ -f "$f" ]
then
exit 127
return 127
else
touch "$f"
echo "$f"
fi
return 0
}
fi
# session data file
f=$(mktemp)
# helper script file
helper=$(mktemp)
# output from dialog command
RESULT=$(mktemp)
# remove tempfiles on exit
trap "rm -f $f $helper $RESULT" 0
export DIALOGOPTS="--no-mouse --title Flip-file"
echo "form_item_readonly_color = (WHITE,BLUE,ON)" >flip.rc
append_record()
{
infile=$1
awk -F "\n" -v OFS="\t" -v ORS="\r\n" -v RS="\xFF" '{
print $1, $2, $3, $4, $5, $6, $7
}' $infile >>$f
return
}
add_card()
{
limit=$((mx - 1))
if [ $last -eq $limit ]
then
dialog --backtitle "$backtitle" \
--msgbox "File full. Save cards." 0 0
return
fi
card=$((last + 1))
last=$((last + 1))
backtitle=$(get_backtitle)
dialog --backtitle "$backtitle" \
--no-cancel \
--ok-label "Back" \
--form "Add a card" 0 -1 7 \
Name: 1 1 "" 1 10 60 $il \
Street: 2 1 "" 2 10 60 $il \
Town: 3 1 "" 3 10 60 $il \
County: 4 1 "" 4 10 60 $il \
Code: 5 1 "" 5 10 60 $il \
Phone: 6 1 "" 6 10 60 $il \
Info: 7 1 "" 7 10 60 $il \
2>$RESULT
EXITCODE=$?
case $EXITCODE in
255) exit ;;
0) append_record $RESULT ;;
-1) error_exit $RESULT ;;
esac
return
}
browse()
{
while :
do
backtitle=$(get_backtitle)
cat >$helper <<__EOF__
DIALOGRC=flip.rc dialog \\
--backtitle "$backtitle" \\
--extra-button \\
--help-button \\
--ok-label Previous \\
--extra-label Next \\
--cancel-label Search \\
--help-label Back \\
--form "Browse & search" 0 -1 7 \\
__EOF__
view_card_fields $card -60 $helper
echo " 2>$RESULT" >>$helper
sh $helper
EXITCODE=$?
case $EXITCODE in
255) exit ;;
3) [ $card -lt $last ] && card=$((card + 1)) ;;
2) break ;;
1) search ;;
0) [ $card -gt 1 ] && card=$((card - 1)) ;;
-1) error_exit $RESULT ;;
esac
done
return
}
del_card()
{
update=$(mktemp)
backtitle=$(get_backtitle)
while :
do
cat >$helper <<__EOF__
DIALOGRC=flip.rc dialog \\
--backtitle "$backtitle" \\
--cancel-label Back \\
--ok-label Delete \\
--form "Remove a card" 0 -1 7 \\
__EOF__
view_card_fields $card -60 $helper
echo " 2>$RESULT" >>$helper
sh $helper
EXITCODE=$?
case $EXITCODE in
255) exit ;;
1) break ;;
-1) error_exit $RESULT ;;
esac
awk -F "\t" -v RS="\r\n" -v OFS="\t" -v ORS="\r\n" \
-v card=$card -v delmsg="$delmsg" '
NR != 1 + card {print $0}
NR == 1 + card {print $1, $2, $3, $4, $5, $6, delmsg}
' $f >$update
cat $update >$f
done
rm -f $update
return
}
edit_card()
{
backtitle=$(get_backtitle)
cat >$helper <<__EOF__
dialog --backtitle "$backtitle" \\
--no-cancel \\
--ok-label Back \\
--form "Edit a card" 0 -1 7 \\
__EOF__
view_card_fields $card 60 $helper
echo " 2>$RESULT" >>$helper
sh $helper
EXITCODE=$?
case $EXITCODE in
255) exit ;;
0) update_record $RESULT ;;
-1) error_exit $RESULT ;;
esac
return
}
error_exit() {
RESULT="$1"
printf "Error: "
cat $RESULT
exit
}
find_record()
{
infile=$1
outfile=$(mktemp)
pos=0
backtitle=$(get_backtitle)
term=$(awk -v OFS="\t" '{
if (length($0) > 0) {
print NR, $0
exit
}
}' $infile)
while :
do
awk -F "\t" -v RS="\r\n" -v pos=$pos -v term="$term" '
BEGIN {
OFS = FS
found = 0
nextpos = 0
num = split(term, fields, /\t/)
if (num == 2) {
field = fields[1]
value = fields[2]
} else {
field = 1
value = ""
}
}
{
result = 0
if (length(value) > 0) {
if (value == $field) {
result = 1
}
}
if (result == 1) {
found++
if (nextpos == 0 && NR - 1 > pos) {
nextpos = NR - 1
}
lastpos = NR - 1
}
}
END {
if (nextpos == lastpos) {
islast = 1
} else {
islast = 0
}
print nextpos, found, islast, value
}' $f >$outfile
pos=$(cat $outfile | cut -f 1)
found=$(cat $outfile | cut -f 2)
islast=$(cat $outfile | cut -f 3)
value=$(cat $outfile | cut -f 4)
if [ $found -eq 0 ]
then
dialog --backtitle "$backtitle" \
--msgbox "Didn't find: $value" 0 0
break
else
card=$pos
backtitle=$(get_backtitle)
cat >$helper <<__EOF__
DIALOGRC=flip.rc dialog \\
--backtitle "$backtitle" \\
--ok-label Next \\
--cancel-label Back \\
--form "Searching for $value " 0 -1 7 \\
__EOF__
view_card_fields $card -60 $helper
echo " 2>$RESULT" >>$helper
sh $helper
EXITCODE=$?
case $EXITCODE in
255) exit ;;
1) break ;;
0)
if [ $islast -eq 1 ]
then
msg="Search over\nFound: $found times"
dialog --backtitle "$backtitle" \
--msgbox "$msg" 0 0
break
fi
;;
-1) error_exit $RESULT ;;
esac
fi
done
rm -f $outfile
return
}
get_backtitle()
{
if [ $last -eq 0 ]
then
echo "No cards in file"
elif [ $last -gt 0 ]
then
if [ $card -lt $((mx + 1)) ]
then
echo "$last cards on card $card "
else
echo "$last cards"
fi
fi
return
}
load()
{
last=$(awk -v RS="\r\n" 'NR == 1 {print $1}' $cardfile)
card=1
printf "Name\tStreet\tTown\tCounty\tCode\tPhone\tInfo\r\n" >$f
awk -F "\t" -v RS="\r\n" -v OFS="\t" -v ORS="\r\n" '
NR > 1 {
name=$0
getline street
getline town
getline county
getline code
getline phone
getline info
print name, street, town, county, code, phone, info
}' $cardfile >>$f
return
}
save()
{
# write count of not-deleted records
awk -F "\t" -v RS="\r\n" -v ORS="\r\n" -v delmsg="$delmsg" '
NR > 1 {
if ($7 != delmsg) {
notdel++
}
}
END {
print notdel
}' $f >$cardfile
# write lines for not-deleted records
awk -F "\t" -v RS="\r\n" -v OFS="\r\n" -v ORS="\r\n" \
-v delmsg="$delmsg" '
NR > 1 {
if ($7 != delmsg) {
print $1, $2, $3, $4, $5, $6, $7
}
}' $f >>$cardfile
cat $f >$exported
echo "Cards saved. You are no longer in Flip-file."
exit 0
}
search()
{
backtitle=$(get_backtitle)
dialog --backtitle "$backtitle" \
--no-cancel \
--ok-label "Search" \
--form "Search" 0 -1 7 \
Name: 1 1 "" 1 10 60 $il \
Street: 2 1 "" 2 10 60 $il \
Town: 3 1 "" 3 10 60 $il \
County: 4 1 "" 4 10 60 $il \
Code: 5 1 "" 5 10 60 $il \
Phone: 6 1 "" 6 10 60 $il \
Info: 7 1 "" 7 10 60 $il \
2>$RESULT
EXITCODE=$?
case $EXITCODE in
255) exit ;;
0)
find_record $RESULT
break
;;
-1) error_exit $RESULT ;;
esac
return
}
update_record()
{
infile=$1
record=$(mktemp)
update=$(mktemp)
awk -F "\n" -v OFS="\t" -v RS="\xFF" '{
print $1, $2, $3, $4, $5, $6, $7
}' $infile >$record
rec=$(cat $record)
awk -F "\t" -v RS="\r\n" -v ORS="\r\n" -v card=$card \
-v rec="$rec" '
NR != 1 + card {print $0}
NR == 1 + card {print rec}
' $f >$update
cat $update >$f
rm -f $record $update
return
}
view_card_fields()
{
pos="$1"
len="$2"
out="$3"
awk -F "\t" -v RS="\r\n" -v ORS="\r\n" -v card=$pos \
-v len=$len -v il=$il '
NR == 1 {
for (i = 1; i <= NF; i++) {
name[i] = toupper(substr($i, 1, 1)) substr($i, 2) ":"
}
}
NR == 1 + card {
for (i = 1; i <= NF; i++) {
q = "\"" $i "\""
printf "%-7s %d 1 %-9s %d 10 %d %d \\\n",
name[i], i, q, i, len, il
}
}' $f >>$out
return
}
# ==================================================================
main() {
printf "Name\tStreet\tTown\tCounty\tCode\tPhone\tInfo\r\n" >$f
while :
do
backtitle=$(get_backtitle)
dialog --backtitle "$backtitle" \
--no-cancel \
--menu "Would you like to:" 0 0 7 \
ADD "Add a card" \
REMOVE "Remove a card" \
ALTER "Alter a card (edit/update)" \
BROWSE "Flip cards (browse/search)" \
LOAD "Load cards" \
SAVE "File these cards (save & quit)" \
2>$RESULT
EXITCODE=$?
case $EXITCODE in
255) exit ;;
-1) error_exit $RESULT ;;
esac
OUTPUT=$(cat $RESULT)
if [ "$OUTPUT" != "ADD" -a "$OUTPUT" != "LOAD" ]
then
if [ $last -eq 0 ]
then
dialog --backtitle "$backtitle" \
--msgbox "No cards in file" 0 0
continue
fi
fi
case "$OUTPUT" in
REMOVE) del_card ;;
ADD) add_card ;;
ALTER) edit_card ;;
BROWSE) browse ;;
LOAD) load ;;
SAVE) save ;;
esac
done
exit 0
}
main