| /* parsing.c: parsing of config files | |
| * | |
| * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> | |
| * | |
| * Licensed under GNU General Public License v2 | |
| * (see COPYING for full license text) | |
| */ | |
| #define USE_THE_REPOSITORY_VARIABLE | |
| #include "cgit.h" | |
| /* | |
| * url syntax: [repo ['/' cmd [ '/' path]]] | |
| * repo: any valid repo url, may contain '/' | |
| * cmd: log | commit | diff | tree | view | blob | snapshot | |
| * path: any valid path, may contain '/' | |
| * | |
| */ | |
| void cgit_parse_url(const char *url) | |
| { | |
| char *c, *cmd, *p, *buf; | |
| struct cgit_repo *repo; | |
| if (!url || url[0] == '\0') | |
| return; | |
| ctx.qry.page = NULL; | |
| ctx.repo = cgit_get_repoinfo(url); | |
| if (ctx.repo) { | |
| ctx.qry.repo = ctx.repo->url; | |
| return; | |
| } | |
| buf = xstrdup(url); | |
| cmd = NULL; | |
| c = strchr(buf, '/'); | |
| while (c) { | |
| c[0] = '\0'; | |
| repo = cgit_get_repoinfo(buf); | |
| if (repo) { | |
| ctx.repo = repo; | |
| cmd = c; | |
| } | |
| c[0] = '/'; | |
| c = strchr(c + 1, '/'); | |
| } | |
| if (ctx.repo) { | |
| ctx.qry.repo = ctx.repo->url; | |
| p = strchr(cmd + 1, '/'); | |
| if (p) { | |
| p[0] = '\0'; | |
| if (p[1]) | |
| ctx.qry.path = trim_end(p + 1, '/'); | |
| } | |
| if (cmd[1]) | |
| ctx.qry.page = xstrdup(cmd + 1); | |
| } | |
| free(buf); | |
| } | |
| static char *substr(const char *head, const char *tail) | |
| { | |
| char *buf; | |
| if (tail < head) | |
| return xstrdup(""); | |
| buf = xmalloc(tail - head + 1); | |
| strlcpy(buf, head, tail - head + 1); | |
| return buf; | |
| } | |
| static void parse_user(const char *t, char **name, char **email, unsigned long *date, int *tz) | |
| { | |
| struct ident_split ident; | |
| unsigned email_len; | |
| if (!split_ident_line(&ident, t, strchrnul(t, '\n') - t)) { | |
| *name = substr(ident.name_begin, ident.name_end); | |
| email_len = ident.mail_end - ident.mail_begin; | |
| *email = xmalloc(strlen("<") + email_len + strlen(">") + 1); | |
| xsnprintf(*email, email_len + 3, "<%.*s>", email_len, ident.mail_begin); | |
| if (ident.date_begin) | |
| *date = strtoul(ident.date_begin, NULL, 10); | |
| if (ident.tz_begin) | |
| *tz = atoi(ident.tz_begin); | |
| } | |
| } | |
| #ifdef NO_ICONV | |
| #define reencode(a, b, c) | |
| #else | |
| static const char *reencode(char **txt, const char *src_enc, const char *dst_enc) | |
| { | |
| char *tmp; | |
| if (!txt) | |
| return NULL; | |
| if (!*txt || !src_enc || !dst_enc) | |
| return *txt; | |
| /* no encoding needed if src_enc equals dst_enc */ | |
| if (!strcasecmp(src_enc, dst_enc)) | |
| return *txt; | |
| tmp = reencode_string(*txt, dst_enc, src_enc); | |
| if (tmp) { | |
| free(*txt); | |
| *txt = tmp; | |
| } | |
| return *txt; | |
| } | |
| #endif | |
| static const char *next_header_line(const char *p) | |
| { | |
| p = strchr(p, '\n'); | |
| if (!p) | |
| return NULL; | |
| return p + 1; | |
| } | |
| static int end_of_header(const char *p) | |
| { | |
| return !p || (*p == '\n'); | |
| } | |
| struct commitinfo *cgit_parse_commit(struct commit *commit) | |
| { | |
| struct commitinfo *ret; | |
| const char *p = repo_get_commit_buffer(the_repository, commit, NULL); | |
| const char *t; | |
| ret = xcalloc(1, sizeof(struct commitinfo)); | |
| ret->commit = commit; | |
| if (!p) | |
| return ret; | |
| if (!skip_prefix(p, "tree ", &p)) | |
| die("Bad commit: %s", oid_to_hex(&commit->object.oid)); | |
| p += the_hash_algo->hexsz + 1; | |
| while (skip_prefix(p, "parent ", &p)) | |
| p += the_hash_algo->hexsz + 1; | |
| if (p && skip_prefix(p, "author ", &p)) { | |
| parse_user(p, &ret->author, &ret->author_email, | |
| &ret->author_date, &ret->author_tz); | |
| p = next_header_line(p); | |
| } | |
| if (p && skip_prefix(p, "committer ", &p)) { | |
| parse_user(p, &ret->committer, &ret->committer_email, | |
| &ret->committer_date, &ret->committer_tz); | |
| p = next_header_line(p); | |
| } | |
| if (p && skip_prefix(p, "encoding ", &p)) { | |
| t = strchr(p, '\n'); | |
| if (t) { | |
| ret->msg_encoding = substr(p, t + 1); | |
| p = t + 1; | |
| } | |
| } | |
| if (!ret->msg_encoding) | |
| ret->msg_encoding = xstrdup("UTF-8"); | |
| while (!end_of_header(p)) | |
| p = next_header_line(p); | |
| while (p && *p == '\n') | |
| p++; | |
| if (!p) | |
| return ret; | |
| t = strchrnul(p, '\n'); | |
| ret->subject = substr(p, t); | |
| while (*t == '\n') | |
| t++; | |
| ret->msg = xstrdup(t); | |
| reencode(&ret->author, ret->msg_encoding, PAGE_ENCODING); | |
| reencode(&ret->author_email, ret->msg_encoding, PAGE_ENCODING); | |
| reencode(&ret->committer, ret->msg_encoding, PAGE_ENCODING); | |
| reencode(&ret->committer_email, ret->msg_encoding, PAGE_ENCODING); | |
| reencode(&ret->subject, ret->msg_encoding, PAGE_ENCODING); | |
| reencode(&ret->msg, ret->msg_encoding, PAGE_ENCODING); | |
| return ret; | |
| } | |
| struct taginfo *cgit_parse_tag(struct tag *tag) | |
| { | |
| void *data; | |
| enum object_type type; | |
| unsigned long size; | |
| const char *p; | |
| struct taginfo *ret = NULL; | |
| data = odb_read_object(the_repository->objects, &tag->object.oid, &type, &size); | |
| if (!data || type != OBJ_TAG) | |
| goto cleanup; | |
| ret = xcalloc(1, sizeof(struct taginfo)); | |
| for (p = data; !end_of_header(p); p = next_header_line(p)) { | |
| if (skip_prefix(p, "tagger ", &p)) { | |
| parse_user(p, &ret->tagger, &ret->tagger_email, | |
| &ret->tagger_date, &ret->tagger_tz); | |
| } | |
| } | |
| while (p && *p == '\n') | |
| p++; | |
| if (p && *p) | |
| ret->msg = xstrdup(p); | |
| cleanup: | |
| free(data); | |
| return ret; | |
| } |