mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
guestfs_read_lines: Reimplement to avoid protocol limits.
This also makes a larger test suite for this command.
This commit is contained in:
1
TODO
1
TODO
@@ -567,7 +567,6 @@ the p.o.v of the API and ABI.
|
||||
|
||||
- guestfs_lstatlist
|
||||
- guestfs_lxattrlist
|
||||
- guestfs_read_lines
|
||||
- guestfs_readlinklist
|
||||
- guestfs_write
|
||||
- guestfs_write_append
|
||||
|
||||
@@ -85,54 +85,6 @@ do_touch (const char *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **
|
||||
do_read_lines (const char *path)
|
||||
{
|
||||
DECLARE_STRINGSBUF (r);
|
||||
FILE *fp;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t n;
|
||||
|
||||
CHROOT_IN;
|
||||
fp = fopen (path, "r");
|
||||
CHROOT_OUT;
|
||||
|
||||
if (!fp) {
|
||||
reply_with_perror ("fopen: %s", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((n = getline (&line, &len, fp)) != -1) {
|
||||
/* Remove either LF or CRLF. */
|
||||
if (n >= 2 && line[n-2] == '\r' && line[n-1] == '\n')
|
||||
line[n-2] = '\0';
|
||||
else if (n >= 1 && line[n-1] == '\n')
|
||||
line[n-1] = '\0';
|
||||
|
||||
if (add_string (&r, line) == -1) {
|
||||
free (line);
|
||||
fclose (fp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
free (line);
|
||||
|
||||
if (end_stringsbuf (&r) == -1) {
|
||||
fclose (fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fclose (fp) == EOF) {
|
||||
reply_with_perror ("fclose: %s", path);
|
||||
free_stringslen (r.argv, r.size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return r.argv;
|
||||
}
|
||||
|
||||
int
|
||||
do_rm (const char *path)
|
||||
{
|
||||
|
||||
@@ -2059,6 +2059,51 @@ buffer.
|
||||
Unlike C<guestfs_cat>, this function can correctly
|
||||
handle files that contain embedded ASCII NUL characters." };
|
||||
|
||||
{ defaults with
|
||||
name = "read_lines";
|
||||
style = RStringList "lines", [Pathname "path"], [];
|
||||
tests = [
|
||||
InitISOFS, Always, TestOutputList (
|
||||
[["read_lines"; "/known-4"]], ["abc"; "def"; "ghi"]);
|
||||
InitISOFS, Always, TestOutputList (
|
||||
[["read_lines"; "/empty"]], []);
|
||||
InitScratchFS, Always, TestOutputList (
|
||||
[["write"; "/read_lines1"; "\n"];
|
||||
["read_lines"; "/read_lines1"]], [""]);
|
||||
InitScratchFS, Always, TestOutputList (
|
||||
[["write"; "/read_lines2"; "\r\n"];
|
||||
["read_lines"; "/read_lines2"]], [""]);
|
||||
InitScratchFS, Always, TestOutputList (
|
||||
[["write"; "/read_lines3"; "\n\r\n"];
|
||||
["read_lines"; "/read_lines3"]], [""; ""]);
|
||||
InitScratchFS, Always, TestOutputList (
|
||||
[["write"; "/read_lines4"; "a"];
|
||||
["read_lines"; "/read_lines4"]], ["a"]);
|
||||
InitScratchFS, Always, TestOutputList (
|
||||
[["write"; "/read_lines5"; "a\nb"];
|
||||
["read_lines"; "/read_lines5"]], ["a"; "b"]);
|
||||
InitScratchFS, Always, TestOutputList (
|
||||
[["write"; "/read_lines6"; "a\nb\n"];
|
||||
["read_lines"; "/read_lines6"]], ["a"; "b"]);
|
||||
InitScratchFS, Always, TestOutputList (
|
||||
[["write"; "/read_lines7"; "a\nb\r\n"];
|
||||
["read_lines"; "/read_lines7"]], ["a"; "b"]);
|
||||
InitScratchFS, Always, TestOutputList (
|
||||
[["write"; "/read_lines8"; "a\nb\r\n\n"];
|
||||
["read_lines"; "/read_lines8"]], ["a"; "b"; ""]);
|
||||
];
|
||||
shortdesc = "read file as lines";
|
||||
longdesc = "\
|
||||
Return the contents of the file named C<path>.
|
||||
|
||||
The file contents are returned as a list of lines. Trailing
|
||||
C<LF> and C<CRLF> character sequences are I<not> returned.
|
||||
|
||||
Note that this function cannot correctly handle binary files
|
||||
(specifically, files containing C<\\0> character which is treated
|
||||
as end of string). For those you need to use the C<guestfs_read_file>
|
||||
function and split the buffer into lines yourself." };
|
||||
|
||||
]
|
||||
|
||||
(* daemon_functions are any functions which cause some action
|
||||
@@ -2332,28 +2377,6 @@ of the L<vgs(8)> command. The \"full\" version includes all fields." };
|
||||
List all the logical volumes detected. This is the equivalent
|
||||
of the L<lvs(8)> command. The \"full\" version includes all fields." };
|
||||
|
||||
{ defaults with
|
||||
name = "read_lines";
|
||||
style = RStringList "lines", [Pathname "path"], [];
|
||||
proc_nr = Some 15;
|
||||
tests = [
|
||||
InitISOFS, Always, TestOutputList (
|
||||
[["read_lines"; "/known-4"]], ["abc"; "def"; "ghi"]);
|
||||
InitISOFS, Always, TestOutputList (
|
||||
[["read_lines"; "/empty"]], [])
|
||||
];
|
||||
shortdesc = "read file as lines";
|
||||
longdesc = "\
|
||||
Return the contents of the file named C<path>.
|
||||
|
||||
The file contents are returned as a list of lines. Trailing
|
||||
C<LF> and C<CRLF> character sequences are I<not> returned.
|
||||
|
||||
Note that this function cannot correctly handle binary files
|
||||
(specifically, files containing C<\\0> character which is treated
|
||||
as end of line). For those you need to use the C<guestfs_read_file>
|
||||
function which has a more complex interface." };
|
||||
|
||||
{ defaults with
|
||||
name = "aug_init";
|
||||
style = RErr, [Pathname "root"; Int "flags"], [];
|
||||
|
||||
66
src/file.c
66
src/file.c
@@ -122,6 +122,72 @@ guestfs__read_file (guestfs_h *g, const char *path, size_t *size_r)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **
|
||||
guestfs__read_lines (guestfs_h *g, const char *file)
|
||||
{
|
||||
size_t i, count, size, len;
|
||||
char *buf = NULL;
|
||||
char **ret = NULL;
|
||||
|
||||
/* Read the whole file into memory. */
|
||||
buf = guestfs__read_file (g, file, &size);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
/* 'buf' contains the list of strings, separated by LF or CRLF
|
||||
* characters. Convert this to a list of lines. Note we have to
|
||||
* handle the cases where the buffer is zero length and where the
|
||||
* final string is not terminated.
|
||||
*/
|
||||
count = 0;
|
||||
for (i = 0; i < size; ++i)
|
||||
if (buf[i] == '\n')
|
||||
count++;
|
||||
if (size > 0 && buf[size-1] != '\n')
|
||||
count++;
|
||||
|
||||
ret = malloc ((count + 1) * sizeof (char *));
|
||||
if (!ret) {
|
||||
perrorf (g, "malloc");
|
||||
goto err;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
if (size > 0) {
|
||||
ret[count++] = buf;
|
||||
for (i = 0; i < size; ++i) {
|
||||
if (buf[i] == '\n') {
|
||||
buf[i] = '\0';
|
||||
if (i+1 < size)
|
||||
ret[count++] = &buf[i+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
ret[count] = NULL;
|
||||
|
||||
/* Duplicate the strings, and remove the trailing \r characters if any. */
|
||||
for (i = 0; ret[i] != NULL; ++i) {
|
||||
ret[i] = strdup (ret[i]);
|
||||
if (ret[i] == NULL) {
|
||||
perrorf (g, "strdup");
|
||||
while (i > 0)
|
||||
free (ret[--i]);
|
||||
goto err;
|
||||
}
|
||||
len = strlen (ret[i]);
|
||||
if (len > 0 && ret[i][len-1] == '\r')
|
||||
ret[i][len-1] = '\0';
|
||||
}
|
||||
free (buf);
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
free (buf);
|
||||
free (ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **
|
||||
guestfs__find (guestfs_h *g, const char *directory)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user