2153 lines
56 KiB
Bash
Executable File
2153 lines
56 KiB
Bash
Executable File
#!/opt/homebrew/bin/bash
|
|
#set -x
|
|
|
|
trap "exit 1" TERM
|
|
export MYPID=$$
|
|
|
|
if [[ ${BASH_VERSION:0:1} == 5 ]]; then
|
|
b5=1
|
|
declare -A idmap
|
|
else
|
|
b5=0
|
|
fi
|
|
|
|
. ${HOME}/code/bashtools/bashtools.sh
|
|
if [[ -z $HAVE_BASHTOOLS ]]; then
|
|
echo "ERROR: bashtools not installed download from https://git.nethack.net/rob/bashtools" >/dev/stderr
|
|
exit 1
|
|
fi
|
|
|
|
INFORMCOL="$GREEN"
|
|
INFORMCOLB="$GREENBOLD"
|
|
|
|
brack="$CYAN"
|
|
brackfold="$CYAN"
|
|
HILITE="\033[43;1m"
|
|
|
|
GREYBG="\033[48;2;30;30;30m"
|
|
|
|
TOPTITLE="\033[44;1m"
|
|
LEFTTITLE="\033[31;1m"
|
|
FOLDBG="$BOLDD"
|
|
|
|
|
|
function genidx() {
|
|
local idxname=$1 x
|
|
shift
|
|
local -a vals=("$@")
|
|
unset ${idxname}
|
|
declare -g -A ${idxname}
|
|
for x in "${!vals[@]}"; do
|
|
#echo "setting ${idxname}[${vals[$x]}] to $x" >/dev/stderr
|
|
eval ${idxname}["${vals[$x]}"]=$x
|
|
done
|
|
}
|
|
|
|
|
|
function die() {
|
|
kill -s TERM $MYPID
|
|
}
|
|
|
|
function getalts() {
|
|
local verb alt var x
|
|
verb=$1
|
|
alt=""
|
|
var="words_$verb"
|
|
IFS='|'
|
|
for x in ${!var}; do
|
|
if [[ $x != $verb ]]; then
|
|
[[ -z $alt ]] && alt=$x || alt="$alt,$x"
|
|
fi
|
|
done
|
|
IFS=' '
|
|
echo "$alt"
|
|
}
|
|
|
|
function usage() {
|
|
local titleformat format
|
|
local usage_var desc_var
|
|
format="%22s %-40s %s\n"
|
|
echo "usage: $0 COMMAND [OPTIONS] [commandopts]"
|
|
echo
|
|
echo " -c Select calendar to use (use '$0 showcals' to list)"
|
|
echo " -f Assume 'yes' to all questions."
|
|
echo " -l xx Specify name of local vdirsyncer storage name (default: $DEFAULT_VDS_LOCAL)"
|
|
echo " -m Maintenance mode - find and fix bad idmapping entries, then exit."
|
|
echo " -p Enable profiling"
|
|
echo " -r xx Specify name of remote vdirsyncer storage name (default: $DEFAULT_VDS_REMOTE)"
|
|
echo " -s Auto-sync with server when complete."
|
|
echo " -S Sort tasks and subtasks alphabetically"
|
|
echo
|
|
printf "$format" "COMMAND" "DESCRIPTION" "SYNONYMS"
|
|
for x in $valid_modes; do
|
|
usage_var="usage_$x"
|
|
desc_var="desc_$x"
|
|
printf "$format" "${!usage_var}" "${!desc_var}" "$(getalts $x)"
|
|
done
|
|
}
|
|
|
|
function inc() {
|
|
indent=$((indent + $indentamt))
|
|
}
|
|
|
|
function dec() {
|
|
indent=$((indent - $indentamt))
|
|
}
|
|
|
|
function getuid_witherror() { # [noun]
|
|
local x rv noun
|
|
if [[ $$ -ge 2 ]]; then
|
|
noun=$2
|
|
else
|
|
noun="task"
|
|
fi
|
|
x=$(getuid $1)
|
|
rv=$?
|
|
if [[ $rv -ne 0 ]]; then
|
|
error "Specified $noun #$1 does not exist."
|
|
die
|
|
fi
|
|
echo "$x"
|
|
return $rv
|
|
}
|
|
|
|
function getid() {
|
|
local res="" id
|
|
if [[ $b5 ]]; then
|
|
if [[ "${i_idmap[$1]}" ]]; then
|
|
echo "${i_idmap[$1]}"
|
|
return 0
|
|
fi
|
|
return 1
|
|
else
|
|
for id in ${!idmap[@]}; do
|
|
if [[ ${idmap[$id]} == $1 ]]; then
|
|
echo "$id"
|
|
return 0
|
|
fi
|
|
done
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
function getuid() {
|
|
local res id
|
|
id=$1
|
|
res=${idmap[$id]}
|
|
if [[ -z $res ]]; then
|
|
return 1
|
|
fi
|
|
echo $res
|
|
return 0
|
|
}
|
|
|
|
function loadids() {
|
|
local LINE n uid id max count
|
|
count=0
|
|
if [[ ! -e $idfile ]]; then
|
|
error "No ID file found (looking here: $idfile)"
|
|
return 1
|
|
fi
|
|
profile "start"
|
|
|
|
# check for duplicate id mappings
|
|
sort -o $f -t: -k1,1 --stable --unique "$idfile"
|
|
profile "finished removing dupes"
|
|
|
|
max=0
|
|
while read LINE; do
|
|
uid=${LINE%:*}
|
|
id=${LINE#*:}
|
|
#uid=$(echo "$LINE" | sed 's/:.*$//g')
|
|
#id=$(echo "$LINE" | sed 's/^.*://g')
|
|
idmap[$id]=$uid
|
|
[[ $b5 -eq 1 ]] && eval i_idmap["$uid"]=$id
|
|
if [[ -z $uid || -z $id ]]; then
|
|
error "invalid idmapping line: '$LINE'"
|
|
profile "end"
|
|
return 1
|
|
fi
|
|
[[ $id -gt $max ]] && max=$id
|
|
count=$((count + 1))
|
|
done < "$idfile"
|
|
nextid=$((max + 1))
|
|
profile "end"
|
|
dblog "got $count IDs from $idfile"
|
|
|
|
return 0
|
|
}
|
|
|
|
function saveids() {
|
|
local id verbose=0
|
|
local ess nids=0
|
|
|
|
[[ $1 == "-v" ]] && verbose=1
|
|
|
|
if [[ ${#idmap[@]} -gt 0 ]]; then
|
|
cp /dev/null "$idfile"
|
|
for id in ${!idmap[@]}; do
|
|
#dblog "save to $idfile: uid ${idmap[$id]} id $id sum ${tasksum[$id]}" >> $idfile
|
|
echo "${idmap[$id]}:$id" >> "$idfile"
|
|
nids=$((nids + 1))
|
|
done
|
|
[[ $nids -eq 1 ]] && ess="" || ess="s"
|
|
[[ $verbose -eq 1 ]] && inform "Saved $nids ID mapping$ess to $idfile."
|
|
else
|
|
[[ $verbose -eq 1 ]] && warn "No ID mappings found to save"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
function dumpids() {
|
|
local id
|
|
for id in ${!idmap[@]}; do
|
|
echo "$id --> ${idmap[$id]}"
|
|
done
|
|
}
|
|
|
|
function loadtasks() {
|
|
local f ess x thislev par parid show thissum thisdesc
|
|
local verbose=0 nextid2=1
|
|
|
|
profile "start"
|
|
maxid=-1
|
|
maxindent=-1
|
|
maxtaskwidth=-1
|
|
ntasks=0
|
|
while [[ $# -ge 1 ]]; do
|
|
[[ $1 == "-v" ]] && verbose=1 && shift 1
|
|
done
|
|
dblog "Loading tasks..."
|
|
for f in "$dir"/*.vcf; do
|
|
dblog "--> loading from $f"
|
|
loadtask "$f" || return 1
|
|
ntasks=$((ntasks + 1))
|
|
done
|
|
numwid=${#maxid}
|
|
profile "finished loadtask calls"
|
|
|
|
# ---ooooo implement get_next_id()
|
|
# minid=$( echo "${taskid[@]}" | tr ' ' '\n' | sort -nr | tail -1)
|
|
# if [[ $minid -lt $nextid ]]; then
|
|
# nextid2=1
|
|
# while [[ " ${taskid[@]}" == *\ $nextid2* ]]; do
|
|
# nextid2=$((nextid2 + 1))
|
|
# done
|
|
# nextid=$nextid2
|
|
# fi
|
|
|
|
dblog "Calculating max indent level"
|
|
# figure out max indent level and next id
|
|
for x in ${!taskuid[@]}; do
|
|
show=1
|
|
par=${taskparent[$x]}
|
|
if [[ -n $par ]]; then
|
|
parid=$(getid $par)
|
|
if [[ ${taskfolded[$parid]} -eq 1 ]]; then
|
|
show=0
|
|
fi
|
|
fi
|
|
dblog " uid $x id [${taskid[$x]}] parent [$par] parent [$parid] show [$show]"
|
|
if [[ $show -eq 1 ]]; then
|
|
dblog " ->showing $x [${tasksum[$x]}]"
|
|
thislev=$(getindentlevel $x)
|
|
dblog " ->lv: $thislev"
|
|
[[ $thislev -gt $maxindent ]] && maxindent=$thislev
|
|
thissum=${tasksum[$x]}
|
|
thisdesc=${taskdesc[$x]}
|
|
[[ ${#thissum} -gt $maxtaskwidth ]] && maxtaskwidth=${#thissum}
|
|
[[ ${#thisdesc} -gt $maxtaskwidth ]] && maxtaskwidth=${#thisdesc}
|
|
dblog " ->sum: $thissum"
|
|
fi
|
|
done
|
|
profile "got max indent"
|
|
|
|
[[ $ntasks -eq 1 ]] && ess="" || ess="s"
|
|
[[ $verbose -eq 1 ]] && inform "Finished loading $ntasks task$ess."
|
|
|
|
dblog "Finished loading $ntasks task$ess."
|
|
profile "end"
|
|
return 0
|
|
}
|
|
|
|
function getindentlevel() { ## $0 id
|
|
local id thisid parentuid parentid level
|
|
id=$1
|
|
thisid=$id
|
|
#dblog "gil() id $id thisid $thisid"
|
|
|
|
level=0
|
|
|
|
while [ ! -z $parentuid ] ; do
|
|
parentuid=${taskparent[$thisid]}
|
|
if [[ ! -z $parentuid ]]; then
|
|
thisid=$(getid $parentuid)
|
|
dblog " parent uid: [$parentuid] id: [$thisid]"
|
|
level=$((level + 1))
|
|
fi
|
|
done
|
|
|
|
echo $level
|
|
}
|
|
|
|
function dblog() {
|
|
[[ $DEBUG -eq 1 ]] && echo "$(date) ${FUNCNAME[1]}() $*" >/dev/stderr
|
|
}
|
|
|
|
function confirm() {
|
|
local yn
|
|
if [[ $AUTOYES -eq 1 ]]; then
|
|
return 0
|
|
fi
|
|
echo -en "$BOLD${MAGENTA}CONFIRM: $PLAIN$MAGENTA$* (y/N)? $PLAIN"
|
|
read yn
|
|
if [[ $yn == "y" ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
function loadtask() {
|
|
local uid sum checked folded desc file id parent res i cats
|
|
local blockreason
|
|
#local re
|
|
|
|
#if iscached "$1"; then
|
|
#fi
|
|
|
|
dblog "open $1"
|
|
file="$1"
|
|
#re="^(CATEGORIES:|DESCRIPTION:|REQUEST-STATUS:4.1;|RELATED-TO|X-OC-HIDESUBTASKS:1|STATUS:)"
|
|
res=$(cat "$file" | mawk -F: 'BEGIN { checked=0; folded=0; } /^UID:/ { printf("uid©%s©",$2); } /^SUMMARY:/ { sub("SUMMARY:",""); printf("sum©%s©",$0); } /^CATEGORIES:/ { sub("^CATEGORIES:",""); printf("cats©%s©",$0); } /^DESCRIPTION:/ { sub("DESCRIPTION:",""); printf("desc©%s©",$0); } /^REQUEST-STATUS:4.1;/ { sub("REQUEST-STATUS:4.1;",""); printf("breason©%s©",$0);} /^RELATED-TO/ { sub("^RELATED-TO.*:",""); printf("parent©%s©",$0); } /^X-OC-HIDESUBTASKS:1/ { folded=1; } /^STATUS:/ { stat=substr($0,8); if (stat == "COMPLETED") { checked=2; } else if (stat == "NEEDS-inform") { checked=3; } else if (stat == "IN-PROCESS") { checked = 1 } } END { printf("checked©%d©folded©%d\n",checked,folded); }' )
|
|
|
|
toadd2="REQUEST-STATUS:4.1;$reason"
|
|
IFS='©'
|
|
read -ra tok <<< "$res"
|
|
parent=""
|
|
i=0
|
|
while [[ ! -z ${tok[$i]} ]]; do
|
|
dblog "process token '${tok[$i]}'"
|
|
if [[ ${tok[$i]} == "uid" ]]; then
|
|
uid=${tok[$((i + 1))]}
|
|
id=$(getid $uid)
|
|
if [[ -z $id ]]; then
|
|
id=$nextid
|
|
idmap[$nextid]=$uid
|
|
[[ $b5 -eq 1 ]] && eval i_idmap["$uid"]=$id
|
|
nextid=$((nextid + 1))
|
|
needsave=1
|
|
fi
|
|
elif [[ ${tok[$i]} == "sum" ]]; then
|
|
sum="${tok[$((i + 1))]}"
|
|
elif [[ ${tok[$i]} == "cats" ]]; then
|
|
cats="${tok[$((i + 1))]}"
|
|
elif [[ ${tok[$i]} == "breason" ]]; then
|
|
blockreason="${tok[$((i + 1))]}"
|
|
elif [[ ${tok[$i]} == "desc" ]]; then
|
|
desc="${tok[$((i + 1))]}"
|
|
elif [[ ${tok[$i]} == "parent" ]]; then
|
|
parent="${tok[$((i + 1))]}"
|
|
elif [[ ${tok[$i]} == "checked" ]]; then
|
|
checked="${tok[$((i + 1))]}"
|
|
elif [[ ${tok[$i]} == "folded" ]]; then
|
|
folded="${tok[$((i + 1))]}"
|
|
fi
|
|
i=$((i + 2))
|
|
done
|
|
IFS=' '
|
|
|
|
taskuid[$id]=$uid
|
|
taskid[$id]=$id
|
|
tasksum[$id]=$sum
|
|
taskcats[$id]=$cats
|
|
taskblockreason[$id]="$blockreason"
|
|
taskdesc[$id]=$desc
|
|
taskchecked[$id]=$checked
|
|
taskfolded[$id]=$folded
|
|
taskparent[$id]=$parent
|
|
|
|
if [[ $DEBUG -eq 1 ]]; then
|
|
echo "taskuid[$id]=$uid"
|
|
echo "taskid[$id]=$id"
|
|
echo "tasksum[$id]=$sum"
|
|
echo "taskcats[$id]=$cats"
|
|
echo "taskblockreason[$id]=$blockreason"
|
|
echo "taskdesc[$id]=$desc"
|
|
echo "taskchecked[$id]=$checked"
|
|
echo "taskfolded[$id]=$folded"
|
|
echo "taskparent[$id]=$parent"
|
|
fi
|
|
|
|
[[ $id -gt $maxid ]] && maxid=$id
|
|
[[ ${#sum} -gt $taskwid ]] && taskwid=${#sum}
|
|
[[ ${#desc} -gt $taskwid ]] && taskwid=${#desc}
|
|
if [[ -z $uid ]]; then
|
|
error "task has no uid:"
|
|
echo "$res"
|
|
return 1
|
|
fi
|
|
# cache it
|
|
#cachetask "$file"
|
|
return 0
|
|
}
|
|
|
|
function cachetask() { #1=cardfile
|
|
stat -f "%Uc" "$1" >${cachedir}/$(basename "$1")
|
|
}
|
|
|
|
function iscached() { #1=cardfile
|
|
local changetime cachetime
|
|
changetime=$(stat -f "%Uc" "$1")
|
|
cachetime=$(cat ${cachedir}/$(basename "$1"))
|
|
if [[ $changetime -eq $cachetime ]]; then
|
|
return 1;
|
|
fi
|
|
return 0;
|
|
}
|
|
|
|
function marknotdone() {
|
|
local id uid rv file sedrv
|
|
local children childuid ischild noun
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
[[ $# -gt 1 ]] && ischild=1 || ischild=0
|
|
[[ $ischild -eq 1 ]] && noun=subtask || noun=task
|
|
|
|
|
|
# already not done?
|
|
if [[ ${taskchecked[$id]} -eq 0 ]]; then
|
|
error "$noun #${id} is already unfinished"
|
|
return 1
|
|
fi
|
|
file="$dir/$uid.vcf"
|
|
#sed -i '/^COMPLETED:/d;/^STATUS:COMPLETED/d;/PERCENT-COMPLETE:100/d' "$file"
|
|
#sed -i '/^COMPLETED:/d;/^STATUS:/d;/PERCENT-COMPLETE:/d' "$file"
|
|
clearstatus "$file"
|
|
sedrv=$((sedrv + $?))
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
inform "Marked $noun #${id} as unfinished."
|
|
processed="$processed ${uid} "
|
|
loadtask "$file"
|
|
rv=0
|
|
children=$(getchildren $uid)
|
|
for childuid in ${children}; do
|
|
marknotdone $childuid noprint
|
|
rv=$((rv + $?))
|
|
done
|
|
else
|
|
failed="$failed ${uid} "
|
|
error "failed to mark $noun #${id} as unfinished"
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function delq_clear() {
|
|
deluids=""
|
|
}
|
|
|
|
function delq_add() {
|
|
local x
|
|
if [[ ! $deluids == *\ $1\ * ]]; then
|
|
deluids="$deluids $1"
|
|
for x in $(getchildren $1); do
|
|
delq_add $x
|
|
done
|
|
fi
|
|
}
|
|
|
|
function delq_process() {
|
|
local uidlist uid rv yn file count ess wantshow
|
|
local od
|
|
rv=0
|
|
|
|
# Remove duplicates
|
|
deluids=$(echo $deluids | tr ' ' '\n' | sort -u | tr '\n' ' ')
|
|
|
|
info "The following tasks will be removed:"
|
|
needtitle=0
|
|
for uid in $deluids; do
|
|
wantshow=0
|
|
id=$(getid $uid)
|
|
|
|
# don't show if it's a subtask of one already in the list
|
|
if [[ -z ${taskparent[$id]} ]]; then
|
|
wantshow=1
|
|
elif [[ $deluids == *${taskparent[$id]}\ * ]]; then
|
|
wantshow=0
|
|
else
|
|
wantshow=1
|
|
fi
|
|
|
|
if [[ $wantshow -eq 1 ]]; then
|
|
showit $uid | sed -e 's/^/ /'
|
|
fi
|
|
done
|
|
confirm "Really remove these tasks"
|
|
if [[ $? -eq 0 ]]; then
|
|
count=0
|
|
for uid in $deluids; do
|
|
file="$dir/$uid.vcf"
|
|
rm -f "$file"
|
|
|
|
id=$(getid $uid)
|
|
unset idmap[$id]
|
|
|
|
count=$((count + 1))
|
|
done
|
|
saveids
|
|
[[ $count -eq 1 ]] && ess="" || ess="s"
|
|
inform "$count task$ess removed."
|
|
else
|
|
inform "Aborted."
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function set_lastmodified() {
|
|
local uid file now
|
|
uid=$1
|
|
file="$dir/$uid.vcf"
|
|
now=$(date -u +'%Y%m%dT%H%M%SZ')
|
|
sed -i "s/^LAST-MODIFIED:.*/LAST-MODIFIED:$now/" "$file"
|
|
return $?
|
|
}
|
|
|
|
function move_left() { # move_left uid
|
|
local uid rv parent parentid parent2 sedrv
|
|
local id
|
|
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
rv=0
|
|
parent=${taskparent[$id]}
|
|
if [[ -z $parent ]]; then
|
|
error "Can't shift task #${id} any further left."
|
|
exit 1
|
|
fi
|
|
parentid=$(getid ${taskparent[$id]})
|
|
|
|
parent2=${taskparent[$parentid]}
|
|
taskparent[$id]="$parent2"
|
|
|
|
file="$dir/$uid.vcf"
|
|
|
|
sed -i "s/^RELATED-TO.*/RELATED-TO;RELTYPE=PARENT:${taskparent[$id]}/" "$file"
|
|
sedrv=$?
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
inform "Shifted task #${id} upwards/left."
|
|
loadtask "$file"
|
|
|
|
showit ${taskparent[$id]} $uid
|
|
|
|
rv=0
|
|
else
|
|
error "Failed to shift task #${id} left."
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function move_right() { # move_right uid new_parent
|
|
local uid rv newparent sedrv
|
|
local id newparentid str toadd
|
|
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
rv=0
|
|
newparent=$2
|
|
if [[ $newparent == "-" ]]; then
|
|
newparent=""
|
|
newparentid=""
|
|
else
|
|
newparentid=$(getid $newparent)
|
|
fi
|
|
rv=0
|
|
|
|
taskparent[$id]="$newparent"
|
|
file="$dir/$uid.vcf"
|
|
if [[ -z $newparent ]]; then
|
|
sed -i "/^RELATED-TO.*/d" "$file"
|
|
sedrv=$?
|
|
else
|
|
if grep -q ^RELATED-TO "$file"; then
|
|
sed -i "s/^RELATED-TO.*/RELATED-TO;RELTYPE=PARENT:${taskparent[$id]}/" "$file"
|
|
sedrv=$?
|
|
else
|
|
str="END:VTODO"
|
|
toadd="RELATED-TO;RELTYPE=PARENT:${taskparent[$id]}"
|
|
sed -i "/$str/i $toadd\n" "$file"
|
|
sedrv=$?
|
|
fi
|
|
fi
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
if [[ -z $newparent ]]; then
|
|
str="to toplevel."
|
|
else
|
|
str="underneath #${newparentid} ('${tasksum[$newparentid]}')."
|
|
fi
|
|
inform "Shifted task #${id} $str"
|
|
loadtask "$file"
|
|
|
|
showit ${taskparent[$id]} $uid
|
|
|
|
rv=0
|
|
else
|
|
if [[ -z $newparent ]]; then
|
|
str="to toplevel."
|
|
else
|
|
str="under #${newparentid}."
|
|
fi
|
|
error "Failed to shift task #${id} $str"
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function setnote() { # setnote uid new note goes here
|
|
local uid newnote sedrv rv file str
|
|
local id
|
|
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
rv=0
|
|
shift
|
|
newnote="$*"
|
|
|
|
file="$dir/$uid.vcf"
|
|
|
|
if grep -q ^DESCRIPTION: "$file"; then
|
|
sed -i "s/^DESCRIPTION:.*/DESCRIPTION:$newnote/" "$file"
|
|
sedrv=$?
|
|
else
|
|
str="END:VTODO"
|
|
toadd="DESCRIPTION:$newnote"
|
|
sed -i "/$str/i $toadd\n" "$file"
|
|
sedrv=$?
|
|
fi
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
if [[ -z $newnote ]]; then
|
|
inform "Removed note from task #${id}."
|
|
else
|
|
inform "Set note of task #${id} to '$newnote'."
|
|
fi
|
|
loadtask "$file"
|
|
rv=0
|
|
else
|
|
error "Failed to set note of task #${id}."
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function modtag() { # setnote uid add|del new tag goes here
|
|
local uid arg sedrv rv file str
|
|
local id inform x catarr newcat
|
|
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
rv=0
|
|
shift
|
|
inform=$1
|
|
shift
|
|
arg="$*"
|
|
|
|
file="$dir/$uid.vcf"
|
|
|
|
if [[ -z $arg ]]; then
|
|
if [[ $inform == "del" ]]; then
|
|
sed -i "/^CATEGORIES:/d" "$file"
|
|
sedrv=$?
|
|
else
|
|
error "No tag specified to add."
|
|
return 1
|
|
fi
|
|
else
|
|
IFS=","
|
|
catarr=( ${taskcats[$id]} )
|
|
IFS=" "
|
|
for x in ${!catarr[@]} ; do
|
|
if [[ $inform == "add" || ${catarr[$x]} != $arg ]]; then
|
|
[[ -z $newcat ]] && newcat="${catarr[$x]}" || newcat="$newcat,${catarr[$x]}"
|
|
fi
|
|
done
|
|
if [[ $inform == "add" ]]; then
|
|
[[ -z $newcat ]] && newcat="${arg}" || newcat="$newcat,$arg"
|
|
|
|
fi
|
|
|
|
if grep -q ^CATEGORIES: "$file"; then
|
|
sed -i "s/^CATEGORIES:.*/CATEGORIES:${newcat}/" "$file"
|
|
sedrv=$?
|
|
else
|
|
str="END:VTODO"
|
|
toadd="CATEGORIES:${newcat}"
|
|
sed -i "/$str/i $toadd\n" "$file"
|
|
sedrv=$?
|
|
fi
|
|
fi
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
if [[ $inform == "del" ]]; then
|
|
if [[ -z $arg ]]; then
|
|
inform "Removed all tags from task #${id}."
|
|
else
|
|
inform "Removed tag '$arg' from task #${id}."
|
|
fi
|
|
else
|
|
inform "Added tag '$arg' to task #${id}."
|
|
fi
|
|
loadtask "$file"
|
|
rv=0
|
|
else
|
|
error "Failed to set/remove tag on task #${id}."
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function rename() { # rename uid new name goes here
|
|
local uid newname sedrv rv file
|
|
local id
|
|
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
rv=0
|
|
shift
|
|
newname="$*"
|
|
|
|
file="$dir/$uid.vcf"
|
|
|
|
sed -i "s/^SUMMARY:.*/SUMMARY:$newname/" "$file"
|
|
sedrv=$?
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
inform "Renamed task #${id} to '$newname'."
|
|
loadtask "$file"
|
|
rv=0
|
|
else
|
|
error "failed to rename task #${id}."
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function sedmod() { # sedmod uid 'sed command'
|
|
local uid sedscript newname sedrv rv file
|
|
local id one two cur new
|
|
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
rv=0
|
|
shift
|
|
|
|
if [[ $# -ge 2 ]]; then
|
|
sedscript="s/$1/$2/g"
|
|
else
|
|
sedscript="$*"
|
|
fi
|
|
newname=$(echo "${tasksum[$id]}" | sed "$sedscript" 2>/dev/null )
|
|
if [[ $? -ne 0 ]]; then
|
|
error "Invalid sed script: $BOLD$sedscript"
|
|
return 1
|
|
fi
|
|
|
|
cur="${tasksum[$id]}"
|
|
new="${newname}"
|
|
if [[ $sedscript =~ ^s\/.*\/.*\/.*$ ]]; then
|
|
one=$(echo "$sedscript" | awk -F/ '{ print $2 }')
|
|
two=$(echo "$sedscript" | awk -F/ '{ print $3 }')
|
|
# ooo
|
|
cur=${cur/$one/$BOLD$STRIKE$RED$one^p^b}
|
|
new=${new/$two/$BOLD$GREEN$two^b}
|
|
fi
|
|
|
|
info "Current name: ${BOLD}${cur}"
|
|
info "New name: ${BOLD}${new}"
|
|
confirm "Is this correct"
|
|
if [[ $? -ne 0 ]]; then
|
|
inform "Aborted."
|
|
return 1
|
|
fi
|
|
|
|
file="$dir/$uid.vcf"
|
|
|
|
sed -i "s/^SUMMARY:.*/SUMMARY:$newname/" "$file"
|
|
sedrv=$?
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
inform "Renamed task #${id} to '$newname'."
|
|
loadtask "$file"
|
|
rv=0
|
|
else
|
|
error "failed to rename task #${id}."
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function toggle() {
|
|
local uid id rv children
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
|
|
# already done this one?
|
|
if [[ $processed == *\ $uid\ * || $failed == *\ $uid\ * ]]; then
|
|
return 1
|
|
fi
|
|
|
|
# not a parent?
|
|
children=$(getchildren $uid)
|
|
if [[ -z ${children} ]]; then
|
|
error "Can't fold/unfold task #${id} with no children."
|
|
return 1
|
|
fi
|
|
|
|
if [[ ${taskfolded[$id]} -eq 1 ]]; then
|
|
unfold $uid
|
|
rv=$?
|
|
else
|
|
fold $uid
|
|
rv=$?
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function fold() {
|
|
local uid id sedrv rv file str
|
|
local children
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
|
|
# not a parent?
|
|
children=$(getchildren $uid)
|
|
if [[ -z ${children} ]]; then
|
|
error "Can't fold task #${id} with no children."
|
|
return 1
|
|
fi
|
|
# already folded?
|
|
if [[ ${taskfolded[$id]} -eq 1 ]]; then
|
|
error "Task #${id} is already folded."
|
|
return 1
|
|
fi
|
|
|
|
file="$dir/$uid.vcf"
|
|
str="END:VTODO"
|
|
if grep -q ^X-OC-HIDESUBTASKS: "$file" ; then
|
|
sed -i "s/X-OC-HIDESUBTASKS:.*/X-OC-HIDESUBTASKS:1/" "$file"
|
|
sedrv=$?
|
|
else
|
|
sed -i "/$str/i X-OC-HIDESUBTASKS:1" "$file"
|
|
sedrv=$?
|
|
fi
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
inform "Folded task #${id}."
|
|
processed="$processed ${uid} "
|
|
loadtask "$file"
|
|
rv=0
|
|
else
|
|
failed="$failed ${uid} "
|
|
error "failed to fold task #${id}"
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function unfold() {
|
|
local uid id sedrv rv file str
|
|
local children
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
|
|
# not a parent?
|
|
children=$(getchildren $uid)
|
|
if [[ -z ${children} ]]; then
|
|
error "Can't unfold task #${id} with no children."
|
|
return 1
|
|
fi
|
|
|
|
# not folded?
|
|
if [[ ${taskfolded[$id]} -ne 1 ]]; then
|
|
error "Task #${id} is not folded."
|
|
return 1
|
|
fi
|
|
|
|
file="$dir/$uid.vcf"
|
|
str="END:VTODO"
|
|
if grep -q ^X-OC-HIDESUBTASKS: "$file" ; then
|
|
sed -i "s/X-OC-HIDESUBTASKS:.*/X-OC-HIDESUBTASKS:0/" "$file"
|
|
sedrv=$?
|
|
else
|
|
sed -i "/$str/i X-OC-HIDESUBTASKS:0" "$file"
|
|
sedrv=$?
|
|
fi
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
inform "Unfolded task #${id}."
|
|
processed="$processed ${uid} "
|
|
loadtask "$file"
|
|
rv=0
|
|
else
|
|
failed="$failed ${uid} "
|
|
error "failed to unfold task #${id}"
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function clearstatus() {
|
|
local file
|
|
file="$1"
|
|
[[ -e $file ]] && sed -i '/^COMPLETED:/d;/^STATUS:COMPLETED/d;/PERCENT-COMPLETE:/d;/^STATUS:IN-PROCESS:/d;/^STATUS:NEEDS-inform/d;/^$/d;/^REQUEST-STATUS:/d;/^STATUS:IN-PROCESS/d' "$file"
|
|
}
|
|
|
|
function markblocked() { # $1=uid $2=reason
|
|
local uid sedrv rv file toadd1 str now
|
|
local children childuid ischild noun id
|
|
uid=$1
|
|
reason="$2"
|
|
id=$(getid $uid)
|
|
[[ $# -gt 1 ]] && ischild=1 || ischild=0
|
|
[[ $ischild -eq 1 ]] && noun=subtask || noun=task
|
|
# already blocked?
|
|
if [[ ${taskchecked[$id]} -eq 3 ]]; then
|
|
if [[ ${taskblockreason[$id]} == "$reason" ]]; then
|
|
error "$noun #${id} is already blocked"
|
|
return 1
|
|
fi
|
|
fi
|
|
str="END:VTODO"
|
|
now=$(date -u +'%Y%m%dT%H%M%SZ')
|
|
|
|
file="$dir/$uid.vcf"
|
|
|
|
# first remove any existing completion
|
|
#sed -i '/^COMPLETED:/d;/^STATUS:COMPLETED/d;/PERCENT-COMPLETE:/d;/STATUS:IN-PROCESS:/d;/STATUS:NEEDS-inform/d' "$file"
|
|
clearstatus "$file"
|
|
# then add blocked
|
|
toadd1="STATUS:NEEDS-inform"
|
|
sed -i "/$str/i $toadd1\n" "$file"
|
|
sedrv=$?
|
|
if [[ -n $reason ]]; then
|
|
toadd2="REQUEST-STATUS:4.1;$reason"
|
|
sed -i "/$str/i $toadd2\n" "$file"
|
|
sedrv=$((sedrv + $?))
|
|
fi
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
local txt
|
|
txt="Marked $noun #${id} as blocked"
|
|
|
|
if [[ -n $reason ]]; then
|
|
txt="${txt} due to ${BOLD}$reason$PLAIN"
|
|
fi
|
|
txt="${txt}."
|
|
inform "$txt"
|
|
processed="$processed ${uid} "
|
|
loadtask "$file"
|
|
rv=0
|
|
# dont block children
|
|
#children=$(getchildren $uid)
|
|
#for childuid in ${children}; do
|
|
# markblocked $childuid noprint
|
|
# rv=$((rv + $?))
|
|
#done
|
|
#echo "file: $file"
|
|
#cat $file
|
|
else
|
|
failed="$failed ${uid} "
|
|
error "failed to mark $noun #${id} as blocked"
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function markinprogress() {
|
|
local uid sedrv rv file toadd1 str now
|
|
local children childuid ischild noun id
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
[[ $# -gt 1 ]] && ischild=1 || ischild=0
|
|
[[ $ischild -eq 1 ]] && noun=subtask || noun=task
|
|
# already in progress?
|
|
if [[ ${taskchecked[$id]} -eq 1 ]]; then
|
|
error "$noun #${id} is already in progress"
|
|
return 1
|
|
fi
|
|
str="END:VTODO"
|
|
now=$(date -u +'%Y%m%dT%H%M%SZ')
|
|
|
|
file="$dir/$uid.vcf"
|
|
|
|
# first remove any existing completion
|
|
#sed -i '/^COMPLETED:/d;/^STATUS:COMPLETED/d;/PERCENT-COMPLETE:/d;/STATUS:NEEDS-inform/d;' "$file"
|
|
clearstatus "$file"
|
|
|
|
# then add partial completion
|
|
toadd1="STATUS:IN-PROCESS"
|
|
sed -i "/$str/i $toadd1\n" "$file"
|
|
sedrv=$?
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
inform "Marked $noun #${id} as in-progress."
|
|
processed="$processed ${uid} "
|
|
loadtask "$file"
|
|
rv=0
|
|
children=$(getchildren $uid)
|
|
for childuid in ${children}; do
|
|
markinprogress $childuid noprint
|
|
rv=$((rv + $?))
|
|
done
|
|
else
|
|
failed="$failed ${uid} "
|
|
error "failed to mark $noun #${id} as in-progress"
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function markdone() {
|
|
local uid sedrv rv file toadd1 toadd2 toadd3 str now
|
|
local children childuid ischild noun id
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
[[ $# -gt 1 ]] && ischild=1 || ischild=0
|
|
[[ $ischild -eq 1 ]] && noun=subtask || noun=task
|
|
# already complete?
|
|
if [[ ${taskchecked[$id]} -eq 2 ]]; then
|
|
error "$noun #${id} is already completed"
|
|
return 1
|
|
fi
|
|
str="END:VTODO"
|
|
now=$(date -u +'%Y%m%dT%H%M%SZ')
|
|
toadd1="STATUS:COMPLETED"
|
|
toadd2="PERCENT-COMPLETE:100"
|
|
toadd3="COMPLETED:$now"
|
|
|
|
file="$dir/$uid.vcf"
|
|
|
|
clearstatus "$file"
|
|
sed -i "/$str/i $toadd1\n$toadd2\n$toadd3" "$file"
|
|
sedrv=$?
|
|
set_lastmodified $uid
|
|
sedrv=$((sedrv + $?))
|
|
if [[ $sedrv -eq 0 ]]; then
|
|
inform "Marked $noun #${id} as completed."
|
|
processed="$processed ${uid} "
|
|
loadtask "$file"
|
|
rv=0
|
|
children=$(getchildren $uid)
|
|
for childuid in ${children}; do
|
|
markdone $childuid noprint
|
|
rv=$((rv + $?))
|
|
done
|
|
else
|
|
failed="$failed ${uid} "
|
|
error "failed to mark $noun #${id} as completed"
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function viewtask() {
|
|
local uid id file
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
file="$dir/$uid.vcf"
|
|
|
|
inform "Viewing task #$id ($file):"
|
|
cat "$file" | sed -e 's/^/ /'
|
|
return 0
|
|
}
|
|
|
|
function randomuid() {
|
|
local dupe uid
|
|
dupe=1
|
|
while [[ $dupe -eq 1 ]]; do
|
|
uid=$(( $RANDOM % 4 + 1))
|
|
uid="$uid$(( $RANDOM % 5 + 1))"
|
|
while [[ ${#uid} -lt 19 ]]; do
|
|
uid="$uid$(( $RANDOM % 9 + 1))"
|
|
done
|
|
dupe=0
|
|
for x in ${!taskuid[@]}; do
|
|
if [[ $x == $uid ]]; then
|
|
dupe=1
|
|
break
|
|
fi
|
|
done
|
|
done
|
|
echo $uid
|
|
}
|
|
|
|
function addtask() {
|
|
local uid parent parentid parentuid file thisid
|
|
if [[ $1 == "-p" ]]; then
|
|
parentuid=$2
|
|
parentid=$(getid $parentuid)
|
|
shift 2
|
|
else
|
|
parentuid=""
|
|
fi
|
|
uid=$(randomuid)
|
|
thisid=$nextid
|
|
idmap[$thisid]=$uid
|
|
taskid[$thisid]=$thisid
|
|
taskuid[$thisid]=$uid
|
|
tasksum[$thisid]="$*"
|
|
taskdesc[$thisid]=""
|
|
taskchecked[$thisid]=0
|
|
taskfolded[$thisid]=0
|
|
taskparent[$thisid]=$parentuid
|
|
file="$dir/$uid.vcf"
|
|
now=$(date -u +'%Y%m%dT%H%M%SZ')
|
|
cat >"$file" <<EOF
|
|
BEGIN:VCALENDAR
|
|
VERSION:2.0
|
|
PRODID:+//IDN tasks.org//android-111003//EN
|
|
BEGIN:VTODO
|
|
DTSTAMP:$now
|
|
UID:$uid
|
|
CREATED:$now
|
|
LAST-MODIFIED:$now
|
|
SUMMARY:${tasksum[$thisid]}
|
|
EOF
|
|
if [[ ! -z ${taskparent[$thisid]} ]]; then
|
|
echo "RELATED-TO;RELTYPE=PARENT:${taskparent[$thisid]}" >>"$file"
|
|
fi
|
|
cat >>"$file" <<EOF2
|
|
PRIORITY:9
|
|
X-APPLE-SORT-ORDER:611376630
|
|
X-OC-HIDESUBTASKS:0
|
|
END:VTODO
|
|
END:VCALENDAR
|
|
EOF2
|
|
|
|
nextid=$((nextid + 1))
|
|
saveids
|
|
needtitle=1
|
|
if [[ -z ${parentuid} ]]; then
|
|
inform "Created new task #${taskid[$thisid]}."
|
|
showit $uid
|
|
else
|
|
inform "Created new subtask #${taskid[$thisid]} under '${tasksum[${parentid}]}'."
|
|
showit ${taskparent[$thisid]} $uid
|
|
fi
|
|
}
|
|
|
|
function nextline() {
|
|
local dummy morestring
|
|
morestring="<------ more ------>"
|
|
printed=$((printed + 1));
|
|
if [[ $USEPAGER -eq 1 ]]; then
|
|
if [[ $printed -ge $((rows - 2)) ]]; then
|
|
echo -en "${CYAN}${morestring}${PLAIN}"
|
|
read -n1 -s dummy
|
|
printf "\r%*s\r" ${#morestring}
|
|
printed=0
|
|
needtitle=1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function showit() {
|
|
local uid childuid children char hilite col wid screenwid id ilev
|
|
local children nchildren charcol
|
|
local notecol bcol bl br space pre end
|
|
local showkids
|
|
local reset catstr catcol lrogcol
|
|
local blockedcol blockedrcol blockstr
|
|
|
|
reset="$PLAIN"
|
|
catcol="\033[38;2;255;165;0m"
|
|
progcol="\033[38;2;212;0;255m"
|
|
blockedcol="\033[38;2;212;0;0m"
|
|
blockedrcol="$blockedcol$BOLD"
|
|
|
|
showkids=1
|
|
if [[ $1 == "--nochildren" ]]; then
|
|
showkids=0
|
|
shift
|
|
fi
|
|
|
|
uid=$1
|
|
id=$(getid $uid)
|
|
children=$(getchildren $uid)
|
|
|
|
childrenarray=( $children )
|
|
nchildren=${#childrenarray[@]}
|
|
|
|
if [[ $# -ge 2 ]]; then
|
|
hilite=$2
|
|
fi
|
|
|
|
if [[ $needtitle -eq 1 ]]; then
|
|
cols=$(tput cols)
|
|
screenwid=$((cols - $numwid - 2 - 1))
|
|
wid=$(echo "scale=0; 4 + ($maxindent * $indentamt) + $maxtaskwidth + 3" | bc)
|
|
[[ $wid -gt $screenwid ]] && wid=$screenwid
|
|
printf "$TOPTITLE%-${numwid}s %-${wid}s$reset\n" "ID" "Task"
|
|
needtitle=0
|
|
fi
|
|
|
|
col="$reset"
|
|
donecol="$GREY"
|
|
notecol="$YELLOW"
|
|
end=""
|
|
if [[ ${taskchecked[$id]} -eq 2 ]]; then
|
|
char="✓"
|
|
charcol="$GREEN"
|
|
col="$donecol$STRIKE"
|
|
notecol="$notecol$STRIKE"
|
|
elif [[ ${taskchecked[$id]} -eq 1 ]]; then
|
|
char="&"
|
|
charcol="$progcol"
|
|
end="${progcol}...${reset}"
|
|
elif [[ ${taskchecked[$id]} -eq 3 ]]; then
|
|
#char="🛑"
|
|
#char="⛔"
|
|
char="⬣"
|
|
charcol="$blockedcol"
|
|
col="$blockedcol"
|
|
else
|
|
char=" "
|
|
charcol="$GREEN"
|
|
fi
|
|
|
|
if [[ $uid == $hilite ]]; then
|
|
col="$col$HILITE"
|
|
notecol="$notecol$HILITE"
|
|
fi
|
|
|
|
if [[ $nchildren -gt 0 && ${taskfolded[$id]} -eq 1 ]]; then
|
|
bcol=$brackfold
|
|
bl="("
|
|
br=")"
|
|
#col="$col$FOLDBG"
|
|
space=" "
|
|
pre=""
|
|
end=" $end$reset$CYAN${BOLD}[$nchildren subtasks]"
|
|
if [[ $char == " " ]]; then
|
|
char="+"
|
|
charcol="$BOLD$CYAN"
|
|
fi
|
|
else
|
|
bcol=$brack
|
|
bl="["
|
|
br="]"
|
|
space=" "
|
|
pre=""
|
|
fi
|
|
|
|
if [[ -z ${taskblockreason[$id]} ]]; then
|
|
blockstr=""
|
|
else
|
|
blockstr="${blockedrcol} (${taskblockreason[$id]}$reset$blockedrcol)"
|
|
fi
|
|
if [[ -z ${taskcats[$id]} ]]; then
|
|
catstr=""
|
|
else
|
|
catstr="${catcol} (${taskcats[$id]}$reset$catcol)"
|
|
fi
|
|
|
|
printf "${LEFTTITLE}%-${numwid}d${reset}" ${id}
|
|
|
|
printf "$space$space"
|
|
|
|
if [[ $space == " " ]]; then
|
|
printf "%*s" $indent
|
|
elif [[ $indent -gt 0 ]]; then
|
|
for x in $(eval echo {1..${indent} ); do
|
|
echo -n "$space"
|
|
done
|
|
fi
|
|
|
|
ilev=$(getindentlevel $id)
|
|
|
|
printf "$reset"
|
|
printf "$bcol$bl$reset$charcol%s$reset$bcol$br$reset ${col}${pre}%s$end${reset}${blockstr}${reset}${catstr}${reset}\n" "${char}" "${tasksum[$id]}"
|
|
nextline
|
|
|
|
if [[ ! -z ${taskdesc[$id]} ]]; then
|
|
printf "${LEFTTITLE}%*s${reset} " $numwid
|
|
printf "%*s" $(( $indent + 4))
|
|
printf "${notecol}${ITALIC}%s${reset}\n" "${taskdesc[$id]}"
|
|
nextline
|
|
fi
|
|
|
|
if [[ $showkids -eq 1 && ${taskfolded[$id]} -ne 1 ]]; then
|
|
inc
|
|
for childuid in ${children}; do
|
|
showit $childuid $hilite
|
|
done
|
|
dec
|
|
fi
|
|
}
|
|
|
|
function addcmd() { # name usage description alt1|alt2|etc supportsall
|
|
local cmd usage desc alts
|
|
cmd="$1"
|
|
usage="$2"
|
|
desc="$3"
|
|
alts="$4"
|
|
|
|
[[ -z $valid_modes ]] && valid_modes=$cmd || valid_modes="$valid_modes $cmd"
|
|
eval "usage_${cmd}=\"${usage}\""
|
|
eval "desc_${cmd}=\"${desc}\""
|
|
eval "words_${cmd}=\"${alts}\""
|
|
|
|
if [[ $* == *SUPPORTS_ALL* ]]; then
|
|
if [[ -z $modes_that_support_all ]]; then
|
|
modes_that_support_all="$cmd"
|
|
else
|
|
modes_that_support_all="$modes_that_support_all|$cmd"
|
|
fi
|
|
fi
|
|
if [[ $* == *PASSIVE* ]]; then
|
|
if [[ -z $passive_modes ]]; then
|
|
passive_modes="$cmd"
|
|
else
|
|
passive_modes="$passive_modes|$cmd"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function sortuidlist() { # sort a list of task uids by task summng aries
|
|
local unsorted uidandsum withnl sorted_arr sorted unsorted_arr uid
|
|
unsorted="$1"
|
|
|
|
# change "123 456 789" to
|
|
# ("123#do something" "234#other task" "789#something#)
|
|
unsorted_arr=""
|
|
for uid in ${unsorted}; do
|
|
id=$(getid $uid)
|
|
unsorted_arr+=("${taskuid[$id]}#${tasksum[$id]}")
|
|
done
|
|
IFS=$'\n'
|
|
withnl=("${unsorted_arr[*]}")
|
|
sorted_arr=($(sort -df -t '#' -k2 <<<"${withnl[*]}"))
|
|
sorted=""
|
|
for uidandsum in ${sorted_arr[@]}; do
|
|
sorted="$sorted ${uidandsum%#*}"
|
|
done
|
|
unset IFS
|
|
echo "$sorted"
|
|
}
|
|
|
|
function getchildren() { # parentuid
|
|
local uid childuid children id
|
|
uid=$1
|
|
children=""
|
|
for id in ${!taskid[@]}; do
|
|
if [[ ${taskparent[$id]} == $uid ]]; then
|
|
children="$children ${taskuid[$id]}"
|
|
fi
|
|
done
|
|
[[ -z $children ]] && return 1
|
|
|
|
|
|
if [[ $WANTSORT -eq 1 ]]; then
|
|
children=$(sortuidlist "$children")
|
|
fi
|
|
echo "$children"
|
|
return 0
|
|
}
|
|
|
|
function makedir() {
|
|
local d
|
|
d="$1"
|
|
|
|
if [[ -d "$d" ]]; then
|
|
info "Directory $d already exists"
|
|
else
|
|
mkdir -p "${d}"
|
|
if [[ $? -eq 0 ]]; then
|
|
inform "Created directory ${BOLD}$d${PLAIN}"
|
|
else
|
|
error "Couldn't mkdir ${BOLD}$d${PLAIN}${RED}"
|
|
return 1
|
|
fi
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
function init() {
|
|
local gotvdconf=0 d url count cals cals_arr thiscal selcal
|
|
inform "Initialising configration"
|
|
|
|
if ! which -s vdirsyncer; then
|
|
error "Can't find ${BOLD}vdirsyncer${PLAIN}${RED} - please install it."
|
|
return 1
|
|
fi
|
|
if [[ -e $vdirsyncconfig ]]; then
|
|
confirm "Use existing vdirsyncer config in ${BOLD}$vdirsyncconfig${PLAIN}${MAGENTA}"
|
|
if [[ $? -eq 0 ]]; then
|
|
gotvdconf=1
|
|
fi
|
|
fi
|
|
|
|
if [[ $gotvdconf -ne 1 ]]; then
|
|
for d in "$vdirsyncdir" "$vdircaldir" "$vdirstatdir"; do
|
|
makedir "$d" || return 1
|
|
done
|
|
|
|
echo -e "${MAGENTA}Please provide the URL of your caldav server.${PLAIN}"
|
|
echo -en "${BOLD}${MAGENTA}==> ${PLAIN}"
|
|
read url
|
|
if [[ ! $url =~ ^http.* ]]; then
|
|
error "Provided URL 'url' appears invalid."
|
|
return 1
|
|
fi
|
|
echo -en "${BOLD}${MAGENTA}Caldav username: ${PLAIN}"
|
|
read u
|
|
echo -en "${BOLD}${MAGENTA}Caldav password: ${PLAIN}"
|
|
read -s p
|
|
|
|
cat >"${vdirsyncconfig}" <<EOF
|
|
[general]
|
|
status_path = "$vdirstatdir"
|
|
|
|
[pair cal]
|
|
a = "cal_local"
|
|
b = "cal_remote"
|
|
collections = ["from a", "from b"]
|
|
|
|
[storage cal_local]
|
|
type = "filesystem"
|
|
path = "$vdircaldir"
|
|
fileext = ".vcf"
|
|
|
|
[storage cal_remote]
|
|
type = "caldav"
|
|
url = "$url"
|
|
username = "$u"
|
|
password = "$p"
|
|
EOF
|
|
unset u
|
|
unset p
|
|
info "Created vdirsyncer config in ${vdirsyncconfig}"
|
|
fi
|
|
|
|
cals=$(ls -1q $vdircaldir 2>/dev/null)
|
|
count=$(echo "$cals" | wc -l | tr -d ' ')
|
|
|
|
if [[ $count -eq 0 ]]; then
|
|
inform "Running initial vdirsyncer discover..."
|
|
vdirsyncer discover
|
|
inform "Running initial vdirsyncer sync..."
|
|
vdirsyncer sync
|
|
fi
|
|
|
|
getcals count cals_arr
|
|
|
|
makedir "$confdir" || return 1
|
|
if [[ -e "${allcalsdir}" ]] ; then
|
|
warn "Calendar symlink ${allcalsdir} already exists. Not overwriting it."
|
|
else
|
|
ln -s "${vdircaldir}" "${allcalsdir}"
|
|
inform "Created symlink to $vdircaldir"
|
|
fi
|
|
if [[ ! -e "${defaultcalfile}" ]] ; then
|
|
selcal=""
|
|
if [[ $count -gt 0 ]]; then
|
|
local sel
|
|
|
|
echo
|
|
for d in ${!cals_arr[@]}; do
|
|
echo -e "${MAGENTA}${BOLD}$((d + 1)). ${PLAIN}${MAGENTA}${cals_arr[$d]}${PLAIN}"
|
|
done
|
|
sel=-1
|
|
while [[ $sel -le 0 || $sel -gt ${#cals_arr[@]} ]]; do
|
|
echo -en "${MAGENTA}${BOLD}Select calendar: ${PLAIN}"
|
|
read sel
|
|
done
|
|
selcal=${cals_arr[$(($sel - 1))]}
|
|
echo "$selcal" >"$defaultcalfile"
|
|
else
|
|
warn "No calendars found."
|
|
fi
|
|
fi
|
|
|
|
if [[ -z $selcal ]]; then
|
|
warn "No calendar selected "
|
|
echo "${YELLOW}Please put default calendar name in ${defaultcalfile}"
|
|
echo "${YELLOW}then: touch \"${idfile}_\$(cat $defaultcalfile)\""
|
|
selcal=""
|
|
else
|
|
selcal=${selcal// /_}
|
|
if [[ -e "${idfile}_${selcal}" ]]; then
|
|
warn "ID mapping file for $selcal already exists. Not overwriting it."
|
|
else
|
|
touch "${idfile}_${selcal}"
|
|
fi
|
|
fi
|
|
|
|
info "Initialisation complete"
|
|
[[ -z $selcal ]] && selcal="<none>"
|
|
echo -e "${CYAN} vdirsyncer config is in ${BOLD}${vdirsyncdir}${PLAIN}"
|
|
echo -e "${CYAN} task_cli config is in ${BOLD}${confdir}${PLAIN}"
|
|
echo -e "${CYAN} selected calendar is '${BOLD}${selcal}${PLAIN}'"
|
|
echo
|
|
}
|
|
|
|
function tasksync() {
|
|
local res upcount downcount noun="Sync" mode="sync"
|
|
|
|
while [[ $# -ge 1 ]]; do
|
|
if [[ $1 == "-a" ]]; then
|
|
noun="Auto-sync"
|
|
elif [[ $1 == "push" ]]; then
|
|
mode="push"
|
|
noun="Push"
|
|
elif [[ $1 == "pull" ]]; then
|
|
mode="pull"
|
|
noun="Pull"
|
|
fi
|
|
shift
|
|
done
|
|
notify "${noun}ing tasks"
|
|
|
|
# adjust conflict resolution
|
|
## remove existing setting
|
|
sed -i '/^conflict/ s/^/#/' $vdirsyncconfig
|
|
## add new one
|
|
if [[ $mode == "push" ]]; then
|
|
sed -i '/conflict.*a wins/ s/^#\+//' $vdirsyncconfig
|
|
elif [[ $mode == "pull" ]]; then
|
|
sed -i '/conflict.*b wins/ s/^#\+//' $vdirsyncconfig
|
|
fi
|
|
|
|
res=$(${VDIRSYNCER} sync 2>&1)
|
|
rv=$?
|
|
[[ $rv -eq 0 ]] && ok || fail
|
|
|
|
if [[ $rv -eq 0 ]]; then
|
|
upcount=$(echo "$res" | grep -v ^Sync | grep -c ${VDS_REMOTE})
|
|
downcount=$(echo "$res" | grep -v ^Sync | grep -c ${VDS_LOCAL})
|
|
|
|
[[ $upcount -eq 1 ]] && upess="" || upess="s"
|
|
[[ $downcount -eq 1 ]] && downess="" || downess="s"
|
|
|
|
if [[ $upcount -eq 0 && $downcount -eq 0 ]]; then
|
|
inform "$noun complete - no changes."
|
|
elif [[ $upcount -eq 0 ]]; then
|
|
inform "$noun complete - $downcount change$downess pulled."
|
|
elif [[ $downcount -eq 0 ]]; then
|
|
inform "$noun complete - $upcount change$upess pushed."
|
|
else
|
|
inform "$noun complete - $upcount change$upess pushed, $downcount change$downess pulled."
|
|
fi
|
|
else
|
|
errmsg="$noun failed."
|
|
if [[ $mode == "sync" ]]; then
|
|
errmsg="${errmsg} Try ^bt push^p or ^bt pull^p."
|
|
fi
|
|
error "$errmsg"
|
|
if [[ $mode != "sync" ]]; then
|
|
cecho -s "$RED" "^bDetails:^p"
|
|
cecho -s "$RED" "$res" | sed -e 's/^/ /'
|
|
echo
|
|
cecho -s "$RED" "^bConflict settings:^p"
|
|
errmsg=$(grep "conflict" $vdirsyncconfig 2>&1)
|
|
cecho -s "$YELLOW" "$errmsg" | sed -e 's/^/ /'
|
|
fi
|
|
fi
|
|
|
|
## remove conflict setting again
|
|
sed -i '/^conflict/ s/^/#/' $vdirsyncconfig
|
|
|
|
}
|
|
|
|
function getcals() { # countvar arrayvar
|
|
local loc_cals loc_count thiscal
|
|
local countvar arrayvar
|
|
countvar=$1
|
|
arrayvar=$2
|
|
loc_cals=$(ls -1q $vdircaldir 2>/dev/null)
|
|
loc_count=$(echo "$loc_cals" | wc -l | tr -d ' ')
|
|
eval "$countvar=$loc_count"
|
|
eval "$arrayvar=()"
|
|
while read thiscal; do
|
|
eval "$arrayvar+=(\"$thiscal\")"
|
|
done <<< "$loc_cals"
|
|
}
|
|
|
|
function getcalname() { # cal_name_or_number
|
|
local lc larr d found=""
|
|
getcals lc larr
|
|
for d in ${!larr[@]}; do
|
|
if [[ ${larr[$d]} == $1 ]]; then
|
|
found="${larr[$d]}"
|
|
break
|
|
elif [[ $1 =~ ^[0-9]+$ && $d -eq $(($1 - 1)) ]]; then
|
|
# make this the default
|
|
found="${larr[$d]}"
|
|
break
|
|
fi
|
|
done
|
|
echo "$found"
|
|
if [[ -z $found ]]; then
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
|
|
# Should be in config file:
|
|
#basedir=/Users/rpearce/scripts/t
|
|
#dir=$basedir/docs
|
|
#idfile=$basedir/idmappings.txt
|
|
#VDS_LOCAL=$cal_local
|
|
#VDS_REMOTE=$cal_remote
|
|
#VDIRSYNCER=/usr/local/bin/vdirsyncer
|
|
#VDS_BASEDIR="~/.config/vdirsyncer"
|
|
#VDS_CONFIG="${VDS_BASEDIR}/config"
|
|
#VDS_STATUSDIR="${VDS_BASEDIR}/status"
|
|
#VDS_CALDIR="${VDS_BASEDIR}/calendar"
|
|
|
|
# Base vdirsycner config in ${VDS_CONFIG}:
|
|
#[general]
|
|
#status_path = "${VDS_STATUSDIR}/"
|
|
#
|
|
#[pair cal]
|
|
#a = "${VDS_LOCAL}"
|
|
#b = "${VDS_REMOTE}"
|
|
#collections = ["from a", "from b"]
|
|
#
|
|
#[storage ${VDS_LOCAL}]
|
|
#type = "filesystem"
|
|
#path = "${VDS_CALDIR}/"
|
|
#fileext = ".vcf"
|
|
#
|
|
#[storage ${VDS_REMOTE}]
|
|
#type = "caldav"
|
|
#url = "https://tasks.haven.family/caldav.php/haven/"
|
|
#username = "xxx"
|
|
#password = "xxx"
|
|
|
|
###
|
|
#
|
|
#
|
|
# Ask:
|
|
# - where is this script located?
|
|
# - remote<->local task id mapping file? [default: $thisdir/idmappings.txt ]
|
|
# - url for your remote caldav
|
|
# - username for your remote caldav
|
|
# - password for your remote caldav
|
|
# - vdirsyncer dir [default ${HOME}/.config/vdirsyncer]
|
|
# - vdirsyncer status dir [default ${vdirsyncer_dir/status}
|
|
# - vdirsyncer config file [default ${vdirsyncer_dir}/config]
|
|
# - vdirsyncer calendar dir [default ${vdirsyncer_dir/calendar}
|
|
|
|
###### - vdirsyncer name for your local calender [default cal_local]
|
|
###### - vdirsyncer name for your remote calender [default cal_remote]
|
|
DEFAULT_VDS_LOCAL=cal_local
|
|
DEFAULT_VDS_REMOTE=cal_remote
|
|
basedir=/Users/rpearce/code/task_cli
|
|
confdir="${HOME}/.task_cli"
|
|
cachedir="${confdir}/cache"
|
|
allcalsdir="$confdir/allcals" # symlink to vdirsyncer/calendar/
|
|
defaultcalfile="$confdir/defaultcal"
|
|
idfile="$confdir/idmappings"
|
|
nextid=1
|
|
indent=0
|
|
indentamt=4
|
|
needsave=0
|
|
mode=list
|
|
modes_that_support_all=""
|
|
passive_modes="" # modes that do not change data
|
|
|
|
vdirsyncdir="${HOME}/.config/vdirsyncer"
|
|
vdircaldir="${HOME}/.config/vdirsyncer/calendar"
|
|
vdirstatdir="${HOME}/.config/vdirsyncer/status"
|
|
vdirsyncconfig="${vdirsyncdir}/config"
|
|
|
|
# for pager
|
|
printed=0
|
|
rows=$(tput lines)
|
|
|
|
addcmd "list" "list [id1] .. [idX]" "List [just the specified] tasks" "list|ls|show" SUPPORTS_ALL PASSIVE
|
|
addcmd "fold" "fold <id>" "Fold a parent task (hide its children)" "fold|f|zc" SUPPORTS_ALL
|
|
addcmd "unfold" "unfold <id>" "Unfold a parent task (show its children)" "unfold|u|zo" SUPPORTS_ALL
|
|
addcmd "toggle" "toggle <id>" "Fold/Unfold a parent task" "toggle|z|zz" SUPPORTS_ALL
|
|
addcmd "done" "done <taskid>" "Complete a task" "done|x|complete" SUPPORTS_ALL
|
|
addcmd "notdone" "notdone <taskid>" "Uncomplete a task" "notdone|o|incomplete|uncomplete|clear" SUPPORTS_ALL
|
|
addcmd "inprogress" "inprogress <taskid>" "Mark a task as in progress" "inprogress|progress|inp|prog|i|p" SUPPORTS_ALL
|
|
addcmd "blocked" "blocked <taskid>" "Mark a task as blocked" "blocked|block|b" SUPPORTS_ALL
|
|
addcmd "add" "add [parent] <name>" "Add a new task [as subtask of parent]" "a|add|new|create"
|
|
addcmd "del" "del [id1] .. [idX]" "Delete given task(s)" "del|rm|delete"
|
|
addcmd "cleanup" "cleanup" "Delete all completed tasks" "cleanup|clean|flush|dc"
|
|
addcmd "rename" "rename <id> <newname>" "Rename given task" "rename"
|
|
addcmd "mod" "mod <id> (<sed_script>|<lookfor> <replacewith>)" "Rename given task using sed script" "mod|sed"
|
|
addcmd "left" "left <id>" "Decrease indent of given task" "left|h|out|up"
|
|
addcmd "right" "right <id> <parent>" "Move task below the given parent" "right|l|mv|in"
|
|
addcmd "note" "note <id> <notes>" "Change notes for given task" "note|desc|description|comment"
|
|
addcmd "tag" "tag <id> <tag>" "Add a tag to the given task" "tag|t"
|
|
addcmd "untag" "untag <id> <tag>" "Remove a tag from the given task" "untag|ut"
|
|
addcmd "sync" "sync" "Sync tasks using vdirsyncer" "sync"
|
|
addcmd "push" "push" "Push all tasks to server" "push"
|
|
addcmd "view" "view <id>" "Show detailed info for given task" "v|view|info|vcal" PASSIVE
|
|
addcmd "showcals" "showcals" "List available calendars" "cals|showcals|cls" PASSIVE
|
|
addcmd "setcal" "setcal" "Set the default calendar." "setcal|sc"
|
|
|
|
[[ ! -e $cachedir ]] && mkdir -p "$cachedir"
|
|
|
|
DEBUG=0
|
|
TESTMODE=0
|
|
AUTOSYNC=0
|
|
CALID=$(cat "$confdir/defaultcal")
|
|
WANTSORT=0
|
|
USEPAGER=1
|
|
MAINTMODE=0
|
|
|
|
ARGS="fhic:dmnsStyl:r:p"
|
|
while getopts "$ARGS" i; do
|
|
case "$i" in
|
|
m)
|
|
MAINTMODE=1
|
|
;;
|
|
h)
|
|
usage;
|
|
exit 1;
|
|
;;
|
|
i)
|
|
init
|
|
exit $?
|
|
;;
|
|
c)
|
|
CALID="$OPTARG"
|
|
;;
|
|
l)
|
|
VDS_LOCAL="$OPTARG"
|
|
;;
|
|
n)
|
|
USEPAGER=0
|
|
;;
|
|
p)
|
|
__PROFILE=1
|
|
;;
|
|
r)
|
|
VDS_REMOTE="$OPTARG"
|
|
;;
|
|
d)
|
|
DEBUG=1
|
|
;;
|
|
s)
|
|
AUTOSYNC=1
|
|
;;
|
|
S)
|
|
WANTSORT=1
|
|
;;
|
|
t)
|
|
DEBUG=1
|
|
TESTMODE=1
|
|
;;
|
|
y|f)
|
|
AUTOYES=1
|
|
;;
|
|
*)
|
|
error "invalid argument: $i";
|
|
usage;
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
shift $((OPTIND - 1))
|
|
|
|
# Make sure vdirsyncer is installed
|
|
if ! which -s vdirsyncer; then
|
|
error "Can't find ${BOLD}vdirsyncer${PLAIN}${RED} - please install it."
|
|
return 1
|
|
fi
|
|
VDIRSYNCER=$(which vdirsyncer)
|
|
|
|
# validate CALID
|
|
foundcal=$(getcalname "$CALID")
|
|
if [[ -n $foundcal ]]; then
|
|
CALID="$foundcal"
|
|
else
|
|
error "Calendar '$CALID' does not exist."
|
|
exit 1
|
|
fi
|
|
|
|
dir=$allcalsdir/$CALID
|
|
idfile="$confdir/idmappings_${CALID// /_}"
|
|
|
|
if [[ ! -e $idfile ]]; then
|
|
warn "No ID mapping file found for calendar '$CALID' ($idfile)"
|
|
confirm "Create one from existing tasks?"
|
|
if [[ $? -eq 0 ]]; then
|
|
loadtasks -v
|
|
saveids -v || exit 1
|
|
else
|
|
die
|
|
fi
|
|
fi
|
|
|
|
[[ -z $VDS_LOCAL ]] && VDS_LOCAL="$DEFAULT_VDS_LOCAL"
|
|
[[ -z $VDS_REMOTE ]] && VDS_REMOTE="$DEFAULT_VDS_REMOTE"
|
|
|
|
mode=""
|
|
for x in $valid_modes; do
|
|
thisone="words_$x"
|
|
tomatch=${!thisone}
|
|
if [[ $1 =~ ^(${tomatch})$ ]]; then
|
|
mode=$x
|
|
shift
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ -z $mode ]]; then
|
|
mode=list
|
|
fi
|
|
|
|
# Commands that don't need tasks loaded
|
|
if [[ $mode == "sync" ]]; then
|
|
tasksync
|
|
exit $rv
|
|
elif [[ $mode == "push" ]]; then
|
|
tasksync push
|
|
exit $rv
|
|
fi
|
|
|
|
notify "Processing"
|
|
loadids || exit 1
|
|
loadtasks || exit 1
|
|
ok
|
|
|
|
|
|
if [[ $MAINTMODE -eq 1 ]]; then
|
|
# check for id mappings with missing tasks
|
|
notify "Checking ID mappings"
|
|
nbad=0
|
|
for x in ${!idmap[@]}; do
|
|
if (! IFS=$'\n'; echo "${taskid[*]}" ) | grep -qFx "${idmap[$x]}" ; then
|
|
warn "idmap has missing task ${idmap[$x]}"
|
|
tofix="$tofix $x "
|
|
nbad=$(( nbad + 1))
|
|
fi
|
|
done
|
|
[[ $nbad -ge 1 ]] && partial "$nbad need fixing" || ok
|
|
|
|
if [[ -n $tofix ]]; then
|
|
notify "Fixing ID mappings for missing tasks"
|
|
for x in $tofix; do
|
|
[[ $b5 -eq 1 ]] && unset i_idmap[${idmap[$x]}];
|
|
unset idmap[$x]
|
|
done
|
|
ok
|
|
idfilebackup="$idfile.bak.$(date +%Y%m%d%H%M%S)"
|
|
inform "idmap backup is in $idfilebackup"
|
|
cp -a "$idfile" "$idfilebackup"
|
|
saveids
|
|
fi
|
|
exit 0
|
|
fi
|
|
|
|
|
|
[[ $TESTMODE -eq 1 ]] && exit 1
|
|
|
|
[[ $needsave -eq 1 ]] && saveids
|
|
|
|
# single-task commands
|
|
if [[ $mode == "add" ]]; then
|
|
if [[ $1 =~ ^[0-9]+$ ]]; then
|
|
parentid=$1
|
|
parentuid=$(getuid_witherror $parentid "parent task")
|
|
shift 1
|
|
else
|
|
parentid=""
|
|
parentuid=""
|
|
fi
|
|
newtasksum="$*"
|
|
|
|
if [[ -z $parentuid ]]; then
|
|
addtask "$newtasksum"
|
|
else
|
|
addtask -p $parentuid "$newtasksum"
|
|
fi
|
|
elif [[ $mode == "showcals" ]]; then
|
|
getcals count cals_arr
|
|
if [[ $count -gt 0 ]]; then
|
|
echo -e "${BOLD}${MAGENTA}Available Calendars:$PLAIN"
|
|
for d in ${!cals_arr[@]}; do
|
|
echo -en "${MAGENTA}${BOLD}$((d + 1)). ${PLAIN}${MAGENTA}${cals_arr[$d]}${PLAIN}"
|
|
[[ ${cals_arr[$d]} == $CALID ]] && echo -e "$BOLD$MAGENTA <- default$PLAIN" || echo
|
|
done
|
|
else
|
|
error "No calendars found."
|
|
fi
|
|
elif [[ $mode == "setcal" ]]; then
|
|
if [[ -z $1 ]]; then
|
|
error "No calendar name provided."
|
|
exit 1
|
|
fi
|
|
foundcal=$(getcalname $1)
|
|
if [[ -n $foundcal ]]; then
|
|
inform "Default calendar set to '${foundcal}'"
|
|
echo "${foundcal}" > "$defaultcalfile"
|
|
else
|
|
error "Calendar '$1' not found"
|
|
fi
|
|
elif [[ $mode == "rename" ]]; then
|
|
uid=$(getuid_witherror $1)
|
|
shift
|
|
rename $uid "$*"
|
|
elif [[ $mode == "mod" ]]; then
|
|
uid=$(getuid_witherror $1)
|
|
shift
|
|
if [[ $# -ge 2 ]]; then
|
|
sedmod $uid "$1" "$2"
|
|
else
|
|
sedmod $uid "$*"
|
|
fi
|
|
elif [[ $mode == "note" ]]; then
|
|
uid=$(getuid_witherror $1)
|
|
shift
|
|
setnote $uid "$*"
|
|
elif [[ $mode == "tag" ]]; then
|
|
uid=$(getuid_witherror $1)
|
|
shift
|
|
modtag $uid add "$*"
|
|
elif [[ $mode == "untag" ]]; then
|
|
uid=$(getuid_witherror $1)
|
|
shift
|
|
modtag $uid del "$*"
|
|
elif [[ $mode == "left" ]]; then
|
|
uid=$(getuid_witherror $1)
|
|
shift
|
|
move_left $uid
|
|
elif [[ $mode == "blocked" ]]; then
|
|
uid=$(getuid_witherror $1)
|
|
shift
|
|
markblocked $uid "$*"
|
|
elif [[ $mode == "right" ]]; then
|
|
pid=$BASH_ARGV
|
|
if [[ $puid != "-" ]]; then
|
|
puid=$(getuid_witherror $pid "parent task")
|
|
fi
|
|
|
|
while [[ $# -gt 1 ]]; do
|
|
if [[ $1 =~ ^[0-9]+-[0-9]+$ ]]; then
|
|
s=${1%-*}
|
|
e=${1#*-}
|
|
for ((w2=s; w2<=e; w2++)); do
|
|
uid=$(getuid_witherror $w2)
|
|
move_right $uid $puid
|
|
done
|
|
else
|
|
uid=$(getuid_witherror $1)
|
|
move_right $uid $puid
|
|
fi
|
|
shift
|
|
done
|
|
elif [[ $mode == "view" ]]; then
|
|
uid=$(getuid_witherror $1)
|
|
viewtask $uid
|
|
elif [[ $mode == "cleanup" ]]; then
|
|
inform "Deleting all completed tasks."
|
|
for id in ${!taskuid[@]}; do
|
|
uid=$(getuid $id)
|
|
[[ ${taskchecked[$id]} -eq 2 ]] && delq_add $uid
|
|
done
|
|
|
|
delq_process
|
|
# show results
|
|
if [[ ! -z $processed ]]; then
|
|
for uid in ${processed}; do
|
|
showit $uid
|
|
done
|
|
fi
|
|
else
|
|
# multi-task commands
|
|
filter=$*
|
|
|
|
uids=""
|
|
if [[ -z $filter ]]; then
|
|
#for id in ${!taskid[@]}; do
|
|
# [[ -z ${taskparent[$id]} ]] && uids="$uids ${taskuid[$id]}"
|
|
#done
|
|
filter=all
|
|
fi
|
|
if [[ $filter == "all" ]]; then
|
|
na=0
|
|
if [[ ! $mode =~ $modes_that_support_all ]]; then
|
|
error "Command '$mode' doesn't support 'all'"
|
|
exit 1
|
|
fi
|
|
kids=""
|
|
for id in ${!taskid[@]}; do
|
|
match=0
|
|
uid=$(getuid $id)
|
|
children=$(getchildren $uid)
|
|
[[ -z ${taskparent[$id]} ]] && hasparent=0 || hasparent=1
|
|
[[ -z $children ]] && haschildren=0 || haschildren=1
|
|
if [[ $mode == "fold" && $haschildren -eq 1 && ${taskfolded[$id]} -ne 1 ]]; then
|
|
match=1
|
|
kids="--nochildren"
|
|
elif [[ $mode == "unfold" && ${taskfolded[$id]} -eq 1 ]]; then
|
|
match=1
|
|
kids="--nochildren"
|
|
elif [[ $mode == "done" && ${taskchecked[$id]} -ne 2 ]]; then
|
|
match=1
|
|
elif [[ $mode == "notdone" && ${taskchecked[$id]} -ne 0 ]]; then
|
|
match=1
|
|
elif [[ $mode == "inprogress" && ${taskchecked[$id]} -ne 1 ]]; then
|
|
match=1
|
|
elif [[ $mode == "blocked" && ${taskchecked[$id]} -ne 3 ]]; then
|
|
match=1
|
|
elif [[ $mode == "list" && $hasparent -eq 0 ]]; then
|
|
match=1
|
|
fi
|
|
|
|
if [[ $match -eq 1 ]]; then
|
|
uids="$uids ${taskuid[$id]}"
|
|
fi
|
|
done
|
|
if [[ $WANTSORT -eq 1 ]]; then
|
|
uids=$(sortuidlist "$uids")
|
|
fi
|
|
if [[ ! $mode =~ $passive_modes ]]; then
|
|
info "About to run '$mode' on the following tasks:"
|
|
needtitle=0
|
|
for uid in $uids; do
|
|
wantshow=0
|
|
id=$(getid $uid)
|
|
|
|
# don't show if it's a subtask of one already in the list
|
|
if [[ -z ${taskparent[$id]} ]]; then
|
|
wantshow=1
|
|
elif [[ $uids == *${taskparent[$id]}\ * ]]; then
|
|
wantshow=0
|
|
else
|
|
wantshow=1
|
|
fi
|
|
|
|
if [[ $wantshow -eq 1 ]]; then
|
|
showit $kids $uid | sed -e 's/^/ /'
|
|
fi
|
|
done
|
|
confirm "Really proceed"
|
|
if [[ $? -ne 0 ]]; then
|
|
inform "Aborted."
|
|
exit 1
|
|
fi
|
|
fi
|
|
elif [[ $filter =~ ^[0-9\ \-]+$ ]]; then
|
|
# list of task IDs
|
|
for wantid in $filter; do
|
|
if [[ $wantid =~ ^[0-9]+-[0-9]+$ ]]; then
|
|
s=${wantid%-*}
|
|
e=${wantid#*-}
|
|
for ((w2=s; w2<=e; w2++)); do
|
|
uids="$uids ${taskuid[$w2]}"
|
|
done
|
|
else
|
|
uids="$uids ${taskuid[$wantid]}"
|
|
fi
|
|
done
|
|
else
|
|
# text to match
|
|
a=$( echo ${filter} | tr 'A-Z' 'a-z')
|
|
for id in ${!taskid[@]}; do
|
|
b=$( echo ${tasksum[$id]} | tr 'A-Z' 'a-z')
|
|
if [[ ${b} == *"${a}"* ]]; then
|
|
uids="$uids ${taskuid[$id]}"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
|
|
needtitle=1
|
|
if [[ -z $uids ]]; then
|
|
error "no matching tasks found."
|
|
exit 1
|
|
else
|
|
processed=""
|
|
failed=""
|
|
for uid in ${uids}; do
|
|
if [[ $mode == "list" ]]; then
|
|
showit $uid
|
|
elif [[ $mode == "done" ]]; then
|
|
markdone $uid
|
|
elif [[ $mode == "notdone" ]]; then
|
|
marknotdone $uid
|
|
elif [[ $mode == "inprogress" ]]; then
|
|
markinprogress $uid
|
|
elif [[ $mode == "fold" ]]; then
|
|
fold $uid
|
|
elif [[ $mode == "unfold" ]]; then
|
|
unfold $uid
|
|
elif [[ $mode == "toggle" ]]; then
|
|
toggle $uid
|
|
elif [[ $mode == "del" ]]; then
|
|
delq_add $uid
|
|
fi
|
|
done
|
|
|
|
# do queued informs
|
|
if [[ $mode == "del" ]]; then
|
|
delq_process
|
|
fi
|
|
# remove duplicates where list contains both parent
|
|
# and child of parent
|
|
if [[ ! -z $processed ]]; then
|
|
for uid in ${processed}; do
|
|
thisid=$(getid $uid)
|
|
parentuid=${taskparent[$thisid]}
|
|
# is this NOT the child of something in the list
|
|
if [[ -z $parentuid || $processed != *$parentuid* ]]; then
|
|
newprocessed="$newprocessed $uid"
|
|
fi
|
|
done
|
|
processed="$newprocessed"
|
|
fi
|
|
|
|
if [[ ! -z $processed ]]; then
|
|
# show results
|
|
for uid in ${processed}; do
|
|
showit $uid
|
|
done
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
|
|
if [[ $AUTOSYNC -eq 1 && $mode != "sync" ]]; then
|
|
tasksync -a
|
|
fi
|