#!/usr/bin/ruby # This script watches modifications on the given directory, using the new # FSEvents API in Leopard. require 'osx/foundation' OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework' include OSX require 'set' require 'optparse' require 'ostruct' $COMMAND = File.basename($0) $USAGE = "Usage: #{$COMMAND} [OPTIONS] [ ...]" def die(s) $stderr.puts s exit 1 end options = OpenStruct.new options.directoriesOnly = false options.outputFile = "-" opts = OptionParser.new do |opts| opts.banner = $USAGE opts.separator "" opts.separator "Specific options:" opts.on("-d", "--dir-only", "Only display directories") do options.directoriesOnly = true end opts.on("-o", "--output FILE", "Write output to a file") do |fileName| options.outputFile = fileName end opts.on_tail("-h", "--help", "Show this message") do puts opts exit end end opts.parse!(ARGV) die $USAGE unless ARGV.size >= 1 if options.outputFile == "-" outputHandle = $stdout.clone else outputHandle = File.open(options.outputFile, "w") end path = "/" startId = FSEventsGetCurrentEventId() # Used to compare with mtime, which only has second accuracy startTime = Time.now.to_i fork do begin exec(*ARGV) rescue SystemCallError => e puts e.message exit 127 end end pid = Process.wait returnCode = $? >> 8 sleep(0.1) allPaths = Set.new fsevents_cb = proc do |stream, ctx, numEvents, paths, marks, eventIDs| paths.regard_as('*') numEvents.times do |n| allPaths.add(paths[n]) end end stream = FSEventStreamCreate( KCFAllocatorDefault, fsevents_cb, nil, [path], startId, 1.0, KFSEventStreamCreateFlagNoDefer) die "Failed to create the FSEventStream" unless stream FSEventStreamScheduleWithRunLoop( stream, CFRunLoopGetCurrent(), KCFRunLoopDefaultMode) ok = FSEventStreamStart(stream) die "Failed to start the FSEventStream" unless ok FSEventStreamFlushSync(stream) allPaths.sort.each do |path| if File.exists?(path) outputHandle.puts path if (!options.directoriesOnly) Dir.foreach(path) do |file| fullPath = File.join(path, file) stat = File.stat(fullPath) if (stat.mtime.to_i >= startTime) outputHandle.puts " #{file}" end end end else outputHandle.puts "#{path} !" end end FSEventStreamStop(stream) FSEventStreamInvalidate(stream) FSEventStreamRelease(stream) outputHandle.flush exit(returnCode)