mirror of
https://github.com/git/git
synced 2024-08-25 02:35:46 +00:00
![Jonathan Nieder](/assets/img/avatar_default.png)
POSIX makes the behavior of read(2) from a pipe fairly clear: a read from an empty pipe will block until there is data available and any other read will not block, prefering to return a partial result. Likewise, fread(3) and fgets(3) are clearly specified to act as though implemented by calling fgetc(3) in a simple loop. But the buffering behavior of fgetc is less clear. Luckily, no sane platform is going to implement fgetc by calling the equivalent of read(2) more than once. fgetc has to be able to return without filling its buffer to preserve errno when errors are encountered anyway. So let's assume the simpler behavior (trust) but add some tests to catch insane platforms that violate that when they come (verify). First check that fread can handle a 0-length read from an empty fifo. Because open(O_RDONLY) blocks until the writing end is open, open the writing end of the fifo in advance in a subshell. Next try short inputs from a pipe that is not filled all the way. Lastly (two tests) try very large inputs from a pipe that will not fit in the relevant buffers. The first of these tests reads a little more than 8192 bytes, which is BUFSIZ (the size of stdio's buffers) on this Linux machine. The second reads a little over 64 KiB (the pipe capacity on Linux) and is not run unless requested by setting the GIT_REMOTE_SVN_TEST_BIG_FILES environment variable. Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
84 lines
1.9 KiB
C
84 lines
1.9 KiB
C
/*
|
|
* test-line-buffer.c: code to exercise the svn importer's input helper
|
|
*/
|
|
|
|
#include "git-compat-util.h"
|
|
#include "vcs-svn/line_buffer.h"
|
|
|
|
static uint32_t strtouint32(const char *s)
|
|
{
|
|
char *end;
|
|
uintmax_t n = strtoumax(s, &end, 10);
|
|
if (*s == '\0' || *end != '\0')
|
|
die("invalid count: %s", s);
|
|
return (uint32_t) n;
|
|
}
|
|
|
|
static void handle_command(const char *command, const char *arg, struct line_buffer *buf)
|
|
{
|
|
switch (*command) {
|
|
case 'c':
|
|
if (!prefixcmp(command, "copy ")) {
|
|
buffer_copy_bytes(buf, strtouint32(arg));
|
|
return;
|
|
}
|
|
case 'r':
|
|
if (!prefixcmp(command, "read ")) {
|
|
const char *s = buffer_read_string(buf, strtouint32(arg));
|
|
fputs(s, stdout);
|
|
return;
|
|
}
|
|
case 's':
|
|
if (!prefixcmp(command, "skip ")) {
|
|
buffer_skip_bytes(buf, strtouint32(arg));
|
|
return;
|
|
}
|
|
default:
|
|
die("unrecognized command: %s", command);
|
|
}
|
|
}
|
|
|
|
static void handle_line(const char *line, struct line_buffer *stdin_buf)
|
|
{
|
|
const char *arg = strchr(line, ' ');
|
|
if (!arg)
|
|
die("no argument in line: %s", line);
|
|
handle_command(line, arg + 1, stdin_buf);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct line_buffer stdin_buf = LINE_BUFFER_INIT;
|
|
struct line_buffer file_buf = LINE_BUFFER_INIT;
|
|
struct line_buffer *input = &stdin_buf;
|
|
const char *filename;
|
|
char *s;
|
|
|
|
if (argc == 1)
|
|
filename = NULL;
|
|
else if (argc == 2)
|
|
filename = argv[1];
|
|
else
|
|
usage("test-line-buffer [file] < script");
|
|
|
|
if (buffer_init(&stdin_buf, NULL))
|
|
die_errno("open error");
|
|
if (filename) {
|
|
if (buffer_init(&file_buf, filename))
|
|
die_errno("error opening %s", filename);
|
|
input = &file_buf;
|
|
}
|
|
|
|
while ((s = buffer_read_line(&stdin_buf)))
|
|
handle_line(s, input);
|
|
|
|
if (filename && buffer_deinit(&file_buf))
|
|
die("error reading from %s", filename);
|
|
if (buffer_deinit(&stdin_buf))
|
|
die("input error");
|
|
if (ferror(stdout))
|
|
die("output error");
|
|
buffer_reset(&stdin_buf);
|
|
return 0;
|
|
}
|