@@ -201,17 +201,212 @@ static char *crlf_to_worktree(const char *path, const char *src, unsigned long *
201201 return buffer ;
202202}
203203
204+ static int filter_buffer (const char * path , const char * src ,
205+ unsigned long size , const char * cmd )
206+ {
207+ /*
208+ * Spawn cmd and feed the buffer contents through its stdin.
209+ */
210+ struct child_process child_process ;
211+ int pipe_feed [2 ];
212+ int write_err , status ;
213+
214+ memset (& child_process , 0 , sizeof (child_process ));
215+
216+ if (pipe (pipe_feed ) < 0 ) {
217+ error ("cannot create pipe to run external filter %s" , cmd );
218+ return 1 ;
219+ }
220+
221+ child_process .pid = fork ();
222+ if (child_process .pid < 0 ) {
223+ error ("cannot fork to run external filter %s" , cmd );
224+ close (pipe_feed [0 ]);
225+ close (pipe_feed [1 ]);
226+ return 1 ;
227+ }
228+ if (!child_process .pid ) {
229+ dup2 (pipe_feed [0 ], 0 );
230+ close (pipe_feed [0 ]);
231+ close (pipe_feed [1 ]);
232+ execlp ("sh" , "sh" , "-c" , cmd , NULL );
233+ return 1 ;
234+ }
235+ close (pipe_feed [0 ]);
236+
237+ write_err = (write_in_full (pipe_feed [1 ], src , size ) < 0 );
238+ if (close (pipe_feed [1 ]))
239+ write_err = 1 ;
240+ if (write_err )
241+ error ("cannot feed the input to external filter %s" , cmd );
242+
243+ status = finish_command (& child_process );
244+ if (status )
245+ error ("external filter %s failed %d" , cmd , - status );
246+ return (write_err || status );
247+ }
248+
249+ static char * apply_filter (const char * path , const char * src ,
250+ unsigned long * sizep , const char * cmd )
251+ {
252+ /*
253+ * Create a pipeline to have the command filter the buffer's
254+ * contents.
255+ *
256+ * (child --> cmd) --> us
257+ */
258+ const int SLOP = 4096 ;
259+ int pipe_feed [2 ];
260+ int status ;
261+ char * dst ;
262+ unsigned long dstsize , dstalloc ;
263+ struct child_process child_process ;
264+
265+ if (!cmd )
266+ return NULL ;
267+
268+ memset (& child_process , 0 , sizeof (child_process ));
269+
270+ if (pipe (pipe_feed ) < 0 ) {
271+ error ("cannot create pipe to run external filter %s" , cmd );
272+ return NULL ;
273+ }
274+
275+ fflush (NULL );
276+ child_process .pid = fork ();
277+ if (child_process .pid < 0 ) {
278+ error ("cannot fork to run external filter %s" , cmd );
279+ close (pipe_feed [0 ]);
280+ close (pipe_feed [1 ]);
281+ return NULL ;
282+ }
283+ if (!child_process .pid ) {
284+ dup2 (pipe_feed [1 ], 1 );
285+ close (pipe_feed [0 ]);
286+ close (pipe_feed [1 ]);
287+ exit (filter_buffer (path , src , * sizep , cmd ));
288+ }
289+ close (pipe_feed [1 ]);
290+
291+ dstalloc = * sizep ;
292+ dst = xmalloc (dstalloc );
293+ dstsize = 0 ;
294+
295+ while (1 ) {
296+ ssize_t numread = xread (pipe_feed [0 ], dst + dstsize ,
297+ dstalloc - dstsize );
298+
299+ if (numread <= 0 ) {
300+ if (!numread )
301+ break ;
302+ error ("read from external filter %s failed" , cmd );
303+ free (dst );
304+ dst = NULL ;
305+ break ;
306+ }
307+ dstsize += numread ;
308+ if (dstalloc <= dstsize + SLOP ) {
309+ dstalloc = dstsize + SLOP ;
310+ dst = xrealloc (dst , dstalloc );
311+ }
312+ }
313+ if (close (pipe_feed [0 ])) {
314+ error ("read from external filter %s failed" , cmd );
315+ free (dst );
316+ dst = NULL ;
317+ }
318+
319+ status = finish_command (& child_process );
320+ if (status ) {
321+ error ("external filter %s failed %d" , cmd , - status );
322+ free (dst );
323+ dst = NULL ;
324+ }
325+
326+ if (dst )
327+ * sizep = dstsize ;
328+ return dst ;
329+ }
330+
331+ static struct convert_driver {
332+ const char * name ;
333+ struct convert_driver * next ;
334+ char * smudge ;
335+ char * clean ;
336+ } * user_convert , * * user_convert_tail ;
337+
338+ static int read_convert_config (const char * var , const char * value )
339+ {
340+ const char * ep , * name ;
341+ int namelen ;
342+ struct convert_driver * drv ;
343+
344+ /*
345+ * External conversion drivers are configured using
346+ * "filter.<name>.variable".
347+ */
348+ if (prefixcmp (var , "filter." ) || (ep = strrchr (var , '.' )) == var + 6 )
349+ return 0 ;
350+ name = var + 7 ;
351+ namelen = ep - name ;
352+ for (drv = user_convert ; drv ; drv = drv -> next )
353+ if (!strncmp (drv -> name , name , namelen ) && !drv -> name [namelen ])
354+ break ;
355+ if (!drv ) {
356+ char * namebuf ;
357+ drv = xcalloc (1 , sizeof (struct convert_driver ));
358+ namebuf = xmalloc (namelen + 1 );
359+ memcpy (namebuf , name , namelen );
360+ namebuf [namelen ] = 0 ;
361+ drv -> name = namebuf ;
362+ drv -> next = NULL ;
363+ * user_convert_tail = drv ;
364+ user_convert_tail = & (drv -> next );
365+ }
366+
367+ ep ++ ;
368+
369+ /*
370+ * filter.<name>.smudge and filter.<name>.clean specifies
371+ * the command line:
372+ *
373+ * command-line
374+ *
375+ * The command-line will not be interpolated in any way.
376+ */
377+
378+ if (!strcmp ("smudge" , ep )) {
379+ if (!value )
380+ return error ("%s: lacks value" , var );
381+ drv -> smudge = strdup (value );
382+ return 0 ;
383+ }
384+
385+ if (!strcmp ("clean" , ep )) {
386+ if (!value )
387+ return error ("%s: lacks value" , var );
388+ drv -> clean = strdup (value );
389+ return 0 ;
390+ }
391+ return 0 ;
392+ }
393+
204394static void setup_convert_check (struct git_attr_check * check )
205395{
206396 static struct git_attr * attr_crlf ;
207397 static struct git_attr * attr_ident ;
398+ static struct git_attr * attr_filter ;
208399
209400 if (!attr_crlf ) {
210401 attr_crlf = git_attr ("crlf" , 4 );
211402 attr_ident = git_attr ("ident" , 5 );
403+ attr_filter = git_attr ("filter" , 6 );
404+ user_convert_tail = & user_convert ;
405+ git_config (read_convert_config );
212406 }
213407 check [0 ].attr = attr_crlf ;
214408 check [1 ].attr = attr_ident ;
409+ check [2 ].attr = attr_filter ;
215410}
216411
217412static int count_ident (const char * cp , unsigned long size )
@@ -367,6 +562,20 @@ static int git_path_check_crlf(const char *path, struct git_attr_check *check)
367562 return CRLF_GUESS ;
368563}
369564
565+ static struct convert_driver * git_path_check_convert (const char * path ,
566+ struct git_attr_check * check )
567+ {
568+ const char * value = check -> value ;
569+ struct convert_driver * drv ;
570+
571+ if (ATTR_TRUE (value ) || ATTR_FALSE (value ) || ATTR_UNSET (value ))
572+ return NULL ;
573+ for (drv = user_convert ; drv ; drv = drv -> next )
574+ if (!strcmp (value , drv -> name ))
575+ return drv ;
576+ return NULL ;
577+ }
578+
370579static int git_path_check_ident (const char * path , struct git_attr_check * check )
371580{
372581 const char * value = check -> value ;
@@ -376,18 +585,29 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check)
376585
377586char * convert_to_git (const char * path , const char * src , unsigned long * sizep )
378587{
379- struct git_attr_check check [2 ];
588+ struct git_attr_check check [3 ];
380589 int crlf = CRLF_GUESS ;
381590 int ident = 0 ;
591+ char * filter = NULL ;
382592 char * buf , * buf2 ;
383593
384594 setup_convert_check (check );
385595 if (!git_checkattr (path , ARRAY_SIZE (check ), check )) {
596+ struct convert_driver * drv ;
386597 crlf = git_path_check_crlf (path , check + 0 );
387598 ident = git_path_check_ident (path , check + 1 );
599+ drv = git_path_check_convert (path , check + 2 );
600+ if (drv && drv -> clean )
601+ filter = drv -> clean ;
388602 }
389603
390- buf = crlf_to_git (path , src , sizep , crlf );
604+ buf = apply_filter (path , src , sizep , filter );
605+
606+ buf2 = crlf_to_git (path , buf ? buf : src , sizep , crlf );
607+ if (buf2 ) {
608+ free (buf );
609+ buf = buf2 ;
610+ }
391611
392612 buf2 = ident_to_git (path , buf ? buf : src , sizep , ident );
393613 if (buf2 ) {
@@ -400,15 +620,20 @@ char *convert_to_git(const char *path, const char *src, unsigned long *sizep)
400620
401621char * convert_to_working_tree (const char * path , const char * src , unsigned long * sizep )
402622{
403- struct git_attr_check check [2 ];
623+ struct git_attr_check check [3 ];
404624 int crlf = CRLF_GUESS ;
405625 int ident = 0 ;
626+ char * filter = NULL ;
406627 char * buf , * buf2 ;
407628
408629 setup_convert_check (check );
409630 if (!git_checkattr (path , ARRAY_SIZE (check ), check )) {
631+ struct convert_driver * drv ;
410632 crlf = git_path_check_crlf (path , check + 0 );
411633 ident = git_path_check_ident (path , check + 1 );
634+ drv = git_path_check_convert (path , check + 2 );
635+ if (drv && drv -> smudge )
636+ filter = drv -> smudge ;
412637 }
413638
414639 buf = ident_to_worktree (path , src , sizep , ident );
@@ -419,5 +644,11 @@ char *convert_to_working_tree(const char *path, const char *src, unsigned long *
419644 buf = buf2 ;
420645 }
421646
647+ buf2 = apply_filter (path , buf ? buf : src , sizep , filter );
648+ if (buf2 ) {
649+ free (buf );
650+ buf = buf2 ;
651+ }
652+
422653 return buf ;
423654}
0 commit comments