gawk: Alarm Program

1 
1 11.3.2 An Alarm Clock Program
1 -----------------------------
1 
1      Nothing cures insomnia like a ringing alarm clock.
1                           -- _Arnold Robbins_
1      Sleep is for web developers.
1                           -- _Erik Quanstrom_
1 
1    The following program is a simple "alarm clock" program.  You give it
1 a time of day and an optional message.  At the specified time, it prints
1 the message on the standard output.  In addition, you can give it the
1 number of times to repeat the message as well as a delay between
1 repetitions.
1 
11    This program uses the 'getlocaltime()' function from ⇒
 Getlocaltime Function.
1 
1    All the work is done in the 'BEGIN' rule.  The first part is argument
1 checking and setting of defaults: the delay, the count, and the message
1 to print.  If the user supplied a message without the ASCII BEL
1 character (known as the "alert" character, '"\a"'), then it is added to
1 the message.  (On many systems, printing the ASCII BEL generates an
1 audible alert.  Thus, when the alarm goes off, the system calls
1 attention to itself in case the user is not looking at the computer.)
11 Just for a change, this program uses a 'switch' statement (⇒Switch
 Statement), but the processing could be done with a series of
1 'if'-'else' statements instead.  Here is the program:
1 
1      # alarm.awk --- set an alarm
1      #
1      # Requires getlocaltime() library function
1      # usage: alarm time [ "message" [ count [ delay ] ] ]
1 
1      BEGIN {
1          # Initial argument sanity checking
1          usage1 = "usage: alarm time ['message' [count [delay]]]"
1          usage2 = sprintf("\t(%s) time ::= hh:mm", ARGV[1])
1 
1          if (ARGC < 2) {
1              print usage1 > "/dev/stderr"
1              print usage2 > "/dev/stderr"
1              exit 1
1          }
1          switch (ARGC) {
1          case 5:
1              delay = ARGV[4] + 0
1              # fall through
1          case 4:
1              count = ARGV[3] + 0
1              # fall through
1          case 3:
1              message = ARGV[2]
1              break
1          default:
1              if (ARGV[1] !~ /[[:digit:]]?[[:digit:]]:[[:digit:]]{2}/) {
1                  print usage1 > "/dev/stderr"
1                  print usage2 > "/dev/stderr"
1                  exit 1
1              }
1              break
1          }
1 
1          # set defaults for once we reach the desired time
1          if (delay == 0)
1              delay = 180    # 3 minutes
1          if (count == 0)
1              count = 5
1          if (message == "")
1              message = sprintf("\aIt is now %s!\a", ARGV[1])
1          else if (index(message, "\a") == 0)
1              message = "\a" message "\a"
1 
1    The next minor node of code turns the alarm time into hours and
1 minutes, converts it (if necessary) to a 24-hour clock, and then turns
1 that time into a count of the seconds since midnight.  Next it turns the
1 current time into a count of seconds since midnight.  The difference
1 between the two is how long to wait before setting off the alarm:
1 
1          # split up alarm time
1          split(ARGV[1], atime, ":")
1          hour = atime[1] + 0    # force numeric
1          minute = atime[2] + 0  # force numeric
1 
1          # get current broken down time
1          getlocaltime(now)
1 
1          # if time given is 12-hour hours and it's after that
1          # hour, e.g., `alarm 5:30' at 9 a.m. means 5:30 p.m.,
1          # then add 12 to real hour
1          if (hour < 12 && now["hour"] > hour)
1              hour += 12
1 
1          # set target time in seconds since midnight
1          target = (hour * 60 * 60) + (minute * 60)
1 
1          # get current time in seconds since midnight
1          current = (now["hour"] * 60 * 60) + \
1                     (now["minute"] * 60) + now["second"]
1 
1          # how long to sleep for
1          naptime = target - current
1          if (naptime <= 0) {
1              print "alarm: time is in the past!" > "/dev/stderr"
1              exit 1
1          }
1 
11    Finally, the program uses the 'system()' function (⇒I/O
 Functions) to call the 'sleep' utility.  The 'sleep' utility simply
1 pauses for the given number of seconds.  If the exit status is not zero,
1 the program assumes that 'sleep' was interrupted and exits.  If 'sleep'
1 exited with an OK status (zero), then the program prints the message in
1 a loop, again using 'sleep' to delay for however many seconds are
1 necessary:
1 
1          # zzzzzz..... go away if interrupted
1          if (system(sprintf("sleep %d", naptime)) != 0)
1              exit 1
1 
1          # time to notify!
1          command = sprintf("sleep %d", delay)
1          for (i = 1; i <= count; i++) {
1              print message
1              # if sleep command interrupted, go away
1              if (system(command) != 0)
1                  break
1          }
1 
1          exit 0
1      }
1