+ (car (string-tokenize (utsname:sysname (uname)) char-set:letter)))))
+
+;; We don't use (srfi srfi-39) (parameter objects) here because that
+;; does not give us a name/handle to the underlying fluids themselves.
+
+(define %parser (make-fluid))
+(define %location (make-fluid))
+;; No public setters: should not get overwritten in action
+(define-public (*parser*) (fluid-ref %parser))
+(define-public (*location*) (fluid-ref %location))
+;; but properly scoped location should be fine
+(defmacro-public with-location (loc . body)
+ `(with-fluids ((,%location ,loc)) ,@body))
+
+;; It would be nice to convert occurences of parser/location to
+;; (*parser*)/(*location*) using the syncase module but it is utterly
+;; broken in GUILE 1 and would require changing a lot of unrelated
+;; innocuous constructs which just happen to fall apart with
+;; inscrutable error messages.
+
+;;
+;; Session-handling variables and procedures.
+;;
+;; A "session" corresponds to one .ly file processed on a LilyPond
+;; command line. Every session gets to see a reasonably fresh state
+;; of LilyPond and should work independently from previous files.
+;;
+;; Session management relies on cooperation, namely the user not
+;; trying to change variables and data structures internal to
+;; LilyPond. It is not proof against in-place modification of data
+;; structures (as they are just reinitialized with the original
+;; identities), and it is not proof against tampering with internals.
+;;
+;; As a consequence, session management is not sufficient for
+;; separating multiple independent .ly files in "-dsafe" mode: you
+;; should give each its own LilyPond process when reliable separation
+;; is mandatory.
+;;
+;; For standard tasks and programming practices, multiple sessions in
+;; the same LilyPond job should work reasonably independently and
+;; without "bleed-over" while still loading and compiling the
+;; relevant .scm and .ly files only once.
+;;
+
+(define lilypond-declarations '())
+(define lilypond-exports '())
+(define after-session-hook (make-hook))
+
+(define-public (call-after-session thunk)
+ (if (ly:undead? lilypond-declarations)
+ (ly:error (_ "call-after-session used after session start")))
+ (add-hook! after-session-hook thunk #t))
+
+(define (make-session-variable name value)
+ (if (ly:undead? lilypond-declarations)
+ (ly:error (_ "define-session used after session start")))
+ (let ((var (module-make-local-var! (current-module) name)))
+ (if (variable-bound? var)
+ (ly:error (_ "symbol ~S redefined") name))
+ (variable-set! var value)
+ var))
+
+(defmacro define-session (name value)
+ "This defines a variable @var{name} with the starting value
+@var{value} that is reinitialized at the start of each session.
+A@tie{}session basically corresponds to one LilyPond file on the
+command line. The value is recorded at the start of the first session
+after loading all initialization files and before loading the user
+file and is reinstated for all of the following sessions. This
+happens just by replacing the value, not by copying structures, so you
+should not destructively modify them. For example, lists defined in
+this manner should be changed within a session only be adding material
+to their front or replacing them altogether, not by modifying parts of
+them. It is an error to call @code{define-session} after the first
+session has started."
+ (define (add-session-variable name value)
+ (set! lilypond-declarations
+ (cons (make-session-variable name value) lilypond-declarations)))
+ `(,add-session-variable ',name ,value))
+
+(defmacro define-session-public (name value)
+ "Like @code{define-session}, but also exports @var{name} into parser modules."
+ (define (add-session-variable name value)
+ (set! lilypond-exports
+ (acons name (make-session-variable name value) lilypond-exports)))
+ `(begin
+ ;; this is a bit icky: we place the variable right into every
+ ;; parser module so that both set! and define will affect the
+ ;; original variable in the (lily) module. However, we _also_
+ ;; export it normally from (lily) for the sake of other modules
+ ;; not sharing the name space of the parser.
+ (,add-session-variable ',name ,value)
+ (export ,name)))
+
+(define (session-terminate)
+ (if (ly:undead? lilypond-declarations)
+ (begin
+ (for-each
+ (lambda (p) (variable-set! (cadr p) (cddr p)))
+ (ly:get-undead lilypond-declarations))
+ (run-hook after-session-hook))))
+
+(define lilypond-interfaces #f)
+
+(define-public (session-initialize thunk)
+ "Initialize this session. The first session in a LilyPond run is
+initialized by calling @var{thunk}, then recording the values of all
+variables in the current module as well as those defined with
+@code{define-session}. Subsequent calls of @code{session-initialize}
+ignore @var{thunk} and instead just reinitialize all recorded
+variables to their value after the initial call of @var{thunk}."
+
+ ;; We need to save the variables of the current module along with
+ ;; their values: functions defined in the module might refer to the
+ ;; variables.
+
+ ;; The entries in lilypond-declarations consist of a cons* consisting
+ ;; of symbol, variable, and value. Variables defined with
+ ;; define-session have the symbol set to #f.
+
+ (if (ly:undead? lilypond-declarations)
+ (begin
+ (module-use-interfaces! (current-module) (reverse lilypond-interfaces))
+ (for-each
+ (lambda (p)
+ (let ((var (cadr p))
+ (val (cddr p)))
+ (variable-set! var val)
+ (if (car p)
+ (module-add! (current-module) (car p) var))))
+ (ly:get-undead lilypond-declarations)))
+ (begin
+ ;; import all public session variables natively into parser
+ ;; module. That makes them behave identically under define/set!
+ (for-each (lambda (v)
+ (module-add! (current-module) (car v) (cdr v)))
+ lilypond-exports)
+ ;; Initialize first session
+ (thunk)
+ ;; lilypond-exports is no longer needed since we will grab its
+ ;; values from (current-module).
+ (set! lilypond-exports #f)
+ (set! lilypond-interfaces
+ (filter (lambda (m) (eq? 'interface (module-kind m)))
+ (module-uses (current-module))))
+ (let ((decl (map! (lambda (v)
+ (cons* #f v (variable-ref v)))
+ lilypond-declarations)))
+ (module-for-each
+ (lambda (s v)
+ (let ((val (variable-ref v)))
+ (if (not (ly:lily-parser? val))
+ (set! decl
+ (cons
+ (cons* s v val)
+ decl)))))
+ (current-module))
+ (set! lilypond-declarations (ly:make-undead decl))))))