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
26 ((not non-ws-position
)
27 (unless (zerop (length line
))
30 ((< non-ws-position
(1- (length line
)))
32 (subseq line
0 (1+ non-ws-position
)))
36 (unless (find #\Tab line
:test
#'char
=)
37 (return-from remove-tabs line
))
39 (with-output-to-string (stream)
40 (loop :for char
:across line
:do
41 (if (char= char
#\Tab
)
42 (write-string " " stream
)
43 (write-char char stream
))))))
44 (loop :for line
= (read-line input nil
:eof
)
46 :do
(let ((clean (remove-tabs (remove-trailing-whitespace line
))))
47 (write-line clean output
))))
50 (defun canonicalize-whitespace/file
(file)
51 (macrolet ((with-open-source-file ((stream pathname direction
) &body body
)
52 `(with-open-file (,stream
,pathname
54 :external-format
#-clisp
:utf-8
#+clisp charset
:utf-8
)
56 (let* ((temporary (make-pathname :type
"temp" :defaults file
))
59 (with-open-source-file (input file
:input
)
60 (with-open-source-file (output temporary
:output
)
61 (canonicalize-whitespace input output
)))
62 (#+sbcl sb-int
:stream-decoding-error
#-sbcl error
()
63 (format t
"// Ignoring non-UTF-8 source file ~S~%" file
)
68 (rename-file temporary file
))
69 ((probe-file temporary
)
70 (delete-file temporary
))))))
72 ;;; Timestamp functions
74 (defvar *stamp-file
* "whitespace-stamp")
76 (defun read-stamp-file ()
77 (if (probe-file *stamp-file
*)
78 (file-write-date *stamp-file
*)
81 (defun write-stamp-file ()
82 (with-open-file (stream *stamp-file
*
84 :if-exists
:supersede
)
85 (declare (ignorable stream
))))
87 ;;; Repository-level functions
89 (defvar *source-types
* '("lisp" "lisp-expr" "c" "h" "asd" "texinfo"))
91 (defvar *exceptions
* '("compile-file-pos-utf16be"))
93 (defun canonicalize-whitespace/directory
94 (&optional
(directory *default-pathname-defaults
*))
95 (let ((stamp-date (read-stamp-file)))
96 (labels ((older-than-stamp (file)
97 (< (file-write-date file
) stamp-date
))
99 (member (pathname-name file
) *exceptions
*
102 (or (older-than-stamp file
) (exception-p file
))))
103 (dolist (type *source-types
*)
104 (let* ((pattern (merge-pathnames
105 (make-pathname :type type
107 :directory
'(:relative
:wild-inferiors
))
109 (files (remove-if #'skip-p
(directory pattern
))))
110 (mapc #'canonicalize-whitespace
/file files
))))
115 (canonicalize-whitespace/directory
)