v2v: Move the shell_unquote function to src/utils.c.

This function is useful outside virt-v2v so move it into the core
utilities library.

The function has been rewritten from OCaml into C, but it should be
functionally identical.
This commit is contained in:
Richard W.M. Jones
2016-05-16 07:19:49 -04:00
parent 12fb49c5dd
commit e13334a1a2
9 changed files with 107 additions and 64 deletions

View File

@@ -90,6 +90,7 @@ extern void guestfs_int_fadvise_random (int fd);
extern void guestfs_int_fadvise_noreuse (int fd);
//extern void guestfs_int_fadvise_dontneed (int fd);
//extern void guestfs_int_fadvise_willneed (int fd);
extern char *guestfs_int_shell_unquote (const char *str);
struct uefi_firmware {
const char *code; /* code file (NULL = end of list) */

View File

@@ -539,3 +539,63 @@ guestfs_int_fadvise_willneed (int fd)
#endif
}
#endif
/**
* Unquote a shell-quoted string.
*
* Augeas passes strings to us which may be quoted, eg. if they come
* from files in F</etc/sysconfig>. This function can do simple
* unquoting of these strings.
*
* Note this function does not do variable substitution, since that is
* impossible without knowing the file context and indeed the
* environment under which the shell script is run. Configuration
* files should not use complex quoting.
*
* C<str> is the input string from Augeas, a string that may be
* single- or double-quoted or may not be quoted. The returned string
* is unquoted, and must be freed by the caller. C<NULL> is returned
* on error and C<errno> is set accordingly.
*
* For information on double-quoting in bash, see
* L<https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html>
*/
char *
guestfs_int_shell_unquote (const char *str)
{
size_t len = strlen (str);
char *ret;
if (len >= 2) {
if (str[0] == '\'' && str[len-1] == '\'') {
/* single quoting */
ret = strndup (&str[1], len-2);
if (ret == NULL)
return NULL;
return ret;
}
else if (str[0] == '"' && str[len-1] == '"') {
/* double quoting */
size_t i, j;
ret = malloc (len + 1); /* strings always get smaller */
if (ret == NULL)
return NULL;
for (i = 1, j = 0; i < len-1 /* ignore final quote */; ++i, ++j) {
if (i < len-2 /* ignore final char before final quote */ &&
str[i] == '\\' &&
(str[i+1] == '$' || str[i+1] == '`' || str[i+1] == '"' ||
str[i+1] == '\\' || str[i+1] == '\n'))
++i;
ret[j] = str[i];
}
ret[j] = '\0';
return ret;
}
}
return strdup (str);
}

View File

@@ -516,7 +516,7 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps =
fun line ->
if Str.string_match rex line 0 then (
let path = Str.matched_group 1 line in
let path = Linux.shell_unquote path in
let path = Utils.shell_unquote path in
if String.length path >= 1 && path.[0] = '/' then (
let vboxuninstall = path ^ "/uninstall.sh" in
Some vboxuninstall

View File

@@ -178,47 +178,3 @@ let rec file_owner g inspect path =
and is_file_owned g inspect path =
try file_owner g inspect path; true
with Not_found -> false
let rec shell_unquote str =
let len = String.length str in
if len >= 2 then (
if String.is_prefix str "'" && String.is_suffix str "'" then
String.sub str 1 (len-2)
else if String.is_prefix str "\"" && String.is_suffix str "\"" then
shell_unquote_double str len
else
str
)
else str
(* https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
* but note we don't do any variable expansion etc so really we just
* handle backslash here.
*)
and shell_unquote_double str len =
let i = ref 1 and j = ref 0 in
while !i < len-1 (* ignore final quote *) do
if is_backslash_sequence str !i len then
incr i;
incr i;
incr j
done;
let outlen = !j in
let outstr = String.create outlen in
let i = ref 1 and j = ref 0 in
while !i < len-1 do
if is_backslash_sequence str !i len then
incr i;
outstr.[!j] <- str.[!i];
incr i;
incr j
done;
outstr
and is_backslash_sequence str i len =
i < len-2 (* ignore final character before the final quote *) &&
str.[i] = '\\' &&
(str.[i+1] = '$' || str.[i+1] = '`' || str.[i+1] = '"'
|| str.[i+1] = '\\' || str.[i+1] = '\n')

View File

@@ -39,11 +39,3 @@ val file_owner : Guestfs.guestfs -> Types.inspect -> string -> string
val is_file_owned : Guestfs.guestfs -> Types.inspect -> string -> bool
(** Returns true if the file is owned by an installed package. *)
val shell_unquote : string -> string
(** If the string looks like a shell quoted string, then attempt to
unquote it.
This is just intended to deal with quoting in configuration files
(like ones under /etc/sysconfig), and it doesn't deal with some
situations such as $variable interpolation. *)

View File

@@ -21,12 +21,20 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <caml/alloc.h>
#include <caml/fail.h>
#include <caml/memory.h>
#include <caml/mlvalues.h>
#ifdef HAVE_CAML_UNIXSUPPORT_H
#include <caml/unixsupport.h>
#else
#define Nothing ((value) 0)
extern void unix_error (int errcode, char * cmdname, value arg) Noreturn;
#endif
#include "guestfs.h"
#include "guestfs-internal-frontend.h"
@@ -104,3 +112,19 @@ v2v_utils_aavmf_firmware (value unitv)
{
return get_firmware (guestfs_int_aavmf_firmware);
}
value
v2v_utils_shell_unquote (value strv)
{
CAMLparam1 (strv);
CAMLlocal1 (retv);
char *ret;
ret = guestfs_int_shell_unquote (String_val (strv));
if (ret == NULL)
unix_error (errno, (char *) "guestfs_int_shell_unquote", Nothing);
retv = caml_copy_string (ret);
free (ret);
CAMLreturn (retv);
}

View File

@@ -142,6 +142,8 @@ let du filename =
| line::_ -> Int64.of_string line
| [] -> invalid_arg filename
external shell_unquote : string -> string = "v2v_utils_shell_unquote"
(* The following functions are only exported for unit tests. *)
module UNIT_TESTS = struct
let ovmf_i386_firmware = ovmf_i386_firmware

View File

@@ -66,6 +66,14 @@ val du : string -> int64
This can raise either [Failure] or [Invalid_argument] in case
of errors. *)
val shell_unquote : string -> string
(** If the string looks like a shell quoted string, then attempt to
unquote it.
This is just intended to deal with quoting in configuration files
(like ones under /etc/sysconfig), and it doesn't deal with some
situations such as $variable interpolation. *)
(**/**)
(* The following functions are only exported for unit tests. *)

View File

@@ -767,16 +767,16 @@ let test_virtio_iso_path_matches_guest_os ctx =
let test_shell_unquote ctx =
let printer = identity in
assert_equal ~printer "a" (Linux.shell_unquote "a");
assert_equal ~printer "b" (Linux.shell_unquote "'b'");
assert_equal ~printer "c" (Linux.shell_unquote "\"c\"");
assert_equal ~printer "dd" (Linux.shell_unquote "\"dd\"");
assert_equal ~printer "e\\e" (Linux.shell_unquote "\"e\\\\e\"");
assert_equal ~printer "f\\" (Linux.shell_unquote "\"f\\\\\"");
assert_equal ~printer "\\g" (Linux.shell_unquote "\"\\\\g\"");
assert_equal ~printer "h\\-h" (Linux.shell_unquote "\"h\\-h\"");
assert_equal ~printer "i`" (Linux.shell_unquote "\"i\\`\"");
assert_equal ~printer "j\"" (Linux.shell_unquote "\"j\\\"\"")
assert_equal ~printer "a" (Utils.shell_unquote "a");
assert_equal ~printer "b" (Utils.shell_unquote "'b'");
assert_equal ~printer "c" (Utils.shell_unquote "\"c\"");
assert_equal ~printer "dd" (Utils.shell_unquote "\"dd\"");
assert_equal ~printer "e\\e" (Utils.shell_unquote "\"e\\\\e\"");
assert_equal ~printer "f\\" (Utils.shell_unquote "\"f\\\\\"");
assert_equal ~printer "\\g" (Utils.shell_unquote "\"\\\\g\"");
assert_equal ~printer "h\\-h" (Utils.shell_unquote "\"h\\-h\"");
assert_equal ~printer "i`" (Utils.shell_unquote "\"i\\`\"");
assert_equal ~printer "j\"" (Utils.shell_unquote "\"j\\\"\"")
let test_find_uefi_firmware ctx =
let rec printer = function
@@ -815,7 +815,7 @@ let suite =
"Utils.drive_index" >:: test_drive_index;
"Windows_virtio.virtio_iso_path_matches_guest_os" >::
test_virtio_iso_path_matches_guest_os;
"Linux.shell_unquote" >:: test_shell_unquote;
"Utils.shell_unquote" >:: test_shell_unquote;
"Utils.find_uefi_firmware" >:: test_find_uefi_firmware;
]