gawk: Alarm Program

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