diff --git a/nag b/nag index 305149c..e3b52e2 100755 --- a/nag +++ b/nag @@ -654,6 +654,42 @@ _is_muted() { return 1 } +# Usage: +# _remove_snoozed_entry +# +# Description: +# Remove the entry whose key matches from the snoozed file. +# Returns 1 if no matching entry was found or the file does not exist. +_remove_snoozed_entry() { + local _key="${1}" + [[ -f "${_SNOOZED_FILE}" ]] || return 1 + + local -a _keep=() + local _removed=0 _entry + while IFS= read -r _entry || [[ -n "${_entry}" ]] + do + [[ -n "${_entry}" ]] || continue + local _entry_key="${_entry%%$'\t'*}" + if [[ "${_entry_key}" == "${_key}" ]] + then + _removed=1 + else + _keep+=("${_entry}") + fi + done < "${_SNOOZED_FILE}" + + (( _removed )) || return 1 + + if (( ${#_keep[@]} == 0 )) + then + rm -f "${_SNOOZED_FILE}" + else + printf "%s\n" "${_keep[@]}" > "${_SNOOZED_FILE}.tmp" + mv -f "${_SNOOZED_FILE}.tmp" "${_SNOOZED_FILE}" + fi + return 0 +} + # Usage: # _tag_list # @@ -1238,6 +1274,7 @@ Usage: ${_ME} tag list alarms with a tag ${_ME} untag remove tags from an alarm ${_ME} snooze [] snooze alarms + ${_ME} unsnooze unsnooze alarms ${_ME} check check and fire due alarms ${_ME} mute mute alarm sounds ${_ME} unmute unmute alarm sounds @@ -2086,6 +2123,121 @@ snooze() { fi } +_unsnooze_by_tag() { + local _tag="${1}" + + _acquire_lock + + if [[ ! -f "${_SNOOZED_FILE}" ]] + then + _release_lock + _exit_1 printf "Tag [%s] is not snoozed.\\n" "${_tag}" + fi + + local _found_in_file=0 _entry + while IFS= read -r _entry || [[ -n "${_entry}" ]] + do + [[ -n "${_entry}" ]] || continue + local _key="${_entry%%$'\t'*}" + [[ "${_key}" == "${_tag}" ]] && _found_in_file=1 && break + done < "${_SNOOZED_FILE}" + + if (( ! _found_in_file )) + then + _release_lock + _exit_1 printf "Tag [%s] is not snoozed.\\n" "${_tag}" + fi + + _read_alarms + local _match_count=0 _line + for _line in "${_ALARMS[@]:-}" + do + [[ -n "${_line}" ]] || continue + local _tags + _tags="$(_get_alarm_field "${_line}" 2)" + _alarm_has_tag "${_tags}" "${_tag}" && _match_count=$((_match_count + 1)) + done + + if ! _confirm_action "Unsnooze" "${_match_count}" "${_tag}" + then + _release_lock + return 0 + fi + + _remove_snoozed_entry "${_tag}" + _release_lock + printf "Unsnoozed [%s].\\n" "${_tag}" +} + +# unsnooze ################################################################### + +describe "unsnooze" < + +Description: + Unsnooze alarms. With "all", unsnoozes everything. With an ID, + unsnoozes that alarm. With a tag, unsnoozes all alarms with that tag. +HEREDOC +unsnooze() { + local _target="${1:-}" + [[ -n "${_target}" ]] || _exit_1 printf "Usage: %s unsnooze \\n" "${_ME}" + + if [[ "${_target}" == "all" ]] + then + if [[ -f "${_SNOOZED_FILE}" ]] + then + _acquire_lock + rm -f "${_SNOOZED_FILE}" + _release_lock + printf "Unsnoozed all.\\n" + else + printf "Nothing is snoozed.\\n" + fi + return + fi + + if [[ ! "${_target}" =~ ^[0-9]+$ ]] + then + _unsnooze_by_tag "${_target}" + return + fi + + # Numeric: unsnooze by ID. + _acquire_lock + _read_alarms + + local _found=0 _message="" _line + for _line in "${_ALARMS[@]:-}" + do + [[ -n "${_line}" ]] || continue + local _id + _id="${_line%%$'\t'*}" + if [[ "${_id}" == "${_target}" ]] + then + _found=1 + _message="$(_get_alarm_field "${_line}" 5)" + break + fi + done + + if (( ! _found )) + then + _release_lock + _exit_1 printf "No alarm with ID %s.\\n" "${_target}" + fi + + if ! _remove_snoozed_entry "${_target}" + then + _release_lock + printf "Alarm %s is not snoozed.\\n" "${_target}" + return + fi + + _release_lock + printf "Unsnoozed [%s] %s.\\n" "${_target}" "${_message}" +} + # edit ######################################################################## describe "edit" </dev/null +} + +@test "unsnooze all removes snoozed file" { + run_nag snooze all + run_nag unsnooze all + [ "${status}" -eq 0 ] + [[ "${output}" =~ "Unsnoozed all" ]] + [ ! -f "${NAG_DIR}/snoozed" ] +} + +@test "unsnooze all when nothing snoozed says so" { + run_nag unsnooze all + [ "${status}" -eq 0 ] + [[ "${output}" =~ "Nothing is snoozed" ]] +} + +@test "unsnooze with no args fails" { + run_nag unsnooze + [ "${status}" -eq 1 ] +} + +@test "unsnooze by tag removes tag entry" { + run_nag at "tomorrow 3pm" "work task" + run_nag tag 1 work + run_nag snooze work + run_nag unsnooze work + [ "${status}" -eq 0 ] + [[ "${output}" =~ "Unsnoozed [work]" ]] + ! grep -q "^work" "${NAG_DIR}/snoozed" 2>/dev/null +} + +@test "unsnooze by tag requires -f" { + run_nag at "tomorrow 3pm" "work task" + run_nag tag 1 work + run_nag snooze work + run "${_NAG}" unsnooze work < /dev/null + [ "${status}" -eq 0 ] + [[ "${output}" =~ "Unsnooze" ]] + [[ "${output}" =~ "-f" ]] +} + +@test "unsnooze nonexistent ID fails" { + run_nag unsnooze 99 + [ "${status}" -eq 1 ] +} + +@test "unsnooze tag that is not snoozed fails" { + run_nag at "tomorrow 3pm" "work task" + run_nag tag 1 work + run "${_NAG}" -f unsnooze work + [ "${status}" -eq 1 ] + [[ "${output}" =~ "not snoozed" ]] +}