root/trunk/projects/bos/web/utils.lisp

Revision 3671, 15.4 kB (checked in by ksprotte, 4 months ago)

again whitespace cleanup + removed tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 ;; 2008-01-15: currently not used in the production core
2
3 (in-package :bos.web)
4
5 ;;; date format
6
7 (defun format-date-time (&optional universal-time &key stream
8                          (show-year t) (show-month t)
9                          (show-date t) (show-time t) (show-weekday nil)
10                          (show-seconds t)
11                          vms-style)
12   (or show-date show-time
13       (warn "format-date-time: show-date and show-time are nil, nothing printed"))
14   (multiple-value-bind (sec min hour day month year weekday)
15       (decode-universal-time (or universal-time (get-universal-time)))
16     (when (equal show-year :short)
17       (setq year (mod year 100)))
18     (when show-weekday
19       (setf weekday (nth weekday '("MON" "TUE" "WED" "THU" "FRI" "SA" "SO"))))
20     (let ((s (if stream stream (make-string-output-stream))))
21       (if vms-style
22           (progn
23             (when show-date
24               (setf month (nth (- month 1) '("JAN" "FEB" "MAR" "APR" "MAY" "JUN" "JUL" "AUG" "SEP" "OCT" "NOV" "DEC")))
25               (format s "~2,' d-~a-~d" day month year))
26             (when (and show-date show-time)
27               (princ #\Space s))
28             (when show-time
29               (format s "~2,' d:~2,'0d:~2,'0d" hour min sec)))
30           (progn
31             (when show-weekday
32               (format s "~a " weekday))
33             (when show-date
34               (format s "~2,'0d." day))
35             (when (or show-date show-month)
36               (format s "~2,'0d." month))
37             (when show-year
38               (format s "~4,'0d" year))
39             (when show-time
40               (format s " ~2,'0d:~2,'0d" hour min))
41             (when (and show-seconds show-time)
42               (format s ":~2,'0d" sec))))
43       (unless stream
44         (get-output-stream-string s)))))
45
46 (defun format-time-interval (seconds)
47   (format nil "~d:~2,'0d" (floor (/ seconds 60)) (mod seconds 60)))
48
49 (defun format-duration (duration)
50   (cond
51     ((> duration (* 24 3600)) (format nil "~ad" (round (/ duration (* 24 3600)))))
52     ((> duration 3600)        (format nil "~dh" (round (/ duration 3600))))
53     ((> duration 60)          (format nil "~am" (round (/ duration 60))))
54     (t                        (format nil "~as" duration))))
55
56 (defun month-interval (month year)
57   "Returns two values, the first and last second of the given month"
58   (values
59    (encode-universal-time 0 0 0 1 month year)
60    (- (if (= 12 month)
61           (encode-universal-time 0 0 0 1 1 (+ 1 year))
62           (encode-universal-time 0 0 0 1 (+ 1 month) year))
63       1)))
64
65 (defun day-interval (day month year)
66   "Returns two values, the first and last second of the given day"
67   (values
68    (encode-universal-time 0 0 0 day month year)
69    (encode-universal-time 59 59 23 day month year)))
70
71 (defun year-interval (year)
72   (values
73    (encode-universal-time 0 0 0 1 1 year)
74    (encode-universal-time 59 59 23 31 12 year)))
75
76 (defun get-hourtime (time)
77   (multiple-value-bind (second minute hour)
78       (decode-universal-time time)
79     (declare (ignore second minute))
80     hour))
81
82 (defun get-daytime (time)
83   (multiple-value-bind (second minute hour date month year day)
84       (decode-universal-time time)
85     (declare (ignore second minute hour day))
86     (nth-value 0 (day-interval date month year))))
87
88 (defun get-monthtime (time)
89   (multiple-value-bind (second minute hour date month year day)
90       (decode-universal-time time)
91     (declare (ignore second minute hour date day))
92     (nth-value 0 (month-interval month year))))
93
94 (defun previous-day (count &key (start (get-universal-time)))
95   (- start (* count (* 24 3600))))
96
97 (defun next-day (count &key (start (get-universal-time)))
98   (+ start (* count (* 24 3600))))
99
100 (defun month-num-days (month year)
101   (multiple-value-bind (start end) (month-interval month year)
102     (nth-value 0 (round (/ (- end start) (* 24 3600))))))
103
104 (defun timetag ()
105   (multiple-value-bind (second minute hour date month year)
106       (decode-universal-time (get-universal-time) 0)
107     (format nil
108             "~d~2,'0d~2,'0dT~2,'0d~2,'0d~2,'0d"
109             year month date hour minute second)))
110
111 (defun daytag ()
112   (multiple-value-bind (second minute hour date month year)
113       (decode-universal-time (get-universal-time) 0)
114     (declare (ignore second minute hour))
115     (format nil
116             "~d~2,'0d~2,'0d"
117             year month date)))
118
119
120 ;;; local hostname
121
122 (defun hostname (&key (strip-domain t))
123   (let ((hostname
124          #+acl (sys:getenv "HOST")
125          #+cmu (cdr (assoc :host ext:*environment-list*))))
126     (unless hostname
127       (error "HOST environment variable not set, can't continue"))
128     (if strip-domain
129         (regex-replace "\\..*$" hostname "")
130         hostname)))
131
132
133 ;;; filesystem functions
134
135 (defun directory-empty-p (pathname)
136   (zerop (length (directory pathname))))
137
138 (defun subdir-p (subdir dir)
139   (let ((subdir (probe-file subdir))
140         (dir (probe-file dir)))
141     (when (and subdir dir)
142       (equal (subseq (pathname-directory subdir)
143                      0 (length (pathname-directory dir)))
144              (pathname-directory dir)))))
145
146 (defun copy-file (source target &key (overwrite t))
147   (let ((buffer (make-array 4096 :element-type '(unsigned-byte 8)))
148         (read-count 0))
149     (with-open-file (in source :direction :input
150                         :element-type '(unsigned-byte 8))
151       (with-open-file (out target :direction :output
152                                              :element-type '(unsigned-byte 8)
153                                              :if-exists (if overwrite :overwrite :error) :if-does-not-exist :create)
154         (loop
155            (setf read-count (read-sequence buffer in))
156            (write-sequence buffer out :end read-count)
157            (when (< read-count 4096) (return)))))))
158
159 (defun copy-stream (in out &optional (element-type '(unsigned-byte 8)))
160   "Copy everything from in to out"
161   (let* ((buffer-size 4096)
162          (buffer (make-array buffer-size :element-type element-type)))
163     (labels ((read-chunks ()
164                (let ((size (read-sequence buffer in)))
165                  (if (< size buffer-size)
166                      (write-sequence buffer out :start 0 :end size)
167                      (progn
168                        (write-sequence buffer out)
169                        (read-chunks))))))
170       (read-chunks))))
171
172
173 ;;; list functions
174
175 (defun delete-first (obj list &key (test #'eql))
176   (if (funcall test (first list) obj)
177       (cdr list)
178       (do ((l list (cdr l))
179            (last nil l))
180           ((null l) list)
181         (when (funcall test (car l) obj)
182           (rplacd last (cdr l))
183           (return list)))))
184
185 (defun make-keyword-from-string (string)
186   (if (keywordp string)
187       string
188       (nth-value 0 (intern (string-upcase (regex-replace-all "\\s+" string "-")) 'keyword))))
189
190 (defun assoc-values (item alist &key (test #'equal))
191   (mapcan #'(lambda (x) (and (funcall test item (car x))
192                              (list (cdr x))))
193           alist))
194
195 (defun insert-at-index (idx l elt)
196   (cond ((= idx 0)
197          (cons elt l))
198         ((= idx (1- (length l)))
199          (append l (list elt)))
200         (t (append (subseq l 0 idx)
201                    (list elt)
202                    (subseq l idx)))))
203
204 (defun find-neighbourhood (elt list depth &key (test #'eql))
205   (loop for rest on list
206      with seen = list and i = 0
207      when (funcall test elt (car rest))
208      do (return (subseq seen 0 (+ 1 depth i)))
209      do (if (>= i depth) (setf seen (cdr seen)) (incf i))))
210
211 (defun assoc-to-keywords (args)
212   (loop for (key . value) in args
213      nconc (list (make-keyword-from-string key) value)))
214
215 (defun group-by (list num)
216   (loop for group on list by #'(lambda (seq) (subseq seq num))
217      collect (subseq group 0 num)))
218
219 (defun group-on (list &key (test #'eql) (key #'identity))
220   (let ((hash (make-hash-table :test test)))
221     (dolist (el list)
222       (push el (gethash (funcall key el) hash)))
223     (loop for key being the hash-key of hash using (hash-value val)
224        collect (cons key val))))
225
226 (defun flatten (list)
227   (if (null list)
228       (list)
229       (if (atom (car list))
230           (cons (car list) (flatten (cdr list)))
231           (flatten (append (car list) (cdr list))))))
232
233 (defun count-multiple (objects &rest keys)
234   (let ((hash-tables (loop for i from 1 to (length keys)
235                         collect (make-hash-table :test #'equal)))
236         (sum 0))
237     (dolist (object objects)
238       (incf sum)
239       (loop for key in keys
240          for i from 0
241          do (incf-hash (funcall key object) (nth i hash-tables))))
242     (apply #'values sum hash-tables)))
243
244 (defun rotate (list)
245   (when list
246     (append (cdr list) (list (car list)))))
247
248 (defun nrotate (list)
249   (when list
250     (let ((first (pop list)))
251       (rplacd (last list) (list first))
252       list)))
253
254 (defun genlist (from to)
255   (loop for i from from to to
256      collect i))
257
258 (defun shift-until (list num &key (test #'>=))
259   (do* ((l list (cdr l))
260         (smaller nil (cons i smaller))
261         (i (car l) (car l)))
262        ((funcall test i num)
263         (append l (nreverse smaller)))))
264
265 ;;; hash table
266 (defun hash-to-list (hash &key (key #'cdr) (compare #'>) num)
267   (let ((results (sort (loop for key being the hash-key of hash using (hash-value val)
268                           collect (cons key val))
269                        compare :key key)))
270     (if num
271         (subseq results 0 num)
272         results)))
273
274 (defun hash-values (hash)
275   (loop for value being the hash-values of hash
276      collect value))
277
278 (defun hash-keys (hash)
279   (loop for key being the hash-keys of hash
280      collect key))
281
282 (defun incf-hash (key hash &optional (delta 1))
283   (if (gethash key hash)
284       (incf (gethash key hash) delta)
285       (setf (gethash key hash) delta)))
286
287
288 ;;; randomize
289
290 (defun randomize-list (l)
291   (let ((len (length l)))
292     (flet ((randomize (l)
293              (let ((x (random len))
294                    (mov (pop l)))
295                (insert-at-index x l mov))))
296       (dotimes (x len)
297         (setf l (randomize l)))))
298   l)
299
300 (defun random-elt (choices)
301   (when choices
302     (elt choices (random (length choices)))))
303
304
305 ;;; md5
306
307 #+(or)
308 (defun md5-as-hexstring (input-string)
309   (apply #'concatenate 'string (mapcar #'(lambda (c)
310                                            (format nil "~2,'0X" c))
311                                        (coerce (md5sum-sequence input-string) 'list))))
312
313 ;;; content-types
314
315 (defvar *content-types* '((:jpg . "image/jpeg")
316                           (:png . "image/png")
317                           (:gif . "image/gif")))
318
319 (defun pathname-type-symbol (pathname)
320   (intern (string-upcase (pathname-type pathname)) 'keyword))
321
322 (defun image-content-type (type-symbol)
323   "Return the MIME type of the image - If the type-symbol is a string,
324 it is assumed that the string specifies the MIME type."
325   (if (keywordp type-symbol)
326       (cdr (find type-symbol *content-types* :test #'equal :key #'car))
327       type-symbol))
328
329 (defun image-type-symbol (content-type)
330   (car (find content-type *content-types* :test #'equal :key #'cdr)))
331
332 (defun pathname-content-type (pathname)
333   (image-content-type (pathname-type-symbol pathname)))
334
335 ;;; utf08
336 (defun convert-utf8-to-latin1 (string &key (ignore-errors t))
337   (declare (string string) (optimize (speed 3)))
338   (with-output-to-string (stream)
339     (let ((length (length string))
340           (index 0))
341       (declare (fixnum length index))
342       (loop
343          (unless (< index length) (return nil))
344          (let* ((char (char string index))
345                 (code (char-code char)))
346            (restart-case
347                (handler-bind
348                    ((error #'(lambda (c)
349                                (if ignore-errors
350                                    (invoke-restart 'ignore-byte)
351                                    (error c)))))
352                  (cond
353                    ((< code #x80)       ; ASCII
354                     (write-char char stream)
355                     (incf index 1))
356                    ((< code #xC0)
357
358                     ;; We are in the middle of a multi-byte sequence!
359                     ;; This should never happen, so we raise an error.
360                     (error "Encountered illegal multi-byte sequence."))
361                    ((< code #xC4)
362                     ;; Two byte sequence in Latin-1 range
363                     (unless (< (1+ index) length)
364                       (error "Encountered incomplete two-byte sequence."))
365                     (let* ((char2 (char string (1+ index)))
366                            (code2 (char-code char2)))
367                       (unless (and (logbitp 7 code2) (not (logbitp 6 code2)))
368                         (error "Second byte in sequence is not a continuation."))
369                       (let* ((upper-bits (ldb (byte 2 0) code))
370                              (lower-bits (ldb (byte 6 0) code2))
371                              (new-code (dpb upper-bits (byte 2 6) lower-bits)))
372                         (write-char (code-char new-code) stream)))
373                     (incf index 2))
374                    ((>= code #xFE)
375                     ;; Ignore stray byte-order markers
376                     (incf index 1))
377                    (t
378                     (error (format nil "Multi-byte sequence ~d (~d) outside Latin-1 range."
379                                    code char)))))
380              (ignore-byte ()
381                :report "Ignore byte"
382                (incf index 1))
383              (ignore-n-bytes (n)
384                :report "Ignore some bytes"
385                :interactive (lambda () (format t "Enter a new value: ")
386                                     (multiple-value-list (eval (read))))
387                (incf index n))
388              (write-another-char (b)
389                :report "Write a new char"
390                :interactive (lambda () (format t "Enter a new char: ")
391                                     (multiple-value-list (eval (read))))
392                (write-char b stream)
393                (incf index 1))
394              (write-char ()
395                :report "Write byte to latin-1 string"
396                (write-char char stream)
397                (incf index 1))))))))
398
399 ;;; stirng functions
400 (defun find-matching-strings (regexp strings &key case-sensitive)
401   (let ((scanner (create-scanner regexp :case-insensitive-mode (not case-sensitive))))
402     (remove-if-not #'(lambda (str)
403                        (scan scanner str)) strings)))
404
405 ;;; stream functions
406 ;;; from macho (by Miles Egan)
407 (defun make-extendable-string ()
408   "Creates a resizable string."
409   (make-array 0 :fill-pointer t :adjustable t :element-type 'base-char))
410
411 (defun read-delimited (stream token)
412   "Reads stream up to delimiter."
413   (let ((string (make-extendable-string)))
414     (handler-case
415         (loop with tok-length = (length token)
416            with state = 0
417            initially (vector-push-extend (read-char stream) string) ;; skip first char
418            for c = (read-char stream)
419            while (< state tok-length)
420            do (let ((match (char= c (aref token state))))
421                 (cond
422                   ((and (> state 0) (not match))
423                    (unread-char c stream)
424                    (setf state 0))
425                   (t
426                    (if match (incf state))
427                    (vector-push-extend c string))))
428            finally (progn
429                      (file-position stream (- (file-position stream) tok-length))
430                      (adjust-array string (- (length string) tok-length) :fill-pointer t)
431                      (return (values string t))))
432       (end-of-file () (values (if (> (length string) 0) string nil)
433                               nil)))))
434
435 (defun read-file (stream)
436   "Reads entire contents of stream into a string."
437   (loop with result = (make-extendable-string)
438      for c = (read-char stream nil)
439      while c
440      do (vector-push-extend c result)
441      finally (return result)))
Note: See TracBrowser for help on using the browser.