gnu: htop: Update to 1.0.3.
[jackhill/guix/guix.git] / guix / utils.scm
CommitLineData
233e7676 1;;; GNU Guix --- Functional package management for GNU
2cd5c038 2;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org>
56b943de 3;;; Copyright © 2013 Mark H Weaver <mhw@netris.org>
e3deeebb 4;;;
233e7676 5;;; This file is part of GNU Guix.
e3deeebb 6;;;
233e7676 7;;; GNU Guix is free software; you can redistribute it and/or modify it
e3deeebb
LC
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
233e7676 12;;; GNU Guix is distributed in the hope that it will be useful, but
e3deeebb
LC
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
233e7676 18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
e3deeebb
LC
19
20(define-module (guix utils)
00e219d1 21 #:use-module (guix config)
38b3122a 22 #:use-module (srfi srfi-1)
72d86963 23 #:use-module (srfi srfi-9)
01ac19dc 24 #:use-module (srfi srfi-11)
38b3122a 25 #:use-module (srfi srfi-26)
de4c3f26 26 #:use-module (srfi srfi-39)
e3deeebb
LC
27 #:use-module (srfi srfi-60)
28 #:use-module (rnrs bytevectors)
dba6b34b 29 #:use-module ((rnrs io ports) #:select (put-bytevector))
e0fbbc88 30 #:use-module ((guix build utils) #:select (dump-port))
29fa45f4 31 #:use-module ((guix build syscalls) #:select (errno))
c8369cac 32 #:use-module (ice-9 vlist)
38b3122a 33 #:use-module (ice-9 format)
de4c3f26
LC
34 #:autoload (ice-9 popen) (open-pipe*)
35 #:autoload (ice-9 rdelim) (read-line)
98090557 36 #:use-module (ice-9 regex)
72d86963 37 #:use-module (ice-9 match)
8ef3401f 38 #:use-module (ice-9 format)
2cd5c038 39 #:use-module (system foreign)
ddc29a78 40 #:export (bytevector->base16-string
6d800a80 41 base16-string->bytevector
de4c3f26
LC
42
43 %nixpkgs-directory
44 nixpkgs-derivation
ce5d658c 45 nixpkgs-derivation*
de4c3f26 46
900f7267 47 compile-time-value
2cd5c038 48 fcntl-flock
98090557 49 memoize
0af2c24e
LC
50 default-keyword-arguments
51 substitute-keyword-arguments
ff352cfb 52
64fc89b6 53 <location>
ff352cfb
LC
54 location
55 location?
56 location-file
57 location-line
58 location-column
59 source-properties->location
60
98090557 61 gnu-triplet->nix-system
9b48fb88 62 %current-system
9c1edabd 63 %current-target-system
0d1e6ce4
MW
64 version-compare
65 version>?
7db3ff4a 66 guile-version>?
04fd96ca 67 package-name->name+version
2bcfb9e0 68 string-tokenize*
56b943de 69 string-replace-substring
0fdd3bea 70 file-extension
ac10e0e1 71 file-sans-extension
861693f3 72 call-with-temporary-output-file
04d4c8a4 73 with-atomic-file-output
e0fbbc88 74 fold2
7a8024a3
LC
75
76 filtered-port
77 compressed-port
80dea563 78 decompressed-port
01ac19dc
LC
79 call-with-decompressed-port
80 compressed-output-port
81 call-with-compressed-output-port))
e3deeebb 82
38b3122a 83\f
dba6b34b
LC
84;;;
85;;; Compile-time computations.
86;;;
87
88(define-syntax compile-time-value
89 (syntax-rules ()
90 "Evaluate the given expression at compile time. The expression must
91evaluate to a simple datum."
92 ((_ exp)
93 (let-syntax ((v (lambda (s)
94 (let ((val exp))
95 (syntax-case s ()
96 (_ #`'#,(datum->syntax s val)))))))
97 v))))
98
99\f
38b3122a
LC
100;;;
101;;; Base 16.
102;;;
103
104(define (bytevector->base16-string bv)
105 "Return the hexadecimal representation of BV's contents."
106 (define len
107 (bytevector-length bv))
108
109 (let-syntax ((base16-chars (lambda (s)
110 (syntax-case s ()
111 (_
112 (let ((v (list->vector
113 (unfold (cut > <> 255)
114 (lambda (n)
115 (format #f "~2,'0x" n))
116 1+
117 0))))
118 v))))))
119 (define chars base16-chars)
120 (let loop ((i 0)
121 (r '()))
122 (if (= i len)
123 (string-concatenate-reverse r)
124 (loop (+ 1 i)
125 (cons (vector-ref chars (bytevector-u8-ref bv i)) r))))))
126
6d800a80
LC
127(define base16-string->bytevector
128 (let ((chars->value (fold (lambda (i r)
129 (vhash-consv (string-ref (number->string i 16)
130 0)
131 i r))
132 vlist-null
133 (iota 16))))
134 (lambda (s)
135 "Return the bytevector whose hexadecimal representation is string S."
136 (define bv
137 (make-bytevector (quotient (string-length s) 2) 0))
138
139 (string-fold (lambda (chr i)
140 (let ((j (quotient i 2))
141 (v (and=> (vhash-assv chr chars->value) cdr)))
142 (if v
143 (if (zero? (logand i 1))
144 (bytevector-u8-set! bv j
145 (arithmetic-shift v 4))
146 (let ((w (bytevector-u8-ref bv j)))
147 (bytevector-u8-set! bv j (logior v w))))
148 (error "invalid hexadecimal character" chr)))
149 (+ i 1))
150 0
151 s)
152 bv)))
153
de4c3f26
LC
154
155\f
e0fbbc88
LC
156;;;
157;;; Filtering & pipes.
158;;;
159
160(define (filtered-port command input)
161 "Return an input port where data drained from INPUT is filtered through
162COMMAND (a list). In addition, return a list of PIDs that the caller must
101d9f3f
LC
163wait. When INPUT is a file port, it must be unbuffered; otherwise, any
164buffered data is lost."
e0fbbc88 165 (let loop ((input input)
443eb4e9 166 (pids '()))
e0fbbc88
LC
167 (if (file-port? input)
168 (match (pipe)
169 ((in . out)
170 (match (primitive-fork)
171 (0
443eb4e9
LC
172 (dynamic-wind
173 (const #f)
174 (lambda ()
175 (close-port in)
176 (close-port (current-input-port))
177 (dup2 (fileno input) 0)
178 (close-port (current-output-port))
179 (dup2 (fileno out) 1)
180 (catch 'system-error
181 (lambda ()
182 (apply execl (car command) command))
183 (lambda args
184 (format (current-error-port)
185 "filtered-port: failed to execute '~{~a ~}': ~a~%"
186 command (strerror (system-error-errno args))))))
187 (lambda ()
188 (primitive-_exit 1))))
e0fbbc88
LC
189 (child
190 (close-port out)
191 (values in (cons child pids))))))
192
193 ;; INPUT is not a file port, so fork just for the sake of tunneling it
194 ;; through a file port.
195 (match (pipe)
196 ((in . out)
197 (match (primitive-fork)
198 (0
199 (dynamic-wind
200 (const #t)
201 (lambda ()
202 (close-port in)
203 (dump-port input out))
204 (lambda ()
205 (false-if-exception (close out))
443eb4e9 206 (primitive-_exit 0))))
e0fbbc88
LC
207 (child
208 (close-port out)
209 (loop in (cons child pids)))))))))
210
7a8024a3
LC
211(define (decompressed-port compression input)
212 "Return an input port where INPUT is decompressed according to COMPRESSION,
213a symbol such as 'xz."
214 (match compression
215 ((or #f 'none) (values input '()))
216 ('bzip2 (filtered-port `(,%bzip2 "-dc") input))
217 ('xz (filtered-port `(,%xz "-dc") input))
218 ('gzip (filtered-port `(,%gzip "-dc") input))
219 (else (error "unsupported compression scheme" compression))))
220
221(define (compressed-port compression input)
222 "Return an input port where INPUT is decompressed according to COMPRESSION,
223a symbol such as 'xz."
224 (match compression
225 ((or #f 'none) (values input '()))
226 ('bzip2 (filtered-port `(,%bzip2 "-c") input))
227 ('xz (filtered-port `(,%xz "-c") input))
228 ('gzip (filtered-port `(,%gzip "-c") input))
229 (else (error "unsupported compression scheme" compression))))
230
01ac19dc
LC
231(define (call-with-decompressed-port compression port proc)
232 "Call PROC with a wrapper around PORT, a file port, that decompresses data
30ce8012 233read from PORT according to COMPRESSION, a symbol such as 'xz."
01ac19dc
LC
234 (let-values (((decompressed pids)
235 (decompressed-port compression port)))
236 (dynamic-wind
237 (const #f)
238 (lambda ()
01ac19dc
LC
239 (proc decompressed))
240 (lambda ()
241 (close-port decompressed)
242 (unless (every (compose zero? cdr waitpid) pids)
243 (error "decompressed-port failure" pids))))))
244
80dea563
LC
245(define (filtered-output-port command output)
246 "Return an output port. Data written to that port is filtered through
247COMMAND and written to OUTPUT, an output file port. In addition, return a
248list of PIDs to wait for. OUTPUT must be unbuffered; otherwise, any buffered
249data is lost."
250 (match (pipe)
251 ((in . out)
252 (match (primitive-fork)
253 (0
254 (dynamic-wind
255 (const #f)
256 (lambda ()
257 (close-port out)
258 (close-port (current-input-port))
259 (dup2 (fileno in) 0)
260 (close-port (current-output-port))
261 (dup2 (fileno output) 1)
262 (catch 'system-error
263 (lambda ()
264 (apply execl (car command) command))
265 (lambda args
266 (format (current-error-port)
267 "filtered-output-port: failed to execute '~{~a ~}': ~a~%"
268 command (strerror (system-error-errno args))))))
269 (lambda ()
270 (primitive-_exit 1))))
271 (child
272 (close-port in)
273 (values out (list child)))))))
274
275(define (compressed-output-port compression output)
276 "Return an output port whose input is compressed according to COMPRESSION,
277a symbol such as 'xz, and then written to OUTPUT. In addition return a list
278of PIDs to wait for."
279 (match compression
280 ((or #f 'none) (values output '()))
281 ('bzip2 (filtered-output-port `(,%bzip2 "-c") output))
282 ('xz (filtered-output-port `(,%xz "-c") output))
283 ('gzip (filtered-output-port `(,%gzip "-c") output))
284 (else (error "unsupported compression scheme" compression))))
285
01ac19dc
LC
286(define (call-with-compressed-output-port compression port proc)
287 "Call PROC with a wrapper around PORT, a file port, that compresses data
30ce8012 288that goes to PORT according to COMPRESSION, a symbol such as 'xz."
01ac19dc
LC
289 (let-values (((compressed pids)
290 (compressed-output-port compression port)))
291 (dynamic-wind
292 (const #f)
293 (lambda ()
01ac19dc
LC
294 (proc compressed))
295 (lambda ()
296 (close-port compressed)
297 (unless (every (compose zero? cdr waitpid) pids)
298 (error "compressed-output-port failure" pids))))))
299
e0fbbc88 300\f
de4c3f26
LC
301;;;
302;;; Nixpkgs.
303;;;
304
305(define %nixpkgs-directory
900f7267
LC
306 (make-parameter
307 ;; Capture the build-time value of $NIXPKGS.
a09ec3a5
LC
308 (or %nixpkgs
309 (and=> (getenv "NIXPKGS")
310 (lambda (val)
311 ;; Bail out when passed an empty string, otherwise
312 ;; `nix-instantiate' will sit there and attempt to read
313 ;; from its standard input.
314 (if (string=? val "")
315 #f
316 val))))))
de4c3f26 317
fbc93bed 318(define* (nixpkgs-derivation attribute #:optional (system (%current-system)))
de4c3f26 319 "Return the derivation path of ATTRIBUTE in Nixpkgs."
b86b0056 320 (let* ((p (open-pipe* OPEN_READ (or (getenv "NIX_INSTANTIATE")
00e219d1 321 %nix-instantiate)
fbc93bed
LC
322 "-A" attribute (%nixpkgs-directory)
323 "--argstr" "system" system))
de4c3f26
LC
324 (l (read-line p))
325 (s (close-pipe p)))
326 (and (zero? (status:exit-val s))
327 (not (eof-object? l))
328 l)))
329
ce5d658c
LC
330(define-syntax-rule (nixpkgs-derivation* attribute)
331 "Evaluate the given Nixpkgs derivation at compile-time."
332 (compile-time-value (nixpkgs-derivation attribute)))
333
de4c3f26 334\f
2cd5c038
LC
335;;;
336;;; Advisory file locking.
337;;;
338
339(define %struct-flock
340 ;; 'struct flock' from <fcntl.h>.
341 (list short ; l_type
342 short ; l_whence
343 size_t ; l_start
344 size_t ; l_len
345 int)) ; l_pid
346
347(define F_SETLKW
348 ;; On Linux-based systems, this is usually 7, but not always
349 ;; (exceptions include SPARC.) On GNU/Hurd, it's 9.
350 (compile-time-value
351 (cond ((string-contains %host-type "sparc") 9) ; sparc-*-linux-gnu
352 ((string-contains %host-type "linux") 7) ; *-linux-gnu
353 (else 9)))) ; *-gnu*
354
c7445833
LC
355(define F_SETLK
356 ;; Likewise: GNU/Hurd and SPARC use 8, while the others typically use 6.
357 (compile-time-value
358 (cond ((string-contains %host-type "sparc") 8) ; sparc-*-linux-gnu
359 ((string-contains %host-type "linux") 6) ; *-linux-gnu
360 (else 8)))) ; *-gnu*
361
2cd5c038
LC
362(define F_xxLCK
363 ;; The F_RDLCK, F_WRLCK, and F_UNLCK constants.
364 (compile-time-value
365 (cond ((string-contains %host-type "sparc") #(1 2 3)) ; sparc-*-linux-gnu
366 ((string-contains %host-type "hppa") #(1 2 3)) ; hppa-*-linux-gnu
367 ((string-contains %host-type "linux") #(0 1 2)) ; *-linux-gnu
368 (else #(1 2 3))))) ; *-gnu*
369
370(define fcntl-flock
371 (let* ((ptr (dynamic-func "fcntl" (dynamic-link)))
372 (proc (pointer->procedure int ptr `(,int ,int *))))
c7445833 373 (lambda* (fd-or-port operation #:key (wait? #t))
2cd5c038 374 "Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION
c7445833
LC
375must be a symbol, one of 'read-lock, 'write-lock, or 'unlock. When WAIT? is
376true, block until the lock is acquired; otherwise, thrown an 'flock-error'
377exception if it's already taken."
2cd5c038
LC
378 (define (operation->int op)
379 (case op
380 ((read-lock) (vector-ref F_xxLCK 0))
381 ((write-lock) (vector-ref F_xxLCK 1))
382 ((unlock) (vector-ref F_xxLCK 2))
383 (else (error "invalid fcntl-flock operation" op))))
384
385 (define fd
386 (if (port? fd-or-port)
387 (fileno fd-or-port)
388 fd-or-port))
389
390 ;; XXX: 'fcntl' is a vararg function, but here we happily use the
391 ;; standard ABI; crossing fingers.
392 (let ((err (proc fd
c7445833
LC
393 (if wait?
394 F_SETLKW ; lock & wait
395 F_SETLK) ; non-blocking attempt
2cd5c038
LC
396 (make-c-struct %struct-flock
397 (list (operation->int operation)
398 SEEK_SET
399 0 0 ; whole file
400 0)))))
401 (or (zero? err)
402
403 ;; Presumably we got EAGAIN or so.
9ea3ef26 404 (throw 'flock-error (errno)))))))
2cd5c038
LC
405
406\f
de4c3f26
LC
407;;;
408;;; Miscellaneous.
409;;;
410
411(define (memoize proc)
412 "Return a memoizing version of PROC."
413 (let ((cache (make-hash-table)))
414 (lambda args
415 (let ((results (hash-ref cache args)))
416 (if results
417 (apply values results)
418 (let ((results (call-with-values (lambda ()
419 (apply proc args))
420 list)))
421 (hash-set! cache args results)
422 (apply values results)))))))
98090557 423
0af2c24e
LC
424(define (default-keyword-arguments args defaults)
425 "Return ARGS augmented with any keyword/value from DEFAULTS for
426keywords not already present in ARGS."
427 (let loop ((defaults defaults)
428 (args args))
429 (match defaults
430 ((kw value rest ...)
431 (loop rest
432 (if (assoc-ref kw args)
433 args
434 (cons* kw value args))))
435 (()
436 args))))
437
438(define-syntax substitute-keyword-arguments
439 (syntax-rules ()
440 "Return a new list of arguments where the value for keyword arg KW is
441replaced by EXP. EXP is evaluated in a context where VAR is boud to the
442previous value of the keyword argument."
443 ((_ original-args ((kw var) exp) ...)
444 (let loop ((args original-args)
445 (before '()))
446 (match args
447 ((kw var rest (... ...))
448 (loop rest (cons* exp kw before)))
449 ...
450 ((x rest (... ...))
451 (loop rest (cons x before)))
452 (()
453 (reverse before)))))))
454
98090557
LC
455(define (gnu-triplet->nix-system triplet)
456 "Return the Nix system type corresponding to TRIPLET, a GNU triplet as
457returned by `config.guess'."
458 (let ((triplet (cond ((string-match "^i[345]86-(.*)$" triplet)
459 =>
460 (lambda (m)
461 (string-append "i686-" (match:substring m 1))))
462 (else triplet))))
463 (cond ((string-match "^([^-]+)-([^-]+-)?linux-gnu.*" triplet)
464 =>
465 (lambda (m)
466 ;; Nix omits `-gnu' for GNU/Linux.
467 (string-append (match:substring m 1) "-linux")))
468 ((string-match "^([^-]+)-([^-]+-)?([[:alpha:]]+)([0-9]+\\.?)*$" triplet)
469 =>
470 (lambda (m)
471 ;; Nix strip the version number from names such as `gnu0.3',
472 ;; `darwin10.2.0', etc., and always strips the vendor part.
473 (string-append (match:substring m 1) "-"
474 (match:substring m 3))))
475 (else triplet))))
476
477(define %current-system
478 ;; System type as expected by Nix, usually ARCHITECTURE-KERNEL.
d8eea3d2
LC
479 ;; By default, this is equal to (gnu-triplet->nix-system %host-type).
480 (make-parameter %system))
ff352cfb 481
9c1edabd
LC
482(define %current-target-system
483 ;; Either #f or a GNU triplet representing the target system we are
484 ;; cross-building to.
485 (make-parameter #f))
486
0d1e6ce4
MW
487(define version-compare
488 (let ((strverscmp
489 (let ((sym (or (dynamic-func "strverscmp" (dynamic-link))
490 (error "could not find `strverscmp' (from GNU libc)"))))
491 (pointer->procedure int sym (list '* '*)))))
492 (lambda (a b)
493 "Return '> when A denotes a newer version than B,
494'< when A denotes a older version than B,
495or '= when they denote equal versions."
496 (let ((result (strverscmp (string->pointer a) (string->pointer b))))
497 (cond ((positive? result) '>)
498 ((negative? result) '<)
499 (else '=))))))
500
501(define (version>? a b)
502 "Return #t when A denotes a newer version than B."
503 (eq? '> (version-compare a b)))
504
7db3ff4a
LC
505(define (guile-version>? str)
506 "Return #t if the running Guile version is greater than STR."
507 ;; Note: Using (version>? (version) "2.0.5") or similar doesn't work,
508 ;; because the result of (version) can have a prefix, like "2.0.5-deb1".
509 (version>? (string-append (major-version) "."
510 (minor-version) "."
511 (micro-version))
512 str))
513
9b48fb88
LC
514(define (package-name->name+version name)
515 "Given NAME, a package name like \"foo-0.9.1b\", return two values:
516\"foo\" and \"0.9.1b\". When the version part is unavailable, NAME and
517#f are returned. The first hyphen followed by a digit is considered to
518introduce the version part."
519 ;; See also `DrvName' in Nix.
520
521 (define number?
522 (cut char-set-contains? char-set:digit <>))
523
524 (let loop ((chars (string->list name))
525 (prefix '()))
526 (match chars
527 (()
528 (values name #f))
529 ((#\- (? number? n) rest ...)
530 (values (list->string (reverse prefix))
531 (list->string (cons n rest))))
532 ((head tail ...)
533 (loop tail (cons head prefix))))))
534
0fdd3bea
LC
535(define (file-extension file)
536 "Return the extension of FILE or #f if there is none."
537 (let ((dot (string-rindex file #\.)))
538 (and dot (substring file (+ 1 dot) (string-length file)))))
539
ac10e0e1
LC
540(define (file-sans-extension file)
541 "Return the substring of FILE without its extension, if any."
542 (let ((dot (string-rindex file #\.)))
543 (if dot
544 (substring file 0 dot)
545 file)))
546
2bcfb9e0
LC
547(define (string-tokenize* string separator)
548 "Return the list of substrings of STRING separated by SEPARATOR. This is
549like `string-tokenize', but SEPARATOR is a string."
550 (define (index string what)
551 (let loop ((string string)
552 (offset 0))
553 (cond ((string-null? string)
554 #f)
555 ((string-prefix? what string)
556 offset)
557 (else
558 (loop (string-drop string 1) (+ 1 offset))))))
559
560 (define len
561 (string-length separator))
562
563 (let loop ((string string)
564 (result '()))
565 (cond ((index string separator)
566 =>
567 (lambda (offset)
568 (loop (string-drop string (+ offset len))
569 (cons (substring string 0 offset)
570 result))))
571 (else
572 (reverse (cons string result))))))
573
56b943de
LC
574(define* (string-replace-substring str substr replacement
575 #:optional
576 (start 0)
577 (end (string-length str)))
578 "Replace all occurrences of SUBSTR in the START--END range of STR by
579REPLACEMENT."
580 (match (string-length substr)
581 (0
582 (error "string-replace-substring: empty substring"))
583 (substr-length
584 (let loop ((start start)
585 (pieces (list (substring str 0 start))))
586 (match (string-contains str substr start end)
587 (#f
588 (string-concatenate-reverse
589 (cons (substring str start) pieces)))
590 (index
591 (loop (+ index substr-length)
592 (cons* replacement
593 (substring str start index)
594 pieces))))))))
595
861693f3
LC
596(define (call-with-temporary-output-file proc)
597 "Call PROC with a name of a temporary file and open output port to that
598file; close the file and delete it when leaving the dynamic extent of this
599call."
600 (let* ((template (string-copy "guix-file.XXXXXX"))
601 (out (mkstemp! template)))
602 (dynamic-wind
603 (lambda ()
604 #t)
605 (lambda ()
606 (proc template out))
607 (lambda ()
608 (false-if-exception (close out))
609 (false-if-exception (delete-file template))))))
04d4c8a4
LC
610
611(define (with-atomic-file-output file proc)
612 "Call PROC with an output port for the file that is going to replace FILE.
613Upon success, FILE is atomically replaced by what has been written to the
614output port, and PROC's result is returned."
615 (let* ((template (string-append file ".XXXXXX"))
616 (out (mkstemp! template)))
617 (with-throw-handler #t
618 (lambda ()
619 (let ((result (proc out)))
620 (close out)
621 (rename-file template file)
622 result))
623 (lambda (key . args)
624 (false-if-exception (delete-file template))))))
861693f3 625
04fd96ca
LC
626(define fold2
627 (case-lambda
628 ((proc seed1 seed2 lst)
629 "Like `fold', but with a single list and two seeds."
630 (let loop ((result1 seed1)
631 (result2 seed2)
632 (lst lst))
633 (if (null? lst)
634 (values result1 result2)
635 (call-with-values
636 (lambda () (proc (car lst) result1 result2))
637 (lambda (result1 result2)
638 (loop result1 result2 (cdr lst)))))))
639 ((proc seed1 seed2 lst1 lst2)
640 "Like `fold', but with a two lists and two seeds."
641 (let loop ((result1 seed1)
642 (result2 seed2)
643 (lst1 lst1)
644 (lst2 lst2))
645 (if (or (null? lst1) (null? lst2))
646 (values result1 result2)
647 (call-with-values
648 (lambda () (proc (car lst1) (car lst2) result1 result2))
649 (lambda (result1 result2)
650 (fold2 proc result1 result2 (cdr lst1) (cdr lst2)))))))))
651
ff352cfb
LC
652\f
653;;;
654;;; Source location.
655;;;
656
657;; A source location.
658(define-record-type <location>
659 (make-location file line column)
660 location?
661 (file location-file) ; file name
662 (line location-line) ; 1-indexed line
663 (column location-column)) ; 0-indexed column
664
665(define location
666 (memoize
667 (lambda (file line column)
668 "Return the <location> object for the given FILE, LINE, and COLUMN."
669 (and line column file
670 (make-location file line column)))))
671
672(define (source-properties->location loc)
673 "Return a location object based on the info in LOC, an alist as returned
674by Guile's `source-properties', `frame-source', `current-source-location',
675etc."
676 (let ((file (assq-ref loc 'filename))
677 (line (assq-ref loc 'line))
678 (col (assq-ref loc 'column)))
5fe21fbe
LC
679 ;; In accordance with the GCS, start line and column numbers at 1. Note
680 ;; that unlike LINE and `port-column', COL is actually 1-indexed here...
681 (location file (and line (+ line 1)) col)))