1 ;;;; Convert tabs to spaces and delete trailing whitespace in files.
3 ;;;; To be run in the root directory of the distribution as part of
6 ;;;; This software is part of the SBCL system. See the README file for
9 ;;;; This software is derived from the CMU CL system, which was
10 ;;;; written at Carnegie Mellon University and released into the
11 ;;;; public domain. The software is in the public domain and is
12 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
13 ;;;; files for more information.
15 ;;; Stream and single-file functions
17 (defun whitespace-p (character)
18 (member character
'(#\Space
#\Tab
) :test
#'char
=))
20 (defun canonicalize-whitespace (input output
)
22 (flet ((remove-trailing-whitespace (line)
23 (let ((non-ws-position (position-if-not #'whitespace-p line
25 (unless (and non-ws-position
26 (< non-ws-position
(1- (length line
))))
27 (return-from remove-trailing-whitespace line
))
29 (subseq line
0 (1+ non-ws-position
))))
31 (unless (find #\Tab line
:test
#'char
=)
32 (return-from remove-tabs line
))
34 (with-output-to-string (stream)
35 (loop :for char
:across line
:do
36 (if (char= char
#\Tab
)
37 (write-string " " stream
)
38 (write-char char stream
))))))
39 (loop :for line
= (read-line input nil
:eof
)
41 :do
(let ((clean (remove-tabs (remove-trailing-whitespace line
))))
42 (write-line clean output
))))
45 (defun canonicalize-whitespace/file
(file)
46 (macrolet ((with-open-source-file ((stream pathname direction
) &body body
)
47 `(with-open-file (,stream
,pathname
49 :external-format
#-clisp
:utf-8
#+clisp charset
:utf-8
)
51 (let* ((temporary (make-pathname :type
"temp" :defaults file
))
54 (with-open-source-file (input file
:input
)
55 (with-open-source-file (output temporary
:output
)
56 (canonicalize-whitespace input output
)))
57 (#+sbcl sb-int
:stream-decoding-error
#-sbcl error
()
58 (format t
"// Ignoring non-UTF-8 source file ~S~%" file
)
63 (rename-file temporary file
))
64 ((probe-file temporary
)
65 (delete-file temporary
))))))
67 ;;; Timestamp functions
69 (defvar *stamp-file
* "whitespace-stamp")
71 (defun read-stamp-file ()
72 (if (probe-file *stamp-file
*)
73 (file-write-date *stamp-file
*)
76 (defun write-stamp-file ()
77 (with-open-file (stream *stamp-file
*
79 :if-exists
:supersede
)
80 #-sbcl
(declare (ignore stream
))))
82 ;;; Repository-level functions
84 (defvar *source-types
* '("lisp" "lisp-expr" "c" "h" "asd" "texinfo"))
86 (defvar *exceptions
* '("compile-file-pos-utf16be"))
88 (defun canonicalize-whitespace/directory
89 (&optional
(directory *default-pathname-defaults
*))
90 (let ((stamp-date (read-stamp-file)))
91 (labels ((older-than-stamp (file)
92 (< (file-write-date file
) stamp-date
))
94 (member (pathname-name file
) *exceptions
*
97 (or (older-than-stamp file
) (exception-p file
))))
98 (dolist (type *source-types
*)
99 (let* ((pattern (merge-pathnames
100 (make-pathname :type type
102 :directory
'(:relative
:wild-inferiors
))
104 (files (remove-if #'skip-p
(directory pattern
))))
105 (mapc #'canonicalize-whitespace
/file files
))))
110 (canonicalize-whitespace/directory
)