mod-translator.lisp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. (defpackage :mod-translator
  2. (:use :cl :hurd-common :mach
  3. :hurd :hurd-translator
  4. :hurd-tree-translator))
  5. (in-package :mod-translator)
  6. (defconstant +file+ (first ext:*args*))
  7. (defclass mod-translator (tree-translator)
  8. ((file-stat :initarg :file-stat
  9. :initform nil
  10. :accessor file-stat)
  11. (dir-stat :initarg :dir-stat
  12. :initform nil
  13. :accessor dir-stat)
  14. (timestamp :initform nil
  15. :accessor timestamp
  16. :initarg :timestamp)))
  17. (defclass dirty-entry ()
  18. ((dirty :initform nil
  19. :accessor dirty)))
  20. (defclass mod-entry (dirty-entry entry)
  21. ((contents :initarg :data
  22. :initform nil
  23. :accessor data)))
  24. (defclass mod-dir-entry (dirty-entry dir-entry) ())
  25. (defun %create-data-array (size contents)
  26. (make-array size
  27. :initial-contents contents
  28. :adjustable nil
  29. :element-type '(unsigned-byte 8)))
  30. (define-callback allow-open-p mod-translator
  31. (node user flags is-new-p)
  32. (declare (ignore is-new-p))
  33. (when (flag-is-p flags :write)
  34. (return-from allow-open-p nil))
  35. (when (flag-is-p flags :read)
  36. (unless (has-access-p node user :read)
  37. (return-from allow-open-p nil)))
  38. t)
  39. (define-callback read-file mod-translator
  40. (node user start amount stream)
  41. (when (has-access-p node user :read)
  42. (let* ((size (stat-get (stat node) 'st-size))
  43. (size-res (- size start)))
  44. (unless (plusp size-res)
  45. (return-from read-file t))
  46. (let* ((total (min size-res amount))
  47. (end (+ start total)))
  48. (write-sequence (subseq (data node) start end)
  49. stream)
  50. ; Also write newline.
  51. (write-byte #x0A stream)
  52. t))))
  53. (define-callback refresh-node mod-translator
  54. (node user)
  55. (declare (ignore node user))
  56. (with-port-deallocate (port (file-name-lookup +file+ :flags '(:read :notrans)))
  57. (let* ((stat (io-stat port))
  58. (new-timestamp (stat-get stat 'st-mtime)))
  59. (when (time-value-newer-p new-timestamp (timestamp translator))
  60. ; Mark every node as un-visited.
  61. (iterate-entries-deep (root translator)
  62. (lambda (name node)
  63. (declare (ignore name))
  64. (setf (dirty node) nil)
  65. t))
  66. (%update-data translator
  67. (with-open-file (s +file+) (read s))
  68. (root translator))
  69. ; Now remove the nodes we have not visited during the update.
  70. (iterate-entries-deep (root translator)
  71. (lambda (name node)
  72. (cond
  73. ((dirty node) t) ; Keep going down there
  74. (t
  75. (remove-dir-entry (parent node)
  76. name)
  77. nil))))
  78. (setf (timestamp translator) new-timestamp)))))
  79. (defun %update-data (translator ls node)
  80. (let* ((type (first ls))
  81. (name (second ls))
  82. (args (rest (rest ls)))
  83. (found (get-entry node name)))
  84. (case type
  85. (:dir
  86. (when (or (and found
  87. (typep found 'mod-entry))
  88. (not found))
  89. (when found
  90. (remove-dir-entry node name))
  91. (setf found
  92. (make-instance 'mod-dir-entry
  93. :stat (make-stat (dir-stat translator))
  94. :parent node))
  95. (add-entry node found name))
  96. (loop for item in args
  97. do (%update-data translator item found)))
  98. (:file
  99. (let ((data (first args)))
  100. (when (or (and found
  101. (typep found 'mod-dir-entry))
  102. (not found))
  103. (when found
  104. (remove-dir-entry node name))
  105. (setf found
  106. (make-instance 'mod-entry
  107. :stat (make-stat
  108. (file-stat translator))
  109. :parent node))
  110. (add-entry node found name))
  111. ; Update file size.
  112. (setf (stat-get (stat found) 'st-size) (length data))
  113. ; Update byte array.
  114. (setf (data found) (%read-file-data data)))))
  115. ; Flag this node as visited.
  116. (setf (dirty found) t)))
  117. (defun %read-file-data (str)
  118. (%create-data-array (length str)
  119. (loop for char across str
  120. collect (char-code char))))
  121. (defun %fill-node (translator ls node)
  122. (let ((type (first ls))
  123. (name (second ls))
  124. (args (rest (rest ls))))
  125. (case type
  126. (:dir
  127. (let ((dir (make-instance 'mod-dir-entry
  128. :stat (make-stat (dir-stat translator))
  129. :parent node)))
  130. (add-entry node dir name)
  131. (loop for item in args
  132. do (%fill-node translator item dir))))
  133. (:file
  134. (let* ((data (first args))
  135. (file (make-instance 'mod-entry
  136. :stat (make-stat
  137. (file-stat translator)
  138. :size (length data))
  139. :parent node
  140. :data (%read-file-data data))))
  141. (add-entry node file name))))))
  142. (define-callback fill-root-node mod-translator
  143. ((node dir-entry))
  144. (setf (file-stat translator)
  145. (make-stat (stat node)
  146. :mode (make-mode :perms '((:owner :read)
  147. (:group :read)))
  148. :type :reg)
  149. (dir-stat translator)
  150. (make-stat (stat node)
  151. :mode (make-mode :perms '((:owner :read :exec)
  152. (:group :read :exec)))
  153. :type :dir))
  154. (%fill-node translator
  155. (with-open-file (s +file+) (read s))
  156. node))
  157. (defun main ()
  158. (with-port-deallocate (port (file-name-lookup +file+ :flags '(:read)))
  159. (let ((translator
  160. (make-instance 'mod-translator
  161. :timestamp (stat-get (io-stat port) 'st-mtime)
  162. :name "mod-translator")))
  163. (run-translator translator))))
  164. (main)