Last active
August 29, 2015 14:06
-
-
Save murachue/e7b26cec27ff9d21bd74 to your computer and use it in GitHub Desktop.
Timestamp modifier for Windows, like "touch" but this also can change creation time and directory's. fiddleの練習も兼ねて適当に書いた。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: utf-8 | |
require "fiddle/import" | |
require "time" | |
require "optparse" | |
module Kernel32 | |
extend Fiddle::Importer | |
dlload "kernel32.dll" | |
extern "int CreateFile(void*, unsigned int, unsigned int, void*, unsigned int, unsigned int, int)", :stdcall | |
extern "int SystemTimeToFileTime(void*, void*)", :stdcall | |
extern "int SetFileTime(unsigned int, unsigned long long*, unsigned long long*, unsigned long long*)", :stdcall | |
extern "int CloseHandle(int)", :stdcall | |
extern "unsigned int GetLastError()", :stdcall | |
GENERIC_READ = 0x80000000 | |
GENERIC_WRITE = 0x40000000 | |
FILE_WRITE_ATTRIBUTES = 0x00000100 | |
FILE_SHARE_READ = 0x00000001 | |
OPEN_EXISTING = 0x00000003 | |
FILE_ATTRIBUTE_NORMAL = 0x00000080 | |
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 | |
INVALID_HANDLE_VALUE = -1 | |
SYSTEMTIME = struct([ | |
"unsigned short wYear", | |
"unsigned short wMonth", | |
"unsigned short wDayOfWeek", | |
"unsigned short wDay", | |
"unsigned short wHour", | |
"unsigned short wMinute", | |
"unsigned short wSecond", | |
"unsigned short wMilliseconds", | |
]) | |
SizeofFILETIME = sizeof("unsigned long long") | |
end | |
include Kernel32 | |
# TODO: Why I need to specify Kernel32. in some cases (call func) even after included it? | |
def set_entry_time fn, tt | |
#puts proc{|s| s.methods.select{|e|e.to_s.end_with? "time"}.map{|e|"%s=%s" % [e, s.send(e)]}.join(" ")}.call File.stat(fn) | |
hd = Kernel32.CreateFile(fn, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0) | |
if hd == INVALID_HANDLE_VALUE | |
puts "CreateFile returns %08X: %08X" % [hd, Kernel32.GetLastError] | |
return false | |
end | |
begin | |
st = SYSTEMTIME.malloc | |
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds = tt.year, tt.mon, tt.day, tt.hour, tt.min, tt.sec, tt.usec / 1000 | |
ft = " " * SizeofFILETIME # prepare buffer | |
r = Kernel32.SystemTimeToFileTime(st, ft) | |
#puts "%s => %d" % [tt.to_s, ft.unpack("Q")[0]] | |
r = Kernel32.SetFileTime(hd, ft, ft, ft) | |
if r == 0 | |
puts "SetFileTime returns %08X: %08X" % [r, Kernel32.GetLastError] | |
return false | |
end | |
ensure | |
r = Kernel32.CloseHandle(hd) | |
if r == 0 | |
puts "CloseHandle returns %08X: %08X" % [r, Kernel32.GetLastError] | |
return false | |
end | |
end | |
#puts proc{|s| s.methods.select{|e|e.to_s.end_with? "time"}.map{|e|"%s=%s" % [e, s.send(e)]}.join(" ")}.call File.stat(fn) | |
return true | |
end | |
def set_entries_time fn, tt | |
if FileTest.directory? fn | |
# set inner file time first (or overriding already set directory time by rewriting files in the directory...?) | |
if @recurse | |
Dir.entries(fn).each { |efn| | |
next if efn == "." or efn == ".." | |
set_entries_time fn + "\\" + efn, tt # delimiter is "\\" because that path also used by Win32API, thanks for accepting "\\" on Ruby-win32! | |
} | |
end | |
end | |
if not set_entry_time fn, tt | |
if @abortonerror | |
raise "Error occurred while setting time of #{fn}; aborting." | |
else | |
$stderr.puts "Error occurred while setting time of #{fn}; skipping." | |
end | |
end | |
end | |
def main | |
@recurse = false | |
@abortonerror = true | |
opts = OptionParser.new | |
opts.on("-r", "--recurse") { |v| @recurse = true } | |
opts.on("-i", "--ignore-error") { |v| @abortonerror = false } | |
opts.parse!(ARGV) | |
set_entries_time ARGV[0], Time.parse(ARGV[1]).utc | |
end | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment