src/scripts/fswatch
author Dave Dribin <dave@dribin.org>
Fri Jan 04 15:37:44 2008 -0600 (2008-01-04)
changeset 68 73cc78a030bf
parent 66 152f19907eb1
child 73 e493ac32ba79
permissions -rwxr-xr-x
Re-add die() and remove the rescue of Interrupt
dave@59
     1
#!/usr/bin/ruby
dave@59
     2
dave@59
     3
# This script watches modifications on the given directory, using the new # FSEvents API in Leopard.
dave@59
     4
dave@59
     5
require 'osx/foundation'
dave@59
     6
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
dave@59
     7
include OSX
dave@59
     8
require 'set'
dave@63
     9
require 'optparse'
dave@63
    10
require 'ostruct'
dave@59
    11
dave@62
    12
$COMMAND = File.basename($0)
dave@64
    13
$USAGE = "Usage: #{$COMMAND} [OPTIONS] <command> [<args> ...]"
dave@68
    14
def die(s)
dave@68
    15
  $stderr.puts s
dave@68
    16
  exit 1
dave@68
    17
end
dave@63
    18
dave@63
    19
options = OpenStruct.new
dave@63
    20
options.directoriesOnly = false
dave@64
    21
options.outputFile = "-"
dave@64
    22
dave@63
    23
opts = OptionParser.new do |opts|
dave@63
    24
  opts.banner = $USAGE
dave@63
    25
  opts.separator ""
dave@63
    26
  opts.separator "Specific options:"
dave@63
    27
dave@63
    28
  opts.on("-d", "--dir-only", "Only display directories") do
dave@63
    29
    options.directoriesOnly = true
dave@63
    30
  end
dave@63
    31
  
dave@64
    32
  opts.on("-o", "--output FILE", "Write output to a file") do |fileName|
dave@64
    33
    options.outputFile = fileName
dave@64
    34
  end
dave@64
    35
  
dave@63
    36
  opts.on_tail("-h", "--help", "Show this message") do
dave@63
    37
    puts opts
dave@63
    38
    exit
dave@63
    39
  end
dave@63
    40
end
dave@63
    41
opts.parse!(ARGV)
dave@63
    42
dave@68
    43
die $USAGE unless ARGV.size >= 1
dave@66
    44
dave@64
    45
if options.outputFile == "-"
dave@64
    46
  outputHandle = $stdout.clone
dave@64
    47
else
dave@64
    48
  outputHandle = File.open(options.outputFile, "w")
dave@64
    49
end
dave@64
    50
dave@59
    51
path = "/"
dave@59
    52
dave@59
    53
startId = FSEventsGetCurrentEventId()
dave@62
    54
# Used to compare with mtime, which only has second accuracy
dave@62
    55
startTime = Time.now.to_i
dave@59
    56
dave@62
    57
fork do
dave@62
    58
  begin
dave@59
    59
    exec(*ARGV)
dave@62
    60
  rescue SystemCallError  => e
dave@62
    61
    puts e.message
dave@62
    62
    exit 127
dave@59
    63
  end
dave@59
    64
end
dave@62
    65
pid = Process.wait
dave@62
    66
returnCode = $? >> 8
dave@59
    67
dave@59
    68
sleep(0.1)
dave@59
    69
dave@59
    70
allPaths = Set.new
dave@59
    71
dave@59
    72
fsevents_cb = proc do |stream, ctx, numEvents, paths, marks, eventIDs|
dave@59
    73
  paths.regard_as('*')
dave@59
    74
  numEvents.times do |n|
dave@62
    75
    allPaths.add(paths[n])
dave@59
    76
  end
dave@59
    77
end
dave@59
    78
dave@59
    79
stream = FSEventStreamCreate(
dave@59
    80
  KCFAllocatorDefault,
dave@59
    81
  fsevents_cb,
dave@59
    82
  nil,
dave@59
    83
  [path],
dave@59
    84
  startId,
dave@59
    85
  1.0,
dave@59
    86
  KFSEventStreamCreateFlagNoDefer)
dave@59
    87
dave@59
    88
die "Failed to create the FSEventStream" unless stream
dave@59
    89
dave@59
    90
FSEventStreamScheduleWithRunLoop(
dave@59
    91
  stream,
dave@59
    92
  CFRunLoopGetCurrent(),
dave@59
    93
  KCFRunLoopDefaultMode)
dave@59
    94
dave@59
    95
ok = FSEventStreamStart(stream)
dave@59
    96
die "Failed to start the FSEventStream" unless ok
dave@59
    97
dave@68
    98
FSEventStreamFlushSync(stream)
dave@68
    99
allPaths.sort.each do |path|
dave@68
   100
  if File.exists?(path)
dave@68
   101
    outputHandle.puts path
dave@68
   102
    if (!options.directoriesOnly)
dave@68
   103
      Dir.foreach(path) do |file|
dave@68
   104
        fullPath = File.join(path, file)
dave@68
   105
        stat = File.stat(fullPath)
dave@68
   106
        if (stat.mtime.to_i >= startTime)
dave@68
   107
          outputHandle.puts "  #{file}"
dave@62
   108
        end
dave@62
   109
      end
dave@62
   110
    end
dave@68
   111
  else
dave@68
   112
    outputHandle.puts "#{path} !"
dave@59
   113
  end
dave@59
   114
end
dave@62
   115
dave@68
   116
FSEventStreamStop(stream)
dave@68
   117
FSEventStreamInvalidate(stream)
dave@68
   118
FSEventStreamRelease(stream)
dave@68
   119
dave@68
   120
dave@64
   121
outputHandle.flush
dave@64
   122
dave@62
   123
exit(returnCode)