Hi all,

While working on the docs for (chicken file posix), I noticed some
discrepancies:

- file-access-time and file-change-time have no associated setter,
   but file-modification-time does.
- The setter for file-modification-time sets both mtime AND atime,
   even though (set! (file-modification-time f) x) implies that
   we're setting ONLY the mtime.
- The getters all accept strings, ports and file descriptors; the
   setter only accepts a string.

The first patch improves the situation by getting rid of the generalised
setter, and opting for a simpler approach.  There's no (easy) way to
influence the file-change-time, so adding setters to all of them and
making the setter for file-modification-time only change the modification
time is a no-go, so I decided to just add a set-file-times! procedure.

The name of this procedure is taken from MIT Scheme / SCSH.  SCSH
doesn't add a bang at the end, but we do for our other set-* procedures
in posix.scm, so I decided to go with the MIT convention.
Other Schemes have kind of inconsistent names like "touch-file"
(Gauche), utime (Guile), or some ridiculously long ones like
"file-last-access-and-modification-times-set!" (Gambit) and
"file-or-directory-modify-seconds" (Racket).  Chez doesn't appear
to have anything to change the timestamps (though I really like their
nice consistent naming for the accessors and whether to follow links;
perhaps we can adopt that too?).

For maximum compatibility with both MIT and SCSH, #f means to keep
the original timestamp, but omitting both means to set the timestamp
to the current time.  If only one argument is supplied, the second is
assumed to be the same (this keeps it as conveniently like the old
setter).  To make it keep the original timestamp, I added an
lstat() call to set_file_mtime; utime() also follows symlinks.

So, to clarify:

- (set-file-times! "file") is a simple "touch" of the file: both times
    are set to the current timestamp.

- (set-file-times! "file" 0) will set both atime and mtime to the epoch,
    just like (set! (file-modification-time "file") 0) in the original
    situation would do.

- (set-file-times! "file" #f 0) will keep the atime unchanged, but set
   the mtime to the epoch.

- (set-file-times! "file" 0 #f) will set the atime to the epoch and keep
   the mtime unchanged.

- (set-file-times! "file" #f) will not change either timestamp.

- All of the above _will_ (necessarily) touch the ctime.

Finally, the second patch is a simple improvement of the types database
to reflect the fact that the file-*-time accessors all also accept a
port, in addition to a string or fixnum.

We could consider extending set-file-times! to accept these too, but
that's a bit trickier, as there's AFAIK no portable way to change the
times on an FD.

Cheers,
Peter
From 134ff4ce1d4af3ad416874cfada02df485496049 Mon Sep 17 00:00:00 2001
From: Peter Bex <pe...@more-magic.net>
Date: Wed, 10 May 2017 22:06:13 +0200
Subject: [PATCH] Replace file-modification-time setter with set-file-times!
 procedure

This reduces several inconsistencies and any resulting confusion:

- file-access-time and file-change-time have no associated setter
- The setter for file-modification-time sets both mtime AND ctime
- The getters all accept strings, ports and file descriptors; the
   setter only accepts a string.

While at it, the new procedure also makes it possible to omit the
timestamps (in which case the current time is assumed), supply only
one (in which case the old behaviour stays: we set both timestamps to
the supplied time) or both (in which case you can set either to a
different value).  If #f is supplied, the specific time is unchanged.

This behaviour is maximally compatible with the "specify both or none"
behaviour from SCSH's "set-file-times" procedure (note the missing
bang though), and with MIT Scheme's "set-file-times!" procedure where
passing in #f means to avoid modifying the corresponding time.
---
 posix-common.scm | 28 ++++++++++++++--------------
 posix.scm        |  3 ++-
 posixunix.scm    | 21 ++++++++++++++++++---
 posixwin.scm     | 21 ++++++++++++++++++---
 types.db         |  1 +
 5 files changed, 53 insertions(+), 21 deletions(-)

diff --git a/posix-common.scm b/posix-common.scm
index e3e6739..3475dda 100644
--- a/posix-common.scm
+++ b/posix-common.scm
@@ -257,22 +257,22 @@ EOF
           _stat_st_dev _stat_st_rdev
           _stat_st_blksize _stat_st_blocks) )
 
-(define file-modification-time
-  (getter-with-setter 
-   (lambda (f)
-     (##sys#stat f #f #t 'file-modification-time) _stat_st_mtime)
-   (lambda (f t)
-     (##sys#check-exact-integer t 'set-file-modification-time)
-     (let ((r ((foreign-lambda int "set_file_mtime" c-string scheme-object)
-	       f t)))
-       (when (fx< r 0)
-	 (posix-error 
-	  #:file-error 'set-file-modification-time
-	  "cannot set file modification-time" f t))))
-   "(file-modification-time f)"))
-
+(define (file-modification-time f) (##sys#stat f #f #t 'file-modification-time) _stat_st_mtime)
 (define (file-access-time f) (##sys#stat f #f #t 'file-access-time) _stat_st_atime)
 (define (file-change-time f) (##sys#stat f #f #t 'file-change-time) _stat_st_ctime)
+
+(define (set-file-times! f . rest)
+  (let-optionals* rest ((atime (current-seconds)) (mtime atime))
+    (when atime (##sys#check-exact-integer atime 'set-file-times!))
+    (when mtime (##sys#check-exact-integer mtime 'set-file-times!))
+    (let ((r ((foreign-lambda int "set_file_mtime"
+		c-string scheme-object scheme-object)
+	      f atime mtime)))
+      (when (fx< r 0)
+	(apply posix-error
+	       #:file-error
+	       'set-file-times! "cannot set file times" f rest)))))
+
 (define (file-owner f) (##sys#stat f #f #t 'file-owner) _stat_st_uid)
 (define (file-permissions f) (##sys#stat f #f #t 'file-permissions) _stat_st_mode)
 (define (file-size f) (##sys#stat f #f #t 'file-size) _stat_st_size)
diff --git a/posix.scm b/posix.scm
index 96c932a..2bc058e 100644
--- a/posix.scm
+++ b/posix.scm
@@ -101,7 +101,8 @@
    open/trunc open/write open/wronly perm/irgrp perm/iroth perm/irusr
    perm/irwxg perm/irwxo perm/irwxu perm/isgid perm/isuid perm/isvtx
    perm/iwgrp perm/iwoth perm/iwusr perm/ixgrp perm/ixoth perm/ixusr
-   port->fileno seek/cur seek/end seek/set set-file-position!)
+   port->fileno seek/cur seek/end seek/set set-file-position!
+   set-file-times!)
 (import chicken chicken.posix))
 
 (module chicken.time.posix
diff --git a/posixunix.scm b/posixunix.scm
index 40b5b75..3c5df86 100644
--- a/posixunix.scm
+++ b/posixunix.scm
@@ -60,7 +60,7 @@
    process-group-id process-run process-signal process-sleep process-wait
    read-symbolic-link regular-file? seconds->local-time seconds->string
    seconds->utc-time seek/cur seek/end seek/set set-alarm!
-   set-buffering-mode! set-root-directory!
+   set-buffering-mode! set-file-times! set-root-directory!
    set-signal-handler! set-signal-mask! signal-handler
    signal-mask signal-mask! signal-masked? signal-unmask! signal/abrt
    signal/alrm signal/break signal/chld signal/cont signal/fpe
@@ -352,11 +352,26 @@ static int get_tty_size(int p, int *rows, int *cols)
 }
 #endif
 
-static int set_file_mtime(char *filename, C_word tm)
+static int set_file_mtime(char *filename, C_word atime, C_word mtime)
 {
+  struct stat sb;
   struct utimbuf tb;
 
-  tb.actime = tb.modtime = C_num_to_int(tm);
+  /* Only lstat if needed */
+  if (atime == C_SCHEME_FALSE || mtime == C_SCHEME_FALSE) {
+    if (lstat(filename, &sb) == -1) return -1;
+  }
+
+  if (atime == C_SCHEME_FALSE) {
+    tb.actime = sb.st_atime;
+  } else {
+    tb.actime = C_num_to_int(atime);
+  }
+  if (mtime == C_SCHEME_FALSE) {
+    tb.modtime = sb.st_mtime;
+  } else {
+    tb.modtime = C_num_to_int(mtime);
+  }
   return utime(filename, &tb);
 }
 
diff --git a/posixwin.scm b/posixwin.scm
index 02fc62f..7b549e4 100644
--- a/posixwin.scm
+++ b/posixwin.scm
@@ -612,11 +612,26 @@ C_process(const char * app, const char * cmdlin, const char ** env,
     return success;
 }
 
-static int set_file_mtime(char *filename, C_word tm)
+static int set_file_mtime(char *filename, C_word atime, C_word mtime)
 {
+  struct stat sb;
   struct _utimbuf tb;
 
-  tb.actime = tb.modtime = C_num_to_int(tm);
+  /* Only lstat if needed */
+  if (atime == C_SCHEME_FALSE || mtime == C_SCHEME_FALSE) {
+    if (lstat(filename, &sb) == -1) return -1;
+  }
+
+  if (atime == C_SCHEME_FALSE) {
+    tb.actime = sb.st_atime;
+  } else {
+    tb.actime = C_num_to_int(atime);
+  }
+  if (mtime == C_SCHEME_FALSE) {
+    tb.modtime = sb.st_mtime;
+  } else {
+    tb.modtime = C_num_to_int(mtime);
+  }
   return _utime(filename, &tb);
 }
 EOF
@@ -656,7 +671,7 @@ EOF
    process-spawn process-wait read-symbolic-link regular-file?
    seconds->local-time seconds->string seconds->utc-time seek/cur
    seek/end seek/set set-alarm! set-buffering-mode! set-root-directory!
-   set-signal-handler! set-signal-mask! signal-handler
+   set-file-times! set-signal-handler! set-signal-mask! signal-handler
    signal-mask signal-mask! signal-masked? signal-unmask! signal/abrt
    signal/alrm signal/break signal/chld signal/cont signal/fpe
    signal/bus signal/hup signal/ill signal/int signal/io signal/kill
diff --git a/types.db b/types.db
index 13b911b..180cae2 100644
--- a/types.db
+++ b/types.db
@@ -2046,6 +2046,7 @@
 (chicken.posix#set-alarm! (#(procedure #:clean #:enforce) chicken.posix#set-alarm! (integer) integer))
 (chicken.posix#set-buffering-mode! (#(procedure #:clean #:enforce) chicken.posix#set-buffering-mode! (port symbol #!optional fixnum) undefined))
 (chicken.posix#set-file-position! (#(procedure #:clean #:enforce) chicken.posix#set-file-position! ((or port fixnum) integer #!optional fixnum) undefined))
+(chicken.posix#set-file-times! (#(procedure #:clean #:enforce) chicken.posix#set-file-times! (string #!optional (or false integer) (or false integer)) undefined))
 (chicken.posix#set-root-directory! (#(procedure #:clean #:enforce) chicken.posix#set-root-directory! (string) undefined))
 (chicken.posix#set-signal-handler! (#(procedure #:clean #:enforce) chicken.posix#set-signal-handler! (fixnum (or false (procedure (fixnum) . *))) undefined))
 (chicken.posix#set-signal-mask! (#(procedure #:clean #:enforce) chicken.posix#set-signal-mask! ((list-of fixnum)) undefined))
-- 
2.1.4

From 3fb0a48db1b2b487b734ccb51f65c9d3b5bacce6 Mon Sep 17 00:00:00 2001
From: Peter Bex <pe...@more-magic.net>
Date: Wed, 10 May 2017 22:34:04 +0200
Subject: [PATCH 2/2] Add "port" to accepted file timestamp argument types

---
 types.db | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/types.db b/types.db
index 180cae2..30791a9 100644
--- a/types.db
+++ b/types.db
@@ -1952,8 +1952,8 @@
 (chicken.posix#fcntl/getfl fixnum)
 (chicken.posix#fcntl/setfd fixnum)
 (chicken.posix#fcntl/setfl fixnum)
-(chicken.posix#file-access-time (#(procedure #:clean #:enforce) chicken.posix#file-access-time ((or string fixnum)) integer))
-(chicken.posix#file-change-time (#(procedure #:clean #:enforce) chicken.posix#file-change-time ((or string fixnum)) integer))
+(chicken.posix#file-access-time (#(procedure #:clean #:enforce) chicken.posix#file-access-time ((or string port fixnum)) integer))
+(chicken.posix#file-change-time (#(procedure #:clean #:enforce) chicken.posix#file-change-time ((or string port fixnum)) integer))
 (chicken.posix#file-close (#(procedure #:clean #:enforce) chicken.posix#file-close (fixnum) undefined))
 (chicken.posix#file-control (#(procedure #:clean #:enforce) chicken.posix#file-control (fixnum fixnum #!optional fixnum) fixnum))
 (chicken.posix#file-creation-mode (#(procedure #:clean #:enforce) chicken.posix#file-creation-mode (#!optional fixnum) fixnum))
@@ -1962,7 +1962,7 @@
 (chicken.posix#file-lock (#(procedure #:clean #:enforce) chicken.posix#file-lock (port #!optional fixnum integer) (struct lock)))
 (chicken.posix#file-lock/blocking (#(procedure #:clean #:enforce) chicken.posix#file-lock/blocking (port #!optional fixnum integer) (struct lock)))
 (chicken.posix#file-mkstemp (#(procedure #:clean #:enforce) chicken.posix#file-mkstemp (string) fixnum string))
-(chicken.posix#file-modification-time (#(procedure #:clean #:enforce) chicken.posix#file-modification-time ((or string fixnum)) integer))
+(chicken.posix#file-modification-time (#(procedure #:clean #:enforce) chicken.posix#file-modification-time ((or string fixnum port)) integer))
 (chicken.posix#file-open (#(procedure #:clean #:enforce) chicken.posix#file-open (string fixnum #!optional fixnum) fixnum))
 (chicken.posix#file-owner (#(procedure #:clean #:enforce) chicken.posix#file-owner ((or string fixnum)) fixnum))
 (chicken.posix#file-permissions (#(procedure #:clean #:enforce) chicken.posix#file-permissions ((or string fixnum)) fixnum))
-- 
2.1.4

Attachment: signature.asc
Description: Digital signature

_______________________________________________
Chicken-hackers mailing list
Chicken-hackers@nongnu.org
https://lists.nongnu.org/mailman/listinfo/chicken-hackers

Reply via email to