📄 config_file.c
字号:
/* * config_file.c : parsing configuration files * * ==================================================================== * Copyright (c) 2000-2004 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#include <apr_lib.h>#include <apr_md5.h>#include <apr_env.h>#include "config_impl.h"#include "svn_io.h"#include "svn_types.h"#include "svn_path.h"#include "svn_auth.h"#include "svn_subst.h"#include "svn_utf.h"#include "svn_pools.h"#include "svn_user.h"#include "svn_private_config.h"/* File parsing context */typedef struct parse_context_t{ /* This config struct and file */ svn_config_t *cfg; const char *file; /* The file descriptor */ svn_stream_t *stream; /* The current line in the file */ int line; /* Cached ungotten character - streams don't support ungetc() [emulate it] */ int ungotten_char; svn_boolean_t have_ungotten_char; /* Temporary strings, allocated from the temp pool */ svn_stringbuf_t *section; svn_stringbuf_t *option; svn_stringbuf_t *value;} parse_context_t;/* Emulate getc() because streams don't support it. * * In order to be able to ungetc(), use the CXT instead of the stream * to be able to store the 'ungotton' character. * */static APR_INLINE svn_error_t *parser_getc(parse_context_t *ctx, int *c){ if (ctx->have_ungotten_char) { *c = ctx->ungotten_char; ctx->have_ungotten_char = FALSE; } else { char char_buf; apr_size_t readlen = 1; SVN_ERR(svn_stream_read(ctx->stream, &char_buf, &readlen)); if (readlen == 1) *c = char_buf; else *c = EOF; } return SVN_NO_ERROR;}/* Emulate ungetc() because streams don't support it. * * Use CTX to store the ungotten character C. */static APR_INLINE svn_error_t *parser_ungetc(parse_context_t *ctx, int c){ ctx->ungotten_char = c; ctx->have_ungotten_char = TRUE; return SVN_NO_ERROR;}/* Eat chars from STREAM until encounter non-whitespace, newline, or EOF. Set *PCOUNT to the number of characters eaten, not counting the last one, and return the last char read (the one that caused the break). */static APR_INLINE svn_error_t *skip_whitespace(parse_context_t *ctx, int *c, int *pcount){ int ch; int count = 0; SVN_ERR(parser_getc(ctx, &ch)); while (ch != EOF && ch != '\n' && apr_isspace(ch)) { ++count; SVN_ERR(parser_getc(ctx, &ch)); } *pcount = count; *c = ch; return SVN_NO_ERROR;}/* Skip to the end of the line (or file). Returns the char that ended the line; the char is either EOF or newline. */static APR_INLINE svn_error_t *skip_to_eoln(parse_context_t *ctx, int *c){ int ch; SVN_ERR(parser_getc(ctx, &ch)); while (ch != EOF && ch != '\n') SVN_ERR(parser_getc(ctx, &ch)); *c = ch; return SVN_NO_ERROR;}/* Parse a single option value */static svn_error_t *parse_value(int *pch, parse_context_t *ctx){ svn_boolean_t end_of_val = FALSE; int ch; /* Read the first line of the value */ svn_stringbuf_setempty(ctx->value); SVN_ERR(parser_getc(ctx, &ch)); while (ch != EOF && ch != '\n') /* last ch seen was ':' or '=' in parse_option. */ { const char char_from_int = ch; svn_stringbuf_appendbytes(ctx->value, &char_from_int, 1); SVN_ERR(parser_getc(ctx, &ch)); } /* Leading and trailing whitespace is ignored. */ svn_stringbuf_strip_whitespace(ctx->value); /* Look for any continuation lines. */ for (;;) { if (ch == EOF || end_of_val) { /* At end of file. The value is complete, there can't be any continuation lines. */ svn_config_set(ctx->cfg, ctx->section->data, ctx->option->data, ctx->value->data); break; } else { int count; ++ctx->line; SVN_ERR(skip_whitespace(ctx, &ch, &count)); switch (ch) { case '\n': /* The next line was empty. Ergo, it can't be a continuation line. */ ++ctx->line; end_of_val = TRUE; continue; case EOF: /* This is also an empty line. */ end_of_val = TRUE; continue; default: if (count == 0) { /* This line starts in the first column. That means it's either a section, option or comment. Put the char back into the stream, because it doesn't belong to us. */ SVN_ERR(parser_ungetc(ctx, ch)); end_of_val = TRUE; } else { /* This is a continuation line. Read it. */ svn_stringbuf_appendbytes(ctx->value, " ", 1); while (ch != EOF && ch != '\n') { const char char_from_int = ch; svn_stringbuf_appendbytes(ctx->value, &char_from_int, 1); SVN_ERR(parser_getc(ctx, &ch)); } /* Trailing whitespace is ignored. */ svn_stringbuf_strip_whitespace(ctx->value); } } } } *pch = ch; return SVN_NO_ERROR;}/* Parse a single option */static svn_error_t *parse_option(int *pch, parse_context_t *ctx, apr_pool_t *pool){ svn_error_t *err = SVN_NO_ERROR; int ch; svn_stringbuf_setempty(ctx->option); ch = *pch; /* Yes, the first char is relevant. */ while (ch != EOF && ch != ':' && ch != '=' && ch != '\n') { const char char_from_int = ch; svn_stringbuf_appendbytes(ctx->option, &char_from_int, 1); SVN_ERR(parser_getc(ctx, &ch)); } if (ch != ':' && ch != '=') { ch = EOF; err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, "%s:%d: Option must end with ':' or '='", svn_path_local_style(ctx->file, pool), ctx->line); } else { /* Whitespace around the name separator is ignored. */ svn_stringbuf_strip_whitespace(ctx->option); err = parse_value(&ch, ctx); } *pch = ch; return err;}/* Read chars until enounter ']', then skip everything to the end of * the line. Set *PCH to the character that ended the line (either * newline or EOF), and set CTX->section to the string of characters * seen before ']'. * * This is meant to be called immediately after reading the '[' that * starts a section name. */static svn_error_t *parse_section_name(int *pch, parse_context_t *ctx, apr_pool_t *pool){ svn_error_t *err = SVN_NO_ERROR; int ch; svn_stringbuf_setempty(ctx->section); SVN_ERR(parser_getc(ctx, &ch)); while (ch != EOF && ch != ']' && ch != '\n') { const char char_from_int = ch; svn_stringbuf_appendbytes(ctx->section, &char_from_int, 1); SVN_ERR(parser_getc(ctx, &ch)); } if (ch != ']') { ch = EOF; err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, "%s:%d: Section header must end with ']'", svn_path_local_style(ctx->file, pool), ctx->line); } else { /* Everything from the ']' to the end of the line is ignored. */ SVN_ERR(skip_to_eoln(ctx, &ch)); if (ch != EOF) ++ctx->line; } *pch = ch; return err;}svn_error_t *svn_config__sys_config_path(const char **path_p, const char *fname, apr_pool_t *pool){ /* ### This never actually returns error in practice. Perhaps the prototype should change? */ *path_p = NULL; /* Note that even if fname is null, svn_path_join_many will DTRT. */#ifdef WIN32 { const char *folder; SVN_ERR(svn_config__win_config_path(&folder, TRUE, pool)); *path_p = svn_path_join_many(pool, folder, SVN_CONFIG__SUBDIRECTORY, fname, NULL); }#else /* ! WIN32 */ *path_p = svn_path_join_many(pool, SVN_CONFIG__SYS_DIRECTORY, fname, NULL);#endif /* WIN32 */ return SVN_NO_ERROR;}svn_error_t *svn_config__user_config_path(const char *config_dir, const char **path_p, const char *fname, apr_pool_t *pool){ /* ### This never actually returns error in practice. Perhaps the prototype should change? */ *path_p = NULL; /* Note that even if fname is null, svn_path_join_many will DTRT. */ if (config_dir) { *path_p = svn_path_join_many(pool, config_dir, fname, NULL); return SVN_NO_ERROR; } #ifdef WIN32 { const char *folder; SVN_ERR(svn_config__win_config_path(&folder, FALSE, pool)); *path_p = svn_path_join_many(pool, folder, SVN_CONFIG__SUBDIRECTORY, fname, NULL); }#else /* ! WIN32 */ { const char *homedir = svn_user_get_homedir(pool); if (! homedir) return SVN_NO_ERROR; *path_p = svn_path_join_many(pool, svn_path_canonicalize(homedir, pool), SVN_CONFIG__USR_DIRECTORY, fname, NULL); }#endif /* WIN32 */ return SVN_NO_ERROR;}/*** Exported interfaces. ***/svn_error_t *svn_config__parse_file(svn_config_t *cfg, const char *file, svn_boolean_t must_exist, apr_pool_t *pool){ svn_error_t *err = SVN_NO_ERROR; parse_context_t ctx; int ch, count; apr_file_t *f; /* No need for buffering; a translated stream buffers */ err = svn_io_file_open(&f, file, APR_BINARY | APR_READ, APR_OS_DEFAULT, pool); if (! must_exist && err && APR_STATUS_IS_ENOENT(err->apr_err)) { svn_error_clear(err); return SVN_NO_ERROR; } else SVN_ERR(err); ctx.cfg = cfg; ctx.file = file; ctx.stream = svn_subst_stream_translated(svn_stream_from_aprfile(f, pool), "\n", TRUE, NULL, FALSE, pool); ctx.line = 1; ctx.have_ungotten_char = FALSE; ctx.section = svn_stringbuf_create("", pool); ctx.option = svn_stringbuf_create("", pool); ctx.value = svn_stringbuf_create("", pool); do { SVN_ERR(skip_whitespace(&ctx, &ch, &count)); switch (ch)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -