2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * The keyword API is used to expand the CVS/RCS keywords in files,
45 * such as $Id$, $Revision$, etc. The server does it for us when it
46 * sends us entire files, but we need to handle the expansion when
47 * applying a diff update.
65 typedef enum rcskey rcskey_t;
71 STAILQ_ENTRY(tag) next;
74 static struct tag *tag_new(const char *, rcskey_t);
75 static char *tag_expand(struct tag *, struct diffinfo *);
76 static void tag_free(struct tag *);
79 STAILQ_HEAD(, tag) keywords; /* Enabled keywords. */
84 /* Default CVS keywords. */
89 { "Author", RCSKEY_AUTHOR },
90 { "CVSHeader", RCSKEY_CVSHEADER },
91 { "Date", RCSKEY_DATE },
92 { "Header", RCSKEY_HEADER },
94 { "Locker", RCSKEY_LOCKER },
95 { "Log", RCSKEY_LOG },
96 { "Name", RCSKEY_NAME },
97 { "RCSfile", RCSKEY_RCSFILE },
98 { "Revision", RCSKEY_REVISION },
99 { "Source", RCSKEY_SOURCE },
100 { "State", RCSKEY_STATE },
112 new = xmalloc(sizeof(struct keyword));
113 STAILQ_INIT(&new->keywords);
116 for (i = 0; tag_defaults[i].ident != NULL; i++) {
117 tag = tag_new(tag_defaults[i].ident, tag_defaults[i].key);
118 STAILQ_INSERT_TAIL(&new->keywords, tag, next);
119 len = strlen(tag->ident);
121 * These values are only computed here and not updated when
122 * adding an alias. This is a bug, but CVSup has it and we
123 * need to be bug-to-bug compatible since the server will
124 * expect us to do the same, and we will fail with an MD5
125 * checksum mismatch if we don't.
127 new->minkeylen = min(new->minkeylen, len);
128 new->maxkeylen = max(new->maxkeylen, len);
134 keyword_decode_expand(const char *expand)
137 if (strcmp(expand, ".") == 0)
138 return (EXPAND_DEFAULT);
139 else if (strcmp(expand, "kv") == 0)
140 return (EXPAND_KEYVALUE);
141 else if (strcmp(expand, "kvl") == 0)
142 return (EXPAND_KEYVALUELOCKER);
143 else if (strcmp(expand, "k") == 0)
145 else if (strcmp(expand, "o") == 0)
147 else if (strcmp(expand, "b") == 0)
148 return (EXPAND_BINARY);
149 else if (strcmp(expand, "v") == 0)
150 return (EXPAND_VALUE);
156 keyword_encode_expand(int expand)
162 case EXPAND_KEYVALUE:
164 case EXPAND_KEYVALUELOCKER:
179 keyword_free(struct keyword *keyword)
185 while (!STAILQ_EMPTY(&keyword->keywords)) {
186 tag = STAILQ_FIRST(&keyword->keywords);
187 STAILQ_REMOVE_HEAD(&keyword->keywords, next);
194 keyword_alias(struct keyword *keyword, const char *ident, const char *rcskey)
196 struct tag *new, *tag;
198 STAILQ_FOREACH(tag, &keyword->keywords, next) {
199 if (strcmp(tag->ident, rcskey) == 0) {
200 new = tag_new(ident, tag->key);
201 STAILQ_INSERT_HEAD(&keyword->keywords, new, next);
210 keyword_enable(struct keyword *keyword, const char *ident)
216 if (strcmp(ident, ".") == 0)
219 STAILQ_FOREACH(tag, &keyword->keywords, next) {
220 if (!all && strcmp(tag->ident, ident) != 0)
234 keyword_disable(struct keyword *keyword, const char *ident)
240 if (strcmp(ident, ".") == 0)
243 STAILQ_FOREACH(tag, &keyword->keywords, next) {
244 if (!all && strcmp(tag->ident, ident) != 0)
259 keyword_prepare(struct keyword *keyword)
261 struct tag *tag, *temp;
263 STAILQ_FOREACH_SAFE(tag, &keyword->keywords, next, temp) {
265 STAILQ_REMOVE(&keyword->keywords, tag, tag, next);
273 * Expand appropriate RCS keywords. If there's no tag to expand,
274 * keyword_expand() returns 0, otherwise it returns 1 and writes a
275 * pointer to the new line in *buf and the new len in *len. The
276 * new line is allocated with malloc() and needs to be freed by the
280 keyword_expand(struct keyword *keyword, struct diffinfo *di, char *line,
281 size_t size, char **buf, size_t *len)
284 char *dollar, *keystart, *valstart, *vallim, *next;
285 char *linestart, *newline, *newval, *cp, *tmp;
286 size_t left, newsize, vallen;
288 if (di->di_expand == EXPAND_OLD || di->di_expand == EXPAND_BINARY)
293 linestart = cp = line;
295 dollar = memchr(cp, '$', left);
296 if (dollar == NULL) {
297 if (newline != NULL) {
304 keystart = dollar + 1;
305 left -= keystart - cp;
306 vallim = memchr(keystart, '$', left);
307 if (vallim == NULL) {
308 if (newline != NULL) {
315 if (vallim == keystart) {
319 valstart = memchr(keystart, ':', left);
320 if (valstart == keystart) {
322 left -= vallim - keystart;
325 if (valstart == NULL || valstart > vallim)
328 if (valstart < keystart + keyword->minkeylen ||
329 valstart > keystart + keyword->maxkeylen) {
331 left -= vallim -keystart;
334 STAILQ_FOREACH(tag, &keyword->keywords, next) {
335 if (strncmp(tag->ident, keystart, valstart - keystart) == 0 &&
336 tag->ident[valstart - keystart] == '\0') {
342 if (di->di_expand == EXPAND_KEY) {
343 newsize = dollar - linestart + 1 +
344 valstart - keystart + 1 +
345 size - (vallim + 1 - linestart);
346 newline = xmalloc(newsize);
348 memcpy(cp, linestart, dollar - linestart);
349 cp += dollar - linestart;
351 memcpy(cp, keystart, valstart - keystart);
352 cp += valstart - keystart;
355 memcpy(cp, vallim + 1,
356 size - (vallim + 1 - linestart));
357 } else if (di->di_expand == EXPAND_VALUE) {
358 newval = tag_expand(tag, di);
362 vallen = strlen(newval);
363 newsize = dollar - linestart +
365 size - (vallim + 1 - linestart);
366 newline = xmalloc(newsize);
368 memcpy(cp, linestart, dollar - linestart);
369 cp += dollar - linestart;
370 if (newval != NULL) {
371 memcpy(cp, newval, vallen);
375 memcpy(cp, vallim + 1,
376 size - (vallim + 1 - linestart));
378 assert(di->di_expand == EXPAND_DEFAULT ||
379 di->di_expand == EXPAND_KEYVALUE ||
380 di->di_expand == EXPAND_KEYVALUELOCKER);
381 newval = tag_expand(tag, di);
385 vallen = strlen(newval);
386 newsize = dollar - linestart + 1 +
387 valstart - keystart + 2 +
389 size - (vallim + 1 - linestart);
390 newline = xmalloc(newsize);
392 memcpy(cp, linestart, dollar - linestart);
393 cp += dollar - linestart;
395 memcpy(cp, keystart, valstart - keystart);
396 cp += valstart - keystart;
399 if (newval != NULL) {
400 memcpy(cp, newval, vallen);
406 memcpy(cp, vallim + 1,
407 size - (vallim + 1 - linestart));
414 * Continue looking for tags in the rest of the line.
418 left = size - (cp - newline);
424 left = size - (cp - linestart);
429 tag_new(const char *ident, rcskey_t key)
433 new = xmalloc(sizeof(struct tag));
434 new->ident = xstrdup(ident);
441 tag_free(struct tag *tag)
449 * Expand a specific tag and return the new value. If NULL
450 * is returned, the tag is empty.
453 tag_expand(struct tag *tag, struct diffinfo *di)
456 * CVS formats dates as "XXXX/XX/XX XX:XX:XX". 32 bytes
457 * is big enough until year 10,000,000,000,000,000 :-).
461 char *filename, *val;
464 error = rcsdatetotm(di->di_revdate, &tm);
467 if (strftime(cvsdate, sizeof(cvsdate), "%Y/%m/%d %H:%M:%S", &tm) == 0)
469 filename = strrchr(di->di_rcsfile, '/');
470 if (filename == NULL)
471 filename = di->di_rcsfile;
477 xasprintf(&val, "%s", di->di_author);
479 case RCSKEY_CVSHEADER:
480 xasprintf(&val, "%s %s %s %s %s", di->di_rcsfile,
481 di->di_revnum, cvsdate, di->di_author, di->di_state);
484 xasprintf(&val, "%s", cvsdate);
487 xasprintf(&val, "%s/%s %s %s %s %s", di->di_cvsroot,
488 di->di_rcsfile, di->di_revnum, cvsdate, di->di_author,
492 xasprintf(&val, "%s %s %s %s %s", filename, di->di_revnum,
493 cvsdate, di->di_author, di->di_state);
497 * Unimplemented even in CVSup sources. It seems we don't
498 * even have this information sent by the server.
503 printf("%s: Implement Log keyword expansion\n", __func__);
506 if (di->di_tag != NULL)
507 xasprintf(&val, "%s", di->di_tag);
512 xasprintf(&val, "%s", filename);
514 case RCSKEY_REVISION:
515 xasprintf(&val, "%s", di->di_revnum);
518 xasprintf(&val, "%s/%s", di->di_cvsroot, di->di_rcsfile);
521 xasprintf(&val, "%s", di->di_state);