utils: Add `with-atomic-file-replacement'.
* guix/build/utils.scm (with-atomic-file-replacement): New procedure. (substitute): Use it.
This commit is contained in:
		
							parent
							
								
									df1fab5837
								
							
						
					
					
						commit
						dcd7290654
					
				
					 1 changed files with 42 additions and 30 deletions
				
			
		| 
						 | 
					@ -32,6 +32,7 @@
 | 
				
			||||||
            alist-cons-before
 | 
					            alist-cons-before
 | 
				
			||||||
            alist-cons-after
 | 
					            alist-cons-after
 | 
				
			||||||
            alist-replace
 | 
					            alist-replace
 | 
				
			||||||
 | 
					            with-atomic-file-replacement
 | 
				
			||||||
            substitute
 | 
					            substitute
 | 
				
			||||||
            substitute*
 | 
					            substitute*
 | 
				
			||||||
            dump-port
 | 
					            dump-port
 | 
				
			||||||
| 
						 | 
					@ -157,25 +158,40 @@ An error is raised when no such pair exists."
 | 
				
			||||||
;;; Text substitution (aka. sed).
 | 
					;;; Text substitution (aka. sed).
 | 
				
			||||||
;;;
 | 
					;;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define (substitute file pattern+procs)
 | 
					(define (with-atomic-file-replacement file proc)
 | 
				
			||||||
  "PATTERN+PROCS is a list of regexp/two-argument procedure.  For each line
 | 
					  "Call PROC with two arguments: an input port for FILE, and an output
 | 
				
			||||||
of FILE, and for each PATTERN that it matches, call the corresponding PROC
 | 
					port for the file that is going to replace FILE.  Upon success, FILE is
 | 
				
			||||||
as (PROC LINE MATCHES); PROC must return the line that will be written as a
 | 
					atomically replaced by what has been written to the output port, and
 | 
				
			||||||
substitution of the original line."
 | 
					PROC's result is returned."
 | 
				
			||||||
  (let* ((rx+proc  (map (match-lambda
 | 
					  (let* ((template (string-append file ".XXXXXX"))
 | 
				
			||||||
                         (((? regexp? pattern) . proc)
 | 
					 | 
				
			||||||
                          (cons pattern proc))
 | 
					 | 
				
			||||||
                         ((pattern . proc)
 | 
					 | 
				
			||||||
                          (cons (make-regexp pattern regexp/extended)
 | 
					 | 
				
			||||||
                                proc)))
 | 
					 | 
				
			||||||
                        pattern+procs))
 | 
					 | 
				
			||||||
         (template (string-append file ".XXXXXX"))
 | 
					 | 
				
			||||||
         (out      (mkstemp! template))
 | 
					         (out      (mkstemp! template))
 | 
				
			||||||
         (mode     (stat:mode (stat file))))
 | 
					         (mode     (stat:mode (stat file))))
 | 
				
			||||||
    (with-throw-handler #t
 | 
					    (with-throw-handler #t
 | 
				
			||||||
      (lambda ()
 | 
					      (lambda ()
 | 
				
			||||||
        (call-with-input-file file
 | 
					        (call-with-input-file file
 | 
				
			||||||
          (lambda (in)
 | 
					          (lambda (in)
 | 
				
			||||||
 | 
					            (let ((result (proc in out)))
 | 
				
			||||||
 | 
					              (close out)
 | 
				
			||||||
 | 
					              (chmod template mode)
 | 
				
			||||||
 | 
					              (rename-file template file)
 | 
				
			||||||
 | 
					              result))))
 | 
				
			||||||
 | 
					      (lambda (key . args)
 | 
				
			||||||
 | 
					        (false-if-exception (delete-file template))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (substitute file pattern+procs)
 | 
				
			||||||
 | 
					  "PATTERN+PROCS is a list of regexp/two-argument procedure.  For each line
 | 
				
			||||||
 | 
					of FILE, and for each PATTERN that it matches, call the corresponding PROC
 | 
				
			||||||
 | 
					as (PROC LINE MATCHES); PROC must return the line that will be written as a
 | 
				
			||||||
 | 
					substitution of the original line."
 | 
				
			||||||
 | 
					  (let ((rx+proc  (map (match-lambda
 | 
				
			||||||
 | 
					                        (((? regexp? pattern) . proc)
 | 
				
			||||||
 | 
					                         (cons pattern proc))
 | 
				
			||||||
 | 
					                        ((pattern . proc)
 | 
				
			||||||
 | 
					                         (cons (make-regexp pattern regexp/extended)
 | 
				
			||||||
 | 
					                               proc)))
 | 
				
			||||||
 | 
					                       pattern+procs)))
 | 
				
			||||||
 | 
					    (with-atomic-file-replacement file
 | 
				
			||||||
 | 
					      (lambda (in out)
 | 
				
			||||||
        (let loop ((line (read-line in 'concat)))
 | 
					        (let loop ((line (read-line in 'concat)))
 | 
				
			||||||
          (if (eof-object? line)
 | 
					          (if (eof-object? line)
 | 
				
			||||||
              #t
 | 
					              #t
 | 
				
			||||||
| 
						 | 
					@ -189,12 +205,7 @@ substitution of the original line."
 | 
				
			||||||
                                line
 | 
					                                line
 | 
				
			||||||
                                rx+proc)))
 | 
					                                rx+proc)))
 | 
				
			||||||
                (display line out)
 | 
					                (display line out)
 | 
				
			||||||
                    (loop (read-line in 'concat)))))))
 | 
					                (loop (read-line in 'concat)))))))))
 | 
				
			||||||
        (close out)
 | 
					 | 
				
			||||||
        (chmod template mode)
 | 
					 | 
				
			||||||
        (rename-file template file))
 | 
					 | 
				
			||||||
      (lambda (key . args)
 | 
					 | 
				
			||||||
        (false-if-exception (delete-file template))))))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define-syntax let-matches
 | 
					(define-syntax let-matches
 | 
				
			||||||
| 
						 | 
					@ -329,4 +340,5 @@ patched, #f otherwise."
 | 
				
			||||||
;;; eval: (put 'call-with-output-file/atomic 'scheme-indent-function 1)
 | 
					;;; eval: (put 'call-with-output-file/atomic 'scheme-indent-function 1)
 | 
				
			||||||
;;; eval: (put 'with-throw-handler 'scheme-indent-function 1)
 | 
					;;; eval: (put 'with-throw-handler 'scheme-indent-function 1)
 | 
				
			||||||
;;; eval: (put 'let-matches 'scheme-indent-function 3)
 | 
					;;; eval: (put 'let-matches 'scheme-indent-function 3)
 | 
				
			||||||
 | 
					;;; eval: (put 'with-atomic-file-replacement 'scheme-indent-function 1)
 | 
				
			||||||
;;; End:
 | 
					;;; End:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Reference in a new issue