static svn_error_t * read_handler_apr(void *baton, char *buffer, apr_size_t *len) { struct baton_apr *btn = baton; svn_error_t *err; svn_boolean_t eof; if (*len == 1) { err = svn_io_file_getc(buffer, btn->file, btn->pool); if (err) { *len = 0; if (APR_STATUS_IS_EOF(err->apr_err)) { svn_error_clear(err); err = SVN_NO_ERROR; } } } else err = svn_io_file_read_full2(btn->file, buffer, *len, len, &eof, btn->pool); return svn_error_trace(err); }
svn_error_t * svn_fs_x__rev_file_read(svn_fs_x__revision_file_t *file, void *buf, apr_size_t nbytes) { SVN_ERR(auto_open(file)); return svn_error_trace(svn_io_file_read_full2(file->file, buf, nbytes, NULL, NULL, file->pool)); }
/* If the footer data in FILE has not been read, yet, do so now. * Index locations will only be read upon request as we assume they get * cached and the FILE is usually used for REP data access only. * Hence, the separate step. */ static svn_error_t * auto_read_footer(svn_fs_x__revision_file_t *file) { if (file->l2p_info.start == -1) { apr_off_t filesize = 0; unsigned char footer_length; svn_stringbuf_t *footer; /* Determine file size. */ SVN_ERR(auto_open(file)); SVN_ERR(svn_io_file_seek(file->file, APR_END, &filesize, file->pool)); /* Read last byte (containing the length of the footer). */ SVN_ERR(svn_io_file_aligned_seek(file->file, file->block_size, NULL, filesize - 1, file->pool)); SVN_ERR(svn_io_file_read_full2(file->file, &footer_length, sizeof(footer_length), NULL, NULL, file->pool)); /* Read footer. */ footer = svn_stringbuf_create_ensure(footer_length, file->pool); SVN_ERR(svn_io_file_aligned_seek(file->file, file->block_size, NULL, filesize - 1 - footer_length, file->pool)); SVN_ERR(svn_io_file_read_full2(file->file, footer->data, footer_length, &footer->len, NULL, file->pool)); footer->data[footer->len] = '\0'; /* Extract index locations. */ SVN_ERR(svn_fs_x__parse_footer(&file->l2p_info.start, &file->l2p_info.checksum, &file->p2l_info.start, &file->p2l_info.checksum, footer, file->file_info.start_revision, filesize - footer_length - 1, file->pool)); file->l2p_info.end = file->p2l_info.start; file->p2l_info.end = filesize - footer_length - 1; } return SVN_NO_ERROR; }
/* There are enough quirks in the deprecated svn_hash_read that we should just preserve its implementation. */ svn_error_t * svn_hash_read(apr_hash_t *hash, apr_file_t *srcfile, apr_pool_t *pool) { svn_error_t *err; char buf[SVN_KEYLINE_MAXLEN]; apr_size_t num_read; char c; int first_time = 1; while (1) { /* Read a key length line. Might be END, though. */ apr_size_t len = sizeof(buf); err = svn_io_read_length_line(srcfile, buf, &len, pool); if (err && APR_STATUS_IS_EOF(err->apr_err) && first_time) { /* We got an EOF on our very first attempt to read, which means it's a zero-byte file. No problem, just go home. */ svn_error_clear(err); return SVN_NO_ERROR; } else if (err) /* Any other circumstance is a genuine error. */ return err; first_time = 0; if (((len == 3) && (buf[0] == 'E') && (buf[1] == 'N') && (buf[2] == 'D')) || ((len == 9) && (buf[0] == 'P') && (buf[1] == 'R') /* We formerly used just "END" to */ && (buf[2] == 'O') /* end a property hash, but later */ && (buf[3] == 'P') /* we added "PROPS-END", so that */ && (buf[4] == 'S') /* the fs dump format would be */ && (buf[5] == '-') /* more human-readable. That's */ && (buf[6] == 'E') /* why we accept either way here. */ && (buf[7] == 'N') && (buf[8] == 'D'))) { /* We've reached the end of the dumped hash table, so leave. */ return SVN_NO_ERROR; } else if ((buf[0] == 'K') && (buf[1] == ' ')) { size_t keylen; int parsed_len; void *keybuf; /* Get the length of the key */ SVN_ERR(svn_cstring_atoi(&parsed_len, buf + 2)); keylen = parsed_len; /* Now read that much into a buffer, + 1 byte for null terminator */ keybuf = apr_palloc(pool, keylen + 1); SVN_ERR(svn_io_file_read_full2(srcfile, keybuf, keylen, &num_read, NULL, pool)); ((char *) keybuf)[keylen] = '\0'; /* Suck up extra newline after key data */ SVN_ERR(svn_io_file_getc(&c, srcfile, pool)); if (c != '\n') return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL); /* Read a val length line */ len = sizeof(buf); SVN_ERR(svn_io_read_length_line(srcfile, buf, &len, pool)); if ((buf[0] == 'V') && (buf[1] == ' ')) { svn_string_t *value = apr_palloc(pool, sizeof(*value)); apr_size_t vallen; void *valbuf; /* Get the length of the value */ SVN_ERR(svn_cstring_atoi(&parsed_len, buf + 2)); vallen = parsed_len; /* Again, 1 extra byte for the null termination. */ valbuf = apr_palloc(pool, vallen + 1); SVN_ERR(svn_io_file_read_full2(srcfile, valbuf, vallen, &num_read, NULL, pool)); ((char *) valbuf)[vallen] = '\0'; /* Suck up extra newline after val data */ SVN_ERR(svn_io_file_getc(&c, srcfile, pool)); if (c != '\n') return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL); value->data = valbuf; value->len = vallen; /* The Grand Moment: add a new hash entry! */ apr_hash_set(hash, keybuf, keylen, value); } else { return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL); } } else { return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL); } } /* while (1) */ }
/* Return the next *HUNK from a PATCH in APR_FILE. * If no hunk can be found, set *HUNK to NULL. * Set IS_PROPERTY to TRUE if we have a property hunk. If the returned HUNK * is the first belonging to a certain property, then PROP_NAME and * PROP_OPERATION will be set too. If we have a text hunk, PROP_NAME will be * NULL. If IGNORE_WHITESPACE is TRUE, lines without leading spaces will be * treated as context lines. Allocate results in RESULT_POOL. * Use SCRATCH_POOL for all other allocations. */ static svn_error_t * parse_next_hunk(svn_diff_hunk_t **hunk, svn_boolean_t *is_property, const char **prop_name, svn_diff_operation_kind_t *prop_operation, svn_patch_t *patch, apr_file_t *apr_file, svn_boolean_t ignore_whitespace, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { static const char * const minus = "--- "; static const char * const text_atat = "@@"; static const char * const prop_atat = "##"; svn_stringbuf_t *line; svn_boolean_t eof, in_hunk, hunk_seen; apr_off_t pos, last_line; apr_off_t start, end; apr_off_t original_end; apr_off_t modified_end; svn_linenum_t original_lines; svn_linenum_t modified_lines; svn_linenum_t leading_context; svn_linenum_t trailing_context; svn_boolean_t changed_line_seen; enum { noise_line, original_line, modified_line, context_line } last_line_type; apr_pool_t *iterpool; *prop_operation = svn_diff_op_unchanged; /* We only set this if we have a property hunk header. */ *prop_name = NULL; *is_property = FALSE; if (apr_file_eof(apr_file) == APR_EOF) { /* No more hunks here. */ *hunk = NULL; return SVN_NO_ERROR; } in_hunk = FALSE; hunk_seen = FALSE; leading_context = 0; trailing_context = 0; changed_line_seen = FALSE; original_end = 0; modified_end = 0; *hunk = apr_pcalloc(result_pool, sizeof(**hunk)); /* Get current seek position -- APR has no ftell() :( */ pos = 0; SVN_ERR(svn_io_file_seek(apr_file, APR_CUR, &pos, scratch_pool)); /* Start out assuming noise. */ last_line_type = noise_line; iterpool = svn_pool_create(scratch_pool); do { svn_pool_clear(iterpool); /* Remember the current line's offset, and read the line. */ last_line = pos; SVN_ERR(readline(apr_file, &line, NULL, &eof, APR_SIZE_MAX, iterpool, iterpool)); /* Update line offset for next iteration. */ pos = 0; SVN_ERR(svn_io_file_seek(apr_file, APR_CUR, &pos, iterpool)); /* Lines starting with a backslash are comments, such as * "\ No newline at end of file". */ if (line->data[0] == '\\') { if (in_hunk && ((!*is_property && strcmp(line->data, "\\ No newline at end of file") == 0) || (*is_property && strcmp(line->data, "\\ No newline at end of property") == 0))) { char eolbuf[2]; apr_size_t len; apr_off_t off; apr_off_t hunk_text_end; /* Comment terminates the hunk text and says the hunk text * has no trailing EOL. Snip off trailing EOL which is part * of the patch file but not part of the hunk text. */ off = last_line - 2; SVN_ERR(svn_io_file_seek(apr_file, APR_SET, &off, iterpool)); len = sizeof(eolbuf); SVN_ERR(svn_io_file_read_full2(apr_file, eolbuf, len, &len, &eof, iterpool)); if (eolbuf[0] == '\r' && eolbuf[1] == '\n') hunk_text_end = last_line - 2; else if (eolbuf[1] == '\n' || eolbuf[1] == '\r') hunk_text_end = last_line - 1; else hunk_text_end = last_line; if (last_line_type == original_line && original_end == 0) original_end = hunk_text_end; else if (last_line_type == modified_line && modified_end == 0) modified_end = hunk_text_end; else if (last_line_type == context_line) { if (original_end == 0) original_end = hunk_text_end; if (modified_end == 0) modified_end = hunk_text_end; break; } SVN_ERR(svn_io_file_seek(apr_file, APR_SET, &pos, iterpool)); } continue; } if (in_hunk) { char c; static const char add = '+'; static const char del = '-'; if (! hunk_seen) { /* We're reading the first line of the hunk, so the start * of the line just read is the hunk text's byte offset. */ start = last_line; } c = line->data[0]; if (original_lines > 0 && modified_lines > 0 && ((c == ' ') /* Tolerate chopped leading spaces on empty lines. */ || (! eof && line->len == 0) /* Maybe tolerate chopped leading spaces on non-empty lines. */ || (ignore_whitespace && c != del && c != add))) { /* It's a "context" line in the hunk. */ hunk_seen = TRUE; original_lines--; modified_lines--; if (changed_line_seen) trailing_context++; else leading_context++; last_line_type = context_line; } else if (original_lines > 0 && c == del) { /* It's a "deleted" line in the hunk. */ hunk_seen = TRUE; changed_line_seen = TRUE; /* A hunk may have context in the middle. We only want trailing lines of context. */ if (trailing_context > 0) trailing_context = 0; original_lines--; last_line_type = original_line; } else if (modified_lines > 0 && c == add) { /* It's an "added" line in the hunk. */ hunk_seen = TRUE; changed_line_seen = TRUE; /* A hunk may have context in the middle. We only want trailing lines of context. */ if (trailing_context > 0) trailing_context = 0; modified_lines--; last_line_type = modified_line; } else { if (eof) { /* The hunk ends at EOF. */ end = pos; } else { /* The start of the current line marks the first byte * after the hunk text. */ end = last_line; } if (original_end == 0) original_end = end; if (modified_end == 0) modified_end = end; break; /* Hunk was empty or has been read. */ } } else { if (starts_with(line->data, text_atat)) { /* Looks like we have a hunk header, try to rip it apart. */ in_hunk = parse_hunk_header(line->data, *hunk, text_atat, iterpool); if (in_hunk) { original_lines = (*hunk)->original_length; modified_lines = (*hunk)->modified_length; *is_property = FALSE; } } else if (starts_with(line->data, prop_atat)) { /* Looks like we have a property hunk header, try to rip it * apart. */ in_hunk = parse_hunk_header(line->data, *hunk, prop_atat, iterpool); if (in_hunk) { original_lines = (*hunk)->original_length; modified_lines = (*hunk)->modified_length; *is_property = TRUE; } } else if (starts_with(line->data, "Added: ")) { SVN_ERR(parse_prop_name(prop_name, line->data, "Added: ", result_pool)); if (*prop_name) *prop_operation = svn_diff_op_added; } else if (starts_with(line->data, "Deleted: ")) { SVN_ERR(parse_prop_name(prop_name, line->data, "Deleted: ", result_pool)); if (*prop_name) *prop_operation = svn_diff_op_deleted; } else if (starts_with(line->data, "Modified: ")) { SVN_ERR(parse_prop_name(prop_name, line->data, "Modified: ", result_pool)); if (*prop_name) *prop_operation = svn_diff_op_modified; } else if (starts_with(line->data, minus) || starts_with(line->data, "diff --git ")) /* This could be a header of another patch. Bail out. */ break; } } /* Check for the line length since a file may not have a newline at the * end and we depend upon the last line to be an empty one. */ while (! eof || line->len > 0); svn_pool_destroy(iterpool); if (! eof) /* Rewind to the start of the line just read, so subsequent calls * to this function or svn_diff_parse_next_patch() don't end * up skipping the line -- it may contain a patch or hunk header. */ SVN_ERR(svn_io_file_seek(apr_file, APR_SET, &last_line, scratch_pool)); if (hunk_seen && start < end) { (*hunk)->patch = patch; (*hunk)->apr_file = apr_file; (*hunk)->leading_context = leading_context; (*hunk)->trailing_context = trailing_context; (*hunk)->diff_text_range.start = start; (*hunk)->diff_text_range.current = start; (*hunk)->diff_text_range.end = end; (*hunk)->original_text_range.start = start; (*hunk)->original_text_range.current = start; (*hunk)->original_text_range.end = original_end; (*hunk)->modified_text_range.start = start; (*hunk)->modified_text_range.current = start; (*hunk)->modified_text_range.end = modified_end; } else /* Something went wrong, just discard the result. */ *hunk = NULL; return SVN_NO_ERROR; }
/* A helper for reading a line of text from a range in the patch file. * * Allocate *STRINGBUF in RESULT_POOL, and read into it one line from FILE. * Reading stops either after a line-terminator was found or after MAX_LEN * bytes have been read. The line-terminator is not stored in *STRINGBUF. * * The line-terminator is detected automatically and stored in *EOL * if EOL is not NULL. If EOF is reached and FILE does not end * with a newline character, and EOL is not NULL, *EOL is set to NULL. * * SCRATCH_POOL is used for temporary allocations. */ static svn_error_t * readline(apr_file_t *file, svn_stringbuf_t **stringbuf, const char **eol, svn_boolean_t *eof, apr_size_t max_len, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_stringbuf_t *str; const char *eol_str; apr_size_t numbytes; char c; apr_size_t len; svn_boolean_t found_eof; str = svn_stringbuf_create_ensure(80, result_pool); /* Read bytes into STR up to and including, but not storing, * the next EOL sequence. */ eol_str = NULL; numbytes = 1; len = 0; found_eof = FALSE; while (!found_eof) { if (len < max_len) SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes, &found_eof, scratch_pool)); len++; if (numbytes != 1 || len > max_len) { found_eof = TRUE; break; } if (c == '\n') { eol_str = "\n"; } else if (c == '\r') { eol_str = "\r"; if (!found_eof && len < max_len) { apr_off_t pos; /* Check for "\r\n" by peeking at the next byte. */ pos = 0; SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool)); SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes, &found_eof, scratch_pool)); if (numbytes == 1 && c == '\n') { eol_str = "\r\n"; len++; } else { /* Pretend we never peeked. */ SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool)); found_eof = FALSE; numbytes = 1; } } } else svn_stringbuf_appendbyte(str, c); if (eol_str) break; } if (eol) *eol = eol_str; if (eof) *eof = found_eof; *stringbuf = str; return SVN_NO_ERROR; }