Raw
1 /*
2 * The process provider of the hunk provider interface: consult a
3 * long-running external process via the pkt-line protocol for the
4 * hunks of a blob pair. The process answers from the pair's object
5 * names alone: it can serve a persistent cache keyed on the pair, or
6 * fetch the blobs from the repository itself (e.g. via "git cat-file
7 * --batch") and compute its own notion of which lines changed. The
8 * provider sits at the head of its repository's chain and gates
9 * itself per request; its state is the repository's pool of running
10 * processes, one per configured command, stopped when the provider
11 * is released.
12 *
13 * Protocol: pkt-line over stdin/stdout, following the pattern of
14 * the long-running filter process protocol (see convert.c).
15 *
16 * Handshake:
17 * git> git-diff-client / version=1 / flush
18 * process< git-diff-server / version=1 / flush
19 * git> capability=hunks-by-oid / flush
20 * process< capability=hunks-by-oid / flush
21 *
22 * Per-pair, when both sides are stored blobs:
23 * git> command=hunks-by-oid / pathname=<path>
24 * git> old-oid=<hex> / new-oid=<hex> / flush
25 * process< hunk <old_start> <old_count> <new_start> <new_count>
26 * process< ... / flush
27 * process< status=success / flush
28 *
29 * No content is sent. Because Git holds no content for the exchange,
30 * the answer is used as the process sent it: the hunks are not re-run
31 * through xdiff's compaction, and a status=success response with zero
32 * hunks asserts that the blobs are equivalent, including their
33 * trailing newlines. A process that cannot answer from the object names
34 * (or cannot rule out a trailing-newline-only difference) responds
35 * status=need-content; the pair then gets the builtin answer, served
36 * from the diff-hunks store or computed. A later
37 * protocol extension can define a content-carrying request for such
38 * processes and for sides that are not stored blobs.
39 */
40
41 #include "git-compat-util.h"
42 #include "diff.h"
43 #include "diff-provider-internal.h"
44 #include "gettext.h"
45 #include "hex.h"
46 #include "odb.h"
47 #include "repository.h"
48 #include "sigchain.h"
49 #include "userdiff.h"
50 #include "sub-process.h"
51 #include "pkt-line.h"
52 #include "strbuf.h"
53
54 #define CAP_OID_HUNKS (1u << 0)
55
56 /*
57 * The provider's state: the repository's diff processes, keyed by
58 * their command string, so drivers that configure the same command
59 * share one process. An entry whose process failed stays in the
60 * pool with the failed bit set, so the command is not retried while
61 * the entry lives; the pool and its entries last until the provider
62 * is released.
63 */
64 struct diff_process_state {
65 struct hashmap subprocesses;
66 };
67
68 struct diff_subprocess {
69 struct subprocess_entry subprocess;
70 /*
71 * Owns the string subprocess.cmd and the hashmap key borrow: the
72 * entry outlives the userdiff config a re-read may replace.
73 */
74 char *cmd;
75 unsigned int supported_capabilities;
76 unsigned failed : 1;
77 };
78
79 static int start_diff_process_fn(struct subprocess_entry *subprocess)
80 {
81 static int versions[] = { 1, 0 };
82 static struct subprocess_capability capabilities[] = {
83 { "hunks-by-oid", CAP_OID_HUNKS },
84 { NULL, 0 }
85 };
86 struct diff_subprocess *entry =
87 container_of(subprocess, struct diff_subprocess, subprocess);
88
89 return subprocess_handshake(subprocess, "git-diff",
90 versions, NULL,
91 capabilities,
92 &entry->supported_capabilities);
93 }
94
95 /*
96 * The pool entry for a command, or NULL when its process fails to
97 * start here: the failure leaves a failed entry in the pool, so only
98 * the request that observed it maps it to an error and later
99 * requests pass the provider by.
100 */
101 static struct diff_subprocess *get_or_launch_process(
102 struct diff_process_state *state,
103 struct userdiff_driver *drv)
104 {
105 struct subprocess_entry *running;
106 struct diff_subprocess *entry;
107
108 running = subprocess_find_entry(&state->subprocesses, drv->process);
109 if (running) {
110 entry = container_of(running, struct diff_subprocess,
111 subprocess);
112 return entry->failed ? NULL : entry;
113 }
114
115 entry = xcalloc(1, sizeof(*entry));
116 entry->cmd = xstrdup(drv->process);
117 if (subprocess_start_command(&entry->subprocess, entry->cmd,
118 start_diff_process_fn))
119 entry->failed = 1;
120 hashmap_entry_init(&entry->subprocess.ent, strhash(entry->cmd));
121 hashmap_add(&state->subprocesses, &entry->subprocess.ent);
122 if (entry->failed) {
123 warning(_("diff process '%s' failed to start;"
124 " using the builtin diff"), drv->process);
125 return NULL;
126 }
127 return entry;
128 }
129
130 /*
131 * A hunk in the diff process's presentation coordinates: the line
132 * numbering it reports over the protocol. Kept distinct from struct
133 * xdl_hunk (xdiff's coordinates) so that only translated hunks ever
134 * reach a consumer; diff_process_hunk_to_xdl() is the single
135 * crossing point.
136 */
137 struct diff_process_hunk {
138 long old_start, old_count;
139 long new_start, new_count;
140 };
141
142 /*
143 * Parse one non-negative decimal field of a hunk line into *out and
144 * advance *line past it. Fields must be plain decimal with no leading
145 * whitespace or sign (isdigit() takes an unsigned char to stay defined
146 * for high-bit bytes). The first three fields are followed by a single
147 * space; the last (is_last) is followed by end-of-string or a space.
148 * Trailing space-separated tokens after the last field are allowed and
149 * ignored, so a future protocol version can append fields (e.g. a
150 * "moved" marker) without an older Git rejecting the line, mirroring
151 * the request-side rule that processes ignore unknown keys.
152 *
153 * A value that overflows strtol() is not a parse failure: the line is
154 * well-formed, so the stream stays in protocol sync. It is reported
155 * through *out_of_range, and the caller skips the pair the same way
156 * it skips any other out-of-range coordinate.
157 */
158 static int parse_hunk_field(const char **line, long *out, int is_last,
159 int *out_of_range)
160 {
161 const char *p = *line;
162 char *end;
163
164 if (!isdigit((unsigned char)*p))
165 return -1;
166 errno = 0;
167 *out = strtol(p, &end, 10);
168 if (end == p)
169 return -1;
170 if (errno == ERANGE)
171 *out_of_range = 1;
172 else if (errno)
173 return -1;
174 if (is_last) {
175 if (*end != '\0' && *end != ' ')
176 return -1;
177 } else {
178 if (*end != ' ')
179 return -1;
180 end++;
181 }
182 *line = end;
183 return 0;
184 }
185
186 static int parse_hunk_line(const char *line,
187 struct diff_process_hunk *presented,
188 int *out_of_range)
189 {
190 *out_of_range = 0;
191 /* Format: "hunk <old_start> <old_count> <new_start> <new_count>" */
192 if (!skip_prefix(line, "hunk ", &line))
193 return -1;
194 if (parse_hunk_field(&line, &presented->old_start, 0, out_of_range) ||
195 parse_hunk_field(&line, &presented->old_count, 0, out_of_range) ||
196 parse_hunk_field(&line, &presented->new_start, 0, out_of_range) ||
197 parse_hunk_field(&line, &presented->new_count, 1, out_of_range))
198 return -1;
199 return 0;
200 }
201
202 /*
203 * Translate a hunk from the diff process's presentation coordinates
204 * into xdiff's.
205 *
206 * Protocol starts are already 1-based positions (the line a change
207 * sits before), the same numbering xdiff uses, so the only adjustment
208 * is for an empty file side: "git diff" addresses it with a start of 0
209 * and a count of 0 (e.g. "0 0 1 5" adds five lines to an empty old
210 * side), and since xdiff uses start-1 as an array index that 0 becomes
211 * 1 here. This is NOT the full inverse of xdl_emit_hunk_hdr()
212 * (xdiff/xutils.c): that emitter shifts a count-0 range to start-1 for
213 * the displayed "@@" header, but the protocol keeps the unshifted
214 * 1-based position for a mid-file insert or delete. This is the single
215 * point where presentation coordinates become xdiff coordinates, so
216 * any consumer of these coordinates may assume 1-based starts.
217 *
218 * Returns -1 for a start of 0 paired with a nonzero count, which names
219 * no line in either coordinate system. (parse_hunk_line() already
220 * guarantees non-negative starts and counts.)
221 */
222 static int diff_process_hunk_to_xdl(const struct diff_process_hunk *presented,
223 struct xdl_hunk *xdl)
224 {
225 long old_start = presented->old_start;
226 long new_start = presented->new_start;
227
228 if ((!old_start && presented->old_count) ||
229 (!new_start && presented->new_count))
230 return -1;
231 if (!old_start)
232 old_start = 1;
233 if (!new_start)
234 new_start = 1;
235
236 xdl->old_start = old_start;
237 xdl->old_count = presented->old_count;
238 xdl->new_start = new_start;
239 xdl->new_count = presented->new_count;
240 return 0;
241 }
242
243 /*
244 * Validate the process's hunks (already in xdiff coordinates) before they
245 * bypass the diff algorithm. The content-independent rules (in-order,
246 * non-overlapping, lockstep-aligned, int32-bounded coordinates) are the
247 * provider interface's shared rule, diff_provider_check_hunk(); this
248 * function adds the two checks that need the blobs' line counts (a hunk
249 * past the end of a file, the run after the last hunk) and the
250 * per-rule diagnostics naming the process. On a bad response we warn
251 * and the caller falls back to the builtin diff. Returns 0 if valid,
252 * -1 (after warning) otherwise.
253 *
254 * old_lines/new_lines bound the line count of each side, or are
255 * negative when no bound is known. An oid-only answer arrives without
256 * content, so its caller passes upper bounds derived from the blobs'
257 * byte sizes, which caps coordinate magnitude but cannot support the
258 * run-after-the-last-hunk check: that one compares exact line counts,
259 * so it runs only when lines_exact is set, which no caller does today.
260 * It is kept for a content-carrying request, whose loaded buffers
261 * would provide exact counts.
262 */
263 static int validate_external_hunks(const struct xdl_hunk *hunks, size_t nr,
264 long old_lines, long new_lines,
265 int lines_exact,
266 const char *process, const char *path)
267 {
268 struct diff_provider_hunks_check c = { 0 };
269 size_t i;
270
271 for (i = 0; i < nr; i++) {
272 const struct xdl_hunk *h = &hunks[i];
273
274 if (old_lines >= 0 &&
275 (h->old_count > old_lines - h->old_start + 1 ||
276 h->new_count > new_lines - h->new_start + 1)) {
277 warning(_("diff process '%s' returned a hunk past the "
278 "end of '%s'; using the builtin diff"),
279 process, path);
280 return -1;
281 }
282 switch (diff_provider_check_hunk(&c, h->old_start,
283 h->old_count, h->new_start,
284 h->new_count)) {
285 case DIFF_PROVIDER_HUNKS_OK:
286 break;
287 case DIFF_PROVIDER_HUNKS_RANGE:
288 warning(_("diff process '%s' returned out-of-range "
289 "coordinates for '%s'; using the builtin diff"),
290 process, path);
291 return -1;
292 case DIFF_PROVIDER_HUNKS_OVERLAP:
293 warning(_("diff process '%s' returned overlapping hunks "
294 "for '%s'; using the builtin diff"),
295 process, path);
296 return -1;
297 case DIFF_PROVIDER_HUNKS_MISALIGNED:
298 warning(_("diff process '%s' returned hunks that leave "
299 "'%s' misaligned; using the builtin diff"),
300 process, path);
301 return -1;
302 }
303 }
304 if (lines_exact &&
305 old_lines - c.prev_old_end != new_lines - c.prev_new_end) {
306 warning(_("diff process '%s' returned hunks that leave '%s' "
307 "misaligned; using the builtin diff"),
308 process, path);
309 return -1;
310 }
311 return 0;
312 }
313
314 /*
315 * The most lines a blob can hold, from its size alone: every line,
316 * even an empty one, costs at least one byte, so a blob of N bytes
317 * holds at most N lines. Returns -1 when the size is unavailable,
318 * leaving the response bounded only by the shared int32 rule. A size
319 * beyond INT32_MAX clamps to it, which loses nothing: a coordinate
320 * that large fails the shared rule anyway. In a partial clone the
321 * size lookup must not fetch the blob from the promisor remote:
322 * validating an answer that exists to avoid loading content must not
323 * itself download that content, so a missing blob reads as size
324 * unavailable instead.
325 */
326 static long blob_line_cap(struct repository *r, const struct object_id *oid)
327 {
328 unsigned long size;
329 struct object_info oi = OBJECT_INFO_INIT;
330
331 oi.sizep = &size;
332 if (odb_read_object_info_extended(r->objects, oid, &oi,
333 OBJECT_INFO_SKIP_FETCH_OBJECT) < 0)
334 return -1;
335 if (size > INT32_MAX)
336 return INT32_MAX;
337 return (long)size;
338 }
339
340 /*
341 * The driver whose process a consultation for path would ask, or NULL
342 * when none applies (no driver, process not allowed, or xpp carries
343 * options the process is never told about). Needs no content, so
344 * the driver is picked before any blob is loaded.
345 */
346 static struct userdiff_driver *diff_process_driver(struct diff_options *diffopt,
347 const char *path,
348 const xpparam_t *xpp)
349 {
350 struct userdiff_driver *drv;
351
352 if (!diffopt || !path)
353 return NULL;
354 if (!diffopt->flags.allow_diff_process || diffopt->ignore_driver_algorithm)
355 return NULL;
356 /*
357 * Whitespace-ignoring, regex-ignore (-I) and anchored options
358 * change which lines count as different, but the process is never
359 * told about them, so its hunks could not honor them. A forced
360 * diff algorithm (an option or configured algorithm setting)
361 * requests a specific builtin computation, which an
362 * authoritative answer would override. Rather than silently
363 * override the user's request, fall back to the builtin diff,
364 * which does honor these flags. Key this off xpp (the
365 * parameters this diff actually runs with) rather than diffopt,
366 * so a caller like blame, which keeps its algorithm and
367 * whitespace flags outside diffopt, is covered without a
368 * separate guard of its own.
369 */
370 if ((xpp->flags & (XDF_WHITESPACE_FLAGS | XDF_IGNORE_BLANK_LINES |
371 XDF_DIFF_ALGORITHM_MASK)) ||
372 xpp->ignore_regex_nr || xpp->anchors_nr)
373 return NULL;
374
375 /*
376 * A path the protocol cannot carry never selects a process: an
377 * embedded newline would let the rest of the path forge further
378 * request keys, and the pathname must fit one packet. Passing
379 * here keeps the cost local to the path; a failed write would
380 * instead cost the whole command its process.
381 */
382 if (strchr(path, '\n') ||
383 strlen(path) > LARGE_PACKET_DATA_MAX - strlen("pathname=\n"))
384 return NULL;
385
386 drv = userdiff_find_by_path(diffopt->repo->index, path);
387 if (!drv || !drv->process)
388 return NULL;
389 return drv;
390 }
391
392 /*
393 * Without content there is no size-derived bound on a response, so cap
394 * accumulation at a constant instead. A response that exceeds the
395 * cap is a protocol error: the process is disabled for the rest of
396 * the command and the caller falls back to the builtin diff.
397 */
398 #define OID_HUNKS_MAX (1 << 20)
399
400 enum diff_process_result {
401 DIFF_PROCESS_ERROR = -1, /* failed; caller falls back to builtin */
402 DIFF_PROCESS_OK = 0, /* the process supplied hunks */
403 DIFF_PROCESS_SKIP, /* process did not apply: use builtin */
404 DIFF_PROCESS_EQUIVALENT, /* process says files are equivalent */
405 };
406
407 /*
408 * Ask drv's diff process to answer the request from the blob pair's
409 * object ids alone (the "hunks-by-oid" capability): no content is
410 * loaded or sent. On DIFF_PROCESS_OK the process's hunks are emitted
411 * through hunk_cb in 0-based emission coordinates, validated for order,
412 * overlap, and lockstep alignment first; because Git holds no content,
413 * the answer is used as the process sent it, without xdiff's compaction.
414 * DIFF_PROCESS_EQUIVALENT means the process asserts the pair equal.
415 * DIFF_PROCESS_SKIP covers everything that should fall through to the
416 * builtin computation: a missing capability, a missing object id, a
417 * status=need-content answer, or an invalid response.
418 */
419 static enum diff_process_result diff_process_query_hunks(
420 struct diff_process_state *state,
421 struct userdiff_driver *drv,
422 const struct diff_provider_request *req,
423 xdl_emit_hunk_consume_func_t hunk_cb,
424 void *cb_data)
425 {
426 const char *path = req->path;
427 struct diff_subprocess *entry;
428 struct child_process *process;
429 int fd_in, fd_out;
430 struct packet_reader reader;
431 struct strbuf status = STRBUF_INIT;
432 struct xdl_hunk *hunks = NULL;
433 struct diff_process_hunk presented;
434 struct xdl_hunk hunk;
435 size_t nr_hunks = 0, alloc_hunks = 0, i;
436 int bad_coords = 0;
437 long old_cap, new_cap;
438 enum diff_process_result res;
439
440 if (!req->old_oid || !req->new_oid)
441 return DIFF_PROCESS_SKIP;
442
443 entry = get_or_launch_process(state, drv);
444 if (!entry)
445 return DIFF_PROCESS_ERROR;
446 if (!(entry->supported_capabilities & CAP_OID_HUNKS))
447 return DIFF_PROCESS_SKIP;
448
449 process = subprocess_get_child_process(&entry->subprocess);
450 fd_in = process->in;
451 fd_out = process->out;
452
453 sigchain_push(SIGPIPE, SIG_IGN);
454
455 if (packet_write_fmt_gently(fd_in, "command=hunks-by-oid\n") ||
456 packet_write_fmt_gently(fd_in, "pathname=%s\n", path) ||
457 packet_write_fmt_gently(fd_in, "old-oid=%s\n",
458 oid_to_hex(req->old_oid)) ||
459 packet_write_fmt_gently(fd_in, "new-oid=%s\n",
460 oid_to_hex(req->new_oid)) ||
461 packet_flush_gently(fd_in))
462 goto comm_error;
463
464 packet_reader_init(&reader, fd_out, NULL, 0,
465 PACKET_READ_CHOMP_NEWLINE |
466 PACKET_READ_GENTLE_ON_EOF |
467 PACKET_READ_GENTLE_ON_READ_ERROR);
468 for (;;) {
469 enum packet_read_status rs = packet_reader_read(&reader);
470 int out_of_range;
471
472 if (rs == PACKET_READ_FLUSH)
473 break;
474 /*
475 * Only a hunk line may precede the flush. EOF and a
476 * malformed frame end the session; an empty packet, which
477 * a length-only read cannot tell from a flush, would
478 * truncate the hunk section here and leave the status
479 * section to poison the next request, so it is a protocol
480 * error too.
481 */
482 if (rs != PACKET_READ_NORMAL || !reader.pktlen)
483 goto comm_error;
484 if (parse_hunk_line(reader.line, &presented,
485 &out_of_range) < 0)
486 goto comm_error;
487 if (bad_coords)
488 continue;
489 if (out_of_range ||
490 diff_process_hunk_to_xdl(&presented, &hunk) < 0) {
491 /*
492 * Semantically invalid coordinates in a well-formed
493 * response: the stream stays in protocol sync, so
494 * drain the rest and fall back for this file while
495 * keeping the process alive, the same treatment
496 * validate_external_hunks() failures receive.
497 */
498 bad_coords = 1;
499 continue;
500 }
501 if (nr_hunks >= OID_HUNKS_MAX) {
502 warning(_("diff process '%s' sent too many hunks"
503 " for '%s'; disabling it for the"
504 " remainder of this command"),
505 drv->process, path);
506 goto disable;
507 }
508 ALLOC_GROW(hunks, nr_hunks + 1, alloc_hunks);
509 hunks[nr_hunks++] = hunk;
510 }
511
512 if (subprocess_read_status_gently(fd_out, &status))
513 goto comm_error;
514
515 if (!strcmp(status.buf, "success")) {
516 if (bad_coords) {
517 warning(_("diff process '%s' returned out-of-range "
518 "coordinates for '%s'; using the builtin diff"),
519 drv->process, path);
520 res = DIFF_PROCESS_SKIP;
521 goto out;
522 }
523 if (!nr_hunks) {
524 res = DIFF_PROCESS_EQUIVALENT;
525 goto out;
526 }
527 /*
528 * Bound the coordinates by the blobs' sizes, read from the
529 * object database without loading content. Either both
530 * bounds hold or neither is applied: a partial bound would
531 * misclassify a response that the other side's size would
532 * have caught.
533 */
534 old_cap = blob_line_cap(req->repo, req->old_oid);
535 new_cap = blob_line_cap(req->repo, req->new_oid);
536 if (old_cap < 0 || new_cap < 0)
537 old_cap = new_cap = -1;
538 if (validate_external_hunks(hunks, nr_hunks, old_cap, new_cap,
539 0, drv->process, path) < 0) {
540 res = DIFF_PROCESS_SKIP;
541 goto out;
542 }
543 /*
544 * Replay in the coordinates a hunk consumer receives from
545 * xdiff's emission: 0-based starts. The answer is used as
546 * the process sent it; with no content in hand it cannot be
547 * re-run through xdiff's compaction.
548 */
549 for (i = 0; i < nr_hunks; i++)
550 hunk_cb(hunks[i].old_start - 1, hunks[i].old_count,
551 hunks[i].new_start - 1, hunks[i].new_count,
552 cb_data);
553 res = DIFF_PROCESS_OK;
554 goto out;
555 }
556 if (!strcmp(status.buf, "need-content")) {
557 /*
558 * The process cannot answer this pair from its object names;
559 * the caller computes the diff itself.
560 */
561 res = DIFF_PROCESS_SKIP;
562 goto out;
563 }
564 if (!strcmp(status.buf, "abort")) {
565 /* The process withdrew: stop asking it for this session. */
566 entry->supported_capabilities &= ~CAP_OID_HUNKS;
567 res = DIFF_PROCESS_SKIP;
568 goto out;
569 }
570 /*
571 * An unrecognized status is a protocol error, not a per-pair
572 * failure: this Git did not request anything it does not know,
573 * so the process is answering some other protocol, and asking
574 * it again would warn on every pair of the traversal.
575 */
576 warning(_("diff process '%s' sent unrecognized status '%s' for "
577 "'%s'; disabling it for the remainder of this command"),
578 drv->process, status.buf, path);
579 goto disable;
580 out:
581 free(hunks);
582 strbuf_release(&status);
583 sigchain_pop(SIGPIPE);
584 return res;
585
586 comm_error:
587 warning(_("diff process '%s' failed for '%s'; disabling it"
588 " for the remainder of this command"),
589 drv->process, path);
590 disable:
591 subprocess_stop_command(&entry->subprocess);
592 entry->failed = 1;
593 free(hunks);
594 strbuf_release(&status);
595 sigchain_pop(SIGPIPE);
596 return DIFF_PROCESS_ERROR;
597 }
598
599 /*
600 * The process outranks every later provider through its chain
601 * position: when it answers, the walk ends, so no later provider
602 * serves the pair, and an answered pair is never recorded. When it
603 * does not answer (it defers with need-content, lacks the
604 * capability, or failed), the caller computes the builtin diff for
605 * that pair. The store holds builtin results and nothing else, so
606 * an identity answer for such a pair equals what the caller would
607 * compute. Every non-answer is therefore a pass: a refusal would
608 * suppress that equal answer, and would keep a warming run from
609 * recording the builtin result the caller computes anyway.
610 */
611 static enum diff_provider_disposition
612 diff_process_consult(struct diff_provider *provider,
613 const struct diff_provider_request *req,
614 diff_provider_fill_fn fill UNUSED, void *fill_data UNUSED,
615 xdl_emit_hunk_consume_func_t hunk_cb, void *cb_data)
616 {
617 struct diff_process_state *state = provider->state;
618 struct userdiff_driver *drv;
619 struct subprocess_entry *running;
620
621 drv = diff_process_driver(req->diffopt, req->path, req->xpp);
622 if (!drv)
623 return DIFF_PROVIDER_DISP_PASS;
624 running = subprocess_find_entry(&state->subprocesses, drv->process);
625 if (running && container_of(running, struct diff_subprocess,
626 subprocess)->failed)
627 return DIFF_PROVIDER_DISP_PASS;
628
629 switch (diff_process_query_hunks(state, drv, req,
630 hunk_cb, cb_data)) {
631 case DIFF_PROCESS_OK:
632 case DIFF_PROCESS_EQUIVALENT:
633 return DIFF_PROVIDER_DISP_ANSWERED;
634 case DIFF_PROCESS_SKIP:
635 case DIFF_PROCESS_ERROR:
636 break;
637 }
638 return DIFF_PROVIDER_DISP_PASS;
639 }
640
641 static void diff_process_release(struct diff_provider *provider)
642 {
643 struct diff_process_state *state = provider->state;
644 struct hashmap_iter iter;
645 struct diff_subprocess *entry;
646
647 /* A failed entry's process is already stopped or never ran. */
648 hashmap_for_each_entry(&state->subprocesses, &iter, entry,
649 subprocess.ent) {
650 if (!entry->failed)
651 subprocess_stop_command(&entry->subprocess);
652 free(entry->cmd);
653 }
654 hashmap_clear_and_free(&state->subprocesses,
655 struct diff_subprocess, subprocess.ent);
656 free(state);
657 }
658
659 struct diff_provider *diff_process_provider_new(void)
660 {
661 struct diff_process_state *state = xcalloc(1, sizeof(*state));
662 struct diff_provider *p = xcalloc(1, sizeof(*p));
663
664 hashmap_init(&state->subprocesses, cmd2process_cmp, NULL, 0);
665 p->consult = diff_process_consult;
666 p->release = diff_process_release;
667 p->state = state;
668 return p;
669 }