📄 subst.c
字号:
static svn_error_t *translate_write(svn_stream_t *stream, const void *buf, apr_size_t len){ apr_size_t wrote = len; svn_error_t *write_err = svn_stream_write(stream, buf, &wrote); if ((write_err) || (len != wrote)) return write_err; return SVN_NO_ERROR;}/* Perform the substition of VALUE into keyword string BUF (with len *LEN), given a pre-parsed KEYWORD (and KEYWORD_LEN), and updating *LEN to the new size of the substituted result. Return TRUE if all goes well, FALSE otherwise. If VALUE is NULL, keyword will be contracted, else it will be expanded. */static svn_boolean_ttranslate_keyword_subst(char *buf, apr_size_t *len, const char *keyword, apr_size_t keyword_len, const svn_string_t *value){ char *buf_ptr; /* Make sure we gotz good stuffs. */ assert(*len <= SVN_KEYWORD_MAX_LEN); assert((buf[0] == '$') && (buf[*len - 1] == '$')); /* Need at least a keyword and two $'s. */ if (*len < keyword_len + 2) return FALSE; /* The keyword needs to match what we're looking for. */ if (strncmp(buf + 1, keyword, keyword_len)) return FALSE; buf_ptr = buf + 1 + keyword_len; /* Check for fixed-length expansion. * The format of fixed length keyword and its data is * Unexpanded keyword: "$keyword:: $" * Expanded keyword: "$keyword:: value $" * Expanded kw with filling: "$keyword:: value $" * Truncated keyword: "$keyword:: longval#$" */ if ((buf_ptr[0] == ':') /* first char after keyword is ':' */ && (buf_ptr[1] == ':') /* second char after keyword is ':' */ && (buf_ptr[2] == ' ') /* third char after keyword is ' ' */ && ((buf[*len - 2] == ' ') /* has ' ' for next to last character */ || (buf[*len - 2] == '#')) /* .. or has '#' for next to last character */ && ((6 + keyword_len) < *len)) /* holds "$kw:: x $" at least */ { /* This is fixed length keyword, so *len remains unchanged */ apr_size_t max_value_len = *len - (6 + keyword_len); if (! value) { /* no value, so unexpand */ buf_ptr += 2; while (*buf_ptr != '$') *(buf_ptr++) = ' '; } else { if (value->len <= max_value_len) { /* replacement not as long as template, pad with spaces */ strncpy(buf_ptr + 3, value->data, value->len); buf_ptr += 3 + value->len; while (*buf_ptr != '$') *(buf_ptr++) = ' '; } else { /* replacement needs truncating */ strncpy(buf_ptr + 3, value->data, max_value_len); buf[*len - 2] = '#'; buf[*len - 1] = '$'; } } return TRUE; } /* Check for unexpanded keyword. */ else if ((buf_ptr[0] == '$') /* "$keyword$" */ || ((buf_ptr[0] == ':') && (buf_ptr[1] == '$'))) /* "$keyword:$" */ { /* unexpanded... */ if (value) { /* ...so expand. */ buf_ptr[0] = ':'; buf_ptr[1] = ' '; if (value->len) { apr_size_t vallen = value->len; /* "$keyword: value $" */ if (vallen > (SVN_KEYWORD_MAX_LEN - 5 - keyword_len)) vallen = SVN_KEYWORD_MAX_LEN - 5 - keyword_len; strncpy(buf_ptr + 2, value->data, vallen); buf_ptr[2 + vallen] = ' '; buf_ptr[2 + vallen + 1] = '$'; *len = 5 + keyword_len + vallen; } else { /* "$keyword: $" */ buf_ptr[2] = '$'; *len = 4 + keyword_len; } } else { /* ...but do nothing. */ } return TRUE; } /* Check for expanded keyword. */ else if ((*len >= 4 + keyword_len ) /* holds at least "$keyword: $" */ && (buf_ptr[0] == ':') /* first char after keyword is ':' */ && (buf_ptr[1] == ' ') /* second char after keyword is ' ' */ && (buf[*len - 2] == ' ')) /* has ' ' for next to last character */ { /* expanded... */ if (! value) { /* ...so unexpand. */ buf_ptr[0] = '$'; *len = 2 + keyword_len; } else { /* ...so re-expand. */ buf_ptr[0] = ':'; buf_ptr[1] = ' '; if (value->len) { apr_size_t vallen = value->len; /* "$keyword: value $" */ if (vallen > (SVN_KEYWORD_MAX_LEN - 5)) vallen = SVN_KEYWORD_MAX_LEN - 5; strncpy(buf_ptr + 2, value->data, vallen); buf_ptr[2 + vallen] = ' '; buf_ptr[2 + vallen + 1] = '$'; *len = 5 + keyword_len + vallen; } else { /* "$keyword: $" */ buf_ptr[2] = '$'; *len = 4 + keyword_len; } } return TRUE; } return FALSE;} /* Parse BUF (whose length is LEN, and which starts and ends with '$'), trying to match one of the keyword names in KEYWORDS. If such a keyword is found, update *KEYWORD_NAME with the keyword name and return TRUE. */static svn_boolean_tmatch_keyword(char *buf, apr_size_t len, char *keyword_name, apr_hash_t *keywords){ apr_size_t i; /* Early return for ignored keywords */ if (! keywords) return FALSE; /* Extract the name of the keyword */ for (i = 0; i < len - 2 && buf[i + 1] != ':'; i++) keyword_name[i] = buf[i + 1]; keyword_name[i] = '\0'; return apr_hash_get(keywords, keyword_name, APR_HASH_KEY_STRING) != NULL;}/* Try to translate keyword *KEYWORD_NAME in BUF (whose length is LEN): optionally perform the substitution in place, update *LEN with the new length of the translated keyword string, and return TRUE. If this buffer doesn't contain a known keyword pattern, leave BUF and *LEN untouched and return FALSE. See the docstring for svn_subst_copy_and_translate for how the EXPAND and KEYWORDS parameters work. NOTE: It is assumed that BUF has been allocated to be at least SVN_KEYWORD_MAX_LEN bytes longs, and that the data in BUF is less than or equal SVN_KEYWORD_MAX_LEN in length. Also, any expansions which would result in a keyword string which is greater than SVN_KEYWORD_MAX_LEN will have their values truncated in such a way that the resultant keyword string is still valid (begins with "$Keyword:", ends in " $" and is SVN_KEYWORD_MAX_LEN bytes long). */static svn_boolean_ttranslate_keyword(char *buf, apr_size_t *len, const char *keyword_name, svn_boolean_t expand, apr_hash_t *keywords){ const svn_string_t *value; /* Make sure we gotz good stuffs. */ assert(*len <= SVN_KEYWORD_MAX_LEN); assert((buf[0] == '$') && (buf[*len - 1] == '$')); /* Early return for ignored keywords */ if (! keywords) return FALSE; value = apr_hash_get(keywords, keyword_name, APR_HASH_KEY_STRING); if (value) { return translate_keyword_subst(buf, len, keyword_name, strlen(keyword_name), expand ? value : NULL); } return FALSE;}/* Translate NEWLINE_BUF (length of NEWLINE_LEN) to the newline format specified in EOL_STR (length of EOL_STR_LEN), and write the translated thing to FILE (whose path is DST_PATH). SRC_FORMAT (length *SRC_FORMAT_LEN) is a cache of the first newline found while processing SRC_PATH. If the current newline is not the same style as that of SRC_FORMAT, look to the REPAIR parameter. If REPAIR is TRUE, ignore the inconsistency, else return an SVN_ERR_IO_INCONSISTENT_EOL error. If we are examining the first newline in the file, copy it to {SRC_FORMAT, *SRC_FORMAT_LEN} to use for later consistency checks. */static svn_error_t *translate_newline(const char *eol_str, apr_size_t eol_str_len, char *src_format, apr_size_t *src_format_len, char *newline_buf, apr_size_t newline_len, svn_stream_t *dst, svn_boolean_t repair){ /* If this is the first newline we've seen, cache it future comparisons, else compare it with our cache to check for consistency. */ if (*src_format_len) { /* Comparing with cache. If we are inconsistent and we are NOT repairing the file, generate an error! */ if ((! repair) && ((*src_format_len != newline_len) || (strncmp(src_format, newline_buf, newline_len)))) return svn_error_create(SVN_ERR_IO_INCONSISTENT_EOL, NULL, NULL); } else { /* This is our first line ending, so cache it before handling it. */ strncpy(src_format, newline_buf, newline_len); *src_format_len = newline_len; } /* Translate the newline */ return translate_write(dst, eol_str, eol_str_len);}/*** Public interfaces. ***/svn_boolean_tsvn_subst_keywords_differ(const svn_subst_keywords_t *a, const svn_subst_keywords_t *b, svn_boolean_t compare_values){ if (((a == NULL) && (b == NULL)) /* no A or B */ /* no A, and B has no contents */ || ((a == NULL) && (b->revision == NULL) && (b->date == NULL) && (b->author == NULL) && (b->url == NULL)) /* no B, and A has no contents */ || ((b == NULL) && (a->revision == NULL) && (a->date == NULL) && (a->author == NULL) && (a->url == NULL)) /* neither A nor B has any contents */ || ((a != NULL) && (b != NULL) && (b->revision == NULL) && (b->date == NULL) && (b->author == NULL) && (b->url == NULL) && (a->revision == NULL) && (a->date == NULL) && (a->author == NULL) && (a->url == NULL))) { return FALSE; } else if ((a == NULL) || (b == NULL)) return TRUE; /* Else both A and B have some keywords. */ if ((! a->revision) != (! b->revision)) return TRUE; else if ((compare_values && (a->revision != NULL)) && (strcmp(a->revision->data, b->revision->data) != 0)) return TRUE; if ((! a->date) != (! b->date)) return TRUE; else if ((compare_values && (a->date != NULL)) && (strcmp(a->date->data, b->date->data) != 0)) return TRUE; if ((! a->author) != (! b->author)) return TRUE; else if ((compare_values && (a->author != NULL)) && (strcmp(a->author->data, b->author->data) != 0)) return TRUE; if ((! a->url) != (! b->url)) return TRUE; else if ((compare_values && (a->url != NULL)) && (strcmp(a->url->data, b->url->data) != 0)) return TRUE; /* Else we never found a difference, so they must be the same. */ return FALSE;}svn_boolean_tsvn_subst_keywords_differ2(apr_hash_t *a, apr_hash_t *b, svn_boolean_t compare_values, apr_pool_t *pool){ apr_hash_index_t *hi; unsigned int a_count, b_count; /* An empty hash is logically equal to a NULL, * as far as this API is concerned. */ a_count = (a == NULL) ? 0 : apr_hash_count(a); b_count = (b == NULL) ? 0 : apr_hash_count(b); if (a_count != b_count) return TRUE; if (a_count == 0) return FALSE; /* The hashes are both non-NULL, and have the same number of items. * We must check that every item of A is present in B. */ for (hi = apr_hash_first(pool, a); hi; hi = apr_hash_next(hi)) { const void *key; apr_ssize_t klen; void *void_a_val; svn_string_t *a_val, *b_val; apr_hash_this(hi, &key, &klen, &void_a_val); a_val = void_a_val; b_val = apr_hash_get(b, key, klen); if (!b_val || (compare_values && !svn_string_compare(a_val, b_val))) return TRUE; } return FALSE;}svn_error_t *svn_subst_translate_stream2(svn_stream_t *s, /* src stream */ svn_stream_t *d, /* dst stream */ const char *eol_str, svn_boolean_t repair, const svn_subst_keywords_t *keywords, svn_boolean_t expand, apr_pool_t *pool){ apr_hash_t *kh = kwstruct_to_kwhash(keywords, pool); return svn_subst_translate_stream3(s, d, eol_str, repair, kh, expand, pool);}/* Baton for translate_chunk() to store its state in. */struct translation_baton{ const char *eol_str; svn_boolean_t repair; apr_hash_t *keywords; svn_boolean_t expand; /* Characters (excluding the terminating NUL character) which may trigger a translation action, hence are 'interesting' */ const char *interesting; /* Length of the string EOL_STR points to. */ apr_size_t eol_str_len; /* Buffer to cache any newline state between translation chunks */ char newline_buf[2]; /* Offset (within newline_buf) of the first *unused* character */ apr_size_t newline_off; /* Buffer to cache keyword-parsing state between translation chunks */ char keyword_buf[SVN_KEYWORD_MAX_LEN]; /* Offset (within keyword-buf) to the first *unused* character */ apr_size_t keyword_off; /* EOL style used in the chunk-source */ char src_format[2]; /* Length of the EOL style string found in the chunk-source, or zero if none encountered yet */ apr_size_t src_format_len;};/* Allocate a baton for use with translate_chunk() in POOL and * initialize it for the first iteration. * * The caller must assure that EOL_STR and KEYWORDS at least
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -