config.con commit Support git+ssh:// and ssh+git:// URL (c05186c)
   1#include <ctype.h>
   2
   3#include "cache.h"
   4
   5#define MAXNAME (256)
   6
   7static FILE *config_file;
   8static int config_linenr;
   9static int get_next_char(void)
  10{
  11        int c;
  12        FILE *f;
  13
  14        c = '\n';
  15        if ((f = config_file) != NULL) {
  16                c = fgetc(f);
  17                if (c == '\n')
  18                        config_linenr++;
  19                if (c == EOF) {
  20                        config_file = NULL;
  21                        c = '\n';
  22                }
  23        }
  24        return c;
  25}
  26
  27static char *parse_value(void)
  28{
  29        static char value[1024];
  30        int quote = 0, comment = 0, len = 0, space = 0;
  31
  32        for (;;) {
  33                int c = get_next_char();
  34                if (len >= sizeof(value))
  35                        return NULL;
  36                if (c == '\n') {
  37                        if (quote)
  38                                return NULL;
  39                        value[len] = 0;
  40                        return value;
  41                }
  42                if (comment)
  43                        continue;
  44                if (isspace(c) && !quote) {
  45                        space = 1;
  46                        continue;
  47                }
  48                if (space) {
  49                        if (len)
  50                                value[len++] = ' ';
  51                        space = 0;
  52                }
  53                if (c == '\\') {
  54                        c = get_next_char();
  55                        switch (c) {
  56                        case '\n':
  57                                continue;
  58                        case 't':
  59                                c = '\t';
  60                                break;
  61                        case 'b':
  62                                c = '\b';
  63                                break;
  64                        case 'n':
  65                                c = '\n';
  66                                break;
  67                        /* Some characters escape as themselves */
  68                        case '\\': case '"':
  69                                break;
  70                        /* Reject unknown escape sequences */
  71                        default:
  72                                return NULL;
  73                        }
  74                        value[len++] = c;
  75                        continue;
  76                }
  77                if (c == '"') {
  78                        quote = 1-quote;
  79                        continue;
  80                }
  81                if (!quote) {
  82                        if (c == ';' || c == '#') {
  83                                comment = 1;
  84                                continue;
  85                        }
  86                }
  87                value[len++] = c;
  88        }
  89}
  90
  91static int get_value(config_fn_t fn, char *name, unsigned int len)
  92{
  93        int c;
  94        char *value;
  95
  96        /* Get the full name */
  97        for (;;) {
  98                c = get_next_char();
  99                if (c == EOF)
 100                        break;
 101                if (!isalnum(c))
 102                        break;
 103                name[len++] = tolower(c);
 104                if (len >= MAXNAME)
 105                        return -1;
 106        }
 107        name[len] = 0;
 108        while (c == ' ' || c == '\t')
 109                c = get_next_char();
 110
 111        value = NULL;
 112        if (c != '\n') {
 113                if (c != '=')
 114                        return -1;
 115                value = parse_value();
 116                if (!value)
 117                        return -1;
 118        }
 119        return fn(name, value);
 120}
 121
 122static int get_base_var(char *name)
 123{
 124        int baselen = 0;
 125
 126        for (;;) {
 127                int c = get_next_char();
 128                if (c == EOF)
 129                        return -1;
 130                if (c == ']')
 131                        return baselen;
 132                if (!isalnum(c))
 133                        return -1;
 134                if (baselen > MAXNAME / 2)
 135                        return -1;
 136                name[baselen++] = tolower(c);
 137        }
 138}
 139
 140static int git_parse_file(config_fn_t fn)
 141{
 142        int comment = 0;
 143        int baselen = 0;
 144        static char var[MAXNAME];
 145
 146        for (;;) {
 147                int c = get_next_char();
 148                if (c == '\n') {
 149                        /* EOF? */
 150                        if (!config_file)
 151                                return 0;
 152                        comment = 0;
 153                        continue;
 154                }
 155                if (comment || isspace(c))
 156                        continue;
 157                if (c == '#' || c == ';') {
 158                        comment = 1;
 159                        continue;
 160                }
 161                if (c == '[') {
 162                        baselen = get_base_var(var);
 163                        if (baselen <= 0)
 164                                break;
 165                        var[baselen++] = '.';
 166                        var[baselen] = 0;
 167                        continue;
 168                }
 169                if (!isalpha(c))
 170                        break;
 171                var[baselen] = tolower(c);
 172                if (get_value(fn, var, baselen+1) < 0)
 173                        break;
 174        }
 175        die("bad config file line %d", config_linenr);
 176}
 177
 178int git_config_int(const char *name, const char *value)
 179{
 180        if (value && *value) {
 181                char *end;
 182                int val = strtol(value, &end, 0);
 183                if (!*end)
 184                        return val;
 185        }
 186        die("bad config value for '%s'", name);
 187}
 188
 189int git_config_bool(const char *name, const char *value)
 190{
 191        if (!value)
 192                return 1;
 193        if (!*value)
 194                return 0;
 195        if (!strcasecmp(value, "true"))
 196                return 1;
 197        if (!strcasecmp(value, "false"))
 198                return 0;
 199        return git_config_int(name, value) != 0;
 200}
 201
 202int git_default_config(const char *var, const char *value)
 203{
 204        /* This needs a better name */
 205        if (!strcmp(var, "core.filemode")) {
 206                trust_executable_bit = git_config_bool(var, value);
 207                return 0;
 208        }
 209
 210        if (!strcmp(var, "user.name")) {
 211                strncpy(git_default_name, value, sizeof(git_default_name));
 212                return 0;
 213        }
 214
 215        if (!strcmp(var, "user.email")) {
 216                strncpy(git_default_email, value, sizeof(git_default_email));
 217                return 0;
 218        }
 219
 220        /* Add other config variables here.. */
 221        return 0;
 222}
 223
 224int git_config(config_fn_t fn)
 225{
 226        int ret;
 227        FILE *f = fopen(git_path("config"), "r");
 228
 229        ret = -1;
 230        if (f) {
 231                config_file = f;
 232                config_linenr = 1;
 233                ret = git_parse_file(fn);
 234                fclose(f);
 235        }
 236        return ret;
 237}