| /* | 
 |  * Licensed under a two-clause BSD-style license. | 
 |  * See LICENSE for details. | 
 |  */ | 
 |  | 
 | #include "git-compat-util.h" | 
 | #include "line_buffer.h" | 
 | #include "strbuf.h" | 
 |  | 
 | #define COPY_BUFFER_LEN 4096 | 
 |  | 
 | int buffer_init(struct line_buffer *buf, const char *filename) | 
 | { | 
 | 	buf->infile = filename ? fopen(filename, "r") : stdin; | 
 | 	if (!buf->infile) | 
 | 		return -1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | int buffer_fdinit(struct line_buffer *buf, int fd) | 
 | { | 
 | 	buf->infile = fdopen(fd, "r"); | 
 | 	if (!buf->infile) | 
 | 		return -1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | int buffer_tmpfile_init(struct line_buffer *buf) | 
 | { | 
 | 	buf->infile = tmpfile(); | 
 | 	if (!buf->infile) | 
 | 		return -1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | int buffer_deinit(struct line_buffer *buf) | 
 | { | 
 | 	int err; | 
 | 	if (buf->infile == stdin) | 
 | 		return ferror(buf->infile); | 
 | 	err = ferror(buf->infile); | 
 | 	err |= fclose(buf->infile); | 
 | 	return err; | 
 | } | 
 |  | 
 | FILE *buffer_tmpfile_rewind(struct line_buffer *buf) | 
 | { | 
 | 	rewind(buf->infile); | 
 | 	return buf->infile; | 
 | } | 
 |  | 
 | long buffer_tmpfile_prepare_to_read(struct line_buffer *buf) | 
 | { | 
 | 	long pos = ftell(buf->infile); | 
 | 	if (pos < 0) | 
 | 		return error_errno("ftell error"); | 
 | 	if (fseek(buf->infile, 0, SEEK_SET)) | 
 | 		return error_errno("seek error"); | 
 | 	return pos; | 
 | } | 
 |  | 
 | int buffer_ferror(struct line_buffer *buf) | 
 | { | 
 | 	return ferror(buf->infile); | 
 | } | 
 |  | 
 | int buffer_read_char(struct line_buffer *buf) | 
 | { | 
 | 	return fgetc(buf->infile); | 
 | } | 
 |  | 
 | /* Read a line without trailing newline. */ | 
 | char *buffer_read_line(struct line_buffer *buf) | 
 | { | 
 | 	char *end; | 
 | 	if (!fgets(buf->line_buffer, sizeof(buf->line_buffer), buf->infile)) | 
 | 		/* Error or data exhausted. */ | 
 | 		return NULL; | 
 | 	end = buf->line_buffer + strlen(buf->line_buffer); | 
 | 	if (end[-1] == '\n') | 
 | 		end[-1] = '\0'; | 
 | 	else if (feof(buf->infile)) | 
 | 		; /* No newline at end of file.  That's fine. */ | 
 | 	else | 
 | 		/* | 
 | 		 * Line was too long. | 
 | 		 * There is probably a saner way to deal with this, | 
 | 		 * but for now let's return an error. | 
 | 		 */ | 
 | 		return NULL; | 
 | 	return buf->line_buffer; | 
 | } | 
 |  | 
 | size_t buffer_read_binary(struct line_buffer *buf, | 
 | 				struct strbuf *sb, size_t size) | 
 | { | 
 | 	return strbuf_fread(sb, size, buf->infile); | 
 | } | 
 |  | 
 | off_t buffer_copy_bytes(struct line_buffer *buf, off_t nbytes) | 
 | { | 
 | 	char byte_buffer[COPY_BUFFER_LEN]; | 
 | 	off_t done = 0; | 
 | 	while (done < nbytes && !feof(buf->infile) && !ferror(buf->infile)) { | 
 | 		off_t len = nbytes - done; | 
 | 		size_t in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN; | 
 | 		in = fread(byte_buffer, 1, in, buf->infile); | 
 | 		done += in; | 
 | 		fwrite(byte_buffer, 1, in, stdout); | 
 | 		if (ferror(stdout)) | 
 | 			return done + buffer_skip_bytes(buf, nbytes - done); | 
 | 	} | 
 | 	return done; | 
 | } | 
 |  | 
 | off_t buffer_skip_bytes(struct line_buffer *buf, off_t nbytes) | 
 | { | 
 | 	char byte_buffer[COPY_BUFFER_LEN]; | 
 | 	off_t done = 0; | 
 | 	while (done < nbytes && !feof(buf->infile) && !ferror(buf->infile)) { | 
 | 		off_t len = nbytes - done; | 
 | 		size_t in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN; | 
 | 		done += fread(byte_buffer, 1, in, buf->infile); | 
 | 	} | 
 | 	return done; | 
 | } |