#!/bin/sh
# the next line restarts using tclsh \
    exec tclsh "$0" "$@"


################################################################################
#
# loopy.tcl
#
# launches a job and keeps retrying until it succeeds
# - job status into an output directory
#
# Usage:
#   loopy [options] <cmd>
#
# Initiated - sp - 2007-03-29
#

################################################################################
#
# simple command line argument parsing
#

proc Usage { {msg ""} } {
    
    set msg "$msg\nusage: loopy \[options\] <cmd>"
    set msg "$msg\n  <cmd> is the command to run"
    set msg "$msg\n  \[options\] is one of the following:"
    set msg "$msg\n   -h --help : prints this message and exits"
    set msg "$msg\n   -d --done-file : file that will not exist until cmd completes ok"
    set msg "$msg\n   -l --log-dir : directory to write log files"
    set msg "$msg\n   -r --retries : number or retries on failure (default 0)"
    set msg "$msg\n                : -1 to loop until killed or success"
    set msg "$msg\n   -w --wrapper : run as a command wrapper and write done-file when command exits"
    puts stderr $msg
}

set ::LOOPY(cmd) ""
set ::LOOPY(doneFile) ""
set ::LOOPY(logDir) ""
set ::LOOPY(retries) 0
set ::LOOPY(wrapper) 0
set strippedargs ""
set argc [llength $argv]
for {set i 0} {$i < $argc} {incr i} {
    set a [lindex $argv $i]
    switch -glob -- $a {
        "--done-file" -
        "-d" {
            incr i
            if { $i == $argc } {
                Usage "Missing done-file argument"
            } else {
                set ::LOOPY(doneFile) [lindex $argv $i]
            }
        }
        "--log-dir" -
        "-l" {
            incr i
            if { $i == $argc } {
                Usage "Missing done-file argument"
            } else {
                set ::LOOPY(logDir) [lindex $argv $i]
            }
        }
        "--retries" -
        "-r" {
            incr i
            if { $i == $argc } {
                Usage "Missing done-file argument"
            } else {
                set ::LOOPY(retries) [lindex $argv $i]
            }
        }
        "--wrapper" -
        "-w" {
          set ::LOOPY(wrapper) 1
        }
        "--help" -
        "-h" {
            Usage
            exit 1
        }
        "-*" {
            Usage "unknown option $a\n"
            exit 1
        }
        default {
            lappend strippedargs $a
        }
    }
}
set argv $strippedargs
set argc [llength $argv]

if {$argc > 1 } {
    Usage
    exit 1
}

eval set ::LOOPY(cmd) $argv


if { $::LOOPY(wrapper) } {
  if { $::LOOPY(cmd) != "" } {
    eval exec $::LOOPY(cmd)
  }
  file mkdir [file dirname $::LOOPY(doneFile)]
  close [open $::LOOPY(doneFile) "w"]
  exit 0
}



################################################################################
#
# Utilities:

proc launchcmd_local {cmd} {

    # print the results line by line to provide feedback during long builds
    # interleaves the results of stdout and stderr
    set pid [eval exec $cmd >>& /tmp/log &]
    return $pid
}

# returns 1 if still running, 0 otherwise
# - note: unix only, uses kill
proc checkcmd_local {id} {
  return [expr ![catch "exec kill -0 $id"]]
}


set tries 0
while { ![file exists $::LOOPY(doneFile)] } { 

  set started $::LOOPY(doneFile).started
  file mkdir [file dirname $::LOOPY(doneFile)]
  if { ![file exists $started] } {
    set id [launchcmd_local $::LOOPY(cmd)]
    set fp [open $started "w"]
    puts $fp $id
    close $fp
  }

  while {1} {
    set fp [open $started "r"]
    set id [read $fp]
    close $fp
    if { [checkcmd_local $id] } {
      after 100
    } else {
      break
    }
  }

  file delete $started

  if { [file exists $::LOOPY(doneFile)] } { 
    break
  }


  incr tries
  if { $::LOOPY(retries) >= 0 && $tries >= $::LOOPY(retries) } {
    puts "too many failures!"
    exit 1
    break
  }

}

exit 0
