Advertisement
layr

TO-DO manager

May 27th, 2012
145
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 56.45 KB | None | 0 0
  1. #!/bin/bash
  2. #
  3. # TO-DO list manager that'll remind you taks to be done; if any task has failed
  4. # to remind, it will be reported.
  5. #
  6. # !!! As the script uses cron, it is advised to install current cron jobs
  7. #     into systemwide corntab (e.g. into '/etc/cron.d/' directory or
  8. #     '/etc/crontab' file). Otherwise currently installed cron jobs will be overwritten !!!
  9. #
  10. # Requires notify-send and zenity to be installed.
  11. #
  12. # -h or --help for more info.
  13. #
  14. # Don't forget to make this file executable!
  15. #
  16. # Last update 17. Mar 2013
  17. # TO DO: - add possibility to add tasks without reminder;
  18. #          hetkel lahendatav 'add' flagiga - st croni ei kanta, kuid db-sse läheb;
  19. #          jääb veel auto-checki vältimise küss.
  20. #
  21. #        - instead of default +week reminder of i-marked birthday,
  22. #          ask user how many days before bd would reminder have to pop up
  23. #
  24. # Written by Laur Aliste [ laur.aliste ät gmail ]
  25. ############################### EDITABLE ITEMS ###############################
  26. todo_database="/data/Documents/.todo.dat"    # Location and name of the todo database.
  27. birthday_database="/data/Documents/.birthdays.dat"    # Location and name of the birthday database.
  28. cron="/data/Documents/.cron.dat"            # Location and name of the cron file.
  29. max_size="1"                    # Max allowed size of to-do and birthday databases (in MB). Simply a precautionary feature.
  30. install_path="/usr/local/bin"   # Where script will be installed by install_ function
  31.  
  32. ##############################################################################
  33. # If you ain't sure what your're doing, don't edit anything below this line.
  34. ##############################################################################
  35.  
  36. # Tags that are part of task/birthday database entry ID field:
  37. tag1="!#"   # DO NOT use single quotes(') in tags!
  38. tag2="#!"
  39. zero=${0##*/}   # (same as running `basename $0`)
  40. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Location of this scriptfile.
  41. disp=":0.0"
  42. disp2="$DISPLAY"
  43. export DISPLAY=:0.0
  44. bold="tput smso"
  45. offbold="tput rmso"
  46. unset add important skip repetitive UI another_date date_arr skip_confirm monthtag daytag
  47. current_year=$(date  +%Y)
  48. NOW=$((`date +%s`))
  49.  
  50. tmp="/tmp/.todo_script_tmpfile_089123.tmp"
  51. todo_cron_dir="/etc/cron.d"
  52. todo_crontab="$todo_cron_dir/todo_script_crontab"
  53. ############################## DEFINE FUNCTIONS ##############################
  54. function check_files() {
  55.    # Will check whether required files are existing; if not, will create
  56.    # or ask whether to create:
  57.    for i in $@; do
  58.        if [ -f $i ]; then
  59.            continue
  60.        elif [[ "$i" == "$todo_database" ]]; then
  61.            echo $(crontab -l) > $tmp
  62.            if [[ -n "$(crontab -l)" ]] || [[ -z "$(grep 'no crontab for' $tmp)" ]]; then
  63.                zenity --question --text="!!!  IMPORTANT !!!\n\nIt seems you're running this script for the first time.\nYou also have some cronjobs installed in crontab.\nMake sure you back up those cron-jobs found in crontab.\n(they can be viewed by running 'crontab -l');\nif you proceed, those entries WILL be overwritten!\n\nSee '$zero -h' for more info.\n\nContinue?" || exit 1
  64.            else
  65.                zenity --question --text="!!!  IMPORTANT !!!\n\nIt seems you're running this script for the first time.\nYou currently have no cron-jobs installed in crontab,\nbut notice that simple crontab can no loger be used, since this script overwrites it;\nyou'll be better off using '/etc/cron.d/' or similar.\n\nSee '$zero -h' for more info.\n\nContinue?" || exit 1
  66.            fi
  67.            > $i
  68.        else
  69.            > $i
  70.        fi
  71.    done
  72. }
  73.  
  74. function check_program() {
  75.    # Will check whether necessary programs are installed; if not, exit:
  76.    for i in $@; do
  77.        if ! command -v $i >/dev/null; then
  78.            printf "ERROR\n$i does not appear to be installed on your system.\nsudo apt-get install $i\n" >&2; exit 1
  79.        fi
  80.    done
  81. }
  82.  
  83. function generate_ident() {
  84.    # Creates unique ID for name in birthday database or task in todo database.
  85.  
  86.    ident="$tag1$RANDOM$tag2"
  87.    # Make sure this ident isn't already existing in corresponding database:
  88.     until [[ -z "$(grep $ident $1)" ]]; do
  89.         ident="$tag1$RANDOM$tag2"
  90.     done
  91. }
  92.  
  93. function check_filesize() {
  94.     # Check if file is larger than predefined size; simply a precautionary feature:
  95.     for i in $@; do
  96.         size=$(du -LshBM $i | cut -f1) #Prints dir size in megs; note the -L option follows symbolic links
  97.         if [[ "${size:0:$(( ${#size} - 1 ))}" -gt "$max_size" ]]; then
  98.         #if [[ "$(( $(stat --printf="%s" $i) / 1000000 ))" -gt "$max_size" ]]; then
  99.             notify-send -t 0 -i gtk-dialog-error "$i is more than $max_size MB in size! Aborting."; exit 1
  100.         fi
  101.     done
  102. }
  103.  
  104. function print_error() {
  105.     # Different kind of errors to display:
  106.     clear
  107.     case $1 in
  108.         "wrong_format")
  109.             notify-send -t 0 -i gtk-dialog-error "$zero script could not identify the date format for the repetitive task
  110. \"$2\":
  111. ( $3 )  Aborting."; exit 1
  112.             ;;
  113. #-----------------------------------------------------------------------------            
  114.         "wrong_format_date")
  115.             notify-send -t 0 -i gtk-dialog-error "$zero script could not identify the date format for the repetitive task
  116. \"$2\":
  117. ( $3 )  Aborting.
  118.  
  119. (one of the probable reason could be writing date as 'D' instead of '0D', e.g.
  120. Dec 1 should be entered as Dec 01)"; exit 1
  121.             ;;
  122. #-----------------------------------------------------------------------------
  123.         "bad_date")
  124.             echo "========================================================================"
  125.             echo " Error: please re-check the date entry ($2)"
  126.             echo -e " for task\n\n\"$3\"\n\n Looks like it contains ill-logical values."
  127.             echo "========================================================================"; exit 1
  128.             ;;
  129. #-----------------------------------------------------------------------------
  130.         "few_args")
  131.             echo "========================================================================="
  132.             echo " Error: exactly one argument (the number of task to be deleted) required!"
  133.             echo "========================================================================="; exit 1
  134.             ;;
  135.     esac
  136. }
  137.  
  138. function find_time_details() {
  139.     # Create different time-values (e.g. hours, minutes, day, date, year...):
  140.     mins=$(eval date  +%M -d \"$@\") > /dev/null 2>&1
  141.     hours=$(eval date  +%H -d \"$@\") > /dev/null 2>&1
  142.     hrs_mins=$(eval date  +%R -d \"$@\") > /dev/null 2>&1
  143.     date=$(eval date  +%d -d \"$@\") > /dev/null 2>&1
  144.     day=$(eval date  +%a -d \"$@\") > /dev/null 2>&1
  145.     month=$(eval date  +%m -d \"$@\") > /dev/null 2>&1
  146.     month_char=$(eval date  +%b -d \"$@\") > /dev/null 2>&1
  147.     year=$(eval date  +%Y -d \"$@\") > /dev/null 2>&1
  148.     datetime=$(eval date -d \"$@\") > /dev/null 2>&1
  149.     month_cap=$(eval date  +%^b -d \"$@\") > /dev/null 2>&1    # For comparison purposes
  150.     day_cap=$(eval date  +%^a -d \"$@\") > /dev/null 2>&1    # For comparison purposes
  151.     TARGET=$(eval date +%s -d \"$@\") > /dev/null 2>&1
  152. }
  153.  
  154. function build_repetitive_dateline() {
  155.     # Builds a repetitive task execution date for todo-database:
  156.     # Note the find_time_details should have already been called by now!
  157.     time="$@"
  158.  
  159.     if [[ "$UI" != "True" ]]; then # in case of GUI usage, $gap and $time are already defined within add_task function
  160.         time_cap="$(echo $time | tr '[:lower:]' '[:upper:]')"   # For comparing strings below
  161.         # Try to figure out which variables were passed as dates:
  162.         if [[ "$monthtag" == "True" ]]; then
  163.             time="$month_char $date $hrs_mins"
  164.             gap="month"
  165.         elif [[ "$daytag" == "True" ]]; then
  166.             time="$hrs_mins"
  167.             gap="day"
  168.         elif [[ "$#" == "1" ]]; then
  169.             if [[ "$1" == $hrs_mins ]]; then
  170.                 time="$hrs_mins"
  171.                 gap="day"
  172.             elif [[ "$time_cap" == *$day_cap* ]]; then
  173.                 time="$day $hrs_mins"
  174.                 gap="next week"
  175.             else
  176.                 print_error "wrong_format" "$task" "$time"
  177.             fi
  178.         elif [[ "$#" == "2" ]]; then
  179.             if [[ "$time_cap" == *$day_cap* ]] && [[ "$time_cap" == *$hrs_mins* ]]; then
  180.                 time="$day $hrs_mins"
  181.                 gap="next week"
  182.             elif [[ "$time_cap" == *$month_cap* ]] && [[ "$time_cap" == *$date* ]]; then
  183.                 time="$month_char $date"
  184.                 gap="next year"
  185.             else
  186.                 print_error "wrong_format_date" "$task" "$time"
  187.             fi
  188.         elif [[ "$#" == "3" ]]; then
  189.             if [[ "$time_cap" == *$month_cap* ]] && [[ "$time_cap" == *$date* ]] && \
  190.             [[ "$time_cap" == *$hrs_mins* ]]; then
  191.                 time="$month_char $date $hrs_mins"
  192.                 gap="next year"
  193.             else
  194.                 print_error "wrong_format_date" "$task" "$time"
  195.             fi
  196.         else
  197.             print_error "wrong_format" "$task" "$time"
  198.         fi
  199.     fi
  200.     # Build first exec time:
  201.     exe_time="$(eval date  -d \"$time\")"
  202.    
  203.     # If the target date is already history, add +gap for the exec time
  204.     if [[ "$((`date +%s -d "$exe_time"`))" -lt "$((`date +%s`))" ]]; then
  205.         exe_time="$(eval date  -d \"$time+$gap\")"
  206.     fi
  207.    
  208.     # Finally, build the entry2, that'll be echoed into DB after ID
  209.     entry2="\"$time\" \"$exe_time\" \"$gap\""
  210. }
  211.  
  212. function check_time () {
  213.     # Checks whether task to be added is valid, in the past or far in the future;
  214.     # also used by automatic checker to inform user if any task has failed to execute.
  215.  
  216.     time="$@"
  217.    
  218.     if [[ "$auto_check" == "True" ]]; then
  219.         if [[ "$TARGET" -lt "$NOW" ]]; then # Meaning task has failed to execute
  220.             skip="True"
  221.             if [[ "$repetitive" == "True" ]]; then
  222.                 if zenity --question --cancel-label="Nope, leave it be" --ok-label="OK" --text="It looks like repetitive task\n\n\"$task\"\n\nhas failed to remind.\n(execution time should have been at $time)\n\nPress OK to update reminder date to next time"; then
  223.                     eval set "$i"   # $i comes from auto_check function
  224.                     time="$1"
  225.                     exe_time_old="$2"
  226.                     gap="$3"
  227.  
  228.                     exe_time="$(eval date  -d \"$time\")"
  229.                     # If the target date is already history, add +gap for the exec time:
  230.                     if [[ "$((`date +%s -d "$exe_time"`))" -lt "$((`date +%s`))" ]]; then
  231.                         exe_time="$(eval date  -d \"$time+$gap\")"
  232.                     fi
  233.                    
  234.                     time_new="\"$time\" \"$exe_time\" \"$gap\""
  235.                    
  236.                     # Update todo database:
  237.                     sed -i '/'"'$ident'"'/s/'"'$i'"'/'"'$time_new'"'/g' $todo_database
  238.  
  239.                     # Update cron:
  240.                     find_time_details "$exe_time"
  241.                     # Only add updated task, if target time is less than year from now:
  242.                     if [[ "$TARGET" -lt "$(( $NOW + 24*60*60*365 ))" ]]; then
  243.                         echo "$mins $hours $date $month * $DIR/$zero '$ident' '$task' '$time_new'" >> $cron
  244.                     fi
  245.                 fi
  246.             else
  247.                 if zenity --question --cancel-label="Nope, leave it be" --ok-label="OK" --text="It looks like task\n\n\"$task\"\n\nhas failed to remind.\n(execution time should have been at $time)\n\nPress OK to remove this reminder date from database."; then
  248.  
  249.                     # Check whether there are any other exe times for this task;
  250.                     # if not, delete the task:
  251.  
  252.                     eval set "$(grep $ident $todo_database)"
  253.                     if [[ "$#" == "3" ]]; then
  254.                         # Meaning this was the only exe date, so delete this entry:
  255.                         sed -i '/'"'$ident'"'/d' $todo_database
  256.                     else
  257.                         # Meaning there are other exe dates present; remove only this exe time:
  258.                         sed -i '/'"'$ident'"'/s/'" '$i'"'//g' $todo_database
  259.                     fi
  260.                 fi
  261.             fi
  262.  
  263.         elif [[ "$TARGET" -gt "$(( $NOW + 24*60*60*365 ))" ]]; then
  264.             skip="True"
  265.         fi
  266.     else    # this part is called only for non-repetitive tasks while adding new tasks
  267.         unset update skip add
  268.  
  269.         if [[ "$TARGET" -lt "$NOW" ]] && [[ "$year" == "$current_year" ]]; then
  270.             if [[ "$UI" == "True" ]]; then
  271.                 if zenity --question --text="Selected date ($datetime) for task\n\n\"$task\"\n\nis already history this year.\nWant to add this task for the next year?\n(in that case exe time would be at \"$date. $month_char $(( $current_year + 1 )) at $hrs_mins\")\nAnswering \"No\" will delete this task from the todo database!"; then
  272.                     update="True"
  273.                 else
  274.                     skip="True"
  275.                 fi
  276.             else    # i.e. it's command-line
  277.                 clear
  278.                 echo -e "Selected date ($datetime) for task\n\n\"$task\"\n\nis already history this year.\nWant to add this task for the next year?\n(in that case exe time would be at \"$date. $month_char $(( $current_year + 1 )) at $hrs_mins\")\n\nAnswering \"n\" will delete this task from the todo database!\n\n(y/n)?"
  279.                 while :; do
  280.                     read yno
  281.                     case $yno in
  282.                         [yY] | YEs | YES | Yes | yes )
  283.                             update="True"
  284.                             echo "Ok, adding this task to next year...";
  285.                             break
  286.                             ;;
  287.                         [nN] | NO | No | no )
  288.                             skip="True"
  289.                             echo "Deleting this task...";
  290.                             break
  291.                             ;;
  292.                         *) echo "Invalid input, try again. (either y/n is accepted)"
  293.                             ;;
  294.                     esac
  295.                 done
  296.             fi
  297.             if [[ "$update" == "True" ]]; then
  298.                 entry2="$(( $current_year + 1 ))-$month-$date $hrs_mins"
  299.                 find_time_details "$entry2"
  300.                 entry2="$datetime"
  301.             fi
  302.         # Skip the jobs far in the future, but install those that are less
  303.         # than one year from now (this comes from the cron limitation):
  304.         elif [[ "$TARGET" -gt "$(( $NOW + 24*60*60*365 ))" ]]; then
  305.                 skip="True"
  306.                 add="True" # i.e. job will be added to db, but not into cron
  307.         # Skip the jobs that are in the past and skip tose tasks:
  308.         elif [[ "$year" -lt "$current_year" ]]; then
  309.             if [[ "$UI" == "1" ]]; then
  310.                 zenity --info --text="Selected date for task \"$task\" is already history. Will skip this."
  311.             else    # i.e. it's command-line
  312.                 clear
  313.                 echo -e "Selected year for task \"$task\" is $year.\nWhy the hell would you make a to-do note for past date?\n\nWill skip this task."
  314.             fi
  315.             skip="True"
  316.         fi
  317.     fi
  318. }
  319.  
  320. function add_task () {
  321.     # Function that's responsible for adding new tasks into database.
  322.     # Note it handles two cases - CLI and GUI.
  323.  
  324.     generate_ident $todo_database
  325.    
  326.     if [[ "$UI" == "True" ]]; then
  327.         task=$(zenity --width="500" --entry --text "Enter the task:")
  328.         [[ -z "$task" ]] && return   # return, if either nothing was input or zenity prompt was closed
  329.  
  330.         # Replace single quotes and &-signs:
  331.         task=$(echo "$task" | sed -e 's/'\''/''\`/g' | sed -e 's/'\&'/''\+/g')
  332.        
  333.        
  334.         until [[ "$another_date" == "False" ]]; do
  335.             unset opt skip add
  336.            
  337.             exe_date=$(zenity --calendar --date-format=%Y-%m-%d --text "Pick a day" --title "$task")
  338.             [[ -z "$exe_date" ]] && return
  339.  
  340.             time=$(zenity --entry --text "Enter the time:" --entry-text "e.g. 19:45")
  341.             [[ -z "$time" ]] && return
  342.             while [[ "$((`date +%s -d "$time"`))" == "0" ]]; do
  343.                 time=$(zenity --entry --text "Bad time! Try again:" --entry-text "e.g. 19:45")
  344.             done
  345.             [[ -z "$time" ]] && return
  346.            
  347.             find_time_details "$exe_date $time"
  348.             entry2="$datetime"  # Creates initial exe time for non-rep task;
  349.  
  350.             if zenity --question --text="Is this repetitive exe date?"; then
  351.                 opt1="$month_char $date at $hrs_mins"
  352.                 opt2="$day at $hrs_mins"
  353.                 opt3="day at $hrs_mins"
  354.                 opt4="month on $date th"
  355.                 until [[ -n "$opt" ]]; do
  356.                     opt=`zenity --list --text="When do you want this task to remind?" \
  357.                      --radiolist --column "" --column "Every..." TRUE "$opt1" FALSE "$opt2" FALSE "$opt3" FALSE "$opt4"`
  358.                 done
  359.                 case $opt in
  360.                     "$opt1") entry2="$month_char $date $hrs_mins"
  361.                              gap="next year"
  362.                         ;;
  363.                     "$opt2") entry2="$day $hrs_mins"
  364.                              gap="next week"
  365.                         ;;
  366.                     "$opt3") entry2="$hrs_mins"
  367.                              gap="day"
  368.                         ;;
  369.                     "$opt4") entry2="$month_char $date $hrs_mins"
  370.                              gap="month"
  371.                         ;;
  372.                 esac
  373.                 build_repetitive_dateline "$entry2" # this function creates new $entry2
  374.             else
  375.                 check_time "$entry2"    # Checks exe time validity for non-rep task;
  376.             fi
  377.            
  378.             # Add new time to the date_arr:
  379.             if [[ "$skip" != "True" ]] || [[ "$add" == "True" ]]; then
  380.                 date_arr=("${date_arr[@]}" "'$entry2'" )                
  381.                 if [[ "$skip" != "True" ]]; then
  382.                     eval set "$entry2"
  383.                     [[ "$#" == "3" ]] && find_time_details "$2" || find_time_details "$entry2"
  384.                     echo "$mins $hours $date $month * $DIR/$zero '$ident' '$task' '$entry2'" >> $cron
  385.                 fi
  386.             fi
  387.  
  388.             # Ask user if another exe time is needed:
  389.             zenity --question --text="Would you like to add another reminder date for this task?" || another_date="False"
  390.         done
  391.     else    # Meaning we're dealing with CLI here.
  392.         echo -e "Enter the task:\n"
  393.         read task
  394.         while [[ -z "$task" ]]; do
  395.             clear
  396.             echo "No characters were entered; try again."
  397.             read task
  398.         done
  399.        
  400.         # Replace single quotes and &-signs:
  401.         task=$(echo "$task" | sed -e 's/'\''/''\`/g' | sed -e 's/'\&'/''\+/g')
  402.        
  403.         until [[ "$another_date" == "False" ]]; do
  404.             unset repetitive skip add monthtag daytag
  405.            
  406.             clear
  407.             echo -e "Enter date:\n(only certain formats are accepted. See \"$zero -h | --help\" for usage.)"
  408.             read date_manual
  409.             while [[ -z "$date_manual" ]]; do
  410.                 clear
  411.                 echo "No characters were entered; try again."
  412.                 read date_manual
  413.             done
  414.            
  415.             eval set "$date_manual"
  416.             if [[ "$1" == "r" ]]; then
  417.                 shift
  418.                 repetitive="True"
  419.                 case $1 in
  420.                     month)
  421.                         shift
  422.                         monthtag="True"
  423.                         ;;
  424.                     day)
  425.                         shift
  426.                         daytag="True"
  427.                         ;;
  428.                 esac
  429.             fi
  430.            
  431.             while [[ "$(eval date +%s -d \"$@\")" == "0" ]] || [[ "$(eval date +%s -d \"$@\")" == "" ]]; do
  432.                 clear
  433.                 echo -e "Bad date format! Try again:"
  434.                 read date_manual
  435.                 while [[ -z "$date_manual" ]]; do
  436.                     clear
  437.                     echo "No characters were entered; try again."
  438.                     read date_manual
  439.                 done
  440.                 eval set "$date_manual"
  441.                 if [[ "$1" == "r" ]]; then
  442.                     shift
  443.                     repetitive="True"
  444.                     case $1 in
  445.                         month)
  446.                             shift
  447.                             monthtag="True"
  448.                             ;;
  449.                         day)
  450.                             shift
  451.                             daytag="True"
  452.                             ;;
  453.                     esac
  454.                 fi
  455.             done
  456.            
  457.            
  458.             # Find the time details (mins, hrs, date etc using find_time_details function:
  459.             if [[ "$monthtag" == "True" ]]; then
  460.                 current_month=$(eval date  +%b -d \"now\")
  461.                 find_time_details "$current_month $@"   # without the 'Mon', date calls from find_time_details might fail
  462.             else
  463.                 find_time_details "$@"
  464.             fi
  465.            
  466.             entry2="$datetime"
  467.            
  468.             [[ "$repetitive" == "True" ]] && { build_repetitive_dateline "$@"; } \
  469.             || { check_time "$entry2"; }
  470.            
  471.             # Add new time to the date_arr:
  472.             if [[ "$skip" != "True" ]] || [[ "$add" == "True" ]]; then
  473.                 date_arr=("${date_arr[@]}" "'$entry2'" )                
  474.                 if [[ "$skip" != "True" ]]; then
  475.                     eval set "$entry2"
  476.                     [[ "$#" == "3" ]] && find_time_details "$2" || find_time_details "$entry2"
  477.                     echo "$mins $hours $date $month * $DIR/$zero '$ident' '$task' '$entry2'" >> $cron
  478.                 fi
  479.             fi
  480.            
  481.             # Ask user if another exe time is needed:
  482.             echo -e "Would you like to add another reminder date for this task? (y/n)"
  483.             while :; do
  484.                 read yno
  485.                 case $yno in
  486.                     [yY] | YEs | YES | Yes | yes )
  487.                         break
  488.                         ;;
  489.                     [nN] | NO | No | no )
  490.                         another_date="False"
  491.                         break
  492.                         ;;
  493.                     *) echo "Invalid input, try again. (either y/n is accepted)"
  494.                         ;;
  495.                 esac
  496.             done
  497.         done
  498.     fi
  499.    
  500.     # If task has valid dates, enter it into database and install cron:
  501.     if [[ "${#date_arr[*]}" != "0" ]]; then  
  502.         echo "'$ident' '$task' ${date_arr[@]}" >> $todo_database
  503.         crontab $cron
  504.     fi
  505. }
  506.  
  507. function add_bd () {
  508.     # Function that's responsible for adding new birthdays into database.
  509.     # Note it handles two cases - CLI and GUI.
  510.     generate_ident $todo_database
  511.    
  512.     if [[ "$UI" == "True" ]]; then
  513.         name=$(zenity --entry --text "Enter name:")
  514.         [[ -z "$name" ]] && return # Return if no characters
  515.        
  516.         # Replace single quotes with back-quotes within the name:
  517.         name=$(echo "$name" | sed -e 's/'\''/''\`/g')
  518.        
  519.         exe_date=$(zenity --calendar --date-format=%Y-%m-%d --text "Pick a day" --title "$name")
  520.         [[ -z "$exe_date" ]] && return
  521.  
  522.         date=$(date  +%d -d "$exe_date") > /dev/null 2>&1
  523.         month=$(date  +%b -d "$exe_date") > /dev/null 2>&1
  524.         if zenity --question --text="Would you want this birthday to remind also one week before?"; then
  525.             important="True"
  526.         fi  
  527.     else    # Meaning we're dealing with CLI here.
  528.         echo -e "Enter the name:"
  529.         read name
  530.         while [[ -z "$name" ]]; do
  531.             clear
  532.             echo "No characters were entered; try again"
  533.             read name
  534.         done
  535.        
  536.         # Replace single quotes with back-quotes within the name:
  537.         name=$(echo "$name" | sed -e 's/'\''/''\`/g')
  538.        
  539.         echo -e "Enter his/her birthday (in a format '28 Jul'):"
  540.         read birthday_date
  541.         while [[ -z "$birthday_date" ]]; do
  542.             clear
  543.             echo "No characters were entered; try again."
  544.             read birthday_date
  545.         done
  546.        
  547.         eval set "$birthday_date"
  548.        
  549.         [[ "$1" == "i" ]] && { important="True"; shift; }
  550.        
  551.         # Check if the entered date makes any sense:
  552.         while [[ "$(eval date +%s -d \"$@\")" == "0" ]] || [[ "$(eval date +%s -d \"$@\")" == "" ]]; do
  553.             clear
  554.             echo -e "Birthday date has to be in a format 'DD Mmm' e.g. 18 Jun, or 'i DD Mmm'\n"
  555.             read birthday_date
  556.             while [[ -z "$birthday_date" ]]; do
  557.                 clear
  558.                 echo "No characters were entered; try again."
  559.                 read birthday_date
  560.             done
  561.            
  562.             eval set "$birthday_date"
  563.             [[ "$1" == "i" ]] && { important="True"; shift; }
  564.         done
  565.        
  566.         date=$(eval date  +%d -d \"$@\") > /dev/null 2>&1
  567.         month=$(eval date  +%b -d \"$@\") > /dev/null 2>&1
  568.     fi
  569.  
  570.     if [[ "$important" == "True" ]]; then
  571.         echo "'$ident' '$name' 'i' '$date $month'" >> $birthday_database
  572.     else
  573.         echo "'$ident' '$name' '$date $month'" >> $birthday_database
  574.     fi
  575. }
  576.  
  577. function auto_check() {
  578.     # Function that'll check whether any tasks have failed to remind or there are
  579.     # any upcoming birthdays and also add tasks that are less than a year from current moment into cron:
  580.    
  581.  
  582.     auto_check="True"
  583.    
  584.     # Rebuild cron file:
  585.     echo -e "# This is the cron file used by the TO-DO manager ($DIR/$zero).\n#------------------------------------------------------------------------" > $cron
  586.    
  587.     while read line; do
  588.         eval set "$line"    
  589.         ident="$1"
  590.         task="$2"
  591.         shift
  592.         shift
  593.        
  594.         for i in "$@"; do
  595.             unset skip repetitive
  596.             eval set "$i"
  597.            
  598.             [[ "$#" == "3" ]] && { time="$2"; repetitive="True"; } || time="$i"
  599.        
  600.             find_time_details "$time"
  601.             check_time "$time"
  602.        
  603.             [[ "$skip" == "True" ]] && continue
  604.             echo "$mins $hours $date $month * $DIR/$zero '$ident' '$task' '$i'" >> $cron
  605.         done
  606.     done < $todo_database
  607.     crontab $cron
  608.    
  609.     # Birthday database cheker:
  610.     while read line; do
  611.         unset important
  612.        
  613.         eval set "$line"
  614.         name="$2"
  615.         shift
  616.         shift
  617.        
  618.         # If date line's first variable is 'i', then remind this person's birthday also one week before:
  619.         [[ "$1" == "i" ]] && { shift; important="True"; }
  620.        
  621.         time="$@"
  622.         eval_date="$(date +%F --date="$time")"
  623.        
  624.         if [[ "$important" == "True" ]] && [[ "$eval_date" == "$(date +%F --date="week")" ]]; then
  625.             date=$(eval date  +%d -d \"$time\") > /dev/null 2>&1
  626.             month=$(eval date  +%b -d \"$time\") > /dev/null 2>&1
  627.             notify-send -t 0 -i gtk-dialog-info "$name's birthday is next week! ($date. $month)"
  628.             continue
  629.         elif [[ "$eval_date" == "$(date +%F --date="today")" ]]; then
  630.             notify-send -t 0 -i gtk-dialog-info "$name's birthday is TODAY!"
  631.             continue
  632.         elif [[ "$eval_date" == "$(date +%F --date="tomorrow")" ]]; then
  633.             date=$(eval date  +%d -d \"$time\") > /dev/null 2>&1
  634.             month=$(eval date  +%b -d \"$time\") > /dev/null 2>&1
  635.             notify-send -t 0 -i gtk-dialog-info "$name's birthday is tomorrow ($date. $month)"
  636.         fi
  637.     done < $birthday_database
  638. }
  639.  
  640. function list_task() {
  641.     # Function for listing all the tasks installed in todo database.
  642.  
  643.     # Create the array for zenity:
  644.     count=0
  645.     while read line; do
  646.         eval set "$line"
  647.         unset first_time
  648.         count2="1"
  649.         task="$2"
  650.         shift   # 'cause ID is not needed here
  651.         shift   # discards of task
  652.        
  653.         for i in "$@"; do  
  654.             eval set "$i"
  655.             [[ "$#" == "3" ]] && { exe_time="$2"; rep="X"; } || { rep=" "; exe_time="$i"; }
  656.  
  657.             if [[ "$first_time" == "False" ]]; then
  658.                 arr=("${arr[@]}" "$count.$count2" "$rep" " " "$exe_time" )
  659.                 let count2=$count2+1
  660.             else
  661.                 let count=$count+1
  662.                 arr=("${arr[@]}" "$count" "$rep" "$task" "$exe_time" )
  663.                 first_time="False"
  664.             fi
  665.         done
  666.     done < $todo_database
  667.  
  668.     # List tasks:
  669.     foo=$(zenity --list --width="800" --height="300" --text="Current tasks:" \
  670.     --column "#" --column "rep" --column "Task" --column "Exe time" "${arr[@]}")
  671. }
  672.  
  673. function list_bd() {
  674.     # Function for listing all the names installed in birthday database.
  675.    
  676.     # Create the array for zenity:
  677.     count=0
  678.     while read line; do
  679.         eval set "$line"
  680.         imp=" "
  681.         name=$2
  682.  
  683.         [[ "$3" == "i" ]] && { imp="X"; exe_time="$4"; } || exe_time="$3"
  684.  
  685.         let count=$count+1
  686.         arr=("${arr[@]}" "$count" "$imp" "$name" "$exe_time" )
  687.     done < $birthday_database
  688.  
  689.     # List birthdays:
  690.     foo=$(zenity --list --width="800" --height="300" --text="Entered birthday entries:" \
  691.     --column "#" --column "i" --column "Name" --column "Birthday" "${arr[@]}")
  692. }
  693.  
  694.  
  695. function delete() {
  696.     # Will create selection dialog for selecting tasks to be deleted.
  697.    
  698.     shift # Gets rid of potential -d/--delete flag
  699.     nr_of_lines=$(sed -n "$=" $todo_database)
  700.    
  701.     if [[ -z "$@" ]]; then  # i.e. no more flags were provided, meaning GUI
  702.         # Create the array for zenity:
  703.         while read line; do
  704.             eval set "$line"
  705.             ID="$1"
  706.             task="$2"
  707.             arr=("${arr[@]}" "FALSE" "$ID" "$task" )
  708.         done < $todo_database
  709.  
  710.         # Display task selection dialog:
  711.         ans=$(zenity --list --width="800" --height="300" --text="Pick task(s) to be deleted:" \
  712.         --checklist --column "Pick" --hide-column=2 --column "Task ID" --column "Task" "${arr[@]}" --separator=" ")
  713.         [[ -n $ans ]] && { eval set "$ans"; } || { set ""; }
  714.  
  715.         if [[ "$#" -gt "3" ]]; then
  716.             if zenity --question --text="You've selected quite a few tasks to delete;\nwant to skip deletion confirmation?"; then
  717.                 skip_confirm="True"
  718.             fi
  719.         fi
  720.         for i in $ans; do   # If nothing was selected, script simply skips this
  721.             eval set "$(grep $i $todo_database)"
  722.             task="$2"
  723.             if [[ "$skip_confirm" != "True" ]]; then
  724.                 if zenity --question --text="Sure you want to delete task\n$task?"; then
  725.                     # Delete task from todo_database
  726.                     sed -i '/'"$i"'/d' $todo_database
  727.                     # ... and from cron:
  728.                     sed -i '/'"$i"'/d' $cron
  729.                 fi
  730.             else
  731.                 # Delete task from todo_database
  732.                 sed -i '/'"$i"'/d' $todo_database
  733.                 # ... and from cron:
  734.                 sed -i '/'"$i"'/d' $cron
  735.             fi
  736.         done
  737.         crontab $cron
  738.         return
  739.  
  740.     # CLI part from here:
  741.  
  742.     # This part deletes by the task #nr entered by the user:
  743.     elif [[ "$#" != "1" ]] || [[ "$1" -le "0" ]]; then
  744.         print_error "few_args"
  745.     # Check that user isn't asking deletion of task that's non-existent:
  746.     elif [[ "$1" -gt "$nr_of_lines" ]]; then
  747.         echo -e "Requested task nr $1 doesn't exist.\nNr of tasks currently in database is $nr_of_lines."; exit 1
  748.     fi
  749.     eval set "$(sed -n "$1"p $todo_database)"
  750.     ID="$1"
  751.     task="$2"
  752.     echo -e "Are you sure you want to delete task"; $bold
  753.     echo -e "$task\n"; $offbold
  754.     echo "(y/n)?"
  755.     while :; do
  756.         read yno
  757.         case $yno in
  758.             [yY] | YEs | YES | Yes | yes )
  759.                 echo "Ok, deleting task '$task'...";
  760.                 # Delete task from todo_database
  761.                 sed -i '/'"$ID"'/d' $todo_database
  762.                 # ... and from cron:
  763.                 sed -i '/'"$ID"'/d' $cron
  764.                 crontab $cron
  765.                 break
  766.                 ;;
  767.             [nN] | NO | No | no )
  768.                 echo "Won't delete.";
  769.                 break
  770.                 ;;
  771.         *) echo "Invalid input, try again. (either y/n is accepted)"
  772.                 ;;
  773.         esac
  774.     done
  775. }
  776.  
  777. function delete_bd() {
  778.     # Will create selection dialog for selecting names to be deleted.
  779.    
  780.     shift # Gets rid of potential -db flag
  781.     nr_of_lines=$(sed -n "$=" $birthday_database)
  782.    
  783.     if [[ -z "$@" ]]; then  # i.e. no flags were provided, meaning GUI
  784.         # Create the array for zenity:
  785.         while read line; do
  786.             eval set "$line"
  787.             ID="$1"
  788.             name="$2"
  789.             arr=("${arr[@]}" "FALSE" "$ID" "$name" )
  790.         done < $birthday_database
  791.  
  792.         # Display name selection dialog:
  793.         ans=$(zenity --list --width="800" --height="300" --text="Pick birthday entries to be deleted:" \
  794.         --checklist --column "Pick" --hide-column=2 --column "Birthday ID" --column "Name" "${arr[@]}" --separator=" ")
  795.  
  796.         [[ -n $ans ]] && { eval set "$ans"; } || { set ""; }
  797.        
  798.         if [[ "$#" -gt "3" ]]; then
  799.             if zenity --question --text="You've selected quite a few names to delete;\nwant to skip deletion confirmation?"; then
  800.                 skip_confirm="True"
  801.             fi
  802.         fi
  803.        
  804.         for i in $ans; do   # If nothing was selected, script simply skips this
  805.             eval set "$(grep $i $birthday_database)"
  806.             ID="$1"
  807.             name="$2"
  808.             if [[ "$skip_confirm" != "True" ]]; then
  809.                 if zenity --question --text="Sure you want to delete entry\n$name?"; then
  810.                     # Delete entry from birthday_database
  811.                     sed -i '/'"$i"'/d' $birthday_database
  812.                 fi
  813.             else
  814.                 # Delete entry from birthday_database
  815.                 sed -i '/'"$i"'/d' $birthday_database
  816.             fi
  817.         done
  818.         return
  819.     # CLI from here on:
  820.  
  821.     # This part deletes by the entry #nr entered by the user:
  822.     elif [[ "$#" != "1" ]] || [[ "$1" -le "0" ]]; then
  823.         print_error "few_args"
  824.     # Check that user isn't asking deletion of entry that's non-existent:
  825.     elif [[ "$1" -gt "$nr_of_lines" ]]; then
  826.         echo -e "Requested entry nr $1 doesn't exist.\nNr of entries currently in database is $nr_of_lines."; exit 1
  827.     fi
  828.     eval set "$(sed -n "$1"p $birthday_database)"
  829.     ID="$1"
  830.     name="$2"
  831.     echo -e "Are you sure you want to delete entry"; $bold
  832.     echo -e "$name\n"; $offbold
  833.     echo "(y/n)?"
  834.     while :; do
  835.         read yno
  836.         case $yno in
  837.             [yY] | YEs | YES | Yes | yes )
  838.                 echo "Ok, deleting entry '$name'...";
  839.                 # Delete entry from birthday_database
  840.                 sed -i '/'"$ID"'/d' $birthday_database
  841.                 break
  842.                 ;;
  843.             [nN] | NO | No | no )
  844.                 echo "Won't delete.";
  845.                 break
  846.                 ;;
  847.         *) echo "Invalid input, try again. (either y/n is accepted)"
  848.                 ;;
  849.         esac
  850.     done
  851. }
  852.        
  853. function cronjob() {
  854.     # Cron will feed jobs here.
  855.  
  856.     ident="$1"
  857.     task="$2"
  858.  
  859.     # Part that displays the task reminder on desktop:
  860.     notify-send -t 0 -i gtk-dialog-info "$task"
  861.  
  862.     old_date="$3"
  863.    
  864.     # Delete old entry from cron:
  865.     sed -i '/'"'$ident' '$task' '$old_date'"'/d' $cron
  866.  
  867.     eval set "$old_date"
  868.    
  869.     if [[ "$#" == "3" ]]; then
  870.         time="$1"
  871.         exe_time_old="$2"
  872.         gap="$3"
  873.  
  874.         exe_time="$(eval date  -d \"$time\")"
  875.         # If the target date is already history, add +gap for the exec time:
  876.         if [[ "$((`date +%s -d "$exe_time"`))" -lt "$((`date +%s`))" ]]; then
  877.             exe_time="$(eval date  -d \"$time+$gap\")"
  878.         fi
  879.        
  880.         time_new="\"$time\" \"$exe_time\" \"$gap\""
  881.        
  882.         # Update todo database:
  883.         sed -i '/'"'$ident'"'/s/'"'$old_date'"'/'"'$time_new'"'/g' $todo_database
  884.  
  885.         # Update cron:
  886.  
  887.         find_time_details "$exe_time"
  888.         # Only add updated task, if target time is less than year from now:
  889.         if [[ "$TARGET" -lt "$(( $NOW + 24*60*60*365 ))" ]]; then
  890.             echo "$mins $hours $date $month * $DIR/$zero '$ident' '$task' '$time_new'" >> $cron
  891.         fi
  892.  
  893.     else    # meaning non-rep date
  894.         # Check whether there are any other exe times for this task;
  895.         # if not, delete the task:
  896.  
  897.         eval set "$(grep $ident $todo_database)"
  898.         if [[ "$#" == "3" ]]; then
  899.             # Meaning this was the only exe date, so delete this entry:
  900.             sed -i '/'"'$ident'"'/d' $todo_database
  901.         else
  902.             # Meaning there are other exe dates pending; remove only this particular exe time:
  903.             sed -i '/'"'$ident'"'/s/'" '$old_date'"'//g' $todo_database
  904.         fi
  905.     fi
  906.     # Install the renewed crontab:
  907.     crontab $cron
  908. }
  909.  
  910. function install_() {
  911.     # Function for moving scriptfile into $install_path and adding cronjobs
  912.     # into '/etc/cron.d/' to run this script at boot-up and some specific time in a day.
  913.    
  914.     # Do not use this. It's a dirty-dirty hack.
  915.     if [[ "$(whoami)" == "root" ]]; then
  916.         echo "Please do not start installation being root. Abort"; exit
  917.     fi
  918.     escaped_path=$(echo $install_path | sed -e 's/\([[\/.*]\|\]\)/\\&/g')
  919.     non_root="$(whoami)"
  920.    
  921.     # Check whether $install_path even exists:
  922.     [[ -d "$install_path" ]] || { notify-send -t 0 -i gtk-dialog-error "Default installation directory $install_path does not exist. Edit scriptfile and change installation destination dir. Aborting."; exit 1; }
  923.    
  924.     if [[ "$UI" == "True" ]]; then
  925.         zenity --question --text="Looks like the script isn't installed.\nThis will install the script into $install_path\nand add cronjobs into $todo_crontab that will run script\nwith --auto flag at every midnight and system startup.\n\n(You can alternatively install by running\n'$DIR/$zero -i' in terminal.)\n  PLEASE NOTE IT'S NOT RECOMMENDED TO USE AUTOMATED INSTALLATION\n\nYou'll be better off:\n  a) copying this file to /usr/local/bin\n  b) adding cron job to run this script once a day with '--auto' flag\n  c) adding this script to startup applications, so it would also be run\n  with '--auto' flag at startup\n\nContinue anyway?" || exit
  926.        
  927.         # Check which graphical sudo program can be used:
  928.         for Gsudo in gksudo beesu kdesu gksu ktsuss NoSudo; do command -v $Gsudo >/dev/null && break; done
  929.         if [[ "$Gsudo" == "NoSudo" ]]; then
  930.             notify-send -t 0 -i gtk-dialog-error "No graphical sudo found. Install script by running '$DIR/$zero -i' in terminal! Aborting."; exit 1
  931.         fi
  932.  
  933.         # Skip file move if script is already at $install_path:
  934.         if [[ "$DIR" != "$install_path" ]]; then
  935.             if [[ -f "$install_path/$zero" ]]; then
  936.                 if zenity --question --text="There already is a file called '$zero' in $install_path\nOverwrite?"; then
  937.                     $Gsudo mv $DIR/$zero $install_path/$zero
  938.                     echo "Ok, overwriting '$install_path/$zero'...";
  939.                     notify-send -t 3000 -i gtk-dialog-info "Ok, overwriting '$install_path/$zero'..."
  940.                 else
  941.                     echo "Won't overwrite.";
  942.                     notify-send -t 3000 -i gtk-dialog-info "Ok, won't overwrite."
  943.                 fi
  944.             else
  945.                 $Gsudo mv $DIR/$zero $install_path/$zero
  946.             fi
  947.            
  948.             # Make sure file move was successful:
  949.             [[ -f "$install_path/$zero" ]] && { DIR="$install_path"; $Gsudo chmod +x $install_path/$zero; } || \
  950.             { notify-send -t 0 -i gtk-dialog-error "Could not move the file, aborting."; exit 1; }
  951.         fi
  952.        
  953.         # Create a file in /etc/pm/sleep.d/ which will execute script with --auto flag after suspension:
  954.         if [[ -d "/etc/pm/sleep.d/" ]] && ! [[ -f "/etc/pm/sleep.d/todo_exe.sh" ]]; then
  955.             echo -e "#!/bin/bash\ncase \$1 in\nresume)\nsleep 5\n$DIR/$zero --auto\n;;\nesac" > $tmp
  956.             $Gsudo mv $tmp /etc/pm/sleep.d/todo_exe.sh
  957.             $Gsudo chown root /etc/pm/sleep.d/todo_exe.sh
  958.             $Gsudo chmod +x /etc/pm/sleep.d/todo_exe.sh
  959.         fi
  960.  
  961.         # Check if system has $todo_crontab:
  962.         if ! [[ -f "$todo_crontab" ]]; then
  963.             # Does $todo_cron_dir dir exist?:
  964.             [[ -d "$todo_cron_dir" ]] || { notify-send -t 0 -i gtk-dialog-error "Oops, it looks like your system has no such folder as as $todo_cron_dir :(. Can't install cronjobs, please contact laur.aliste [at] gmail and report this error"; exit 1; }
  965.             echo -e "# Custom crontab used by $DIR/$zero\n\nSHELL=/bin/bash\nPATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n\n# m h dom mon dow user    command\n#" > $tmp
  966.         else
  967.             cat $todo_crontab > $tmp  # Copies crontab file into tmp-file for processing.
  968.         fi
  969.         # Create cronjob entries that'll run the script at certain times:
  970.  
  971.        
  972.         # Check whether there already are cronjobs for this script:
  973.         #if [[ -n "$(grep "$USER $DIR/$zero --auto" $tmp)" ]]; then
  974.         #    zenity --question --text="There already are cron entries for this script in $todo_crontab\nCreate new one?" || return
  975.         #fi
  976.  
  977.         time=$(zenity --entry --text "What time every day would you like the script to run?\n(leave blank for default midnight)")
  978.        
  979.         while [[ "$(eval date +%s -d \"$time\")" == "0" ]] || [[ "$(eval date +%s -d \"$time\")" == "" ]]; do
  980.             time=$(zenity --entry --text "Bad time! Try again:" --entry-text "e.g. 19:45")
  981.         done
  982.         [[ -z "$time" ]] && time="00:00"
  983.  
  984.         find_time_details "$time"
  985.  
  986.         notify-send -t 3000 -i gtk-dialog-info "Ok, adding cron entry..."
  987.         echo "Ok, adding cron entry...";
  988.         sed -i '/'"$USER $escaped_path\/$zero --auto"'/d' $tmp
  989.         echo "$mins $hours * * * $USER $install_path/$zero --auto" >> $tmp                  
  990.         $Gsudo mv $tmp $todo_crontab
  991.         $Gsudo chown root $todo_crontab
  992.        
  993.         # Create init file in etc/init.d/ and add link, so script would be started at startup:
  994.         # This IS NOT recommended!
  995.        
  996.         # Check if system has /etc/init.d/:
  997.         if ! [ -f /etc/init.d/todo_script_init ]; then
  998.             [ -d /etc/init.d/ ] || { notify-send -t 0 -i gtk-dialog-error "/etc/init.d/ dir not found. Please report this problem to laur.aliste ät gmail."; exit; }
  999.             printf "#! /bin/bash\n\n### BEGIN INIT INFO
  1000. # Provides:          todo-script autocheck at startup
  1001. # Required-Start:    \$all
  1002. # Required-Stop:
  1003. # X-Start-Before:    
  1004. # Default-Start:     2 3 4 5
  1005. # Default-Stop:
  1006. # Description: runs 'todo --auto' at startup, when xserver has started and user is logged on.
  1007. ### END INIT INFO
  1008. usr=\"$non_root\"  # User whose logon to wait for
  1009. script=\"$install_path/$zero --auto\"
  1010. ###########################################################
  1011.  
  1012. case \"\$1\" in
  1013.    start)
  1014.        # Wait until X is running and required user logged in:
  1015.        count=0
  1016.        until [[ \"\$(tty)\" != \"\/dev\/tty1\" ]] && [[ \"\$(who | cut -d' ' -f1 | sort | uniq)\" == \"\$usr\" ]]; do
  1017.            sleep 5
  1018.            let count=\$count+1
  1019.            if [[ \"\$count\" == \"10\" ]]; then
  1020.                echo \"Xserver hasn't started or other error occurred. Abort\"; exit 1
  1021.            fi
  1022.        done
  1023.        # Run the script as YOURUSERNAME instead of root:
  1024.        su - \$usr -c '\$script'
  1025.    ;;
  1026.    stop)
  1027.        :
  1028.    ;;
  1029. esac
  1030. exit 0" > $tmp
  1031.  
  1032.             $Gsudo mv $tmp /etc/init.d/todo_script_init
  1033.             $Gsudo chmod +x /etc/init.d/todo_script_init
  1034.             # ... create the link:
  1035.             sudo insserv todo_script_init
  1036.         fi
  1037.    
  1038.     else    # CLI
  1039.         clear
  1040.         echo -e "Looks like the script isn't installed.\nThis will install the script into $install_path\nand add cronjobs into $todo_crontab that will run script\nwith --auto flag at every midnight and system startup.\n\n(You can alternatively install by running\n'$DIR/$zero -i' in terminal.)\n  PLEASE NOTE IT'S NOT RECOMMENDED TO USE AUTOMATED INSTALLATION\n\nYou'll be better off:\n  a) copying this file to /usr/local/bin\n  b) adding cron job to run this script once a day with '--auto' flag\n  c) adding this script to startup applications, so it would also be run\n  with '--auto' flag at startup\n\nContinue anyway? (y/n)\n"
  1041.         read proceed
  1042.         [[ "$proceed" == "y" ]] && { echo "Ok"; } || { echo "Abort."; exit; }
  1043.         # Skip file move if script is already at $install_path:
  1044.         if [[ "$DIR" != "$install_path" ]]; then
  1045.             if [[ -f "$install_path/$zero" ]]; then
  1046.                 clear
  1047.                 echo -e "There already is a file called '$zero' in $install_path\nOverwrite (y/n)?"
  1048.                 while :; do
  1049.                     read yno
  1050.                     case $yno in
  1051.                         [yY] | YEs | YES | Yes | yes )
  1052.                             echo "Ok, overwriting '$install_path/$zero'...";
  1053.                             sudo mv $DIR/$zero $install_path/$zero
  1054.                             break
  1055.                             ;;
  1056.                         [nN] | NO | No | no )
  1057.                             echo "Won't overwrite.";
  1058.                             break
  1059.                             ;;
  1060.                     *) echo "Invalid input, try again. (either y/n is accepted)"
  1061.                             ;;
  1062.                     esac
  1063.                 done
  1064.             else
  1065.                 sudo mv $DIR/$zero $install_path/$zero
  1066.             fi
  1067.            
  1068.             # Make sure file move was successful:
  1069.             [[ -f "$install_path/$zero" ]] && { DIR="$install_path"; sudo chmod +x $install_path/$zero; } \
  1070.             || { echo "Could not move the file, abort."; exit 1; }
  1071.         fi
  1072.  
  1073.         # Create a file in /etc/pm/sleep.d/ which will execute script with --auto flag after suspension:
  1074.         if [[ -d "/etc/pm/sleep.d/" ]] && ! [[ -f "/etc/pm/sleep.d/todo_exe.sh" ]]; then
  1075.             echo -e "#!/bin/bash\ncase \$1 in\nresume)\nsleep 5\n$DIR/$zero --auto\n;;\nesac" > $tmp
  1076.             sudo mv $tmp /etc/pm/sleep.d/todo_exe.sh
  1077.             sudo chown root /etc/pm/sleep.d/todo_exe.sh
  1078.             sudo chmod +x /etc/pm/sleep.d/todo_exe.sh
  1079.         fi
  1080.        
  1081.         clear
  1082.        
  1083.         # Check if system has $todo_crontab:
  1084.         if ! [[ -f "$todo_crontab" ]]; then
  1085.             # Does $todo_cron_dir dir exist?:
  1086.             [[ -d "$todo_cron_dir" ]] || { echo -e "Oops, it looks like your system has no such folder as as $todo_cron_dir :(\nCan't install cronjobs, please contact laur.aliste [at] gmail and report this error"; exit 1; }
  1087.             echo -e "# Custom crontab used by $DIR/$zero\n\nSHELL=/bin/bash\nPATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n\n# m h dom mon dow user    command\n#" > $tmp
  1088.         else
  1089.             cat $todo_crontab > $tmp
  1090.         fi
  1091.         # Create cronjob entries that'll run the script at certain times:
  1092.  
  1093.         # Check whether there already are cronjobs for this script:
  1094.         #if [[ -n "$(grep "$USER $DIR/$zero --auto" $tmp)" ]]; then
  1095.         #    echo -e "There already are cron entries for this script in $todo_crontab\nCreate new one? (y/n)?"
  1096.         #    while :; do
  1097.         #        read yno
  1098.         #        case $yno in
  1099.         #            [yY] | YEs | YES | Yes | yes )
  1100.         #                break
  1101.         #                ;;
  1102.         #            [nN] | NO | No | no )
  1103.         #                echo "Won't create new crontab entries."; return
  1104.         #                ;;
  1105.         #        *) echo "Invalid input, try again. (either y/n is accepted)"
  1106.         #                ;;
  1107.         #        esac
  1108.         #    done
  1109.         #fi
  1110.         echo -e "What time every day would you like the script to run?\n(it'll run in the background)\n(leave blank for default midnight)"
  1111.         read time
  1112.         [[ -z "$time" ]] && time="00:00"
  1113.  
  1114.         while [[ "$(eval date +%s -d \"$time\")" == "0" ]] || [[ "$(eval date +%s -d \"$time\")" == "" ]]; do
  1115.             clear
  1116.             echo -e "Bad time format, try again! (e.g. 00:00, 15:30, 1200, 1430)"
  1117.             read time
  1118.             [[ -z "$time" ]] && time="00:00"
  1119.         done
  1120.         find_time_details "$time"
  1121.  
  1122.         echo "Ok, adding cron entry...";
  1123.         sed -i '/'"$USER $escaped_path\/$zero --auto"'/d' $tmp
  1124.         echo "$mins $hours * * * $USER $install_path/$zero --auto" >> $tmp                    
  1125.         sudo mv $tmp $todo_crontab
  1126.         sudo chown root $todo_crontab
  1127.        
  1128.         # Create init file in etc/init.d/ and add link, so script would be started at startup:
  1129.         # This IS NOT recommended!
  1130.        
  1131.         # Check if system has /etc/init.d/:
  1132.         if ! [ -f /etc/init.d/todo_script_init ]; then
  1133.             [ -d /etc/init.d/ ] || { echo "/etc/init.d/ dir not found. Please report this problem to laur.aliste ät gmail."; exit; }
  1134.             printf "#! /bin/bash\n\n### BEGIN INIT INFO
  1135. # Provides:          todo-script autocheck at startup
  1136. # Required-Start:    \$all
  1137. # Required-Stop:
  1138. # X-Start-Before:    
  1139. # Default-Start:     2 3 4 5
  1140. # Default-Stop:
  1141. # Description: runs 'todo --auto' at startup, when xserver has started and user is logged on.
  1142. ### END INIT INFO
  1143. usr=\"$non_root\"  # User whose logon to wait for
  1144. script=\"$install_path/$zero --auto\"
  1145. ###########################################################
  1146.  
  1147. case \"\$1\" in
  1148.    start)
  1149.        # Wait until X is running and required user logged in:
  1150.        count=0
  1151.        until [[ \"\$(tty)\" != \"\/dev\/tty1\" ]] && [[ \"\$(who | cut -d' ' -f1 | sort | uniq)\" == \"\$usr\" ]]; do
  1152.            sleep 5
  1153.            let count=\$count+1
  1154.            if [[ \"\$count\" == \"10\" ]]; then
  1155.                echo \"Xserver hasn't started or other error occurred. Abort\"; exit 1
  1156.            fi
  1157.        done
  1158.        # Run the script as YOURUSERNAME instead of root:
  1159.        su - \$usr -c '\$script'
  1160.    ;;
  1161.    stop)
  1162.        :
  1163.    ;;
  1164. esac
  1165. exit 0" > $tmp
  1166.             sudo mv $tmp /etc/init.d/todo_script_init
  1167.             sudo chmod +x /etc/init.d/todo_script_init
  1168.             # ... create the link:
  1169.             sudo insserv todo_script_init
  1170.         fi
  1171.        
  1172.     fi
  1173. }
  1174.  
  1175. function uninstall_() {
  1176.     # Note when script isn't located at $install_path, one will be first
  1177.     # prompted with installation.
  1178.    
  1179.     echo "Sure you want to uninstall this script and corresponding files? (y/n)?"
  1180.     while :; do
  1181.         read yno
  1182.         case $yno in
  1183.             [yY] | YEs | YES | Yes | yes )
  1184.                 break
  1185.                 ;;
  1186.             [nN] | NO | No | no )
  1187.                 clear
  1188.                 echo "Ok, won't uninstall."; exit
  1189.                 ;;
  1190.         *) echo "Invalid input, try again. (either y/n is accepted)"
  1191.                 ;;
  1192.         esac
  1193.     done
  1194.  
  1195.     > $tmp
  1196.     crontab $tmp # Clears currently installed cronjobs
  1197.     rm $tmp
  1198.     sudo rm $todo_crontab
  1199.     sudo rm /etc/pm/sleep.d/todo_exe.sh
  1200.     sudo rm $cron
  1201.    
  1202.     if [ -f /etc/init.d/todo_script_init ]; then
  1203.         sudo rm /etc/init.d/todo_script_init
  1204.         sudo update-rc.d -f todo_script_init remove
  1205.     fi
  1206.    
  1207.     echo "Delete also birthday and tasks files? (y/n)?"
  1208.     while :; do
  1209.         read yno
  1210.         case $yno in
  1211.             [yY] | YEs | YES | Yes | yes )
  1212.                 sudo rm $birthday_database
  1213.                 sudo rm $todo_database
  1214.                 break
  1215.                 ;;
  1216.             [nN] | NO | No | no )
  1217.                 clear
  1218.                 echo "Ok, won't delete birthday and tasks files."
  1219.                 break
  1220.                 ;;
  1221.         *) echo "Invalid input, try again. (either y/n is accepted)"
  1222.                 ;;
  1223.         esac
  1224.     done
  1225.    
  1226.     sudo rm $DIR/$zero  # Finally, remove the scriptfile itself
  1227.     echo "All done, goodbye!"; exit
  1228. }
  1229. ################################## START #######################################
  1230. # Update DISPLAY values if incorrect (check only if script is not run by cron):
  1231. if [[ "$disp" != "$disp2" ]]; then
  1232.     if [[ "$1" != "--auto" && "$1" != $tag1*$tag2 ]] && [[ "$whoami" != "root" ]] ;then
  1233.         [[ "$DIR" == "$install_path" ]] && \
  1234.         { gsudo="sudo"; echo "Please enter password so some changes could be made to $zero:"; }
  1235.         $gsudo sed -i 's/'"export DISPLAY\=$disp"'/'"export DISPLAY\=$DISPLAY"'/g' $DIR/$zero
  1236.         clear
  1237.         $gsudo sed -i 's/'disp\=\"$disp\"'/'disp\=\"$DISPLAY\"'/g' $DIR/$zero
  1238.         echo "Updated some data within $zero, please restart."
  1239.         notify-send -t 3000 -i gtk-dialog-info "Updated some data within $DIR/$zero, please restart."; exit
  1240.     fi
  1241. fi
  1242.  
  1243. # Check if required programs are installed; if not, exit:
  1244. check_program "notify-send" "zenity"
  1245.  
  1246.  
  1247. # Create necessary files-folders if not existing:
  1248. check_files "$todo_database" "$cron" "$birthday_database"
  1249.  
  1250. # Just in case check the size of todo and birthday databases and cron file;
  1251. # if size is larger than predefined value, display an error:
  1252. check_filesize "$todo_database" "$birthday_database" "$cron"
  1253.  
  1254. if [[ -z "$@" ]]; then  # No command-line flags, meaning GUI usage
  1255.     opt_1="Add new tasks"
  1256.     opt_2="View entered tasks"
  1257.     opt_3="Delete tasks"
  1258.     opt_4="Add new birthdays"
  1259.     opt_5="View entered birthdays"
  1260.     opt_6="Delete birthdays"
  1261.     opt_7=" "   # Simply a buffer-line to separate different blocks
  1262.     opt_8="Edit todo database"
  1263.     opt_9="Edit birthday database"
  1264.     opt_="dummy_value"
  1265.     UI="True"
  1266.     until [[ -z "$opt_" ]]; do
  1267.         NOW=$((`date +%s`))
  1268.         unset arr skip repetitive important add another_date date_arr opt skip_confirm opt_
  1269.        
  1270.         opt_=`zenity --width="500" --height="320" --list --cancel-label="Exit" --text="What would you like to do?" \
  1271.          --radiolist --column "" --column "Pick a task:" --hide-header TRUE "$opt_1" \
  1272.          FALSE "$opt_2" FALSE "$opt_3" FALSE "$opt_4" FALSE "$opt_5" FALSE "$opt_6" FALSE "$opt_7" FALSE "$opt_8" FALSE "$opt_9"`
  1273.  
  1274.         case $opt_ in
  1275.             "$opt_1") add_task
  1276.                 ;;
  1277.             "$opt_2") list_task
  1278.                 ;;
  1279.             "$opt_3") delete
  1280.                 ;;
  1281.             "$opt_4") add_bd
  1282.                 ;;
  1283.             "$opt_5") list_bd
  1284.                 ;;
  1285.             "$opt_6") delete_bd
  1286.                 ;;
  1287.             "$opt_7") opt_7="                      What were you hoping to find here?"
  1288.                 ;;
  1289.             "$opt_8")
  1290.                 for txt_editor in gedit gvim NEdit sublime mousepad geany Not_Found; do
  1291.                     command -v $txt_editor >/dev/null && break; done
  1292.                 if [[ "$txt_editor" == "Not_Found" ]]; then
  1293.                     notify-send -t 5000 -i gtk-dialog-error "Didn't find any graphical txt editors :("; exit 1
  1294.                 fi
  1295.                 $txt_editor $todo_database
  1296.                 ;;
  1297.             "$opt_9")
  1298.                 for txt_editor in gedit gvim NEdit sublime mousepad geany Not_Found; do
  1299.                     command -v $txt_editor >/dev/null && break; done
  1300.                 if [[ "$txt_editor" == "Not_Found" ]]; then
  1301.                     notify-send -t 5000 -i gtk-dialog-error "Didn't find any graphical txt editors :("; exit 1
  1302.                 fi
  1303.                 $txt_editor $birthday_database
  1304.                 ;;
  1305.         esac
  1306.     done
  1307. exit
  1308. fi
  1309.  
  1310. case $1 in
  1311.     "-a" | "--add" )    # Graphical task addition:
  1312.         UI="True"
  1313.         add_task
  1314.         ;;
  1315.     "-am" | "--add_manual" )    # Terminal-based task addition:
  1316.         add_task
  1317.         ;;
  1318.     "-b" | "--birthday" )    # Graphical birthday addition:
  1319.         UI="True"
  1320.         add_bd
  1321.         ;;
  1322.     "-bm" | "--bd_manual" )    # Terminal-based birthday addition:
  1323.         add_bd
  1324.         ;;
  1325.     "--auto" )          # This flag has to be used on the system startup run and another random run once a day
  1326.         auto_check
  1327.         ;;
  1328.     "-l" | "--list" )    # Listing of current tasks in the todo database:
  1329.         list_task
  1330.         ;;
  1331.     "-lb" | "--list_bd" )    # Listing of current birthdays in the database:
  1332.         list_bd
  1333.         ;;
  1334.     "-d" | "--delete"| "--del" )    # Task deletion from the database:
  1335.         delete "$@"
  1336.         ;;
  1337.     "-db" | "--delete_bd"| "--del" )    # Birthday deletion from the database:
  1338.         delete_bd "$@"
  1339.         ;;
  1340.     $tag1*$tag2 )    # Cron feeds jobs into this part:
  1341.         cronjob "$@"
  1342.         ;;
  1343.     "-i" | "--install" )    # Moves file to $install_path and adds cronjobs
  1344.         install_
  1345.         ;;
  1346.     "-ui" | "--uninstall" )
  1347.         uninstall_
  1348.         ;;
  1349.     *) echo "Invalid input, try again."
  1350.         ;;
  1351. esac
  1352. exit
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement