3 # This script watches modifications on the given directory, using the new # FSEvents API in Leopard.
5 require 'osx/foundation'
6 OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
12 $COMMAND = File.basename($0)
13 $USAGE = "Usage: #{$COMMAND} [OPTIONS] <command> [<args> ...]"
19 def run_command(*args)
23 rescue SystemCallError => e
34 def wait_until_interrupt
36 $stderr.puts "Press Control-C to stop monitoring"
37 sleep(1.0) while (true)
43 options = OpenStruct.new
44 options.directoriesOnly = false
45 options.outputFile = "-"
47 options.show_current_id = false
48 options.start_id = nil
51 opts = OptionParser.new do |opts|
54 opts.separator "Specific options:"
56 opts.on("-d", "--dir-only", "Only display directories") do
57 options.directoriesOnly = true
60 opts.on("-o", "--output FILE", "Write output to a file") do |fileName|
61 options.outputFile = fileName
64 opts.on("-w", "--wait", "Wait until interrupted") do
68 opts.on("-c", "--current-id", "Show current id and exit") do
69 options.show_current_id = true
72 opts.on("-C", "--use-start-id START_ID", "Use start ID") do |start_id|
73 options.start_id = start_id
76 opts.on("-p", "--path PATH", "Path to show") do |path|
80 opts.on_tail("-h", "--help", "Show this message") do
87 if !options.wait and ARGV == 0
91 if options.outputFile == "-"
92 outputHandle = $stdout.clone
94 outputHandle = File.open(options.outputFile, "w")
99 startId = FSEventsGetCurrentEventId()
100 # Used to compare with mtime, which only has second accuracy
101 startTime = Time.now.to_i
103 if (options.show_current_id)
104 puts "#{startId}:#{startTime}"
108 if !options.start_id.nil? then
109 startId, startTime = options.start_id.split(':').map { |s| s.to_i }
111 elsif options.wait then
115 returnCode = run_command(*ARGV)
120 fsevents_cb = proc do |stream, ctx, numEvents, paths, marks, eventIDs|
122 numEvents.times do |n|
123 allPaths.add(paths[n])
127 stream = FSEventStreamCreate(
134 KFSEventStreamCreateFlagNoDefer)
136 die "Failed to create the FSEventStream" unless stream
138 FSEventStreamScheduleWithRunLoop(
140 CFRunLoopGetCurrent(),
141 KCFRunLoopDefaultMode)
143 ok = FSEventStreamStart(stream)
144 die "Failed to start the FSEventStream" unless ok
146 FSEventStreamFlushSync(stream)
149 allPaths.sort.each do |path|
150 if File.exists?(path)
151 outputHandle.puts path
152 if (!options.directoriesOnly)
153 Dir.foreach(path) do |file|
154 fullPath = File.join(path, file)
155 stat = File.stat(fullPath)
156 if (stat.mtime.to_i >= startTime)
157 outputHandle.puts " #{file}"
162 outputHandle.puts "#{path} !"
166 FSEventStreamStop(stream)
167 FSEventStreamInvalidate(stream)
168 FSEventStreamRelease(stream)