| /* | |
| * 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; | |
| } |