Code

git-clone.sh: properly configure remote even if remote's head is dangling
[git.git] / convert.c
1 #include "cache.h"
2 #include "attr.h"
3 #include "run-command.h"
5 /*
6  * convert.c - convert a file when checking it out and checking it in.
7  *
8  * This should use the pathname to decide on whether it wants to do some
9  * more interesting conversions (automatic gzip/unzip, general format
10  * conversions etc etc), but by default it just does automatic CRLF<->LF
11  * translation when the "auto_crlf" option is set.
12  */
14 #define CRLF_GUESS      (-1)
15 #define CRLF_BINARY     0
16 #define CRLF_TEXT       1
17 #define CRLF_INPUT      2
19 struct text_stat {
20         /* NUL, CR, LF and CRLF counts */
21         unsigned nul, cr, lf, crlf;
23         /* These are just approximations! */
24         unsigned printable, nonprintable;
25 };
27 static void gather_stats(const char *buf, unsigned long size, struct text_stat *stats)
28 {
29         unsigned long i;
31         memset(stats, 0, sizeof(*stats));
33         for (i = 0; i < size; i++) {
34                 unsigned char c = buf[i];
35                 if (c == '\r') {
36                         stats->cr++;
37                         if (i+1 < size && buf[i+1] == '\n')
38                                 stats->crlf++;
39                         continue;
40                 }
41                 if (c == '\n') {
42                         stats->lf++;
43                         continue;
44                 }
45                 if (c == 127)
46                         /* DEL */
47                         stats->nonprintable++;
48                 else if (c < 32) {
49                         switch (c) {
50                                 /* BS, HT, ESC and FF */
51                         case '\b': case '\t': case '\033': case '\014':
52                                 stats->printable++;
53                                 break;
54                         case 0:
55                                 stats->nul++;
56                                 /* fall through */
57                         default:
58                                 stats->nonprintable++;
59                         }
60                 }
61                 else
62                         stats->printable++;
63         }
64 }
66 /*
67  * The same heuristics as diff.c::mmfile_is_binary()
68  */
69 static int is_binary(unsigned long size, struct text_stat *stats)
70 {
72         if (stats->nul)
73                 return 1;
74         if ((stats->printable >> 7) < stats->nonprintable)
75                 return 1;
76         /*
77          * Other heuristics? Average line length might be relevant,
78          * as might LF vs CR vs CRLF counts..
79          *
80          * NOTE! It might be normal to have a low ratio of CRLF to LF
81          * (somebody starts with a LF-only file and edits it with an editor
82          * that adds CRLF only to lines that are added..). But do  we
83          * want to support CR-only? Probably not.
84          */
85         return 0;
86 }
88 static int crlf_to_git(const char *path, const char *src, size_t len,
89                        struct strbuf *buf, int action)
90 {
91         struct text_stat stats;
92         char *dst;
94         if ((action == CRLF_BINARY) || !auto_crlf || !len)
95                 return 0;
97         gather_stats(src, len, &stats);
98         /* No CR? Nothing to convert, regardless. */
99         if (!stats.cr)
100                 return 0;
102         if (action == CRLF_GUESS) {
103                 /*
104                  * We're currently not going to even try to convert stuff
105                  * that has bare CR characters. Does anybody do that crazy
106                  * stuff?
107                  */
108                 if (stats.cr != stats.crlf)
109                         return 0;
111                 /*
112                  * And add some heuristics for binary vs text, of course...
113                  */
114                 if (is_binary(len, &stats))
115                         return 0;
116         }
118         /* only grow if not in place */
119         if (strbuf_avail(buf) + buf->len < len)
120                 strbuf_grow(buf, len - buf->len);
121         dst = buf->buf;
122         if (action == CRLF_GUESS) {
123                 /*
124                  * If we guessed, we already know we rejected a file with
125                  * lone CR, and we can strip a CR without looking at what
126                  * follow it.
127                  */
128                 do {
129                         unsigned char c = *src++;
130                         if (c != '\r')
131                                 *dst++ = c;
132                 } while (--len);
133         } else {
134                 do {
135                         unsigned char c = *src++;
136                         if (! (c == '\r' && (1 < len && *src == '\n')))
137                                 *dst++ = c;
138                 } while (--len);
139         }
140         strbuf_setlen(buf, dst - buf->buf);
141         return 1;
144 static int crlf_to_worktree(const char *path, const char *src, size_t len,
145                             struct strbuf *buf, int action)
147         char *to_free = NULL;
148         struct text_stat stats;
150         if ((action == CRLF_BINARY) || (action == CRLF_INPUT) ||
151             auto_crlf <= 0)
152                 return 0;
154         if (!len)
155                 return 0;
157         gather_stats(src, len, &stats);
159         /* No LF? Nothing to convert, regardless. */
160         if (!stats.lf)
161                 return 0;
163         /* Was it already in CRLF format? */
164         if (stats.lf == stats.crlf)
165                 return 0;
167         if (action == CRLF_GUESS) {
168                 /* If we have any bare CR characters, we're not going to touch it */
169                 if (stats.cr != stats.crlf)
170                         return 0;
172                 if (is_binary(len, &stats))
173                         return 0;
174         }
176         /* are we "faking" in place editing ? */
177         if (src == buf->buf)
178                 to_free = strbuf_detach(buf, NULL);
180         strbuf_grow(buf, len + stats.lf - stats.crlf);
181         for (;;) {
182                 const char *nl = memchr(src, '\n', len);
183                 if (!nl)
184                         break;
185                 if (nl > src && nl[-1] == '\r') {
186                         strbuf_add(buf, src, nl + 1 - src);
187                 } else {
188                         strbuf_add(buf, src, nl - src);
189                         strbuf_addstr(buf, "\r\n");
190                 }
191                 len -= nl + 1 - src;
192                 src  = nl + 1;
193         }
194         strbuf_add(buf, src, len);
196         free(to_free);
197         return 1;
200 struct filter_params {
201         const char *src;
202         unsigned long size;
203         const char *cmd;
204 };
206 static int filter_buffer(int fd, void *data)
208         /*
209          * Spawn cmd and feed the buffer contents through its stdin.
210          */
211         struct child_process child_process;
212         struct filter_params *params = (struct filter_params *)data;
213         int write_err, status;
214         const char *argv[] = { "sh", "-c", params->cmd, NULL };
216         memset(&child_process, 0, sizeof(child_process));
217         child_process.argv = argv;
218         child_process.in = -1;
219         child_process.out = fd;
221         if (start_command(&child_process))
222                 return error("cannot fork to run external filter %s", params->cmd);
224         write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
225         if (close(child_process.in))
226                 write_err = 1;
227         if (write_err)
228                 error("cannot feed the input to external filter %s", params->cmd);
230         status = finish_command(&child_process);
231         if (status)
232                 error("external filter %s failed %d", params->cmd, -status);
233         return (write_err || status);
236 static int apply_filter(const char *path, const char *src, size_t len,
237                         struct strbuf *dst, const char *cmd)
239         /*
240          * Create a pipeline to have the command filter the buffer's
241          * contents.
242          *
243          * (child --> cmd) --> us
244          */
245         int ret = 1;
246         struct strbuf nbuf;
247         struct async async;
248         struct filter_params params;
250         if (!cmd)
251                 return 0;
253         memset(&async, 0, sizeof(async));
254         async.proc = filter_buffer;
255         async.data = &params;
256         params.src = src;
257         params.size = len;
258         params.cmd = cmd;
260         fflush(NULL);
261         if (start_async(&async))
262                 return 0;       /* error was already reported */
264         strbuf_init(&nbuf, 0);
265         if (strbuf_read(&nbuf, async.out, len) < 0) {
266                 error("read from external filter %s failed", cmd);
267                 ret = 0;
268         }
269         if (close(async.out)) {
270                 error("read from external filter %s failed", cmd);
271                 ret = 0;
272         }
273         if (finish_async(&async)) {
274                 error("external filter %s failed", cmd);
275                 ret = 0;
276         }
278         if (ret) {
279                 strbuf_swap(dst, &nbuf);
280         }
281         strbuf_release(&nbuf);
282         return ret;
285 static struct convert_driver {
286         const char *name;
287         struct convert_driver *next;
288         char *smudge;
289         char *clean;
290 } *user_convert, **user_convert_tail;
292 static int read_convert_config(const char *var, const char *value)
294         const char *ep, *name;
295         int namelen;
296         struct convert_driver *drv;
298         /*
299          * External conversion drivers are configured using
300          * "filter.<name>.variable".
301          */
302         if (prefixcmp(var, "filter.") || (ep = strrchr(var, '.')) == var + 6)
303                 return 0;
304         name = var + 7;
305         namelen = ep - name;
306         for (drv = user_convert; drv; drv = drv->next)
307                 if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
308                         break;
309         if (!drv) {
310                 drv = xcalloc(1, sizeof(struct convert_driver));
311                 drv->name = xmemdupz(name, namelen);
312                 *user_convert_tail = drv;
313                 user_convert_tail = &(drv->next);
314         }
316         ep++;
318         /*
319          * filter.<name>.smudge and filter.<name>.clean specifies
320          * the command line:
321          *
322          *      command-line
323          *
324          * The command-line will not be interpolated in any way.
325          */
327         if (!strcmp("smudge", ep)) {
328                 if (!value)
329                         return config_error_nonbool(var);
330                 drv->smudge = strdup(value);
331                 return 0;
332         }
334         if (!strcmp("clean", ep)) {
335                 if (!value)
336                         return config_error_nonbool(var);
337                 drv->clean = strdup(value);
338                 return 0;
339         }
340         return 0;
343 static void setup_convert_check(struct git_attr_check *check)
345         static struct git_attr *attr_crlf;
346         static struct git_attr *attr_ident;
347         static struct git_attr *attr_filter;
349         if (!attr_crlf) {
350                 attr_crlf = git_attr("crlf", 4);
351                 attr_ident = git_attr("ident", 5);
352                 attr_filter = git_attr("filter", 6);
353                 user_convert_tail = &user_convert;
354                 git_config(read_convert_config);
355         }
356         check[0].attr = attr_crlf;
357         check[1].attr = attr_ident;
358         check[2].attr = attr_filter;
361 static int count_ident(const char *cp, unsigned long size)
363         /*
364          * "$Id: 0000000000000000000000000000000000000000 $" <=> "$Id$"
365          */
366         int cnt = 0;
367         char ch;
369         while (size) {
370                 ch = *cp++;
371                 size--;
372                 if (ch != '$')
373                         continue;
374                 if (size < 3)
375                         break;
376                 if (memcmp("Id", cp, 2))
377                         continue;
378                 ch = cp[2];
379                 cp += 3;
380                 size -= 3;
381                 if (ch == '$')
382                         cnt++; /* $Id$ */
383                 if (ch != ':')
384                         continue;
386                 /*
387                  * "$Id: ... "; scan up to the closing dollar sign and discard.
388                  */
389                 while (size) {
390                         ch = *cp++;
391                         size--;
392                         if (ch == '$') {
393                                 cnt++;
394                                 break;
395                         }
396                 }
397         }
398         return cnt;
401 static int ident_to_git(const char *path, const char *src, size_t len,
402                         struct strbuf *buf, int ident)
404         char *dst, *dollar;
406         if (!ident || !count_ident(src, len))
407                 return 0;
409         /* only grow if not in place */
410         if (strbuf_avail(buf) + buf->len < len)
411                 strbuf_grow(buf, len - buf->len);
412         dst = buf->buf;
413         for (;;) {
414                 dollar = memchr(src, '$', len);
415                 if (!dollar)
416                         break;
417                 memcpy(dst, src, dollar + 1 - src);
418                 dst += dollar + 1 - src;
419                 len -= dollar + 1 - src;
420                 src  = dollar + 1;
422                 if (len > 3 && !memcmp(src, "Id:", 3)) {
423                         dollar = memchr(src + 3, '$', len - 3);
424                         if (!dollar)
425                                 break;
426                         memcpy(dst, "Id$", 3);
427                         dst += 3;
428                         len -= dollar + 1 - src;
429                         src  = dollar + 1;
430                 }
431         }
432         memcpy(dst, src, len);
433         strbuf_setlen(buf, dst + len - buf->buf);
434         return 1;
437 static int ident_to_worktree(const char *path, const char *src, size_t len,
438                              struct strbuf *buf, int ident)
440         unsigned char sha1[20];
441         char *to_free = NULL, *dollar;
442         int cnt;
444         if (!ident)
445                 return 0;
447         cnt = count_ident(src, len);
448         if (!cnt)
449                 return 0;
451         /* are we "faking" in place editing ? */
452         if (src == buf->buf)
453                 to_free = strbuf_detach(buf, NULL);
454         hash_sha1_file(src, len, "blob", sha1);
456         strbuf_grow(buf, len + cnt * 43);
457         for (;;) {
458                 /* step 1: run to the next '$' */
459                 dollar = memchr(src, '$', len);
460                 if (!dollar)
461                         break;
462                 strbuf_add(buf, src, dollar + 1 - src);
463                 len -= dollar + 1 - src;
464                 src  = dollar + 1;
466                 /* step 2: does it looks like a bit like Id:xxx$ or Id$ ? */
467                 if (len < 3 || memcmp("Id", src, 2))
468                         continue;
470                 /* step 3: skip over Id$ or Id:xxxxx$ */
471                 if (src[2] == '$') {
472                         src += 3;
473                         len -= 3;
474                 } else if (src[2] == ':') {
475                         /*
476                          * It's possible that an expanded Id has crept its way into the
477                          * repository, we cope with that by stripping the expansion out
478                          */
479                         dollar = memchr(src + 3, '$', len - 3);
480                         if (!dollar) {
481                                 /* incomplete keyword, no more '$', so just quit the loop */
482                                 break;
483                         }
485                         len -= dollar + 1 - src;
486                         src  = dollar + 1;
487                 } else {
488                         /* it wasn't a "Id$" or "Id:xxxx$" */
489                         continue;
490                 }
492                 /* step 4: substitute */
493                 strbuf_addstr(buf, "Id: ");
494                 strbuf_add(buf, sha1_to_hex(sha1), 40);
495                 strbuf_addstr(buf, " $");
496         }
497         strbuf_add(buf, src, len);
499         free(to_free);
500         return 1;
503 static int git_path_check_crlf(const char *path, struct git_attr_check *check)
505         const char *value = check->value;
507         if (ATTR_TRUE(value))
508                 return CRLF_TEXT;
509         else if (ATTR_FALSE(value))
510                 return CRLF_BINARY;
511         else if (ATTR_UNSET(value))
512                 ;
513         else if (!strcmp(value, "input"))
514                 return CRLF_INPUT;
515         return CRLF_GUESS;
518 static struct convert_driver *git_path_check_convert(const char *path,
519                                              struct git_attr_check *check)
521         const char *value = check->value;
522         struct convert_driver *drv;
524         if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value))
525                 return NULL;
526         for (drv = user_convert; drv; drv = drv->next)
527                 if (!strcmp(value, drv->name))
528                         return drv;
529         return NULL;
532 static int git_path_check_ident(const char *path, struct git_attr_check *check)
534         const char *value = check->value;
536         return !!ATTR_TRUE(value);
539 int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst)
541         struct git_attr_check check[3];
542         int crlf = CRLF_GUESS;
543         int ident = 0, ret = 0;
544         char *filter = NULL;
546         setup_convert_check(check);
547         if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
548                 struct convert_driver *drv;
549                 crlf = git_path_check_crlf(path, check + 0);
550                 ident = git_path_check_ident(path, check + 1);
551                 drv = git_path_check_convert(path, check + 2);
552                 if (drv && drv->clean)
553                         filter = drv->clean;
554         }
556         ret |= apply_filter(path, src, len, dst, filter);
557         if (ret) {
558                 src = dst->buf;
559                 len = dst->len;
560         }
561         ret |= crlf_to_git(path, src, len, dst, crlf);
562         if (ret) {
563                 src = dst->buf;
564                 len = dst->len;
565         }
566         return ret | ident_to_git(path, src, len, dst, ident);
569 int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
571         struct git_attr_check check[3];
572         int crlf = CRLF_GUESS;
573         int ident = 0, ret = 0;
574         char *filter = NULL;
576         setup_convert_check(check);
577         if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
578                 struct convert_driver *drv;
579                 crlf = git_path_check_crlf(path, check + 0);
580                 ident = git_path_check_ident(path, check + 1);
581                 drv = git_path_check_convert(path, check + 2);
582                 if (drv && drv->smudge)
583                         filter = drv->smudge;
584         }
586         ret |= ident_to_worktree(path, src, len, dst, ident);
587         if (ret) {
588                 src = dst->buf;
589                 len = dst->len;
590         }
591         ret |= crlf_to_worktree(path, src, len, dst, crlf);
592         if (ret) {
593                 src = dst->buf;
594                 len = dst->len;
595         }
596         return ret | apply_filter(path, src, len, dst, filter);