1251881Speter/* 2251881Speter * old-and-busted.c: routines for reading pre-1.7 working copies. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include "svn_time.h" 27251881Speter#include "svn_xml.h" 28251881Speter#include "svn_dirent_uri.h" 29251881Speter#include "svn_hash.h" 30251881Speter#include "svn_path.h" 31251881Speter#include "svn_ctype.h" 32251881Speter#include "svn_pools.h" 33251881Speter 34251881Speter#include "wc.h" 35251881Speter#include "adm_files.h" 36251881Speter#include "entries.h" 37251881Speter#include "lock.h" 38251881Speter 39251881Speter#include "private/svn_wc_private.h" 40251881Speter#include "svn_private_config.h" 41251881Speter 42251881Speter 43251881Speter/* Within the (old) entries file, boolean values have a specific string 44251881Speter value (thus, TRUE), or they are missing (for FALSE). Below are the 45251881Speter values for each of the booleans stored. */ 46251881Speter#define ENTRIES_BOOL_COPIED "copied" 47251881Speter#define ENTRIES_BOOL_DELETED "deleted" 48251881Speter#define ENTRIES_BOOL_ABSENT "absent" 49251881Speter#define ENTRIES_BOOL_INCOMPLETE "incomplete" 50251881Speter#define ENTRIES_BOOL_KEEP_LOCAL "keep-local" 51251881Speter 52251881Speter/* Tag names used in our old XML entries file. */ 53251881Speter#define ENTRIES_TAG_ENTRY "entry" 54251881Speter 55251881Speter/* Attribute names used in our old XML entries file. */ 56251881Speter#define ENTRIES_ATTR_NAME "name" 57251881Speter#define ENTRIES_ATTR_REPOS "repos" 58251881Speter#define ENTRIES_ATTR_UUID "uuid" 59251881Speter#define ENTRIES_ATTR_INCOMPLETE "incomplete" 60251881Speter#define ENTRIES_ATTR_LOCK_TOKEN "lock-token" 61251881Speter#define ENTRIES_ATTR_LOCK_OWNER "lock-owner" 62251881Speter#define ENTRIES_ATTR_LOCK_COMMENT "lock-comment" 63251881Speter#define ENTRIES_ATTR_LOCK_CREATION_DATE "lock-creation-date" 64251881Speter#define ENTRIES_ATTR_DELETED "deleted" 65251881Speter#define ENTRIES_ATTR_ABSENT "absent" 66251881Speter#define ENTRIES_ATTR_CMT_REV "committed-rev" 67251881Speter#define ENTRIES_ATTR_CMT_DATE "committed-date" 68251881Speter#define ENTRIES_ATTR_CMT_AUTHOR "last-author" 69251881Speter#define ENTRIES_ATTR_REVISION "revision" 70251881Speter#define ENTRIES_ATTR_URL "url" 71251881Speter#define ENTRIES_ATTR_KIND "kind" 72251881Speter#define ENTRIES_ATTR_SCHEDULE "schedule" 73251881Speter#define ENTRIES_ATTR_COPIED "copied" 74251881Speter#define ENTRIES_ATTR_COPYFROM_URL "copyfrom-url" 75251881Speter#define ENTRIES_ATTR_COPYFROM_REV "copyfrom-rev" 76251881Speter#define ENTRIES_ATTR_CHECKSUM "checksum" 77251881Speter#define ENTRIES_ATTR_WORKING_SIZE "working-size" 78251881Speter#define ENTRIES_ATTR_TEXT_TIME "text-time" 79251881Speter#define ENTRIES_ATTR_CONFLICT_OLD "conflict-old" /* saved old file */ 80251881Speter#define ENTRIES_ATTR_CONFLICT_NEW "conflict-new" /* saved new file */ 81251881Speter#define ENTRIES_ATTR_CONFLICT_WRK "conflict-wrk" /* saved wrk file */ 82251881Speter#define ENTRIES_ATTR_PREJFILE "prop-reject-file" 83251881Speter 84251881Speter/* Attribute values used in our old XML entries file. */ 85251881Speter#define ENTRIES_VALUE_FILE "file" 86251881Speter#define ENTRIES_VALUE_DIR "dir" 87251881Speter#define ENTRIES_VALUE_ADD "add" 88251881Speter#define ENTRIES_VALUE_DELETE "delete" 89251881Speter#define ENTRIES_VALUE_REPLACE "replace" 90251881Speter 91251881Speter 92251881Speter/* */ 93251881Speterstatic svn_wc_entry_t * 94251881Speteralloc_entry(apr_pool_t *pool) 95251881Speter{ 96251881Speter svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry)); 97251881Speter entry->revision = SVN_INVALID_REVNUM; 98251881Speter entry->copyfrom_rev = SVN_INVALID_REVNUM; 99251881Speter entry->cmt_rev = SVN_INVALID_REVNUM; 100251881Speter entry->kind = svn_node_none; 101251881Speter entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN; 102251881Speter entry->depth = svn_depth_infinity; 103251881Speter entry->file_external_path = NULL; 104251881Speter entry->file_external_peg_rev.kind = svn_opt_revision_unspecified; 105251881Speter entry->file_external_rev.kind = svn_opt_revision_unspecified; 106251881Speter return entry; 107251881Speter} 108251881Speter 109251881Speter 110251881Speter 111251881Speter/* Read an escaped byte on the form 'xHH' from [*BUF, END), placing 112251881Speter the byte in *RESULT. Advance *BUF to point after the escape 113251881Speter sequence. */ 114251881Speterstatic svn_error_t * 115251881Speterread_escaped(char *result, char **buf, const char *end) 116251881Speter{ 117251881Speter apr_uint64_t val; 118251881Speter char digits[3]; 119251881Speter 120251881Speter if (end - *buf < 3 || **buf != 'x' || ! svn_ctype_isxdigit((*buf)[1]) 121251881Speter || ! svn_ctype_isxdigit((*buf)[2])) 122251881Speter return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 123251881Speter _("Invalid escape sequence")); 124251881Speter (*buf)++; 125251881Speter digits[0] = *((*buf)++); 126251881Speter digits[1] = *((*buf)++); 127251881Speter digits[2] = 0; 128251881Speter if ((val = apr_strtoi64(digits, NULL, 16)) == 0) 129251881Speter return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 130251881Speter _("Invalid escaped character")); 131251881Speter *result = (char) val; 132251881Speter return SVN_NO_ERROR; 133251881Speter} 134251881Speter 135251881Speter/* Read a field, possibly with escaped bytes, from [*BUF, END), 136251881Speter stopping at the terminator. Place the read string in *RESULT, or set 137251881Speter *RESULT to NULL if it is the empty string. Allocate the returned string 138251881Speter in POOL. Advance *BUF to point after the terminator. */ 139251881Speterstatic svn_error_t * 140251881Speterread_str(const char **result, 141251881Speter char **buf, const char *end, 142251881Speter apr_pool_t *pool) 143251881Speter{ 144251881Speter svn_stringbuf_t *s = NULL; 145251881Speter const char *start; 146251881Speter if (*buf == end) 147251881Speter return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 148251881Speter _("Unexpected end of entry")); 149251881Speter if (**buf == '\n') 150251881Speter { 151251881Speter *result = NULL; 152251881Speter (*buf)++; 153251881Speter return SVN_NO_ERROR; 154251881Speter } 155251881Speter 156251881Speter start = *buf; 157251881Speter while (*buf != end && **buf != '\n') 158251881Speter { 159251881Speter if (**buf == '\\') 160251881Speter { 161251881Speter char c; 162251881Speter if (! s) 163251881Speter s = svn_stringbuf_ncreate(start, *buf - start, pool); 164251881Speter else 165251881Speter svn_stringbuf_appendbytes(s, start, *buf - start); 166251881Speter (*buf)++; 167251881Speter SVN_ERR(read_escaped(&c, buf, end)); 168251881Speter svn_stringbuf_appendbyte(s, c); 169251881Speter start = *buf; 170251881Speter } 171251881Speter else 172251881Speter (*buf)++; 173251881Speter } 174251881Speter 175251881Speter if (*buf == end) 176251881Speter return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 177251881Speter _("Unexpected end of entry")); 178251881Speter 179251881Speter if (s) 180251881Speter { 181251881Speter svn_stringbuf_appendbytes(s, start, *buf - start); 182251881Speter *result = s->data; 183251881Speter } 184251881Speter else 185251881Speter *result = apr_pstrndup(pool, start, *buf - start); 186251881Speter (*buf)++; 187251881Speter return SVN_NO_ERROR; 188251881Speter} 189251881Speter 190251881Speter/* This is wrapper around read_str() (which see for details); it 191251881Speter simply asks svn_path_is_canonical() of the string it reads, 192251881Speter returning an error if the test fails. 193251881Speter ### It seems this is only called for entrynames now 194251881Speter */ 195251881Speterstatic svn_error_t * 196251881Speterread_path(const char **result, 197251881Speter char **buf, const char *end, 198251881Speter apr_pool_t *pool) 199251881Speter{ 200251881Speter SVN_ERR(read_str(result, buf, end, pool)); 201251881Speter if (*result && **result && !svn_relpath_is_canonical(*result)) 202251881Speter return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 203251881Speter _("Entry contains non-canonical path '%s'"), 204251881Speter *result); 205251881Speter return SVN_NO_ERROR; 206251881Speter} 207251881Speter 208251881Speter/* This is read_path() for urls. This function does not do the is_canonical 209251881Speter test for entries from working copies older than version 10, as since that 210251881Speter version the canonicalization of urls has been changed. See issue #2475. 211251881Speter If the test is done and fails, read_url returs an error. */ 212251881Speterstatic svn_error_t * 213251881Speterread_url(const char **result, 214251881Speter char **buf, const char *end, 215251881Speter int wc_format, 216251881Speter apr_pool_t *pool) 217251881Speter{ 218251881Speter SVN_ERR(read_str(result, buf, end, pool)); 219251881Speter 220251881Speter /* Always canonicalize the url, as we have stricter canonicalization rules 221251881Speter in 1.7+ then before */ 222251881Speter if (*result && **result) 223251881Speter *result = svn_uri_canonicalize(*result, pool); 224251881Speter 225251881Speter return SVN_NO_ERROR; 226251881Speter} 227251881Speter 228251881Speter/* Read a field from [*BUF, END), terminated by a newline character. 229251881Speter The field may not contain escape sequences. The field is not 230251881Speter copied and the buffer is modified in place, by replacing the 231251881Speter terminator with a NUL byte. Make *BUF point after the original 232251881Speter terminator. */ 233251881Speterstatic svn_error_t * 234251881Speterread_val(const char **result, 235251881Speter char **buf, const char *end) 236251881Speter{ 237251881Speter const char *start = *buf; 238251881Speter 239251881Speter if (*buf == end) 240251881Speter return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 241251881Speter _("Unexpected end of entry")); 242251881Speter if (**buf == '\n') 243251881Speter { 244251881Speter (*buf)++; 245251881Speter *result = NULL; 246251881Speter return SVN_NO_ERROR; 247251881Speter } 248251881Speter 249251881Speter while (*buf != end && **buf != '\n') 250251881Speter (*buf)++; 251251881Speter if (*buf == end) 252251881Speter return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 253251881Speter _("Unexpected end of entry")); 254251881Speter **buf = '\0'; 255251881Speter *result = start; 256251881Speter (*buf)++; 257251881Speter return SVN_NO_ERROR; 258251881Speter} 259251881Speter 260251881Speter/* Read a boolean field from [*BUF, END), placing the result in 261251881Speter *RESULT. If there is no boolean value (just a terminator), it 262251881Speter defaults to false. Else, the value must match FIELD_NAME, in which 263251881Speter case *RESULT will be set to true. Advance *BUF to point after the 264251881Speter terminator. */ 265251881Speterstatic svn_error_t * 266251881Speterread_bool(svn_boolean_t *result, const char *field_name, 267251881Speter char **buf, const char *end) 268251881Speter{ 269251881Speter const char *val; 270251881Speter SVN_ERR(read_val(&val, buf, end)); 271251881Speter if (val) 272251881Speter { 273251881Speter if (strcmp(val, field_name) != 0) 274251881Speter return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 275251881Speter _("Invalid value for field '%s'"), 276251881Speter field_name); 277251881Speter *result = TRUE; 278251881Speter } 279251881Speter else 280251881Speter *result = FALSE; 281251881Speter return SVN_NO_ERROR; 282251881Speter} 283251881Speter 284251881Speter/* Read a revision number from [*BUF, END) stopping at the 285251881Speter terminator. Set *RESULT to the revision number, or 286251881Speter SVN_INVALID_REVNUM if there is none. Use POOL for temporary 287251881Speter allocations. Make *BUF point after the terminator. */ 288251881Speterstatic svn_error_t * 289251881Speterread_revnum(svn_revnum_t *result, 290251881Speter char **buf, 291251881Speter const char *end, 292251881Speter apr_pool_t *pool) 293251881Speter{ 294251881Speter const char *val; 295251881Speter 296251881Speter SVN_ERR(read_val(&val, buf, end)); 297251881Speter 298251881Speter if (val) 299251881Speter *result = SVN_STR_TO_REV(val); 300251881Speter else 301251881Speter *result = SVN_INVALID_REVNUM; 302251881Speter 303251881Speter return SVN_NO_ERROR; 304251881Speter} 305251881Speter 306251881Speter/* Read a timestamp from [*BUF, END) stopping at the terminator. 307251881Speter Set *RESULT to the resulting timestamp, or 0 if there is none. Use 308251881Speter POOL for temporary allocations. Make *BUF point after the 309251881Speter terminator. */ 310251881Speterstatic svn_error_t * 311251881Speterread_time(apr_time_t *result, 312251881Speter char **buf, const char *end, 313251881Speter apr_pool_t *pool) 314251881Speter{ 315251881Speter const char *val; 316251881Speter 317251881Speter SVN_ERR(read_val(&val, buf, end)); 318251881Speter if (val) 319251881Speter SVN_ERR(svn_time_from_cstring(result, val, pool)); 320251881Speter else 321251881Speter *result = 0; 322251881Speter 323251881Speter return SVN_NO_ERROR; 324251881Speter} 325251881Speter 326251881Speter/** 327251881Speter * Parse the string at *STR as an revision and save the result in 328251881Speter * *OPT_REV. After returning successfully, *STR points at next 329251881Speter * character in *STR where further parsing can be done. 330251881Speter */ 331251881Speterstatic svn_error_t * 332251881Speterstring_to_opt_revision(svn_opt_revision_t *opt_rev, 333251881Speter const char **str, 334251881Speter apr_pool_t *pool) 335251881Speter{ 336251881Speter const char *s = *str; 337251881Speter 338251881Speter SVN_ERR_ASSERT(opt_rev); 339251881Speter 340251881Speter while (*s && *s != ':') 341251881Speter ++s; 342251881Speter 343251881Speter /* Should not find a \0. */ 344251881Speter if (!*s) 345251881Speter return svn_error_createf 346251881Speter (SVN_ERR_INCORRECT_PARAMS, NULL, 347251881Speter _("Found an unexpected \\0 in the file external '%s'"), *str); 348251881Speter 349251881Speter if (0 == strncmp(*str, "HEAD:", 5)) 350251881Speter { 351251881Speter opt_rev->kind = svn_opt_revision_head; 352251881Speter } 353251881Speter else 354251881Speter { 355251881Speter svn_revnum_t rev; 356251881Speter const char *endptr; 357251881Speter 358251881Speter SVN_ERR(svn_revnum_parse(&rev, *str, &endptr)); 359251881Speter SVN_ERR_ASSERT(endptr == s); 360251881Speter opt_rev->kind = svn_opt_revision_number; 361251881Speter opt_rev->value.number = rev; 362251881Speter } 363251881Speter 364251881Speter *str = s + 1; 365251881Speter 366251881Speter return SVN_NO_ERROR; 367251881Speter} 368251881Speter 369251881Speter/** 370251881Speter * Given a revision, return a string for the revision, either "HEAD" 371251881Speter * or a string representation of the revision value. All other 372251881Speter * revision kinds return an error. 373251881Speter */ 374251881Speterstatic svn_error_t * 375251881Speteropt_revision_to_string(const char **str, 376251881Speter const char *path, 377251881Speter const svn_opt_revision_t *rev, 378251881Speter apr_pool_t *pool) 379251881Speter{ 380251881Speter switch (rev->kind) 381251881Speter { 382251881Speter case svn_opt_revision_head: 383251881Speter *str = apr_pstrmemdup(pool, "HEAD", 4); 384251881Speter break; 385251881Speter case svn_opt_revision_number: 386251881Speter *str = apr_ltoa(pool, rev->value.number); 387251881Speter break; 388251881Speter default: 389251881Speter return svn_error_createf 390251881Speter (SVN_ERR_INCORRECT_PARAMS, NULL, 391251881Speter _("Illegal file external revision kind %d for path '%s'"), 392251881Speter rev->kind, path); 393251881Speter break; 394251881Speter } 395251881Speter 396251881Speter return SVN_NO_ERROR; 397251881Speter} 398251881Speter 399251881Spetersvn_error_t * 400251881Spetersvn_wc__unserialize_file_external(const char **path_result, 401251881Speter svn_opt_revision_t *peg_rev_result, 402251881Speter svn_opt_revision_t *rev_result, 403251881Speter const char *str, 404251881Speter apr_pool_t *pool) 405251881Speter{ 406251881Speter if (str) 407251881Speter { 408251881Speter svn_opt_revision_t peg_rev; 409251881Speter svn_opt_revision_t op_rev; 410251881Speter const char *s = str; 411251881Speter 412251881Speter SVN_ERR(string_to_opt_revision(&peg_rev, &s, pool)); 413251881Speter SVN_ERR(string_to_opt_revision(&op_rev, &s, pool)); 414251881Speter 415251881Speter *path_result = apr_pstrdup(pool, s); 416251881Speter *peg_rev_result = peg_rev; 417251881Speter *rev_result = op_rev; 418251881Speter } 419251881Speter else 420251881Speter { 421251881Speter *path_result = NULL; 422251881Speter peg_rev_result->kind = svn_opt_revision_unspecified; 423251881Speter rev_result->kind = svn_opt_revision_unspecified; 424251881Speter } 425251881Speter 426251881Speter return SVN_NO_ERROR; 427251881Speter} 428251881Speter 429251881Spetersvn_error_t * 430251881Spetersvn_wc__serialize_file_external(const char **str, 431251881Speter const char *path, 432251881Speter const svn_opt_revision_t *peg_rev, 433251881Speter const svn_opt_revision_t *rev, 434251881Speter apr_pool_t *pool) 435251881Speter{ 436251881Speter const char *s; 437251881Speter 438251881Speter if (path) 439251881Speter { 440251881Speter const char *s1; 441251881Speter const char *s2; 442251881Speter 443251881Speter SVN_ERR(opt_revision_to_string(&s1, path, peg_rev, pool)); 444251881Speter SVN_ERR(opt_revision_to_string(&s2, path, rev, pool)); 445251881Speter 446289180Speter s = apr_pstrcat(pool, s1, ":", s2, ":", path, SVN_VA_NULL); 447251881Speter } 448251881Speter else 449251881Speter s = NULL; 450251881Speter 451251881Speter *str = s; 452251881Speter 453251881Speter return SVN_NO_ERROR; 454251881Speter} 455251881Speter 456251881Speter/* Allocate an entry from POOL and read it from [*BUF, END). The 457251881Speter buffer may be modified in place while parsing. Return the new 458251881Speter entry in *NEW_ENTRY. Advance *BUF to point at the end of the entry 459251881Speter record. 460251881Speter The entries file format should be provided in ENTRIES_FORMAT. */ 461251881Speterstatic svn_error_t * 462251881Speterread_entry(svn_wc_entry_t **new_entry, 463251881Speter char **buf, const char *end, 464251881Speter int entries_format, 465251881Speter apr_pool_t *pool) 466251881Speter{ 467251881Speter svn_wc_entry_t *entry = alloc_entry(pool); 468251881Speter const char *name; 469251881Speter 470251881Speter#define MAYBE_DONE if (**buf == '\f') goto done 471251881Speter 472251881Speter /* Find the name and set up the entry under that name. */ 473251881Speter SVN_ERR(read_path(&name, buf, end, pool)); 474251881Speter entry->name = name ? name : SVN_WC_ENTRY_THIS_DIR; 475251881Speter 476251881Speter /* Set up kind. */ 477251881Speter { 478251881Speter const char *kindstr; 479251881Speter SVN_ERR(read_val(&kindstr, buf, end)); 480251881Speter if (kindstr) 481251881Speter { 482251881Speter if (strcmp(kindstr, ENTRIES_VALUE_FILE) == 0) 483251881Speter entry->kind = svn_node_file; 484251881Speter else if (strcmp(kindstr, ENTRIES_VALUE_DIR) == 0) 485251881Speter entry->kind = svn_node_dir; 486251881Speter else 487251881Speter return svn_error_createf 488251881Speter (SVN_ERR_NODE_UNKNOWN_KIND, NULL, 489251881Speter _("Entry '%s' has invalid node kind"), 490251881Speter (name ? name : SVN_WC_ENTRY_THIS_DIR)); 491251881Speter } 492251881Speter else 493251881Speter entry->kind = svn_node_none; 494251881Speter } 495251881Speter MAYBE_DONE; 496251881Speter 497251881Speter /* Attempt to set revision (resolve_to_defaults may do it later, too) */ 498251881Speter SVN_ERR(read_revnum(&entry->revision, buf, end, pool)); 499251881Speter MAYBE_DONE; 500251881Speter 501251881Speter /* Attempt to set up url path (again, see resolve_to_defaults). */ 502251881Speter SVN_ERR(read_url(&entry->url, buf, end, entries_format, pool)); 503251881Speter MAYBE_DONE; 504251881Speter 505251881Speter /* Set up repository root. Make sure it is a prefix of url. */ 506251881Speter SVN_ERR(read_url(&entry->repos, buf, end, entries_format, pool)); 507251881Speter if (entry->repos && entry->url 508251881Speter && ! svn_uri__is_ancestor(entry->repos, entry->url)) 509251881Speter return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 510251881Speter _("Entry for '%s' has invalid repository " 511251881Speter "root"), 512251881Speter name ? name : SVN_WC_ENTRY_THIS_DIR); 513251881Speter MAYBE_DONE; 514251881Speter 515251881Speter /* Look for a schedule attribute on this entry. */ 516251881Speter { 517251881Speter const char *schedulestr; 518251881Speter SVN_ERR(read_val(&schedulestr, buf, end)); 519251881Speter entry->schedule = svn_wc_schedule_normal; 520251881Speter if (schedulestr) 521251881Speter { 522251881Speter if (strcmp(schedulestr, ENTRIES_VALUE_ADD) == 0) 523251881Speter entry->schedule = svn_wc_schedule_add; 524251881Speter else if (strcmp(schedulestr, ENTRIES_VALUE_DELETE) == 0) 525251881Speter entry->schedule = svn_wc_schedule_delete; 526251881Speter else if (strcmp(schedulestr, ENTRIES_VALUE_REPLACE) == 0) 527251881Speter entry->schedule = svn_wc_schedule_replace; 528251881Speter else 529251881Speter return svn_error_createf( 530251881Speter SVN_ERR_ENTRY_ATTRIBUTE_INVALID, NULL, 531251881Speter _("Entry '%s' has invalid 'schedule' value"), 532251881Speter name ? name : SVN_WC_ENTRY_THIS_DIR); 533251881Speter } 534251881Speter } 535251881Speter MAYBE_DONE; 536251881Speter 537251881Speter /* Attempt to set up text timestamp. */ 538251881Speter SVN_ERR(read_time(&entry->text_time, buf, end, pool)); 539251881Speter MAYBE_DONE; 540251881Speter 541251881Speter /* Checksum. */ 542251881Speter SVN_ERR(read_str(&entry->checksum, buf, end, pool)); 543251881Speter MAYBE_DONE; 544251881Speter 545251881Speter /* Setup last-committed values. */ 546251881Speter SVN_ERR(read_time(&entry->cmt_date, buf, end, pool)); 547251881Speter MAYBE_DONE; 548251881Speter 549251881Speter SVN_ERR(read_revnum(&entry->cmt_rev, buf, end, pool)); 550251881Speter MAYBE_DONE; 551251881Speter 552251881Speter SVN_ERR(read_str(&entry->cmt_author, buf, end, pool)); 553251881Speter MAYBE_DONE; 554251881Speter 555251881Speter /* has-props, has-prop-mods, cachable-props, present-props are all 556251881Speter deprecated. Read any values that may be in the 'entries' file, but 557251881Speter discard them, and just put default values into the entry. */ 558251881Speter { 559251881Speter const char *unused_value; 560251881Speter 561251881Speter /* has-props flag. */ 562251881Speter SVN_ERR(read_val(&unused_value, buf, end)); 563251881Speter entry->has_props = FALSE; 564251881Speter MAYBE_DONE; 565251881Speter 566251881Speter /* has-prop-mods flag. */ 567251881Speter SVN_ERR(read_val(&unused_value, buf, end)); 568251881Speter entry->has_prop_mods = FALSE; 569251881Speter MAYBE_DONE; 570251881Speter 571251881Speter /* Use the empty string for cachable_props, indicating that we no 572251881Speter longer attempt to cache any properties. An empty string for 573251881Speter present_props means that no cachable props are present. */ 574251881Speter 575251881Speter /* cachable-props string. */ 576251881Speter SVN_ERR(read_val(&unused_value, buf, end)); 577251881Speter entry->cachable_props = ""; 578251881Speter MAYBE_DONE; 579251881Speter 580251881Speter /* present-props string. */ 581251881Speter SVN_ERR(read_val(&unused_value, buf, end)); 582251881Speter entry->present_props = ""; 583251881Speter MAYBE_DONE; 584251881Speter } 585251881Speter 586251881Speter /* Is this entry in a state of mental torment (conflict)? */ 587251881Speter { 588251881Speter SVN_ERR(read_path(&entry->prejfile, buf, end, pool)); 589251881Speter MAYBE_DONE; 590251881Speter SVN_ERR(read_path(&entry->conflict_old, buf, end, pool)); 591251881Speter MAYBE_DONE; 592251881Speter SVN_ERR(read_path(&entry->conflict_new, buf, end, pool)); 593251881Speter MAYBE_DONE; 594251881Speter SVN_ERR(read_path(&entry->conflict_wrk, buf, end, pool)); 595251881Speter MAYBE_DONE; 596251881Speter } 597251881Speter 598251881Speter /* Is this entry copied? */ 599251881Speter SVN_ERR(read_bool(&entry->copied, ENTRIES_BOOL_COPIED, buf, end)); 600251881Speter MAYBE_DONE; 601251881Speter 602251881Speter SVN_ERR(read_url(&entry->copyfrom_url, buf, end, entries_format, pool)); 603251881Speter MAYBE_DONE; 604251881Speter SVN_ERR(read_revnum(&entry->copyfrom_rev, buf, end, pool)); 605251881Speter MAYBE_DONE; 606251881Speter 607251881Speter /* Is this entry deleted? */ 608251881Speter SVN_ERR(read_bool(&entry->deleted, ENTRIES_BOOL_DELETED, buf, end)); 609251881Speter MAYBE_DONE; 610251881Speter 611251881Speter /* Is this entry absent? */ 612251881Speter SVN_ERR(read_bool(&entry->absent, ENTRIES_BOOL_ABSENT, buf, end)); 613251881Speter MAYBE_DONE; 614251881Speter 615251881Speter /* Is this entry incomplete? */ 616251881Speter SVN_ERR(read_bool(&entry->incomplete, ENTRIES_BOOL_INCOMPLETE, buf, end)); 617251881Speter MAYBE_DONE; 618251881Speter 619251881Speter /* UUID. */ 620251881Speter SVN_ERR(read_str(&entry->uuid, buf, end, pool)); 621251881Speter MAYBE_DONE; 622251881Speter 623251881Speter /* Lock token. */ 624251881Speter SVN_ERR(read_str(&entry->lock_token, buf, end, pool)); 625251881Speter MAYBE_DONE; 626251881Speter 627251881Speter /* Lock owner. */ 628251881Speter SVN_ERR(read_str(&entry->lock_owner, buf, end, pool)); 629251881Speter MAYBE_DONE; 630251881Speter 631251881Speter /* Lock comment. */ 632251881Speter SVN_ERR(read_str(&entry->lock_comment, buf, end, pool)); 633251881Speter MAYBE_DONE; 634251881Speter 635251881Speter /* Lock creation date. */ 636251881Speter SVN_ERR(read_time(&entry->lock_creation_date, buf, end, pool)); 637251881Speter MAYBE_DONE; 638251881Speter 639251881Speter /* Changelist. */ 640251881Speter SVN_ERR(read_str(&entry->changelist, buf, end, pool)); 641251881Speter MAYBE_DONE; 642251881Speter 643251881Speter /* Keep entry in working copy after deletion? */ 644251881Speter SVN_ERR(read_bool(&entry->keep_local, ENTRIES_BOOL_KEEP_LOCAL, buf, end)); 645251881Speter MAYBE_DONE; 646251881Speter 647251881Speter /* Translated size */ 648251881Speter { 649251881Speter const char *val; 650251881Speter 651251881Speter /* read_val() returns NULL on an empty (e.g. default) entry line, 652251881Speter and entry has already been initialized accordingly already */ 653251881Speter SVN_ERR(read_val(&val, buf, end)); 654251881Speter if (val) 655251881Speter entry->working_size = (apr_off_t)apr_strtoi64(val, NULL, 0); 656251881Speter } 657251881Speter MAYBE_DONE; 658251881Speter 659251881Speter /* Depth. */ 660251881Speter { 661251881Speter const char *result; 662251881Speter SVN_ERR(read_val(&result, buf, end)); 663251881Speter if (result) 664251881Speter { 665251881Speter svn_boolean_t invalid; 666251881Speter svn_boolean_t is_this_dir; 667251881Speter 668251881Speter entry->depth = svn_depth_from_word(result); 669251881Speter 670251881Speter /* Verify the depth value: 671251881Speter THIS_DIR should not have an excluded value and SUB_DIR should only 672251881Speter have excluded value. Remember that infinity value is not stored and 673251881Speter should not show up here. Otherwise, something bad may have 674251881Speter happened. However, infinity value itself will always be okay. */ 675251881Speter is_this_dir = !name; 676251881Speter /* '!=': XOR */ 677251881Speter invalid = is_this_dir != (entry->depth != svn_depth_exclude); 678251881Speter if (entry->depth != svn_depth_infinity && invalid) 679251881Speter return svn_error_createf( 680251881Speter SVN_ERR_ENTRY_ATTRIBUTE_INVALID, NULL, 681251881Speter _("Entry '%s' has invalid 'depth' value"), 682251881Speter name ? name : SVN_WC_ENTRY_THIS_DIR); 683251881Speter } 684251881Speter else 685251881Speter entry->depth = svn_depth_infinity; 686251881Speter 687251881Speter } 688251881Speter MAYBE_DONE; 689251881Speter 690251881Speter /* Tree conflict data. */ 691251881Speter SVN_ERR(read_str(&entry->tree_conflict_data, buf, end, pool)); 692251881Speter MAYBE_DONE; 693251881Speter 694251881Speter /* File external URL and revision. */ 695251881Speter { 696251881Speter const char *str; 697251881Speter SVN_ERR(read_str(&str, buf, end, pool)); 698251881Speter SVN_ERR(svn_wc__unserialize_file_external(&entry->file_external_path, 699251881Speter &entry->file_external_peg_rev, 700251881Speter &entry->file_external_rev, 701251881Speter str, 702251881Speter pool)); 703251881Speter } 704251881Speter MAYBE_DONE; 705251881Speter 706251881Speter done: 707251881Speter *new_entry = entry; 708251881Speter return SVN_NO_ERROR; 709251881Speter} 710251881Speter 711251881Speter 712251881Speter/* If attribute ATTR_NAME appears in hash ATTS, set *ENTRY_FLAG to its 713251881Speter boolean value, else set *ENTRY_FLAG false. ENTRY_NAME is the name 714251881Speter of the WC-entry. */ 715251881Speterstatic svn_error_t * 716251881Speterdo_bool_attr(svn_boolean_t *entry_flag, 717251881Speter apr_hash_t *atts, const char *attr_name, 718251881Speter const char *entry_name) 719251881Speter{ 720251881Speter const char *str = svn_hash_gets(atts, attr_name); 721251881Speter 722251881Speter *entry_flag = FALSE; 723251881Speter if (str) 724251881Speter { 725251881Speter if (strcmp(str, "true") == 0) 726251881Speter *entry_flag = TRUE; 727251881Speter else if (strcmp(str, "false") == 0 || strcmp(str, "") == 0) 728251881Speter *entry_flag = FALSE; 729251881Speter else 730251881Speter return svn_error_createf 731251881Speter (SVN_ERR_ENTRY_ATTRIBUTE_INVALID, NULL, 732251881Speter _("Entry '%s' has invalid '%s' value"), 733251881Speter (entry_name ? entry_name : SVN_WC_ENTRY_THIS_DIR), attr_name); 734251881Speter } 735251881Speter return SVN_NO_ERROR; 736251881Speter} 737251881Speter 738251881Speter 739251881Speter/* */ 740251881Speterstatic const char * 741251881Speterextract_string(apr_hash_t *atts, 742251881Speter const char *att_name, 743251881Speter apr_pool_t *result_pool) 744251881Speter{ 745251881Speter const char *value = svn_hash_gets(atts, att_name); 746251881Speter 747251881Speter if (value == NULL) 748251881Speter return NULL; 749251881Speter 750251881Speter return apr_pstrdup(result_pool, value); 751251881Speter} 752251881Speter 753251881Speter 754251881Speter/* Like extract_string(), but normalizes empty strings to NULL. */ 755251881Speterstatic const char * 756251881Speterextract_string_normalize(apr_hash_t *atts, 757251881Speter const char *att_name, 758251881Speter apr_pool_t *result_pool) 759251881Speter{ 760251881Speter const char *value = svn_hash_gets(atts, att_name); 761251881Speter 762251881Speter if (value == NULL) 763251881Speter return NULL; 764251881Speter 765251881Speter if (*value == '\0') 766251881Speter return NULL; 767251881Speter 768251881Speter return apr_pstrdup(result_pool, value); 769251881Speter} 770251881Speter 771251881Speter 772251881Speter/* NOTE: this is used for upgrading old XML-based entries file. Be wary of 773251881Speter removing items. 774251881Speter 775251881Speter ### many attributes are no longer used within the old-style log files. 776251881Speter ### These attrs need to be recognized for old entries, however. For these 777251881Speter ### cases, the code will parse the attribute, but not set *MODIFY_FLAGS 778251881Speter ### for that particular field. MODIFY_FLAGS is *only* used by the 779251881Speter ### log-based entry modification system, and will go way once we 780251881Speter ### completely move away from loggy. 781251881Speter 782251881Speter Set *NEW_ENTRY to a new entry, taking attributes from ATTS, whose 783251881Speter keys and values are both char *. Allocate the entry and copy 784251881Speter attributes into POOL as needed. */ 785251881Speterstatic svn_error_t * 786251881Speteratts_to_entry(svn_wc_entry_t **new_entry, 787251881Speter apr_hash_t *atts, 788251881Speter apr_pool_t *pool) 789251881Speter{ 790251881Speter svn_wc_entry_t *entry = alloc_entry(pool); 791251881Speter const char *name; 792251881Speter 793251881Speter /* Find the name and set up the entry under that name. */ 794251881Speter name = svn_hash_gets(atts, ENTRIES_ATTR_NAME); 795251881Speter entry->name = name ? apr_pstrdup(pool, name) : SVN_WC_ENTRY_THIS_DIR; 796251881Speter 797251881Speter /* Attempt to set revision (resolve_to_defaults may do it later, too) 798251881Speter 799251881Speter ### not used by loggy; no need to set MODIFY_FLAGS */ 800251881Speter { 801251881Speter const char *revision_str 802251881Speter = svn_hash_gets(atts, ENTRIES_ATTR_REVISION); 803251881Speter 804251881Speter if (revision_str) 805251881Speter entry->revision = SVN_STR_TO_REV(revision_str); 806251881Speter else 807251881Speter entry->revision = SVN_INVALID_REVNUM; 808251881Speter } 809251881Speter 810251881Speter /* Attempt to set up url path (again, see resolve_to_defaults). 811251881Speter 812251881Speter ### not used by loggy; no need to set MODIFY_FLAGS */ 813251881Speter entry->url = extract_string(atts, ENTRIES_ATTR_URL, pool); 814257936Speter if (entry->url) 815257936Speter entry->url = svn_uri_canonicalize(entry->url, pool); 816251881Speter 817251881Speter /* Set up repository root. Make sure it is a prefix of url. 818251881Speter 819251881Speter ### not used by loggy; no need to set MODIFY_FLAGS */ 820251881Speter entry->repos = extract_string(atts, ENTRIES_ATTR_REPOS, pool); 821257936Speter if (entry->repos) 822257936Speter entry->repos = svn_uri_canonicalize(entry->repos, pool); 823251881Speter 824251881Speter if (entry->url && entry->repos 825251881Speter && !svn_uri__is_ancestor(entry->repos, entry->url)) 826251881Speter return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 827251881Speter _("Entry for '%s' has invalid repository " 828251881Speter "root"), 829251881Speter name ? name : SVN_WC_ENTRY_THIS_DIR); 830251881Speter 831251881Speter /* Set up kind. */ 832251881Speter /* ### not used by loggy; no need to set MODIFY_FLAGS */ 833251881Speter { 834251881Speter const char *kindstr 835251881Speter = svn_hash_gets(atts, ENTRIES_ATTR_KIND); 836251881Speter 837251881Speter entry->kind = svn_node_none; 838251881Speter if (kindstr) 839251881Speter { 840251881Speter if (strcmp(kindstr, ENTRIES_VALUE_FILE) == 0) 841251881Speter entry->kind = svn_node_file; 842251881Speter else if (strcmp(kindstr, ENTRIES_VALUE_DIR) == 0) 843251881Speter entry->kind = svn_node_dir; 844251881Speter else 845251881Speter return svn_error_createf 846251881Speter (SVN_ERR_NODE_UNKNOWN_KIND, NULL, 847251881Speter _("Entry '%s' has invalid node kind"), 848251881Speter (name ? name : SVN_WC_ENTRY_THIS_DIR)); 849251881Speter } 850251881Speter } 851251881Speter 852251881Speter /* Look for a schedule attribute on this entry. */ 853251881Speter /* ### not used by loggy; no need to set MODIFY_FLAGS */ 854251881Speter { 855251881Speter const char *schedulestr 856251881Speter = svn_hash_gets(atts, ENTRIES_ATTR_SCHEDULE); 857251881Speter 858251881Speter entry->schedule = svn_wc_schedule_normal; 859251881Speter if (schedulestr) 860251881Speter { 861251881Speter if (strcmp(schedulestr, ENTRIES_VALUE_ADD) == 0) 862251881Speter entry->schedule = svn_wc_schedule_add; 863251881Speter else if (strcmp(schedulestr, ENTRIES_VALUE_DELETE) == 0) 864251881Speter entry->schedule = svn_wc_schedule_delete; 865251881Speter else if (strcmp(schedulestr, ENTRIES_VALUE_REPLACE) == 0) 866251881Speter entry->schedule = svn_wc_schedule_replace; 867251881Speter else if (strcmp(schedulestr, "") == 0) 868251881Speter entry->schedule = svn_wc_schedule_normal; 869251881Speter else 870251881Speter return svn_error_createf( 871251881Speter SVN_ERR_ENTRY_ATTRIBUTE_INVALID, NULL, 872251881Speter _("Entry '%s' has invalid 'schedule' value"), 873251881Speter (name ? name : SVN_WC_ENTRY_THIS_DIR)); 874251881Speter } 875251881Speter } 876251881Speter 877251881Speter /* Is this entry in a state of mental torment (conflict)? */ 878251881Speter entry->prejfile = extract_string_normalize(atts, 879251881Speter ENTRIES_ATTR_PREJFILE, 880251881Speter pool); 881251881Speter entry->conflict_old = extract_string_normalize(atts, 882251881Speter ENTRIES_ATTR_CONFLICT_OLD, 883251881Speter pool); 884251881Speter entry->conflict_new = extract_string_normalize(atts, 885251881Speter ENTRIES_ATTR_CONFLICT_NEW, 886251881Speter pool); 887251881Speter entry->conflict_wrk = extract_string_normalize(atts, 888251881Speter ENTRIES_ATTR_CONFLICT_WRK, 889251881Speter pool); 890251881Speter 891251881Speter /* Is this entry copied? */ 892251881Speter /* ### not used by loggy; no need to set MODIFY_FLAGS */ 893251881Speter SVN_ERR(do_bool_attr(&entry->copied, atts, ENTRIES_ATTR_COPIED, name)); 894251881Speter 895251881Speter /* ### not used by loggy; no need to set MODIFY_FLAGS */ 896251881Speter entry->copyfrom_url = extract_string(atts, ENTRIES_ATTR_COPYFROM_URL, pool); 897251881Speter 898251881Speter /* ### not used by loggy; no need to set MODIFY_FLAGS */ 899251881Speter { 900251881Speter const char *revstr; 901251881Speter 902251881Speter revstr = svn_hash_gets(atts, ENTRIES_ATTR_COPYFROM_REV); 903251881Speter if (revstr) 904251881Speter entry->copyfrom_rev = SVN_STR_TO_REV(revstr); 905251881Speter } 906251881Speter 907251881Speter /* Is this entry deleted? 908251881Speter 909251881Speter ### not used by loggy; no need to set MODIFY_FLAGS */ 910251881Speter SVN_ERR(do_bool_attr(&entry->deleted, atts, ENTRIES_ATTR_DELETED, name)); 911251881Speter 912251881Speter /* Is this entry absent? 913251881Speter 914251881Speter ### not used by loggy; no need to set MODIFY_FLAGS */ 915251881Speter SVN_ERR(do_bool_attr(&entry->absent, atts, ENTRIES_ATTR_ABSENT, name)); 916251881Speter 917251881Speter /* Is this entry incomplete? 918251881Speter 919251881Speter ### not used by loggy; no need to set MODIFY_FLAGS */ 920251881Speter SVN_ERR(do_bool_attr(&entry->incomplete, atts, ENTRIES_ATTR_INCOMPLETE, 921251881Speter name)); 922251881Speter 923251881Speter /* Attempt to set up timestamps. */ 924251881Speter /* ### not used by loggy; no need to set MODIFY_FLAGS */ 925251881Speter { 926251881Speter const char *text_timestr; 927251881Speter 928251881Speter text_timestr = svn_hash_gets(atts, ENTRIES_ATTR_TEXT_TIME); 929251881Speter if (text_timestr) 930251881Speter SVN_ERR(svn_time_from_cstring(&entry->text_time, text_timestr, pool)); 931251881Speter 932251881Speter /* Note: we do not persist prop_time, so there is no need to attempt 933251881Speter to parse a new prop_time value from the log. Certainly, on any 934251881Speter recent working copy, there will not be a log record to alter 935251881Speter the prop_time value. */ 936251881Speter } 937251881Speter 938251881Speter /* Checksum. */ 939251881Speter /* ### not used by loggy; no need to set MODIFY_FLAGS */ 940251881Speter entry->checksum = extract_string(atts, ENTRIES_ATTR_CHECKSUM, pool); 941251881Speter 942251881Speter /* UUID. 943251881Speter 944251881Speter ### not used by loggy; no need to set MODIFY_FLAGS */ 945251881Speter entry->uuid = extract_string(atts, ENTRIES_ATTR_UUID, pool); 946251881Speter 947251881Speter /* Setup last-committed values. */ 948251881Speter { 949251881Speter const char *cmt_datestr, *cmt_revstr; 950251881Speter 951251881Speter cmt_datestr = svn_hash_gets(atts, ENTRIES_ATTR_CMT_DATE); 952251881Speter if (cmt_datestr) 953251881Speter { 954251881Speter SVN_ERR(svn_time_from_cstring(&entry->cmt_date, cmt_datestr, pool)); 955251881Speter } 956251881Speter else 957251881Speter entry->cmt_date = 0; 958251881Speter 959251881Speter cmt_revstr = svn_hash_gets(atts, ENTRIES_ATTR_CMT_REV); 960251881Speter if (cmt_revstr) 961251881Speter { 962251881Speter entry->cmt_rev = SVN_STR_TO_REV(cmt_revstr); 963251881Speter } 964251881Speter else 965251881Speter entry->cmt_rev = SVN_INVALID_REVNUM; 966251881Speter 967251881Speter entry->cmt_author = extract_string(atts, ENTRIES_ATTR_CMT_AUTHOR, pool); 968251881Speter } 969251881Speter 970251881Speter /* ### not used by loggy; no need to set MODIFY_FLAGS */ 971251881Speter entry->lock_token = extract_string(atts, ENTRIES_ATTR_LOCK_TOKEN, pool); 972251881Speter entry->lock_owner = extract_string(atts, ENTRIES_ATTR_LOCK_OWNER, pool); 973251881Speter entry->lock_comment = extract_string(atts, ENTRIES_ATTR_LOCK_COMMENT, pool); 974251881Speter { 975251881Speter const char *cdate_str = 976251881Speter svn_hash_gets(atts, ENTRIES_ATTR_LOCK_CREATION_DATE); 977251881Speter if (cdate_str) 978251881Speter { 979251881Speter SVN_ERR(svn_time_from_cstring(&entry->lock_creation_date, 980251881Speter cdate_str, pool)); 981251881Speter } 982251881Speter } 983251881Speter /* ----- end of lock handling. */ 984251881Speter 985251881Speter /* Note: if there are attributes for the (deprecated) has_props, 986251881Speter has_prop_mods, cachable_props, or present_props, then we're just 987251881Speter going to ignore them. */ 988251881Speter 989251881Speter /* Translated size */ 990251881Speter /* ### not used by loggy; no need to set MODIFY_FLAGS */ 991251881Speter { 992251881Speter const char *val = svn_hash_gets(atts, ENTRIES_ATTR_WORKING_SIZE); 993251881Speter if (val) 994251881Speter { 995251881Speter /* Cast to off_t; it's safe: we put in an off_t to start with... */ 996251881Speter entry->working_size = (apr_off_t)apr_strtoi64(val, NULL, 0); 997251881Speter } 998251881Speter } 999251881Speter 1000251881Speter *new_entry = entry; 1001251881Speter return SVN_NO_ERROR; 1002251881Speter} 1003251881Speter 1004251881Speter/* Used when reading an entries file in XML format. */ 1005251881Speterstruct entries_accumulator 1006251881Speter{ 1007251881Speter /* Keys are entry names, vals are (struct svn_wc_entry_t *)'s. */ 1008251881Speter apr_hash_t *entries; 1009251881Speter 1010251881Speter /* The parser that's parsing it, for signal_expat_bailout(). */ 1011251881Speter svn_xml_parser_t *parser; 1012251881Speter 1013251881Speter /* Don't leave home without one. */ 1014251881Speter apr_pool_t *pool; 1015251881Speter 1016251881Speter /* Cleared before handling each entry. */ 1017251881Speter apr_pool_t *scratch_pool; 1018251881Speter}; 1019251881Speter 1020251881Speter 1021251881Speter 1022251881Speter/* Called whenever we find an <open> tag of some kind. */ 1023251881Speterstatic void 1024251881Speterhandle_start_tag(void *userData, const char *tagname, const char **atts) 1025251881Speter{ 1026251881Speter struct entries_accumulator *accum = userData; 1027251881Speter apr_hash_t *attributes; 1028251881Speter svn_wc_entry_t *entry; 1029251881Speter svn_error_t *err; 1030251881Speter 1031251881Speter /* We only care about the `entry' tag; all other tags, such as `xml' 1032251881Speter and `wc-entries', are ignored. */ 1033251881Speter if (strcmp(tagname, ENTRIES_TAG_ENTRY)) 1034251881Speter return; 1035251881Speter 1036251881Speter svn_pool_clear(accum->scratch_pool); 1037251881Speter /* Make an entry from the attributes. */ 1038251881Speter attributes = svn_xml_make_att_hash(atts, accum->scratch_pool); 1039251881Speter err = atts_to_entry(&entry, attributes, accum->pool); 1040251881Speter if (err) 1041251881Speter { 1042251881Speter svn_xml_signal_bailout(err, accum->parser); 1043251881Speter return; 1044251881Speter } 1045251881Speter 1046251881Speter /* Find the name and set up the entry under that name. This 1047251881Speter should *NOT* be NULL, since svn_wc__atts_to_entry() should 1048251881Speter have made it into SVN_WC_ENTRY_THIS_DIR. */ 1049251881Speter svn_hash_sets(accum->entries, entry->name, entry); 1050251881Speter} 1051251881Speter 1052251881Speter/* Parse BUF of size SIZE as an entries file in XML format, storing the parsed 1053251881Speter entries in ENTRIES. Use SCRATCH_POOL for temporary allocations and 1054251881Speter RESULT_POOL for the returned entries. */ 1055251881Speterstatic svn_error_t * 1056251881Speterparse_entries_xml(const char *dir_abspath, 1057251881Speter apr_hash_t *entries, 1058251881Speter const char *buf, 1059251881Speter apr_size_t size, 1060251881Speter apr_pool_t *result_pool, 1061251881Speter apr_pool_t *scratch_pool) 1062251881Speter{ 1063251881Speter svn_xml_parser_t *svn_parser; 1064251881Speter struct entries_accumulator accum; 1065251881Speter 1066251881Speter /* Set up userData for the XML parser. */ 1067251881Speter accum.entries = entries; 1068251881Speter accum.pool = result_pool; 1069251881Speter accum.scratch_pool = svn_pool_create(scratch_pool); 1070251881Speter 1071251881Speter /* Create the XML parser */ 1072251881Speter svn_parser = svn_xml_make_parser(&accum, 1073251881Speter handle_start_tag, 1074251881Speter NULL, 1075251881Speter NULL, 1076251881Speter scratch_pool); 1077251881Speter 1078251881Speter /* Store parser in its own userdata, so callbacks can call 1079251881Speter svn_xml_signal_bailout() */ 1080251881Speter accum.parser = svn_parser; 1081251881Speter 1082251881Speter /* Parse. */ 1083251881Speter SVN_ERR_W(svn_xml_parse(svn_parser, buf, size, TRUE), 1084251881Speter apr_psprintf(scratch_pool, 1085251881Speter _("XML parser failed in '%s'"), 1086251881Speter svn_dirent_local_style(dir_abspath, scratch_pool))); 1087251881Speter 1088251881Speter svn_pool_destroy(accum.scratch_pool); 1089251881Speter 1090251881Speter /* Clean up the XML parser */ 1091251881Speter svn_xml_free_parser(svn_parser); 1092251881Speter 1093251881Speter return SVN_NO_ERROR; 1094251881Speter} 1095251881Speter 1096251881Speter 1097251881Speter 1098251881Speter/* Use entry SRC to fill in blank portions of entry DST. SRC itself 1099251881Speter may not have any blanks, of course. 1100251881Speter Typically, SRC is a parent directory's own entry, and DST is some 1101251881Speter child in that directory. */ 1102251881Speterstatic void 1103251881Spetertake_from_entry(const svn_wc_entry_t *src, 1104251881Speter svn_wc_entry_t *dst, 1105251881Speter apr_pool_t *pool) 1106251881Speter{ 1107251881Speter /* Inherits parent's revision if doesn't have a revision of one's 1108251881Speter own, unless this is a subdirectory. */ 1109251881Speter if ((dst->revision == SVN_INVALID_REVNUM) && (dst->kind != svn_node_dir)) 1110251881Speter dst->revision = src->revision; 1111251881Speter 1112251881Speter /* Inherits parent's url if doesn't have a url of one's own. */ 1113251881Speter if (! dst->url) 1114251881Speter dst->url = svn_path_url_add_component2(src->url, dst->name, pool); 1115251881Speter 1116251881Speter if (! dst->repos) 1117251881Speter dst->repos = src->repos; 1118251881Speter 1119251881Speter if ((! dst->uuid) 1120251881Speter && (! ((dst->schedule == svn_wc_schedule_add) 1121251881Speter || (dst->schedule == svn_wc_schedule_replace)))) 1122251881Speter { 1123251881Speter dst->uuid = src->uuid; 1124251881Speter } 1125251881Speter} 1126251881Speter 1127251881Speter/* Resolve any missing information in ENTRIES by deducing from the 1128251881Speter directory's own entry (which must already be present in ENTRIES). */ 1129251881Speterstatic svn_error_t * 1130251881Speterresolve_to_defaults(apr_hash_t *entries, 1131251881Speter apr_pool_t *pool) 1132251881Speter{ 1133251881Speter apr_hash_index_t *hi; 1134251881Speter svn_wc_entry_t *default_entry 1135251881Speter = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 1136251881Speter 1137251881Speter /* First check the dir's own entry for consistency. */ 1138251881Speter if (! default_entry) 1139251881Speter return svn_error_create(SVN_ERR_ENTRY_NOT_FOUND, 1140251881Speter NULL, 1141251881Speter _("Missing default entry")); 1142251881Speter 1143251881Speter if (default_entry->revision == SVN_INVALID_REVNUM) 1144251881Speter return svn_error_create(SVN_ERR_ENTRY_MISSING_REVISION, 1145251881Speter NULL, 1146251881Speter _("Default entry has no revision number")); 1147251881Speter 1148251881Speter if (! default_entry->url) 1149251881Speter return svn_error_create(SVN_ERR_ENTRY_MISSING_URL, 1150251881Speter NULL, 1151251881Speter _("Default entry is missing URL")); 1152251881Speter 1153251881Speter 1154251881Speter /* Then use it to fill in missing information in other entries. */ 1155251881Speter for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 1156251881Speter { 1157289180Speter svn_wc_entry_t *this_entry = apr_hash_this_val(hi); 1158251881Speter 1159251881Speter if (this_entry == default_entry) 1160251881Speter /* THIS_DIR already has all the information it can possibly 1161251881Speter have. */ 1162251881Speter continue; 1163251881Speter 1164251881Speter if (this_entry->kind == svn_node_dir) 1165251881Speter /* Entries that are directories have everything but their 1166251881Speter name, kind, and state stored in the THIS_DIR entry of the 1167251881Speter directory itself. However, we are disallowing the perusing 1168251881Speter of any entries outside of the current entries file. If a 1169251881Speter caller wants more info about a directory, it should look in 1170251881Speter the entries file in the directory. */ 1171251881Speter continue; 1172251881Speter 1173251881Speter if (this_entry->kind == svn_node_file) 1174251881Speter /* For file nodes that do not explicitly have their ancestry 1175251881Speter stated, this can be derived from the default entry of the 1176251881Speter directory in which those files reside. */ 1177251881Speter take_from_entry(default_entry, this_entry, pool); 1178251881Speter } 1179251881Speter 1180251881Speter return SVN_NO_ERROR; 1181251881Speter} 1182251881Speter 1183251881Speter 1184251881Speter 1185251881Speter/* Read and parse an old-style 'entries' file in the administrative area 1186251881Speter of PATH, filling in ENTRIES with the contents. The results will be 1187251881Speter allocated in RESULT_POOL, and temporary allocations will be made in 1188251881Speter SCRATCH_POOL. */ 1189251881Spetersvn_error_t * 1190251881Spetersvn_wc__read_entries_old(apr_hash_t **entries, 1191251881Speter const char *dir_abspath, 1192251881Speter apr_pool_t *result_pool, 1193251881Speter apr_pool_t *scratch_pool) 1194251881Speter{ 1195251881Speter char *curp; 1196251881Speter const char *endp; 1197251881Speter svn_wc_entry_t *entry; 1198251881Speter svn_stream_t *stream; 1199251881Speter svn_string_t *buf; 1200251881Speter 1201251881Speter *entries = apr_hash_make(result_pool); 1202251881Speter 1203251881Speter /* Open the entries file. */ 1204251881Speter SVN_ERR(svn_wc__open_adm_stream(&stream, dir_abspath, SVN_WC__ADM_ENTRIES, 1205251881Speter scratch_pool, scratch_pool)); 1206251881Speter SVN_ERR(svn_string_from_stream(&buf, stream, scratch_pool, scratch_pool)); 1207251881Speter 1208251881Speter /* We own the returned data; it is modifiable, so cast away... */ 1209251881Speter curp = (char *)buf->data; 1210251881Speter endp = buf->data + buf->len; 1211251881Speter 1212251881Speter /* If the first byte of the file is not a digit, then it is probably in XML 1213251881Speter format. */ 1214251881Speter if (curp != endp && !svn_ctype_isdigit(*curp)) 1215251881Speter SVN_ERR(parse_entries_xml(dir_abspath, *entries, buf->data, buf->len, 1216251881Speter result_pool, scratch_pool)); 1217251881Speter else 1218251881Speter { 1219251881Speter int entryno, entries_format; 1220251881Speter const char *val; 1221251881Speter 1222251881Speter /* Read the format line from the entries file. In case we're in the 1223251881Speter middle of upgrading a working copy, this line will contain the 1224251881Speter original format pre-upgrade. */ 1225251881Speter SVN_ERR(read_val(&val, &curp, endp)); 1226251881Speter if (val) 1227251881Speter entries_format = (int)apr_strtoi64(val, NULL, 0); 1228251881Speter else 1229251881Speter return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 1230251881Speter _("Invalid version line in entries file " 1231251881Speter "of '%s'"), 1232251881Speter svn_dirent_local_style(dir_abspath, 1233251881Speter scratch_pool)); 1234251881Speter entryno = 1; 1235251881Speter 1236251881Speter while (curp != endp) 1237251881Speter { 1238251881Speter svn_error_t *err = read_entry(&entry, &curp, endp, 1239251881Speter entries_format, result_pool); 1240251881Speter if (! err) 1241251881Speter { 1242251881Speter /* We allow extra fields at the end of the line, for 1243251881Speter extensibility. */ 1244251881Speter curp = memchr(curp, '\f', endp - curp); 1245251881Speter if (! curp) 1246251881Speter err = svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 1247251881Speter _("Missing entry terminator")); 1248251881Speter if (! err && (curp == endp || *(++curp) != '\n')) 1249251881Speter err = svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 1250251881Speter _("Invalid entry terminator")); 1251251881Speter } 1252251881Speter if (err) 1253251881Speter return svn_error_createf(err->apr_err, err, 1254251881Speter _("Error at entry %d in entries file for " 1255251881Speter "'%s':"), 1256251881Speter entryno, 1257251881Speter svn_dirent_local_style(dir_abspath, 1258251881Speter scratch_pool)); 1259251881Speter 1260251881Speter ++curp; 1261251881Speter ++entryno; 1262251881Speter 1263251881Speter svn_hash_sets(*entries, entry->name, entry); 1264251881Speter } 1265251881Speter } 1266251881Speter 1267251881Speter /* Fill in any implied fields. */ 1268251881Speter return svn_error_trace(resolve_to_defaults(*entries, result_pool)); 1269251881Speter} 1270251881Speter 1271251881Speter 1272251881Speter/* For non-directory PATHs full entry information is obtained by reading 1273251881Speter * the entries for the parent directory of PATH and then extracting PATH's 1274251881Speter * entry. If PATH is a directory then only abrieviated information is 1275251881Speter * available in the parent directory, more complete information is 1276251881Speter * available by reading the entries for PATH itself. 1277251881Speter * 1278251881Speter * Note: There is one bit of information about directories that is only 1279251881Speter * available in the parent directory, that is the "deleted" state. If PATH 1280251881Speter * is a versioned directory then the "deleted" state information will not 1281251881Speter * be returned in ENTRY. This means some bits of the code (e.g. revert) 1282251881Speter * need to obtain it by directly extracting the directory entry from the 1283251881Speter * parent directory's entries. I wonder if this function should handle 1284251881Speter * that? 1285251881Speter */ 1286251881Spetersvn_error_t * 1287251881Spetersvn_wc_entry(const svn_wc_entry_t **entry, 1288251881Speter const char *path, 1289251881Speter svn_wc_adm_access_t *adm_access, 1290251881Speter svn_boolean_t show_hidden, 1291251881Speter apr_pool_t *pool) 1292251881Speter{ 1293251881Speter svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); 1294251881Speter const char *local_abspath; 1295251881Speter svn_wc_adm_access_t *dir_access; 1296251881Speter const char *entry_name; 1297251881Speter apr_hash_t *entries; 1298251881Speter 1299251881Speter SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 1300251881Speter 1301251881Speter /* Does the provided path refer to a directory with an associated 1302251881Speter access baton? */ 1303251881Speter dir_access = svn_wc__adm_retrieve_internal2(db, local_abspath, pool); 1304251881Speter if (dir_access == NULL) 1305251881Speter { 1306251881Speter /* Damn. Okay. Assume the path is to a child, and let's look for 1307251881Speter a baton associated with its parent. */ 1308251881Speter 1309251881Speter const char *dir_abspath; 1310251881Speter 1311251881Speter svn_dirent_split(&dir_abspath, &entry_name, local_abspath, pool); 1312251881Speter 1313251881Speter dir_access = svn_wc__adm_retrieve_internal2(db, dir_abspath, pool); 1314251881Speter } 1315251881Speter else 1316251881Speter { 1317251881Speter /* Woo! Got one. Look for "this dir" in the entries hash. */ 1318251881Speter entry_name = ""; 1319251881Speter } 1320251881Speter 1321251881Speter if (dir_access == NULL) 1322251881Speter { 1323251881Speter /* Early exit. */ 1324251881Speter *entry = NULL; 1325251881Speter return SVN_NO_ERROR; 1326251881Speter } 1327251881Speter 1328251881Speter /* Load an entries hash, and cache it into DIR_ACCESS. Go ahead and 1329251881Speter fetch all entries here (optimization) since we know how to filter 1330251881Speter out a "hidden" node. */ 1331251881Speter SVN_ERR(svn_wc__entries_read_internal(&entries, dir_access, TRUE, pool)); 1332251881Speter *entry = svn_hash_gets(entries, entry_name); 1333251881Speter 1334251881Speter if (!show_hidden && *entry != NULL) 1335251881Speter { 1336251881Speter svn_boolean_t hidden; 1337251881Speter 1338251881Speter SVN_ERR(svn_wc__entry_is_hidden(&hidden, *entry)); 1339251881Speter if (hidden) 1340251881Speter *entry = NULL; 1341251881Speter } 1342251881Speter 1343251881Speter return SVN_NO_ERROR; 1344251881Speter} 1345