diff --git a/doc/guix.texi b/doc/guix.texi index 96b4675a01..32e34b7d52 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -41007,13 +41007,15 @@ The easiest way to extend a service type, without defining a new service type is to use the @code{simple-service} helper from @code{(gnu services)}. +@findex literal-string @lisp (simple-service 'some-useful-env-vars-service home-environment-variables-service-type `(("LESSHISTFILE" . "$XDG_CACHE_HOME/.lesshst") ("SHELL" . ,(file-append zsh "/bin/zsh")) ("USELESS_VAR" . #f) - ("_JAVA_AWT_WM_NONREPARENTING" . #t))) + ("_JAVA_AWT_WM_NONREPARENTING" . #t) + ("LITERAL_VALUE" . ,(literal-string "$@{abc@}")))) @end lisp If you include such a service in you home environment definition, it @@ -41021,11 +41023,17 @@ will add the following content to the @file{setup-environment} script (which is expected to be sourced by the login shell): @example -export LESSHISTFILE=$XDG_CACHE_HOME/.lesshst -export SHELL=/gnu/store/2hsg15n644f0glrcbkb1kqknmmqdar03-zsh-5.8/bin/zsh +export LESSHISTFILE="$XDG_CACHE_HOME/.lesshst" +export SHELL="/gnu/store/2hsg15n644f0glrcbkb1kqknmmqdar03-zsh-5.8/bin/zsh" export _JAVA_AWT_WM_NONREPARENTING +export LITERAL_VALUE='$@{abc@}' @end example +Notice that @code{literal-string} above lets us declare that a value is +to be interpreted as a @dfn{literal string}, meaning that ``special +characters'' such as the dollar sign will not be interpreted by the +shell. + @quotation Note Make sure that module @code{(gnu packages shells)} is imported with @code{use-modules} or any other way, this namespace contains the diff --git a/gnu/home/services.scm b/gnu/home/services.scm index e154f5c443..692354c644 100644 --- a/gnu/home/services.scm +++ b/gnu/home/services.scm @@ -1,6 +1,7 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2021 Andrew Tropin ;;; Copyright © 2021 Xinglu Chen +;;; Copyright © 2022 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -33,6 +34,7 @@ #:use-module (guix i18n) #:use-module (guix modules) #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) #:use-module (ice-9 match) #:use-module (ice-9 vlist) @@ -47,6 +49,10 @@ home-run-on-change-service-type home-provenance-service-type + literal-string + literal-string? + literal-string-value + environment-variable-shell-definitions home-files-directory xdg-configuration-files-directory @@ -171,32 +177,50 @@ packages, configuration files, activation script, and so on."))) configuration files that the user has declared in their @code{home-environment} record."))) +;; Representation of a literal string. +(define-record-type + (literal-string str) + literal-string? + (str literal-string-value)) + (define (environment-variable-shell-definitions variables) "Return a gexp that evaluates to a list of POSIX shell statements defining VARIABLES, a list of environment variable name/value pairs. The returned code ensures variable values are properly quoted." - #~(let ((shell-quote - (lambda (value) - ;; Double-quote VALUE, leaving dollar sign as is. - (let ((quoted (list->string - (string-fold-right + #~(let* ((quote-string + (lambda (value quoted-chars) + (list->string (string-fold-right (lambda (chr lst) - (case chr - ((#\" #\\) - (append (list chr #\\) lst)) - (else (cons chr lst)))) + (if (memq chr quoted-chars) + (append (list chr #\\) lst) + (cons chr lst))) '() value)))) - (string-append "\"" quoted "\""))))) + (shell-double-quote + (lambda (value) + ;; Double-quote VALUE, leaving dollar sign as is. + (string-append "\"" (quote-string value '(#\" #\\)) + "\""))) + (shell-single-quote + (lambda (value) + ;; Single-quote VALUE to enter a literal string. + (string-append "'" (quote-string value '(#\' #\\)) + "'")))) (string-append #$@(map (match-lambda ((key . #f) "") ((key . #t) #~(string-append "export " #$key "\n")) - ((key . value) + ((key . (? string? value)) #~(string-append "export " #$key "=" - (shell-quote #$value) "\n"))) + (shell-double-quote #$value) + "\n")) + ((key . (? literal-string? value)) + #~(string-append "export " #$key "=" + (shell-single-quote + #$(literal-string-value value)) + "\n"))) variables)))) (define (environment-variables->setup-environment-script vars) diff --git a/tests/guix-home.sh b/tests/guix-home.sh index d5e2dadbb5..423ebf6f33 100644 --- a/tests/guix-home.sh +++ b/tests/guix-home.sh @@ -81,7 +81,8 @@ trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT (simple-service 'add-environment-variable home-environment-variables-service-type - '(("TODAY" . "26 messidor"))) + `(("TODAY" . "26 messidor") + ("LITERAL" . ,(literal-string "${abc}")))) (simple-service 'home-bash-service-extension-test home-bash-service-type @@ -149,6 +150,7 @@ EOF grep -q "the content of ~/.config/test.conf" "${HOME}/.config/test.conf" grep '^export PS1="\$GUIX_ENVIRONMENT λ "$' "${HOME}/.bash_profile" ( . "${HOME}/.guix-home/setup-environment"; test "$TODAY" = "26 messidor" ) + ( . "${HOME}/.guix-home/setup-environment"; test "$LITERAL" = '${abc}' ) # This one should still be here. grep "stay around" "$HOME/.config/random-file"