Student
Student

Reputation: 719

Un-Escaping double quotes in a Common Lisp string

For generating a string command for using in uiop:run-command I need to have a string which contains other strings (as required by the command in question). However the command (Fontforg's legacy scripting language) requires that some strings are enclosed in double quotes, e.g.:

"fontforge -lang=ff -c 'Print("A Doublequote String")'"

How can I get such a string with the "A Doublequote String" being literally part of the command-string, i.e. no doublequote being escaped?

Update

To be more concrete, the command I want to send to uiop:run-command is fontforge -lang=ff -c 'Open($1);SelectAll();foreach Print(GlyphInfo("Name")); endloop' haydn-11.svg, where the "Name" argument to GlyphInfo should be written with double quotes. Using escape characters backslash to preserve the quotes

(uiop:run-program
 (format nil
     "fontforge -lang=ff -c 'Open($1);SelectAll();foreach Print(GlyphInfo(\"Name\")); endloop' haydn-11.svg")
 :output t)

the subprocess command exits with error code 1:

Subprocess with command "fontforge -lang=ff -c 'Open($1);SelectAll();foreach Print(GlyphInfo(\"Name\")); endloop' haydn-11.svg"
 exited with error code 1
   [Condition of type UIOP/RUN-PROGRAM:SUBPROCESS-ERROR]

I suppose the command is seeing the escape characters too, and that is why it fails to perform, since otherwise the command is syntactically correct.

Upvotes: 0

Views: 1073

Answers (2)

sds
sds

Reputation: 60004

Use Single Escape Character:

Backslash is a single escape character in standard syntax.

I.e., what you are looking for is

"fontforge -lang=ff -c 'Print(\"A Doublequote String\")'"

Note that by default *print-escape* is t, i.e., the above string will be printed with backslashes even though the string itself does not contain it:

(defparameter s (string #\"))
s
==> "\""
(length s)
==> 1
(char s 0)
==> #\"

Upvotes: 1

user5920214
user5920214

Reputation:

I don't have fontforge, but I can do the equivalent. First of all the way you are doing it is not one, but two levels of language-in-a-string: the shell language is in a string in CL, and whatever fontforge language is in a string in the shell language. So let's minimise that by avoiding the whole shell language altogether. We still have one level of language-in-a-string, but that's a whole lot better than two.

I also don't understand why you're using (format nil <fixed string>) to make ... a fixed string. I'm guessing that eventually you're intending to replace the filename using format, but this is not needed since we're no longer going to use the shell at all.

So instead, do this

(defun runit (file)
  (uiop:run-program
   (list "echo"                         ; because I don't have fontforge
        "fontforge" "-lang=ff" "-c"
        "Open($1); SelectAll(); foreach Print(GlyphInfo(\"Name\")); endloop"
        file)
   :output t
   :force-shell nil))

Note that the command here is echo: because I don't have fontforge I'll just get echo to print the command line. I've also added some spaces into the big chunk of whatever-language-fontforge-uses to make it clearer.

So now

> (runit "foo.svg")
fontforge -lang=ff -c Open($1); SelectAll(); foreach Print(GlyphInfo("Name")); endloop foo.svg
nil
nil
0

And this is fine, although it's not clear what the individual arguments are because echo doesn't do that. Well, I have a little utility called argv whose whole job is to print its argv clearly, so rewriting runit as

(defun runit (file)
  (uiop:run-program
   (list "argv"                         ; because I don't have fontforge
        "fontforge" "-lang=ff" "-c"
        "Open($1); SelectAll(); foreach Print(GlyphInfo(\"Name\")); endloop"
        file)
   :output t
   :force-shell nil))

We get

> (runit "foo.svg")
"fontforge"
"-lang=ff"
"-c"
"Open($1); SelectAll(); foreach Print(GlyphInfo("Name")); endloop"
"foo.svg"
nil
nil
0

Note that argv isn't smart enough to escape the quotes inside the long argument: it's just a very tiny Perl script I use for debugging things.

Upvotes: 0

Related Questions