@@ -180,14 +180,59 @@ and to stop the Watcher and cleanup all its resources use:
180
180
for more information. This callback also gets called if a
181
181
error occures by calling hook." )))
182
182
183
+ (defun escape-wildcards (thing &optional (escape-char #\\ ))
184
+ " Got the inspiration for that code from
185
+ sb-impl::unparse-physical-piece, credits go to Xach for finding it.
186
+ Thanks again for the helping me out"
187
+ (let* ((srclen (length thing))
188
+ (dstlen srclen))
189
+ (dotimes (i srclen)
190
+ (let ((char (char thing i)))
191
+ (case char
192
+ ((#\* #\? #\[ )
193
+ (incf dstlen))
194
+ (t (when (char= char escape-char)
195
+ (incf dstlen))))))
196
+ (let ((result (make-string dstlen))
197
+ (dst 0 ))
198
+ (dotimes (src srclen)
199
+ (let ((char (char thing src)))
200
+ (case char
201
+ ((#\* #\? #\[ )
202
+ (setf (char result dst) escape-char)
203
+ (incf dst))
204
+ (t (when (char= char escape-char)
205
+ (setf (char result dst) escape-char)
206
+ (incf dst))))
207
+ (setf (char result dst) char )
208
+ (incf dst)))
209
+ result)))
210
+
211
+ (defun escaped-directory-exists-p (directory )
212
+ (uiop :directory-exists-p
213
+ (escape-wildcards directory )))
214
+
215
+ (defun escaped-file-exists-p (file)
216
+ (uiop :file-exists-p
217
+ (escape-wildcards file)))
218
+
219
+ (defun escaped-directory-files (directory &rest args)
220
+ (apply #' uiop:directory-files
221
+ (escape-wildcards directory )
222
+ args))
223
+
224
+ (defun escaped-subdirectories (directory )
225
+ (uiop :subdirectories
226
+ (escape-wildcards directory )))
227
+
183
228
(defun get-event-type (filename renamed-p changed-p)
184
- " Will determine the Event-Type by using UIOP: DIRECTORY-EXISTS-P and
185
- UIOP: FILE-EXISTS-P. Will return one of the following types:
186
- :file-added, :file-removed, :file-changed, :directory-added.
187
- Since its not possible to determine :directory-removed and
188
- :on-delete a : file-removed will be returned instead."
189
- (let ((file-exists-p (uiop : file-exists-p filename))
190
- (directory-exists-p (uiop : directory-exists-p filename)))
229
+ " Will determine the Event-Type by using ESCAPED- DIRECTORY-EXISTS-P
230
+ and ESCAPED- FILE-EXISTS-P. Will return one of the following types:
231
+ :file-added, :file-removed, :file-changed, :directory-added. Since
232
+ its not possible to determine :directory-removed and :on-delete a
233
+ :file-removed will be returned instead."
234
+ (let ((file-exists-p (escaped- file-exists-p filename))
235
+ (directory-exists-p (escaped- directory-exists-p filename)))
191
236
(cond ((and renamed-p
192
237
(not changed-p)
193
238
file-exists-p
@@ -208,6 +253,14 @@ and to stop the Watcher and cleanup all its resources use:
208
253
(not file-exists-p)
209
254
directory-exists-p)
210
255
:directory-added )
256
+ ; ; i dont know what exactly it means to get a renamed and
257
+ ; ; changed event on a directory, but it happens when 'touch'
258
+ ; ; is run on a directory. Guess we could just ignore it.
259
+ ((and renamed-p
260
+ changed-p
261
+ (not file-exists-p)
262
+ directory-exists-p)
263
+ nil )
211
264
; ; if we get a change event but the file is already gone
212
265
; ; ignore it. It should be fine since a file-removed event
213
266
; ; will follow.
@@ -218,51 +271,49 @@ and to stop the Watcher and cleanup all its resources use:
218
271
nil )
219
272
(t
220
273
(error (format nil
221
- (concatenate ' string
222
- " Could not determine event type in GET-EVENT-TYPE, file: ~a~% "
223
- " (file-exists-p: ~a , directory-exists-p: ~a , renamed-p: ~a , changed-p: ~a )" )
274
+ " Could not determine event type in GET-EVENT-TYPE, file: ~a~% ~
275
+ (file-exists-p: ~a , directory-exists-p: ~a , renamed-p: ~a , changed-p: ~a )"
224
276
filename
225
- file-exists-p directory-exists-p renamed-p changed-p filename ))))))
277
+ file-exists-p directory-exists-p renamed-p changed-p))))))
226
278
227
279
(defun add-dir (watcher dir)
228
280
" adds the specified dir to watcher, this function has to be called
229
281
from the watcher-thread! See also: ADD-DIRECTORY-TO-WATCH."
230
- (let ((table (directory-handles watcher)))
231
- (multiple-value-bind (value present-p) (gethash dir table)
232
- (declare (ignore value))
233
- (unless present-p
234
- (let ((handle (if (or (recursive-p watcher)
235
- (string= (dir watcher) dir))
236
- ; ; add a fs-watch if either RECURSIVE-P is true
237
- ; ; or its the main directory
238
- (as :fs-watch dir
239
- (lambda (h f e s)
240
- (callback watcher h f e s)))
241
- nil )))
242
- (setf (gethash dir table) handle)
243
- ; ; this is _not_ nice... but there is no way to tell if we
244
- ; ; attached the handler fast enough. Since the OS could have
245
- ; ; already put some files inside the folder before we
246
- ; ; attached the handler. To (somewhat) fix that this will
247
- ; ; throw :file-created callbacks for each file, already
248
- ; ; inside the directory. The ugly part is that this will
249
- ; ; likely create duplicated :file-created events, since
250
- ; ; files could have been created while the handler was
251
- ; ; attached, but before this dolist finishes. But at least
252
- ; ; this will catch all files.
253
- (dolist (sub-file (uiop :directory-files dir))
254
- (callback watcher handle (subseq (format nil " ~a " sub-file)
255
- (length (get-handle-path handle)))
256
- t
257
- nil ))
258
- ; ; this makes sure that we dont miss any added directory
259
- ; ; events. In case ADD-DIR is called with a sub-directoy
260
- ; ; (from a filesystem event callback) which we already added
261
- ; ; by iterating over all sub-directories, ADD-DIR will
262
- ; ; return.
263
- (when (recursive-p watcher)
264
- (dolist (sub-dir (uiop :subdirectories dir))
265
- (add-dir watcher (format nil " ~a " sub-dir)))))))))
282
+ (when (or (recursive-p watcher)
283
+ (string= (dir watcher) dir))
284
+ (let ((table (directory-handles watcher)))
285
+ (multiple-value-bind (value present-p) (gethash dir table)
286
+ (declare (ignore value))
287
+ (unless present-p
288
+ (let ((handle (as :fs-watch dir
289
+ (lambda (h f e s)
290
+ (callback watcher h f e s)))))
291
+ (setf (gethash dir table) handle)
292
+ ; ; this is _not_ nice... but there is no way to tell if we
293
+ ; ; attached the handler fast enough. Since the OS could have
294
+ ; ; already put some files inside the folder before we
295
+ ; ; attached the handler. To (somewhat) fix that this will
296
+ ; ; throw :file-created callbacks for each file, already
297
+ ; ; inside the directory. The ugly part is that this will
298
+ ; ; likely create duplicated :file-created events, since
299
+ ; ; files could have been created while the handler was
300
+ ; ; attached, but before this dolist finishes. But at least
301
+ ; ; this will catch all files.
302
+ (dolist (sub-file (mapcar #' uiop:native-namestring
303
+ (escaped-directory-files dir)))
304
+ (callback watcher handle (subseq sub-file
305
+ (length (get-handle-path handle)))
306
+ t
307
+ nil ))
308
+ ; ; this makes sure that we dont miss any added directory
309
+ ; ; events. In case ADD-DIR is called with a sub-directoy
310
+ ; ; (from a filesystem event callback) which we already added
311
+ ; ; by iterating over all sub-directories, ADD-DIR will
312
+ ; ; return.
313
+ (when (recursive-p watcher)
314
+ (dolist (sub-dir (mapcar #' uiop:native-namestring
315
+ (escaped-subdirectories dir)))
316
+ (add-dir watcher sub-dir)))))))))
266
317
267
318
(defun add-directory-to-watch (watcher dir)
268
319
" adds dir to watcher, can be safetly called by any thread, will
@@ -368,7 +419,6 @@ and to stop the Watcher and cleanup all its resources use:
368
419
(list fn watcher full-filename event-type)
369
420
(slot-value watcher ' hook-queue))))
370
421
(when (eql event-type :on-deleted )
371
- (remove-directory-from-watch watcher full-filename)
372
422
(stop-watcher watcher))))
373
423
374
424
(defun watcher-event-loop (watcher)
@@ -377,38 +427,23 @@ and to stop the Watcher and cleanup all its resources use:
377
427
only happen if STOP-WATCHER is called or the Main Directory gets
378
428
deleted. This thread will get interrupted by
379
429
add-directory-to-watch-dir if a new directory is added."
380
- (let ((initial-directories (list ))
381
- (root-dir (dir watcher)))
382
- (if (recursive-p watcher)
383
- (uiop :collect-sub*directories (pathname root-dir)
384
- t
385
- t
386
- (lambda (dir) (push (format nil " ~a " dir)
387
- initial-directories)))
388
- (progn
389
- (push root-dir initial-directories)
390
- (loop
391
- :for dir :in (uiop :subdirectories root-dir)
392
- :do (push (format nil " ~a " dir) initial-directories))))
393
- (as :with-event-loop (:catch-app-errors (error-cb watcher))
394
- (loop
395
- :for dir :in initial-directories
396
- ; ; we can call add-dir directly here, since we are inside the
397
- ; ; event-loop thread
398
- :do (add-dir watcher dir))
399
- (setf (slot-value watcher ' alive-p) t ))))
430
+ (as :with-event-loop (:catch-app-errors (error-cb watcher))
431
+ (add-dir watcher (dir watcher))
432
+ (setf (slot-value watcher ' alive-p) t )))
400
433
401
434
; ; overwrite constructor and set DIR to a absolute Path, also start
402
435
; ; the event-loop Thread
403
436
(defmethod initialize-instance :after ((watcher watcher) &rest initargs)
404
437
; ; get fullpath as string and check if something went wrong
438
+ (declare (ignorable initargs))
405
439
(with-slots (dir) watcher
406
- (let ((fullpath (car (directory dir))))
440
+ (let ((fullpath (car (directory (escape-wildcards dir) ))))
407
441
(if fullpath
408
- (setf dir (format nil " ~a " fullpath))
409
- (error " TODO: ERROR: The given Directory does not exist (or is
410
- fishy, no read rights for example). calling DIRECTORY
411
- on it returned NIL." )))))
442
+ (setf dir (uiop :native-namestring fullpath))
443
+ (error " ERROR: The given Directory does not exist (or cannot~
444
+ be opened, no read/execute rights for example). calling~
445
+ (DIRECTORY ~a ) returned NIL."
446
+ dir)))))
412
447
413
448
(defun hook-thread-main-loop (watcher)
414
449
" Main Function of hook-thread. If watcher gets started, the
@@ -480,14 +515,12 @@ and to stop the Watcher and cleanup all its resources use:
480
515
(defun get-all-tracked-files (watcher)
481
516
" returns all files (excluding directories) which are tracked by the
482
517
given watcher"
483
- (mapcar
484
- (lambda (pathname ) (format nil " ~a " pathname ))
485
- (apply #' append
486
- (loop
487
- :for key :being :the :hash-keys :of (directory-handles watcher)
488
- :using (hash-value value)
489
- :if value
490
- :collect (uiop :directory-files key)))))
518
+ (apply #' append
519
+ (loop
520
+ :for key :being :the :hash-keys :of (directory-handles watcher)
521
+ :using (hash-value value)
522
+ :if value
523
+ :collect key)))
491
524
492
525
(defun busy-p (watcher)
493
526
" Returns t if Watcher is 'busy' and there are items on the
0 commit comments