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_free(struct keyword *keyword)
162 while (!STAILQ_EMPTY(&keyword->keywords)) {
163 tag = STAILQ_FIRST(&keyword->keywords);
164 STAILQ_REMOVE_HEAD(&keyword->keywords, next);
171 keyword_alias(struct keyword *keyword, const char *ident, const char *rcskey)
173 struct tag *new, *tag;
175 STAILQ_FOREACH(tag, &keyword->keywords, next) {
176 if (strcmp(tag->ident, rcskey) == 0) {
177 new = tag_new(ident, tag->key);
178 STAILQ_INSERT_HEAD(&keyword->keywords, new, next);
187 keyword_enable(struct keyword *keyword, const char *ident)
193 if (strcmp(ident, ".") == 0)
196 STAILQ_FOREACH(tag, &keyword->keywords, next) {
197 if (!all && strcmp(tag->ident, ident) != 0)
211 keyword_disable(struct keyword *keyword, const char *ident)
217 if (strcmp(ident, ".") == 0)
220 STAILQ_FOREACH(tag, &keyword->keywords, next) {
221 if (!all && strcmp(tag->ident, ident) != 0)
236 keyword_prepare(struct keyword *keyword)
238 struct tag *tag, *temp;
240 STAILQ_FOREACH_SAFE(tag, &keyword->keywords, next, temp) {
242 STAILQ_REMOVE(&keyword->keywords, tag, tag, next);
250 * Expand appropriate RCS keywords. If there's no tag to expand,
251 * keyword_expand() returns 0, otherwise it returns 1 and writes a
252 * pointer to the new line in *buf and the new len in *len. The
253 * new line is allocated with malloc() and needs to be freed by the
257 keyword_expand(struct keyword *keyword, struct diffinfo *di, char *line,
258 size_t size, char **buf, size_t *len)
261 char *dollar, *keystart, *valstart, *vallim, *next;
262 char *linestart, *newline, *newval, *cp, *tmp;
263 size_t left, newsize, vallen;
265 if (di->di_expand == EXPAND_OLD || di->di_expand == EXPAND_BINARY)
270 linestart = cp = line;
272 dollar = memchr(cp, '$', left);
273 if (dollar == NULL) {
274 if (newline != NULL) {
281 keystart = dollar + 1;
282 left -= keystart - cp;
283 vallim = memchr(keystart, '$', left);
284 if (vallim == NULL) {
285 if (newline != NULL) {
292 if (vallim == keystart) {
296 valstart = memchr(keystart, ':', left);
297 if (valstart == keystart) {
299 left -= vallim - keystart;
302 if (valstart == NULL || valstart > vallim)
305 if (valstart < keystart + keyword->minkeylen ||
306 valstart > keystart + keyword->maxkeylen) {
308 left -= vallim -keystart;
311 STAILQ_FOREACH(tag, &keyword->keywords, next) {
312 if (strncmp(tag->ident, keystart, valstart - keystart) == 0 &&
313 tag->ident[valstart - keystart] == '\0') {
319 if (di->di_expand == EXPAND_KEY) {
320 newsize = dollar - linestart + 1 +
321 valstart - keystart + 1 +
322 size - (vallim + 1 - linestart);
323 newline = xmalloc(newsize);
325 memcpy(cp, linestart, dollar - linestart);
326 cp += dollar - linestart;
328 memcpy(cp, keystart, valstart - keystart);
329 cp += valstart - keystart;
332 memcpy(cp, vallim + 1,
333 size - (vallim + 1 - linestart));
334 } else if (di->di_expand == EXPAND_VALUE) {
335 newval = tag_expand(tag, di);
339 vallen = strlen(newval);
340 newsize = dollar - linestart +
342 size - (vallim + 1 - linestart);
343 newline = xmalloc(newsize);
345 memcpy(cp, linestart, dollar - linestart);
346 cp += dollar - linestart;
347 if (newval != NULL) {
348 memcpy(cp, newval, vallen);
352 memcpy(cp, vallim + 1,
353 size - (vallim + 1 - linestart));
355 assert(di->di_expand == EXPAND_DEFAULT ||
356 di->di_expand == EXPAND_KEYVALUE ||
357 di->di_expand == EXPAND_KEYVALUELOCKER);
358 newval = tag_expand(tag, di);
362 vallen = strlen(newval);
363 newsize = dollar - linestart + 1 +
364 valstart - keystart + 2 +
366 size - (vallim + 1 - linestart);
367 newline = xmalloc(newsize);
369 memcpy(cp, linestart, dollar - linestart);
370 cp += dollar - linestart;
372 memcpy(cp, keystart, valstart - keystart);
373 cp += valstart - keystart;
376 if (newval != NULL) {
377 memcpy(cp, newval, vallen);
383 memcpy(cp, vallim + 1,
384 size - (vallim + 1 - linestart));
391 * Continue looking for tags in the rest of the line.
395 left = size - (cp - newline);
401 left = size - (cp - linestart);
406 tag_new(const char *ident, rcskey_t key)
410 new = xmalloc(sizeof(struct tag));
411 new->ident = xstrdup(ident);
418 tag_free(struct tag *tag)
426 * Expand a specific tag and return the new value. If NULL
427 * is returned, the tag is empty.
430 tag_expand(struct tag *tag, struct diffinfo *di)
433 * CVS formats dates as "XXXX/XX/XX XX:XX:XX". 32 bytes
434 * is big enough until year 10,000,000,000,000,000 :-).
438 char *filename, *val;
441 error = rcsdatetotm(di->di_revdate, &tm);
444 if (strftime(cvsdate, sizeof(cvsdate), "%Y/%m/%d %H:%M:%S", &tm) == 0)
446 filename = strrchr(di->di_rcsfile, '/');
447 if (filename == NULL)
448 filename = di->di_rcsfile;
454 xasprintf(&val, "%s", di->di_author);
456 case RCSKEY_CVSHEADER:
457 xasprintf(&val, "%s %s %s %s %s", di->di_rcsfile,
458 di->di_revnum, cvsdate, di->di_author, di->di_state);
461 xasprintf(&val, "%s", cvsdate);
464 xasprintf(&val, "%s/%s %s %s %s %s", di->di_cvsroot,
465 di->di_rcsfile, di->di_revnum, cvsdate, di->di_author,
469 xasprintf(&val, "%s %s %s %s %s", filename, di->di_revnum,
470 cvsdate, di->di_author, di->di_state);
474 * Unimplemented even in CVSup sources. It seems we don't
475 * even have this information sent by the server.
480 printf("%s: Implement Log keyword expansion\n", __func__);
483 if (di->di_tag != NULL)
484 xasprintf(&val, "%s", di->di_tag);
489 xasprintf(&val, "%s", filename);
491 case RCSKEY_REVISION:
492 xasprintf(&val, "%s", di->di_revnum);
495 xasprintf(&val, "%s/%s", di->di_cvsroot, di->di_rcsfile);
498 xasprintf(&val, "%s", di->di_state);