Bug Summary

File:tog/tog.c
Warning:line 4043, column 2
Potential leak of memory pointed to by 'wline'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd6.9 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name tog.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/local/lib/clang/11.1.0 -I /home/ben/Projects/got/tog/../include -I /home/ben/Projects/got/tog/../lib -D GOT_LIBEXECDIR=/home/ben/bin -D GOT_VERSION=0.53-current -internal-isystem /usr/local/lib/clang/11.1.0/include -internal-externc-isystem /usr/include -O0 -fdebug-compilation-dir /home/ben/Projects/got/tog/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/got/scan/2021-05-28-230913-68537-1 -x c /home/ben/Projects/got/tog/tog.c
1/*
2 * Copyright (c) 2018, 2019, 2020 Stefan Sperling <stsp@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <sys/queue.h>
18#include <sys/stat.h>
19#include <sys/ioctl.h>
20
21#include <ctype.h>
22#include <errno(*__errno()).h>
23#define _XOPEN_SOURCE_EXTENDED
24#include <curses.h>
25#undef _XOPEN_SOURCE_EXTENDED
26#include <panel.h>
27#include <locale.h>
28#include <signal.h>
29#include <stdlib.h>
30#include <stdarg.h>
31#include <stdio.h>
32#include <getopt.h>
33#include <string.h>
34#include <err.h>
35#include <unistd.h>
36#include <limits.h>
37#include <wchar.h>
38#include <time.h>
39#include <pthread.h>
40#include <libgen.h>
41#include <regex.h>
42
43#include "got_version.h"
44#include "got_error.h"
45#include "got_object.h"
46#include "got_reference.h"
47#include "got_repository.h"
48#include "got_diff.h"
49#include "got_opentemp.h"
50#include "got_utf8.h"
51#include "got_cancel.h"
52#include "got_commit_graph.h"
53#include "got_blame.h"
54#include "got_privsep.h"
55#include "got_path.h"
56#include "got_worktree.h"
57
58#ifndef MIN
59#define MIN(_a,_b)((_a) < (_b) ? (_a) : (_b)) ((_a) < (_b) ? (_a) : (_b))
60#endif
61
62#ifndef MAX
63#define MAX(_a,_b)((_a) > (_b) ? (_a) : (_b)) ((_a) > (_b) ? (_a) : (_b))
64#endif
65
66#define CTRL(x)((x) & 0x1f) ((x) & 0x1f)
67
68#ifndef nitems
69#define nitems(_a)(sizeof((_a)) / sizeof((_a)[0])) (sizeof((_a)) / sizeof((_a)[0]))
70#endif
71
72struct tog_cmd {
73 const char *name;
74 const struct got_error *(*cmd_main)(int, char *[]);
75 void (*cmd_usage)(void);
76};
77
78__dead__attribute__((__noreturn__)) static void usage(int, int);
79__dead__attribute__((__noreturn__)) static void usage_log(void);
80__dead__attribute__((__noreturn__)) static void usage_diff(void);
81__dead__attribute__((__noreturn__)) static void usage_blame(void);
82__dead__attribute__((__noreturn__)) static void usage_tree(void);
83__dead__attribute__((__noreturn__)) static void usage_ref(void);
84
85static const struct got_error* cmd_log(int, char *[]);
86static const struct got_error* cmd_diff(int, char *[]);
87static const struct got_error* cmd_blame(int, char *[]);
88static const struct got_error* cmd_tree(int, char *[]);
89static const struct got_error* cmd_ref(int, char *[]);
90
91static struct tog_cmd tog_commands[] = {
92 { "log", cmd_log, usage_log },
93 { "diff", cmd_diff, usage_diff },
94 { "blame", cmd_blame, usage_blame },
95 { "tree", cmd_tree, usage_tree },
96 { "ref", cmd_ref, usage_ref },
97};
98
99enum tog_view_type {
100 TOG_VIEW_DIFF,
101 TOG_VIEW_LOG,
102 TOG_VIEW_BLAME,
103 TOG_VIEW_TREE,
104 TOG_VIEW_REF,
105};
106
107#define TOG_EOF_STRING"(END)" "(END)"
108
109struct commit_queue_entry {
110 TAILQ_ENTRY(commit_queue_entry)struct { struct commit_queue_entry *tqe_next; struct commit_queue_entry
**tqe_prev; }
entry;
111 struct got_object_id *id;
112 struct got_commit_object *commit;
113 int idx;
114};
115TAILQ_HEAD(commit_queue_head, commit_queue_entry)struct commit_queue_head { struct commit_queue_entry *tqh_first
; struct commit_queue_entry **tqh_last; }
;
116struct commit_queue {
117 int ncommits;
118 struct commit_queue_head head;
119};
120
121struct tog_color {
122 SIMPLEQ_ENTRY(tog_color)struct { struct tog_color *sqe_next; } entry;
123 regex_t regex;
124 short colorpair;
125};
126SIMPLEQ_HEAD(tog_colors, tog_color)struct tog_colors { struct tog_color *sqh_first; struct tog_color
**sqh_last; }
;
127
128static struct got_reflist_head tog_refs = TAILQ_HEAD_INITIALIZER(tog_refs){ ((void*)0), &(tog_refs).tqh_first };
129static struct got_reflist_object_id_map *tog_refs_idmap;
130
131static const struct got_error *
132tog_load_refs(struct got_repository *repo)
133{
134 const struct got_error *err;
135
136 err = got_ref_list(&tog_refs, repo, NULL((void*)0), got_ref_cmp_by_name, NULL((void*)0));
137 if (err)
138 return err;
139
140 return got_reflist_object_id_map_create(&tog_refs_idmap, &tog_refs,
141 repo);
142}
143
144static void
145tog_free_refs(void)
146{
147 if (tog_refs_idmap) {
148 got_reflist_object_id_map_free(tog_refs_idmap);
149 tog_refs_idmap = NULL((void*)0);
150 }
151 got_ref_list_free(&tog_refs);
152}
153
154static const struct got_error *
155add_color(struct tog_colors *colors, const char *pattern,
156 int idx, short color)
157{
158 const struct got_error *err = NULL((void*)0);
159 struct tog_color *tc;
160 int regerr = 0;
161
162 if (idx < 1 || idx > COLOR_PAIRS - 1)
163 return NULL((void*)0);
164
165 init_pair(idx, color, -1);
166
167 tc = calloc(1, sizeof(*tc));
168 if (tc == NULL((void*)0))
169 return got_error_from_errno("calloc");
170 regerr = regcomp(&tc->regex, pattern,
171 REG_EXTENDED0001 | REG_NOSUB0004 | REG_NEWLINE0010);
172 if (regerr) {
173 static char regerr_msg[512];
174 static char err_msg[512];
175 regerror(regerr, &tc->regex, regerr_msg,
176 sizeof(regerr_msg));
177 snprintf(err_msg, sizeof(err_msg), "regcomp: %s",
178 regerr_msg);
179 err = got_error_msg(GOT_ERR_REGEX112, err_msg);
180 free(tc);
181 return err;
182 }
183 tc->colorpair = idx;
184 SIMPLEQ_INSERT_HEAD(colors, tc, entry)do { if (((tc)->entry.sqe_next = (colors)->sqh_first) ==
((void*)0)) (colors)->sqh_last = &(tc)->entry.sqe_next
; (colors)->sqh_first = (tc); } while (0)
;
185 return NULL((void*)0);
186}
187
188static void
189free_colors(struct tog_colors *colors)
190{
191 struct tog_color *tc;
192
193 while (!SIMPLEQ_EMPTY(colors)(((colors)->sqh_first) == ((void*)0))) {
194 tc = SIMPLEQ_FIRST(colors)((colors)->sqh_first);
195 SIMPLEQ_REMOVE_HEAD(colors, entry)do { if (((colors)->sqh_first = (colors)->sqh_first->
entry.sqe_next) == ((void*)0)) (colors)->sqh_last = &(
colors)->sqh_first; } while (0)
;
196 regfree(&tc->regex);
197 free(tc);
198 }
199}
200
201struct tog_color *
202get_color(struct tog_colors *colors, int colorpair)
203{
204 struct tog_color *tc = NULL((void*)0);
205
206 SIMPLEQ_FOREACH(tc, colors, entry)for((tc) = ((colors)->sqh_first); (tc) != ((void*)0); (tc)
= ((tc)->entry.sqe_next))
{
207 if (tc->colorpair == colorpair)
208 return tc;
209 }
210
211 return NULL((void*)0);
212}
213
214static int
215default_color_value(const char *envvar)
216{
217 if (strcmp(envvar, "TOG_COLOR_DIFF_MINUS") == 0)
218 return COLOR_MAGENTA5;
219 if (strcmp(envvar, "TOG_COLOR_DIFF_PLUS") == 0)
220 return COLOR_CYAN6;
221 if (strcmp(envvar, "TOG_COLOR_DIFF_CHUNK_HEADER") == 0)
222 return COLOR_YELLOW3;
223 if (strcmp(envvar, "TOG_COLOR_DIFF_META") == 0)
224 return COLOR_GREEN2;
225 if (strcmp(envvar, "TOG_COLOR_TREE_SUBMODULE") == 0)
226 return COLOR_MAGENTA5;
227 if (strcmp(envvar, "TOG_COLOR_TREE_SYMLINK") == 0)
228 return COLOR_MAGENTA5;
229 if (strcmp(envvar, "TOG_COLOR_TREE_DIRECTORY") == 0)
230 return COLOR_CYAN6;
231 if (strcmp(envvar, "TOG_COLOR_TREE_EXECUTABLE") == 0)
232 return COLOR_GREEN2;
233 if (strcmp(envvar, "TOG_COLOR_COMMIT") == 0)
234 return COLOR_GREEN2;
235 if (strcmp(envvar, "TOG_COLOR_AUTHOR") == 0)
236 return COLOR_CYAN6;
237 if (strcmp(envvar, "TOG_COLOR_DATE") == 0)
238 return COLOR_YELLOW3;
239 if (strcmp(envvar, "TOG_COLOR_REFS_HEADS") == 0)
240 return COLOR_GREEN2;
241 if (strcmp(envvar, "TOG_COLOR_REFS_TAGS") == 0)
242 return COLOR_MAGENTA5;
243 if (strcmp(envvar, "TOG_COLOR_REFS_REMOTES") == 0)
244 return COLOR_YELLOW3;
245
246 return -1;
247}
248
249static int
250get_color_value(const char *envvar)
251{
252 const char *val = getenv(envvar);
253
254 if (val == NULL((void*)0))
255 return default_color_value(envvar);
256
257 if (strcasecmp(val, "black") == 0)
258 return COLOR_BLACK0;
259 if (strcasecmp(val, "red") == 0)
260 return COLOR_RED1;
261 if (strcasecmp(val, "green") == 0)
262 return COLOR_GREEN2;
263 if (strcasecmp(val, "yellow") == 0)
264 return COLOR_YELLOW3;
265 if (strcasecmp(val, "blue") == 0)
266 return COLOR_BLUE4;
267 if (strcasecmp(val, "magenta") == 0)
268 return COLOR_MAGENTA5;
269 if (strcasecmp(val, "cyan") == 0)
270 return COLOR_CYAN6;
271 if (strcasecmp(val, "white") == 0)
272 return COLOR_WHITE7;
273 if (strcasecmp(val, "default") == 0)
274 return -1;
275
276 return default_color_value(envvar);
277}
278
279
280struct tog_diff_view_state {
281 struct got_object_id *id1, *id2;
282 const char *label1, *label2;
283 FILE *f;
284 int first_displayed_line;
285 int last_displayed_line;
286 int eof;
287 int diff_context;
288 int ignore_whitespace;
289 int force_text_diff;
290 struct got_repository *repo;
291 struct tog_colors colors;
292 size_t nlines;
293 off_t *line_offsets;
294 int matched_line;
295 int selected_line;
296
297 /* passed from log view; may be NULL */
298 struct tog_view *log_view;
299};
300
301pthread_mutex_t tog_mutex = PTHREAD_MUTEX_INITIALIZER((void*)0);
302
303struct tog_log_thread_args {
304 pthread_cond_t need_commits;
305 pthread_cond_t commit_loaded;
306 int commits_needed;
307 struct got_commit_graph *graph;
308 struct commit_queue *commits;
309 const char *in_repo_path;
310 struct got_object_id *start_id;
311 struct got_repository *repo;
312 int log_complete;
313 sig_atomic_t *quit;
314 struct commit_queue_entry **first_displayed_entry;
315 struct commit_queue_entry **selected_entry;
316 int *searching;
317 int *search_next_done;
318 regex_t *regex;
319};
320
321struct tog_log_view_state {
322 struct commit_queue commits;
323 struct commit_queue_entry *first_displayed_entry;
324 struct commit_queue_entry *last_displayed_entry;
325 struct commit_queue_entry *selected_entry;
326 int selected;
327 char *in_repo_path;
328 char *head_ref_name;
329 int log_branches;
330 struct got_repository *repo;
331 struct got_object_id *start_id;
332 sig_atomic_t quit;
333 pthread_t thread;
334 struct tog_log_thread_args thread_args;
335 struct commit_queue_entry *matched_entry;
336 struct commit_queue_entry *search_entry;
337 struct tog_colors colors;
338};
339
340#define TOG_COLOR_DIFF_MINUS1 1
341#define TOG_COLOR_DIFF_PLUS2 2
342#define TOG_COLOR_DIFF_CHUNK_HEADER3 3
343#define TOG_COLOR_DIFF_META4 4
344#define TOG_COLOR_TREE_SUBMODULE5 5
345#define TOG_COLOR_TREE_SYMLINK6 6
346#define TOG_COLOR_TREE_DIRECTORY7 7
347#define TOG_COLOR_TREE_EXECUTABLE8 8
348#define TOG_COLOR_COMMIT9 9
349#define TOG_COLOR_AUTHOR10 10
350#define TOG_COLOR_DATE11 11
351#define TOG_COLOR_REFS_HEADS12 12
352#define TOG_COLOR_REFS_TAGS13 13
353#define TOG_COLOR_REFS_REMOTES14 14
354
355struct tog_blame_cb_args {
356 struct tog_blame_line *lines; /* one per line */
357 int nlines;
358
359 struct tog_view *view;
360 struct got_object_id *commit_id;
361 int *quit;
362};
363
364struct tog_blame_thread_args {
365 const char *path;
366 struct got_repository *repo;
367 struct tog_blame_cb_args *cb_args;
368 int *complete;
369 got_cancel_cb cancel_cb;
370 void *cancel_arg;
371};
372
373struct tog_blame {
374 FILE *f;
375 off_t filesize;
376 struct tog_blame_line *lines;
377 int nlines;
378 off_t *line_offsets;
379 pthread_t thread;
380 struct tog_blame_thread_args thread_args;
381 struct tog_blame_cb_args cb_args;
382 const char *path;
383};
384
385struct tog_blame_view_state {
386 int first_displayed_line;
387 int last_displayed_line;
388 int selected_line;
389 int blame_complete;
390 int eof;
391 int done;
392 struct got_object_id_queue blamed_commits;
393 struct got_object_qid *blamed_commit;
394 char *path;
395 struct got_repository *repo;
396 struct got_object_id *commit_id;
397 struct tog_blame blame;
398 int matched_line;
399 struct tog_colors colors;
400};
401
402struct tog_parent_tree {
403 TAILQ_ENTRY(tog_parent_tree)struct { struct tog_parent_tree *tqe_next; struct tog_parent_tree
**tqe_prev; }
entry;
404 struct got_tree_object *tree;
405 struct got_tree_entry *first_displayed_entry;
406 struct got_tree_entry *selected_entry;
407 int selected;
408};
409
410TAILQ_HEAD(tog_parent_trees, tog_parent_tree)struct tog_parent_trees { struct tog_parent_tree *tqh_first; struct
tog_parent_tree **tqh_last; }
;
411
412struct tog_tree_view_state {
413 char *tree_label;
414 struct got_tree_object *root;
415 struct got_tree_object *tree;
416 struct got_tree_entry *first_displayed_entry;
417 struct got_tree_entry *last_displayed_entry;
418 struct got_tree_entry *selected_entry;
419 int ndisplayed, selected, show_ids;
420 struct tog_parent_trees parents;
421 struct got_object_id *commit_id;
422 char *head_ref_name;
423 struct got_repository *repo;
424 struct got_tree_entry *matched_entry;
425 struct tog_colors colors;
426};
427
428struct tog_reflist_entry {
429 TAILQ_ENTRY(tog_reflist_entry)struct { struct tog_reflist_entry *tqe_next; struct tog_reflist_entry
**tqe_prev; }
entry;
430 struct got_reference *ref;
431 int idx;
432};
433
434TAILQ_HEAD(tog_reflist_head, tog_reflist_entry)struct tog_reflist_head { struct tog_reflist_entry *tqh_first
; struct tog_reflist_entry **tqh_last; }
;
435
436struct tog_ref_view_state {
437 struct tog_reflist_head refs;
438 struct tog_reflist_entry *first_displayed_entry;
439 struct tog_reflist_entry *last_displayed_entry;
440 struct tog_reflist_entry *selected_entry;
441 int nrefs, ndisplayed, selected, show_ids;
442 struct got_repository *repo;
443 struct tog_reflist_entry *matched_entry;
444 struct tog_colors colors;
445};
446
447/*
448 * We implement two types of views: parent views and child views.
449 *
450 * The 'Tab' key switches focus between a parent view and its child view.
451 * Child views are shown side-by-side to their parent view, provided
452 * there is enough screen estate.
453 *
454 * When a new view is opened from within a parent view, this new view
455 * becomes a child view of the parent view, replacing any existing child.
456 *
457 * When a new view is opened from within a child view, this new view
458 * becomes a parent view which will obscure the views below until the
459 * user quits the new parent view by typing 'q'.
460 *
461 * This list of views contains parent views only.
462 * Child views are only pointed to by their parent view.
463 */
464TAILQ_HEAD(tog_view_list_head, tog_view)struct tog_view_list_head { struct tog_view *tqh_first; struct
tog_view **tqh_last; }
;
465
466struct tog_view {
467 TAILQ_ENTRY(tog_view)struct { struct tog_view *tqe_next; struct tog_view **tqe_prev
; }
entry;
468 WINDOW *window;
469 PANEL *panel;
470 int nlines, ncols, begin_y, begin_x;
471 int lines, cols; /* copies of LINES and COLS */
472 int focussed; /* Only set on one parent or child view at a time. */
473 int dying;
474 struct tog_view *parent;
475 struct tog_view *child;
476
477 /*
478 * This flag is initially set on parent views when a new child view
479 * is created. It gets toggled when the 'Tab' key switches focus
480 * between parent and child.
481 * The flag indicates whether focus should be passed on to our child
482 * view if this parent view gets picked for focus after another parent
483 * view was closed. This prevents child views from losing focus in such
484 * situations.
485 */
486 int focus_child;
487
488 /* type-specific state */
489 enum tog_view_type type;
490 union {
491 struct tog_diff_view_state diff;
492 struct tog_log_view_state log;
493 struct tog_blame_view_state blame;
494 struct tog_tree_view_state tree;
495 struct tog_ref_view_state ref;
496 } state;
497
498 const struct got_error *(*show)(struct tog_view *);
499 const struct got_error *(*input)(struct tog_view **,
500 struct tog_view *, int);
501 const struct got_error *(*close)(struct tog_view *);
502
503 const struct got_error *(*search_start)(struct tog_view *);
504 const struct got_error *(*search_next)(struct tog_view *);
505 int search_started;
506 int searching;
507#define TOG_SEARCH_FORWARD1 1
508#define TOG_SEARCH_BACKWARD2 2
509 int search_next_done;
510#define TOG_SEARCH_HAVE_MORE1 1
511#define TOG_SEARCH_NO_MORE2 2
512#define TOG_SEARCH_HAVE_NONE3 3
513 regex_t regex;
514 regmatch_t regmatch;
515};
516
517static const struct got_error *open_diff_view(struct tog_view *,
518 struct got_object_id *, struct got_object_id *,
519 const char *, const char *, int, int, int, struct tog_view *,
520 struct got_repository *);
521static const struct got_error *show_diff_view(struct tog_view *);
522static const struct got_error *input_diff_view(struct tog_view **,
523 struct tog_view *, int);
524static const struct got_error* close_diff_view(struct tog_view *);
525static const struct got_error *search_start_diff_view(struct tog_view *);
526static const struct got_error *search_next_diff_view(struct tog_view *);
527
528static const struct got_error *open_log_view(struct tog_view *,
529 struct got_object_id *, struct got_repository *,
530 const char *, const char *, int);
531static const struct got_error * show_log_view(struct tog_view *);
532static const struct got_error *input_log_view(struct tog_view **,
533 struct tog_view *, int);
534static const struct got_error *close_log_view(struct tog_view *);
535static const struct got_error *search_start_log_view(struct tog_view *);
536static const struct got_error *search_next_log_view(struct tog_view *);
537
538static const struct got_error *open_blame_view(struct tog_view *, char *,
539 struct got_object_id *, struct got_repository *);
540static const struct got_error *show_blame_view(struct tog_view *);
541static const struct got_error *input_blame_view(struct tog_view **,
542 struct tog_view *, int);
543static const struct got_error *close_blame_view(struct tog_view *);
544static const struct got_error *search_start_blame_view(struct tog_view *);
545static const struct got_error *search_next_blame_view(struct tog_view *);
546
547static const struct got_error *open_tree_view(struct tog_view *,
548 struct got_tree_object *, struct got_object_id *, const char *,
549 struct got_repository *);
550static const struct got_error *show_tree_view(struct tog_view *);
551static const struct got_error *input_tree_view(struct tog_view **,
552 struct tog_view *, int);
553static const struct got_error *close_tree_view(struct tog_view *);
554static const struct got_error *search_start_tree_view(struct tog_view *);
555static const struct got_error *search_next_tree_view(struct tog_view *);
556
557static const struct got_error *open_ref_view(struct tog_view *,
558 struct got_repository *);
559static const struct got_error *show_ref_view(struct tog_view *);
560static const struct got_error *input_ref_view(struct tog_view **,
561 struct tog_view *, int);
562static const struct got_error *close_ref_view(struct tog_view *);
563static const struct got_error *search_start_ref_view(struct tog_view *);
564static const struct got_error *search_next_ref_view(struct tog_view *);
565
566static volatile sig_atomic_t tog_sigwinch_received;
567static volatile sig_atomic_t tog_sigpipe_received;
568static volatile sig_atomic_t tog_sigcont_received;
569
570static void
571tog_sigwinch(int signo)
572{
573 tog_sigwinch_received = 1;
574}
575
576static void
577tog_sigpipe(int signo)
578{
579 tog_sigpipe_received = 1;
580}
581
582static void
583tog_sigcont(int signo)
584{
585 tog_sigcont_received = 1;
586}
587
588static const struct got_error *
589view_close(struct tog_view *view)
590{
591 const struct got_error *err = NULL((void*)0);
592
593 if (view->child) {
594 view_close(view->child);
595 view->child = NULL((void*)0);
596 }
597 if (view->close)
598 err = view->close(view);
599 if (view->panel)
600 del_panel(view->panel);
601 if (view->window)
602 delwin(view->window);
603 free(view);
604 return err;
605}
606
607static struct tog_view *
608view_open(int nlines, int ncols, int begin_y, int begin_x,
609 enum tog_view_type type)
610{
611 struct tog_view *view = calloc(1, sizeof(*view));
612
613 if (view == NULL((void*)0))
614 return NULL((void*)0);
615
616 view->type = type;
617 view->lines = LINES;
618 view->cols = COLS;
619 view->nlines = nlines ? nlines : LINES - begin_y;
620 view->ncols = ncols ? ncols : COLS - begin_x;
621 view->begin_y = begin_y;
622 view->begin_x = begin_x;
623 view->window = newwin(nlines, ncols, begin_y, begin_x);
624 if (view->window == NULL((void*)0)) {
625 view_close(view);
626 return NULL((void*)0);
627 }
628 view->panel = new_panel(view->window);
629 if (view->panel == NULL((void*)0) ||
630 set_panel_userptr(view->panel, view) != OK(0)) {
631 view_close(view);
632 return NULL((void*)0);
633 }
634
635 keypad(view->window, TRUE1);
636 return view;
637}
638
639static int
640view_split_begin_x(int begin_x)
641{
642 if (begin_x > 0 || COLS < 120)
643 return 0;
644 return (COLS - MAX(COLS / 2, 80)((COLS / 2) > (80) ? (COLS / 2) : (80)));
645}
646
647static const struct got_error *view_resize(struct tog_view *);
648
649static const struct got_error *
650view_splitscreen(struct tog_view *view)
651{
652 const struct got_error *err = NULL((void*)0);
653
654 view->begin_y = 0;
655 view->begin_x = view_split_begin_x(0);
656 view->nlines = LINES;
657 view->ncols = COLS - view->begin_x;
658 view->lines = LINES;
659 view->cols = COLS;
660 err = view_resize(view);
661 if (err)
662 return err;
663
664 if (mvwin(view->window, view->begin_y, view->begin_x) == ERR(-1))
665 return got_error_from_errno("mvwin");
666
667 return NULL((void*)0);
668}
669
670static const struct got_error *
671view_fullscreen(struct tog_view *view)
672{
673 const struct got_error *err = NULL((void*)0);
674
675 view->begin_x = 0;
676 view->begin_y = 0;
677 view->nlines = LINES;
678 view->ncols = COLS;
679 view->lines = LINES;
680 view->cols = COLS;
681 err = view_resize(view);
682 if (err)
683 return err;
684
685 if (mvwin(view->window, view->begin_y, view->begin_x) == ERR(-1))
686 return got_error_from_errno("mvwin");
687
688 return NULL((void*)0);
689}
690
691static int
692view_is_parent_view(struct tog_view *view)
693{
694 return view->parent == NULL((void*)0);
695}
696
697static const struct got_error *
698view_resize(struct tog_view *view)
699{
700 int nlines, ncols;
701
702 if (view->lines > LINES)
703 nlines = view->nlines - (view->lines - LINES);
704 else
705 nlines = view->nlines + (LINES - view->lines);
706
707 if (view->cols > COLS)
708 ncols = view->ncols - (view->cols - COLS);
709 else
710 ncols = view->ncols + (COLS - view->cols);
711
712 if (wresize(view->window, nlines, ncols) == ERR(-1))
713 return got_error_from_errno("wresize");
714 if (replace_panel(view->panel, view->window) == ERR(-1))
715 return got_error_from_errno("replace_panel");
716 wclear(view->window);
717
718 view->nlines = nlines;
719 view->ncols = ncols;
720 view->lines = LINES;
721 view->cols = COLS;
722
723 if (view->child) {
724 view->child->begin_x = view_split_begin_x(view->begin_x);
725 if (view->child->begin_x == 0) {
726 view_fullscreen(view->child);
727 if (view->child->focussed)
728 show_panel(view->child->panel);
729 else
730 show_panel(view->panel);
731 } else {
732 view_splitscreen(view->child);
733 show_panel(view->child->panel);
734 }
735 }
736
737 return NULL((void*)0);
738}
739
740static const struct got_error *
741view_close_child(struct tog_view *view)
742{
743 const struct got_error *err = NULL((void*)0);
744
745 if (view->child == NULL((void*)0))
746 return NULL((void*)0);
747
748 err = view_close(view->child);
749 view->child = NULL((void*)0);
750 return err;
751}
752
753static void
754view_set_child(struct tog_view *view, struct tog_view *child)
755{
756 view->child = child;
757 child->parent = view;
758}
759
760static int
761view_is_splitscreen(struct tog_view *view)
762{
763 return view->begin_x > 0;
764}
765
766static void
767tog_resizeterm(void)
768{
769 int cols, lines;
770 struct winsize size;
771
772 if (ioctl(STDOUT_FILENO1, TIOCGWINSZ((unsigned long)0x40000000 | ((sizeof(struct winsize) & 0x1fff
) << 16) | ((('t')) << 8) | ((104)))
, &size) < 0) {
773 cols = 80; /* Default */
774 lines = 24;
775 } else {
776 cols = size.ws_col;
777 lines = size.ws_row;
778 }
779 resize_term(lines, cols);
780}
781
782static const struct got_error *
783view_search_start(struct tog_view *view)
784{
785 const struct got_error *err = NULL((void*)0);
786 char pattern[1024];
787 int ret;
788
789 if (view->search_started) {
790 regfree(&view->regex);
791 view->searching = 0;
792 memset(&view->regmatch, 0, sizeof(view->regmatch));
793 }
794 view->search_started = 0;
795
796 if (view->nlines < 1)
797 return NULL((void*)0);
798
799 mvwaddstr(view->window, view->begin_y + view->nlines - 1, 0, "/")(wmove(view->window,view->begin_y + view->nlines - 1
,0) == (-1) ? (-1) : waddnstr(view->window,"/",-1))
;
800 wclrtoeol(view->window);
801
802 nocbreak();
803 echo();
804 ret = wgetnstr(view->window, pattern, sizeof(pattern));
805 cbreak();
806 noecho();
807 if (ret == ERR(-1))
808 return NULL((void*)0);
809
810 if (regcomp(&view->regex, pattern, REG_EXTENDED0001 | REG_NEWLINE0010) == 0) {
811 err = view->search_start(view);
812 if (err) {
813 regfree(&view->regex);
814 return err;
815 }
816 view->search_started = 1;
817 view->searching = TOG_SEARCH_FORWARD1;
818 view->search_next_done = 0;
819 view->search_next(view);
820 }
821
822 return NULL((void*)0);
823}
824
825static const struct got_error *
826view_input(struct tog_view **new, int *done, struct tog_view *view,
827 struct tog_view_list_head *views)
828{
829 const struct got_error *err = NULL((void*)0);
830 struct tog_view *v;
831 int ch, errcode;
832
833 *new = NULL((void*)0);
834
835 /* Clear "no matches" indicator. */
836 if (view->search_next_done == TOG_SEARCH_NO_MORE2 ||
837 view->search_next_done == TOG_SEARCH_HAVE_NONE3)
838 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
839
840 if (view->searching && !view->search_next_done) {
841 errcode = pthread_mutex_unlock(&tog_mutex);
842 if (errcode)
843 return got_error_set_errno(errcode,
844 "pthread_mutex_unlock");
845 pthread_yield();
846 errcode = pthread_mutex_lock(&tog_mutex);
847 if (errcode)
848 return got_error_set_errno(errcode,
849 "pthread_mutex_lock");
850 view->search_next(view);
851 return NULL((void*)0);
852 }
853
854 nodelay(stdscr, FALSE0);
855 /* Allow threads to make progress while we are waiting for input. */
856 errcode = pthread_mutex_unlock(&tog_mutex);
857 if (errcode)
858 return got_error_set_errno(errcode, "pthread_mutex_unlock");
859 ch = wgetch(view->window);
860 errcode = pthread_mutex_lock(&tog_mutex);
861 if (errcode)
862 return got_error_set_errno(errcode, "pthread_mutex_lock");
863 nodelay(stdscr, TRUE1);
864
865 if (tog_sigwinch_received || tog_sigcont_received) {
866 tog_resizeterm();
867 tog_sigwinch_received = 0;
868 tog_sigcont_received = 0;
869 TAILQ_FOREACH(v, views, entry)for((v) = ((views)->tqh_first); (v) != ((void*)0); (v) = (
(v)->entry.tqe_next))
{
870 err = view_resize(v);
871 if (err)
872 return err;
873 err = v->input(new, v, KEY_RESIZE0632);
874 if (err)
875 return err;
876 if (v->child) {
877 err = view_resize(v->child);
878 if (err)
879 return err;
880 err = v->child->input(new, v->child,
881 KEY_RESIZE0632);
882 if (err)
883 return err;
884 }
885 }
886 }
887
888 switch (ch) {
889 case ERR(-1):
890 break;
891 case '\t':
892 if (view->child) {
893 view->focussed = 0;
894 view->child->focussed = 1;
895 view->focus_child = 1;
896 } else if (view->parent) {
897 view->focussed = 0;
898 view->parent->focussed = 1;
899 view->parent->focus_child = 0;
900 }
901 break;
902 case 'q':
903 err = view->input(new, view, ch);
904 view->dying = 1;
905 break;
906 case 'Q':
907 *done = 1;
908 break;
909 case 'f':
910 if (view_is_parent_view(view)) {
911 if (view->child == NULL((void*)0))
912 break;
913 if (view_is_splitscreen(view->child)) {
914 view->focussed = 0;
915 view->child->focussed = 1;
916 err = view_fullscreen(view->child);
917 } else
918 err = view_splitscreen(view->child);
919 if (err)
920 break;
921 err = view->child->input(new, view->child,
922 KEY_RESIZE0632);
923 } else {
924 if (view_is_splitscreen(view)) {
925 view->parent->focussed = 0;
926 view->focussed = 1;
927 err = view_fullscreen(view);
928 } else {
929 err = view_splitscreen(view);
930 }
931 if (err)
932 break;
933 err = view->input(new, view, KEY_RESIZE0632);
934 }
935 break;
936 case KEY_RESIZE0632:
937 break;
938 case '/':
939 if (view->search_start)
940 view_search_start(view);
941 else
942 err = view->input(new, view, ch);
943 break;
944 case 'N':
945 case 'n':
946 if (view->search_started && view->search_next) {
947 view->searching = (ch == 'n' ?
948 TOG_SEARCH_FORWARD1 : TOG_SEARCH_BACKWARD2);
949 view->search_next_done = 0;
950 view->search_next(view);
951 } else
952 err = view->input(new, view, ch);
953 break;
954 default:
955 err = view->input(new, view, ch);
956 break;
957 }
958
959 return err;
960}
961
962void
963view_vborder(struct tog_view *view)
964{
965 PANEL *panel;
966 const struct tog_view *view_above;
967
968 if (view->parent)
969 return view_vborder(view->parent);
970
971 panel = panel_above(view->panel);
972 if (panel == NULL((void*)0))
973 return;
974
975 view_above = panel_userptr(panel);
976 mvwvline(view->window, view->begin_y, view_above->begin_x - 1,(wmove(view->window,view->begin_y,view_above->begin_x
- 1) == (-1) ? (-1) : wvline(view->window,got_locale_is_utf8
() ? (acs_map[(unsigned char)('x')]) : '|',view->nlines))
977 got_locale_is_utf8() ? ACS_VLINE : '|', view->nlines)(wmove(view->window,view->begin_y,view_above->begin_x
- 1) == (-1) ? (-1) : wvline(view->window,got_locale_is_utf8
() ? (acs_map[(unsigned char)('x')]) : '|',view->nlines))
;
978}
979
980int
981view_needs_focus_indication(struct tog_view *view)
982{
983 if (view_is_parent_view(view)) {
984 if (view->child == NULL((void*)0) || view->child->focussed)
985 return 0;
986 if (!view_is_splitscreen(view->child))
987 return 0;
988 } else if (!view_is_splitscreen(view))
989 return 0;
990
991 return view->focussed;
992}
993
994static const struct got_error *
995view_loop(struct tog_view *view)
996{
997 const struct got_error *err = NULL((void*)0);
998 struct tog_view_list_head views;
999 struct tog_view *new_view;
1000 int fast_refresh = 10;
1001 int done = 0, errcode;
1002
1003 errcode = pthread_mutex_lock(&tog_mutex);
1004 if (errcode)
1005 return got_error_set_errno(errcode, "pthread_mutex_lock");
1006
1007 TAILQ_INIT(&views)do { (&views)->tqh_first = ((void*)0); (&views)->
tqh_last = &(&views)->tqh_first; } while (0)
;
1008 TAILQ_INSERT_HEAD(&views, view, entry)do { if (((view)->entry.tqe_next = (&views)->tqh_first
) != ((void*)0)) (&views)->tqh_first->entry.tqe_prev
= &(view)->entry.tqe_next; else (&views)->tqh_last
= &(view)->entry.tqe_next; (&views)->tqh_first
= (view); (view)->entry.tqe_prev = &(&views)->
tqh_first; } while (0)
;
1009
1010 view->focussed = 1;
1011 err = view->show(view);
1012 if (err)
1013 return err;
1014 update_panels();
1015 doupdate();
1016 while (!TAILQ_EMPTY(&views)(((&views)->tqh_first) == ((void*)0)) && !done && !tog_sigpipe_received) {
1017 /* Refresh fast during initialization, then become slower. */
1018 if (fast_refresh && fast_refresh-- == 0)
1019 halfdelay(10); /* switch to once per second */
1020
1021 err = view_input(&new_view, &done, view, &views);
1022 if (err)
1023 break;
1024 if (view->dying) {
1025 struct tog_view *v, *prev = NULL((void*)0);
1026
1027 if (view_is_parent_view(view))
1028 prev = TAILQ_PREV(view, tog_view_list_head,(*(((struct tog_view_list_head *)((view)->entry.tqe_prev))
->tqh_last))
1029 entry)(*(((struct tog_view_list_head *)((view)->entry.tqe_prev))
->tqh_last))
;
1030 else if (view->parent)
1031 prev = view->parent;
1032
1033 if (view->parent) {
1034 view->parent->child = NULL((void*)0);
1035 view->parent->focus_child = 0;
1036 } else
1037 TAILQ_REMOVE(&views, view, entry)do { if (((view)->entry.tqe_next) != ((void*)0)) (view)->
entry.tqe_next->entry.tqe_prev = (view)->entry.tqe_prev
; else (&views)->tqh_last = (view)->entry.tqe_prev;
*(view)->entry.tqe_prev = (view)->entry.tqe_next; ; ; }
while (0)
;
1038
1039 err = view_close(view);
1040 if (err)
1041 goto done;
1042
1043 view = NULL((void*)0);
1044 TAILQ_FOREACH(v, &views, entry)for((v) = ((&views)->tqh_first); (v) != ((void*)0); (v
) = ((v)->entry.tqe_next))
{
1045 if (v->focussed)
1046 break;
1047 }
1048 if (view == NULL((void*)0) && new_view == NULL((void*)0)) {
1049 /* No view has focus. Try to pick one. */
1050 if (prev)
1051 view = prev;
1052 else if (!TAILQ_EMPTY(&views)(((&views)->tqh_first) == ((void*)0))) {
1053 view = TAILQ_LAST(&views,(*(((struct tog_view_list_head *)((&views)->tqh_last))
->tqh_last))
1054 tog_view_list_head)(*(((struct tog_view_list_head *)((&views)->tqh_last))
->tqh_last))
;
1055 }
1056 if (view) {
1057 if (view->focus_child) {
1058 view->child->focussed = 1;
1059 view = view->child;
1060 } else
1061 view->focussed = 1;
1062 }
1063 }
1064 }
1065 if (new_view) {
1066 struct tog_view *v, *t;
1067 /* Only allow one parent view per type. */
1068 TAILQ_FOREACH_SAFE(v, &views, entry, t)for ((v) = ((&views)->tqh_first); (v) != ((void*)0) &&
((t) = ((v)->entry.tqe_next), 1); (v) = (t))
{
1069 if (v->type != new_view->type)
1070 continue;
1071 TAILQ_REMOVE(&views, v, entry)do { if (((v)->entry.tqe_next) != ((void*)0)) (v)->entry
.tqe_next->entry.tqe_prev = (v)->entry.tqe_prev; else (
&views)->tqh_last = (v)->entry.tqe_prev; *(v)->entry
.tqe_prev = (v)->entry.tqe_next; ; ; } while (0)
;
1072 err = view_close(v);
1073 if (err)
1074 goto done;
1075 break;
1076 }
1077 TAILQ_INSERT_TAIL(&views, new_view, entry)do { (new_view)->entry.tqe_next = ((void*)0); (new_view)->
entry.tqe_prev = (&views)->tqh_last; *(&views)->
tqh_last = (new_view); (&views)->tqh_last = &(new_view
)->entry.tqe_next; } while (0)
;
1078 view = new_view;
1079 }
1080 if (view) {
1081 if (view_is_parent_view(view)) {
1082 if (view->child && view->child->focussed)
1083 view = view->child;
1084 } else {
1085 if (view->parent && view->parent->focussed)
1086 view = view->parent;
1087 }
1088 show_panel(view->panel);
1089 if (view->child && view_is_splitscreen(view->child))
1090 show_panel(view->child->panel);
1091 if (view->parent && view_is_splitscreen(view)) {
1092 err = view->parent->show(view->parent);
1093 if (err)
1094 goto done;
1095 }
1096 err = view->show(view);
1097 if (err)
1098 goto done;
1099 if (view->child) {
1100 err = view->child->show(view->child);
1101 if (err)
1102 goto done;
1103 }
1104 update_panels();
1105 doupdate();
1106 }
1107 }
1108done:
1109 while (!TAILQ_EMPTY(&views)(((&views)->tqh_first) == ((void*)0))) {
1110 view = TAILQ_FIRST(&views)((&views)->tqh_first);
1111 TAILQ_REMOVE(&views, view, entry)do { if (((view)->entry.tqe_next) != ((void*)0)) (view)->
entry.tqe_next->entry.tqe_prev = (view)->entry.tqe_prev
; else (&views)->tqh_last = (view)->entry.tqe_prev;
*(view)->entry.tqe_prev = (view)->entry.tqe_next; ; ; }
while (0)
;
1112 view_close(view);
1113 }
1114
1115 errcode = pthread_mutex_unlock(&tog_mutex);
1116 if (errcode && err == NULL((void*)0))
1117 err = got_error_set_errno(errcode, "pthread_mutex_unlock");
1118
1119 return err;
1120}
1121
1122__dead__attribute__((__noreturn__)) static void
1123usage_log(void)
1124{
1125 endwin();
1126 fprintf(stderr(&__sF[2]),
1127 "usage: %s log [-b] [-c commit] [-r repository-path] [path]\n",
1128 getprogname());
1129 exit(1);
1130}
1131
1132/* Create newly allocated wide-character string equivalent to a byte string. */
1133static const struct got_error *
1134mbs2ws(wchar_t **ws, size_t *wlen, const char *s)
1135{
1136 char *vis = NULL((void*)0);
1137 const struct got_error *err = NULL((void*)0);
1138
1139 *ws = NULL((void*)0);
1140 *wlen = mbstowcs(NULL((void*)0), s, 0);
1141 if (*wlen == (size_t)-1) {
1142 int vislen;
1143 if (errno(*__errno()) != EILSEQ84)
1144 return got_error_from_errno("mbstowcs");
1145
1146 /* byte string invalid in current encoding; try to "fix" it */
1147 err = got_mbsavis(&vis, &vislen, s);
1148 if (err)
1149 return err;
1150 *wlen = mbstowcs(NULL((void*)0), vis, 0);
1151 if (*wlen == (size_t)-1) {
1152 err = got_error_from_errno("mbstowcs"); /* give up */
1153 goto done;
1154 }
1155 }
1156
1157 *ws = calloc(*wlen + 1, sizeof(**ws));
1158 if (*ws == NULL((void*)0)) {
1159 err = got_error_from_errno("calloc");
1160 goto done;
1161 }
1162
1163 if (mbstowcs(*ws, vis ? vis : s, *wlen) != *wlen)
1164 err = got_error_from_errno("mbstowcs");
1165done:
1166 free(vis);
1167 if (err) {
1168 free(*ws);
1169 *ws = NULL((void*)0);
1170 *wlen = 0;
1171 }
1172 return err;
1173}
1174
1175/* Format a line for display, ensuring that it won't overflow a width limit. */
1176static const struct got_error *
1177format_line(wchar_t **wlinep, int *widthp, const char *line, int wlimit,
1178 int col_tab_align)
1179{
1180 const struct got_error *err = NULL((void*)0);
1181 int cols = 0;
1182 wchar_t *wline = NULL((void*)0);
1183 size_t wlen;
1184 int i;
1185
1186 *wlinep = NULL((void*)0);
1187 *widthp = 0;
1188
1189 err = mbs2ws(&wline, &wlen, line);
1190 if (err)
1191 return err;
1192
1193 if (wlen > 0 && wline[wlen - 1] == L'\n') {
1194 wline[wlen - 1] = L'\0';
1195 wlen--;
1196 }
1197 if (wlen > 0 && wline[wlen - 1] == L'\r') {
1198 wline[wlen - 1] = L'\0';
1199 wlen--;
1200 }
1201
1202 i = 0;
1203 while (i < wlen) {
1204 int width = wcwidth(wline[i]);
1205
1206 if (width == 0) {
1207 i++;
1208 continue;
1209 }
1210
1211 if (width == 1 || width == 2) {
1212 if (cols + width > wlimit)
1213 break;
1214 cols += width;
1215 i++;
1216 } else if (width == -1) {
1217 if (wline[i] == L'\t') {
1218 width = TABSIZE -
1219 ((cols + col_tab_align) % TABSIZE);
1220 } else {
1221 width = 1;
1222 wline[i] = L'.';
1223 }
1224 if (cols + width > wlimit)
1225 break;
1226 cols += width;
1227 i++;
1228 } else {
1229 err = got_error_from_errno("wcwidth");
1230 goto done;
1231 }
1232 }
1233 wline[i] = L'\0';
1234 if (widthp)
1235 *widthp = cols;
1236done:
1237 if (err)
1238 free(wline);
1239 else
1240 *wlinep = wline;
1241 return err;
1242}
1243
1244static const struct got_error*
1245build_refs_str(char **refs_str, struct got_reflist_head *refs,
1246 struct got_object_id *id, struct got_repository *repo)
1247{
1248 static const struct got_error *err = NULL((void*)0);
1249 struct got_reflist_entry *re;
1250 char *s;
1251 const char *name;
1252
1253 *refs_str = NULL((void*)0);
1254
1255 TAILQ_FOREACH(re, refs, entry)for((re) = ((refs)->tqh_first); (re) != ((void*)0); (re) =
((re)->entry.tqe_next))
{
1256 struct got_tag_object *tag = NULL((void*)0);
1257 struct got_object_id *ref_id;
1258 int cmp;
1259
1260 name = got_ref_get_name(re->ref);
1261 if (strcmp(name, GOT_REF_HEAD"HEAD") == 0)
1262 continue;
1263 if (strncmp(name, "refs/", 5) == 0)
1264 name += 5;
1265 if (strncmp(name, "got/", 4) == 0)
1266 continue;
1267 if (strncmp(name, "heads/", 6) == 0)
1268 name += 6;
1269 if (strncmp(name, "remotes/", 8) == 0) {
1270 name += 8;
1271 s = strstr(name, "/" GOT_REF_HEAD"HEAD");
1272 if (s != NULL((void*)0) && s[strlen(s)] == '\0')
1273 continue;
1274 }
1275 err = got_ref_resolve(&ref_id, repo, re->ref);
1276 if (err)
1277 break;
1278 if (strncmp(name, "tags/", 5) == 0) {
1279 err = got_object_open_as_tag(&tag, repo, ref_id);
1280 if (err) {
1281 if (err->code != GOT_ERR_OBJ_TYPE11) {
1282 free(ref_id);
1283 break;
1284 }
1285 /* Ref points at something other than a tag. */
1286 err = NULL((void*)0);
1287 tag = NULL((void*)0);
1288 }
1289 }
1290 cmp = got_object_id_cmp(tag ?
1291 got_object_tag_get_object_id(tag) : ref_id, id);
1292 free(ref_id);
1293 if (tag)
1294 got_object_tag_close(tag);
1295 if (cmp != 0)
1296 continue;
1297 s = *refs_str;
1298 if (asprintf(refs_str, "%s%s%s", s ? s : "",
1299 s ? ", " : "", name) == -1) {
1300 err = got_error_from_errno("asprintf");
1301 free(s);
1302 *refs_str = NULL((void*)0);
1303 break;
1304 }
1305 free(s);
1306 }
1307
1308 return err;
1309}
1310
1311static const struct got_error *
1312format_author(wchar_t **wauthor, int *author_width, char *author, int limit,
1313 int col_tab_align)
1314{
1315 char *smallerthan;
1316
1317 smallerthan = strchr(author, '<');
1318 if (smallerthan && smallerthan[1] != '\0')
1319 author = smallerthan + 1;
1320 author[strcspn(author, "@>")] = '\0';
1321 return format_line(wauthor, author_width, author, limit, col_tab_align);
1322}
1323
1324static const struct got_error *
1325draw_commit(struct tog_view *view, struct got_commit_object *commit,
1326 struct got_object_id *id, const size_t date_display_cols,
1327 int author_display_cols)
1328{
1329 struct tog_log_view_state *s = &view->state.log;
1330 const struct got_error *err = NULL((void*)0);
1331 char datebuf[12]; /* YYYY-MM-DD + SPACE + NUL */
1332 char *logmsg0 = NULL((void*)0), *logmsg = NULL((void*)0);
1333 char *author = NULL((void*)0);
1334 wchar_t *wlogmsg = NULL((void*)0), *wauthor = NULL((void*)0);
1335 int author_width, logmsg_width;
1336 char *newline, *line = NULL((void*)0);
1337 int col, limit;
1338 const int avail = view->ncols;
1339 struct tm tm;
1340 time_t committer_time;
1341 struct tog_color *tc;
1342
1343 committer_time = got_object_commit_get_committer_time(commit);
1344 if (localtime_r(&committer_time, &tm) == NULL((void*)0))
1345 return got_error_from_errno("localtime_r");
1346 if (strftime(datebuf, sizeof(datebuf), "%G-%m-%d ", &tm) == 0)
1347 return got_error(GOT_ERR_NO_SPACE9);
1348
1349 if (avail <= date_display_cols)
1350 limit = MIN(sizeof(datebuf) - 1, avail)((sizeof(datebuf) - 1) < (avail) ? (sizeof(datebuf) - 1) :
(avail))
;
1351 else
1352 limit = MIN(date_display_cols, sizeof(datebuf) - 1)((date_display_cols) < (sizeof(datebuf) - 1) ? (date_display_cols
) : (sizeof(datebuf) - 1))
;
1353 tc = get_color(&s->colors, TOG_COLOR_DATE11);
1354 if (tc)
1355 wattr_on(view->window,
1356 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
1357 waddnstr(view->window, datebuf, limit);
1358 if (tc)
1359 wattr_off(view->window,
1360 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
1361 col = limit;
1362 if (col > avail)
1363 goto done;
1364
1365 if (avail >= 120) {
1366 char *id_str;
1367 err = got_object_id_str(&id_str, id);
1368 if (err)
1369 goto done;
1370 tc = get_color(&s->colors, TOG_COLOR_COMMIT9);
1371 if (tc)
1372 wattr_on(view->window,
1373 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
1374 wprintw(view->window, "%.8s ", id_str);
1375 if (tc)
1376 wattr_off(view->window,
1377 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
1378 free(id_str);
1379 col += 9;
1380 if (col > avail)
1381 goto done;
1382 }
1383
1384 author = strdup(got_object_commit_get_author(commit));
1385 if (author == NULL((void*)0)) {
1386 err = got_error_from_errno("strdup");
1387 goto done;
1388 }
1389 err = format_author(&wauthor, &author_width, author, avail - col, col);
1390 if (err)
1391 goto done;
1392 tc = get_color(&s->colors, TOG_COLOR_AUTHOR10);
1393 if (tc)
1394 wattr_on(view->window,
1395 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
1396 waddwstr(view->window, wauthor)waddnwstr(view->window,wauthor,-1);
1397 if (tc)
1398 wattr_off(view->window,
1399 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
1400 col += author_width;
1401 while (col < avail && author_width < author_display_cols + 2) {
1402 waddch(view->window, ' ');
1403 col++;
1404 author_width++;
1405 }
1406 if (col > avail)
1407 goto done;
1408
1409 err = got_object_commit_get_logmsg(&logmsg0, commit);
1410 if (err)
1411 goto done;
1412 logmsg = logmsg0;
1413 while (*logmsg == '\n')
1414 logmsg++;
1415 newline = strchr(logmsg, '\n');
1416 if (newline)
1417 *newline = '\0';
1418 limit = avail - col;
1419 err = format_line(&wlogmsg, &logmsg_width, logmsg, limit, col);
1420 if (err)
1421 goto done;
1422 waddwstr(view->window, wlogmsg)waddnwstr(view->window,wlogmsg,-1);
1423 col += logmsg_width;
1424 while (col < avail) {
1425 waddch(view->window, ' ');
1426 col++;
1427 }
1428done:
1429 free(logmsg0);
1430 free(wlogmsg);
1431 free(author);
1432 free(wauthor);
1433 free(line);
1434 return err;
1435}
1436
1437static struct commit_queue_entry *
1438alloc_commit_queue_entry(struct got_commit_object *commit,
1439 struct got_object_id *id)
1440{
1441 struct commit_queue_entry *entry;
1442
1443 entry = calloc(1, sizeof(*entry));
1444 if (entry == NULL((void*)0))
1445 return NULL((void*)0);
1446
1447 entry->id = id;
1448 entry->commit = commit;
1449 return entry;
1450}
1451
1452static void
1453pop_commit(struct commit_queue *commits)
1454{
1455 struct commit_queue_entry *entry;
1456
1457 entry = TAILQ_FIRST(&commits->head)((&commits->head)->tqh_first);
1458 TAILQ_REMOVE(&commits->head, entry, entry)do { if (((entry)->entry.tqe_next) != ((void*)0)) (entry)->
entry.tqe_next->entry.tqe_prev = (entry)->entry.tqe_prev
; else (&commits->head)->tqh_last = (entry)->entry
.tqe_prev; *(entry)->entry.tqe_prev = (entry)->entry.tqe_next
; ; ; } while (0)
;
1459 got_object_commit_close(entry->commit);
1460 commits->ncommits--;
1461 /* Don't free entry->id! It is owned by the commit graph. */
1462 free(entry);
1463}
1464
1465static void
1466free_commits(struct commit_queue *commits)
1467{
1468 while (!TAILQ_EMPTY(&commits->head)(((&commits->head)->tqh_first) == ((void*)0)))
1469 pop_commit(commits);
1470}
1471
1472static const struct got_error *
1473match_commit(int *have_match, struct got_object_id *id,
1474 struct got_commit_object *commit, regex_t *regex)
1475{
1476 const struct got_error *err = NULL((void*)0);
1477 regmatch_t regmatch;
1478 char *id_str = NULL((void*)0), *logmsg = NULL((void*)0);
1479
1480 *have_match = 0;
1481
1482 err = got_object_id_str(&id_str, id);
1483 if (err)
1484 return err;
1485
1486 err = got_object_commit_get_logmsg(&logmsg, commit);
1487 if (err)
1488 goto done;
1489
1490 if (regexec(regex, got_object_commit_get_author(commit), 1,
1491 &regmatch, 0) == 0 ||
1492 regexec(regex, got_object_commit_get_committer(commit), 1,
1493 &regmatch, 0) == 0 ||
1494 regexec(regex, id_str, 1, &regmatch, 0) == 0 ||
1495 regexec(regex, logmsg, 1, &regmatch, 0) == 0)
1496 *have_match = 1;
1497done:
1498 free(id_str);
1499 free(logmsg);
1500 return err;
1501}
1502
1503static const struct got_error *
1504queue_commits(struct tog_log_thread_args *a)
1505{
1506 const struct got_error *err = NULL((void*)0);
1507
1508 /*
1509 * We keep all commits open throughout the lifetime of the log
1510 * view in order to avoid having to re-fetch commits from disk
1511 * while updating the display.
1512 */
1513 do {
1514 struct got_object_id *id;
1515 struct got_commit_object *commit;
1516 struct commit_queue_entry *entry;
1517 int errcode;
1518
1519 err = got_commit_graph_iter_next(&id, a->graph, a->repo,
1520 NULL((void*)0), NULL((void*)0));
1521 if (err || id == NULL((void*)0))
1522 break;
1523
1524 err = got_object_open_as_commit(&commit, a->repo, id);
1525 if (err)
1526 break;
1527 entry = alloc_commit_queue_entry(commit, id);
1528 if (entry == NULL((void*)0)) {
1529 err = got_error_from_errno("alloc_commit_queue_entry");
1530 break;
1531 }
1532
1533 errcode = pthread_mutex_lock(&tog_mutex);
1534 if (errcode) {
1535 err = got_error_set_errno(errcode,
1536 "pthread_mutex_lock");
1537 break;
1538 }
1539
1540 entry->idx = a->commits->ncommits;
1541 TAILQ_INSERT_TAIL(&a->commits->head, entry, entry)do { (entry)->entry.tqe_next = ((void*)0); (entry)->entry
.tqe_prev = (&a->commits->head)->tqh_last; *(&
a->commits->head)->tqh_last = (entry); (&a->commits
->head)->tqh_last = &(entry)->entry.tqe_next; } while
(0)
;
1542 a->commits->ncommits++;
1543
1544 if (*a->searching == TOG_SEARCH_FORWARD1 &&
1545 !*a->search_next_done) {
1546 int have_match;
1547 err = match_commit(&have_match, id, commit, a->regex);
1548 if (err)
1549 break;
1550 if (have_match)
1551 *a->search_next_done = TOG_SEARCH_HAVE_MORE1;
1552 }
1553
1554 errcode = pthread_mutex_unlock(&tog_mutex);
1555 if (errcode && err == NULL((void*)0))
1556 err = got_error_set_errno(errcode,
1557 "pthread_mutex_unlock");
1558 if (err)
1559 break;
1560 } while (*a->searching == TOG_SEARCH_FORWARD1 && !*a->search_next_done);
1561
1562 return err;
1563}
1564
1565static void
1566select_commit(struct tog_log_view_state *s)
1567{
1568 struct commit_queue_entry *entry;
1569 int ncommits = 0;
1570
1571 entry = s->first_displayed_entry;
1572 while (entry) {
1573 if (ncommits == s->selected) {
1574 s->selected_entry = entry;
1575 break;
1576 }
1577 entry = TAILQ_NEXT(entry, entry)((entry)->entry.tqe_next);
1578 ncommits++;
1579 }
1580}
1581
1582static const struct got_error *
1583draw_commits(struct tog_view *view)
1584{
1585 const struct got_error *err = NULL((void*)0);
1586 struct tog_log_view_state *s = &view->state.log;
1587 struct commit_queue_entry *entry = s->selected_entry;
1588 const int limit = view->nlines;
1589 int width;
1590 int ncommits, author_cols = 4;
1591 char *id_str = NULL((void*)0), *header = NULL((void*)0), *ncommits_str = NULL((void*)0);
1592 char *refs_str = NULL((void*)0);
1593 wchar_t *wline;
1594 struct tog_color *tc;
1595 static const size_t date_display_cols = 12;
1596
1597 if (s->selected_entry &&
1598 !(view->searching && view->search_next_done == 0)) {
1599 struct got_reflist_head *refs;
1600 err = got_object_id_str(&id_str, s->selected_entry->id);
1601 if (err)
1602 return err;
1603 refs = got_reflist_object_id_map_lookup(tog_refs_idmap,
1604 s->selected_entry->id);
1605 if (refs) {
1606 err = build_refs_str(&refs_str, refs,
1607 s->selected_entry->id, s->repo);
1608 if (err)
1609 goto done;
1610 }
1611 }
1612
1613 if (s->thread_args.commits_needed == 0)
1614 halfdelay(10); /* disable fast refresh */
1615
1616 if (s->thread_args.commits_needed > 0) {
1617 if (asprintf(&ncommits_str, " [%d/%d] %s",
1618 entry ? entry->idx + 1 : 0, s->commits.ncommits,
1619 (view->searching && !view->search_next_done) ?
1620 "searching..." : "loading...") == -1) {
1621 err = got_error_from_errno("asprintf");
1622 goto done;
1623 }
1624 } else {
1625 const char *search_str = NULL((void*)0);
1626
1627 if (view->searching) {
1628 if (view->search_next_done == TOG_SEARCH_NO_MORE2)
1629 search_str = "no more matches";
1630 else if (view->search_next_done == TOG_SEARCH_HAVE_NONE3)
1631 search_str = "no matches found";
1632 else if (!view->search_next_done)
1633 search_str = "searching...";
1634 }
1635
1636 if (asprintf(&ncommits_str, " [%d/%d] %s",
1637 entry ? entry->idx + 1 : 0, s->commits.ncommits,
1638 search_str ? search_str :
1639 (refs_str ? refs_str : "")) == -1) {
1640 err = got_error_from_errno("asprintf");
1641 goto done;
1642 }
1643 }
1644
1645 if (s->in_repo_path && strcmp(s->in_repo_path, "/") != 0) {
1646 if (asprintf(&header, "commit %s %s%s",
1647 id_str ? id_str : "........................................",
1648 s->in_repo_path, ncommits_str) == -1) {
1649 err = got_error_from_errno("asprintf");
1650 header = NULL((void*)0);
1651 goto done;
1652 }
1653 } else if (asprintf(&header, "commit %s%s",
1654 id_str ? id_str : "........................................",
1655 ncommits_str) == -1) {
1656 err = got_error_from_errno("asprintf");
1657 header = NULL((void*)0);
1658 goto done;
1659 }
1660 err = format_line(&wline, &width, header, view->ncols, 0);
1661 if (err)
1662 goto done;
1663
1664 werase(view->window);
1665
1666 if (view_needs_focus_indication(view))
1667 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
1668 tc = get_color(&s->colors, TOG_COLOR_COMMIT9);
1669 if (tc)
1670 wattr_on(view->window,
1671 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
1672 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
1673 if (tc)
1674 wattr_off(view->window,
1675 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
1676 while (width < view->ncols) {
1677 waddch(view->window, ' ');
1678 width++;
1679 }
1680 if (view_needs_focus_indication(view))
1681 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
1682 free(wline);
1683 if (limit <= 1)
1684 goto done;
1685
1686 /* Grow author column size if necessary. */
1687 entry = s->first_displayed_entry;
1688 ncommits = 0;
1689 while (entry) {
1690 char *author;
1691 wchar_t *wauthor;
1692 int width;
1693 if (ncommits >= limit - 1)
1694 break;
1695 author = strdup(got_object_commit_get_author(entry->commit));
1696 if (author == NULL((void*)0)) {
1697 err = got_error_from_errno("strdup");
1698 goto done;
1699 }
1700 err = format_author(&wauthor, &width, author, COLS,
1701 date_display_cols);
1702 if (author_cols < width)
1703 author_cols = width;
1704 free(wauthor);
1705 free(author);
1706 ncommits++;
1707 entry = TAILQ_NEXT(entry, entry)((entry)->entry.tqe_next);
1708 }
1709
1710 entry = s->first_displayed_entry;
1711 s->last_displayed_entry = s->first_displayed_entry;
1712 ncommits = 0;
1713 while (entry) {
1714 if (ncommits >= limit - 1)
1715 break;
1716 if (ncommits == s->selected)
1717 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
1718 err = draw_commit(view, entry->commit, entry->id,
1719 date_display_cols, author_cols);
1720 if (ncommits == s->selected)
1721 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
1722 if (err)
1723 goto done;
1724 ncommits++;
1725 s->last_displayed_entry = entry;
1726 entry = TAILQ_NEXT(entry, entry)((entry)->entry.tqe_next);
1727 }
1728
1729 view_vborder(view);
1730done:
1731 free(id_str);
1732 free(refs_str);
1733 free(ncommits_str);
1734 free(header);
1735 return err;
1736}
1737
1738static void
1739log_scroll_up(struct tog_log_view_state *s, int maxscroll)
1740{
1741 struct commit_queue_entry *entry;
1742 int nscrolled = 0;
1743
1744 entry = TAILQ_FIRST(&s->commits.head)((&s->commits.head)->tqh_first);
1745 if (s->first_displayed_entry == entry)
1746 return;
1747
1748 entry = s->first_displayed_entry;
1749 while (entry && nscrolled < maxscroll) {
1750 entry = TAILQ_PREV(entry, commit_queue_head, entry)(*(((struct commit_queue_head *)((entry)->entry.tqe_prev))
->tqh_last))
;
1751 if (entry) {
1752 s->first_displayed_entry = entry;
1753 nscrolled++;
1754 }
1755 }
1756}
1757
1758static const struct got_error *
1759trigger_log_thread(struct tog_view *view, int wait)
1760{
1761 struct tog_log_thread_args *ta = &view->state.log.thread_args;
1762 int errcode;
1763
1764 halfdelay(1); /* fast refresh while loading commits */
1765
1766 while (ta->commits_needed > 0) {
1767 if (ta->log_complete)
1768 break;
1769
1770 /* Wake the log thread. */
1771 errcode = pthread_cond_signal(&ta->need_commits);
1772 if (errcode)
1773 return got_error_set_errno(errcode,
1774 "pthread_cond_signal");
1775
1776 /*
1777 * The mutex will be released while the view loop waits
1778 * in wgetch(), at which time the log thread will run.
1779 */
1780 if (!wait)
1781 break;
1782
1783 /* Display progress update in log view. */
1784 show_log_view(view);
1785 update_panels();
1786 doupdate();
1787
1788 /* Wait right here while next commit is being loaded. */
1789 errcode = pthread_cond_wait(&ta->commit_loaded, &tog_mutex);
1790 if (errcode)
1791 return got_error_set_errno(errcode,
1792 "pthread_cond_wait");
1793
1794 /* Display progress update in log view. */
1795 show_log_view(view);
1796 update_panels();
1797 doupdate();
1798 }
1799
1800 return NULL((void*)0);
1801}
1802
1803static const struct got_error *
1804log_scroll_down(struct tog_view *view, int maxscroll)
1805{
1806 struct tog_log_view_state *s = &view->state.log;
1807 const struct got_error *err = NULL((void*)0);
1808 struct commit_queue_entry *pentry;
1809 int nscrolled = 0, ncommits_needed;
1810
1811 if (s->last_displayed_entry == NULL((void*)0))
1812 return NULL((void*)0);
1813
1814 ncommits_needed = s->last_displayed_entry->idx + 1 + maxscroll;
1815 if (s->commits.ncommits < ncommits_needed &&
1816 !s->thread_args.log_complete) {
1817 /*
1818 * Ask the log thread for required amount of commits.
1819 */
1820 s->thread_args.commits_needed += maxscroll;
1821 err = trigger_log_thread(view, 1);
1822 if (err)
1823 return err;
1824 }
1825
1826 do {
1827 pentry = TAILQ_NEXT(s->last_displayed_entry, entry)((s->last_displayed_entry)->entry.tqe_next);
1828 if (pentry == NULL((void*)0))
1829 break;
1830
1831 s->last_displayed_entry = pentry;
1832
1833 pentry = TAILQ_NEXT(s->first_displayed_entry, entry)((s->first_displayed_entry)->entry.tqe_next);
1834 if (pentry == NULL((void*)0))
1835 break;
1836 s->first_displayed_entry = pentry;
1837 } while (++nscrolled < maxscroll);
1838
1839 return err;
1840}
1841
1842static const struct got_error *
1843open_diff_view_for_commit(struct tog_view **new_view, int begin_x,
1844 struct got_commit_object *commit, struct got_object_id *commit_id,
1845 struct tog_view *log_view, struct got_repository *repo)
1846{
1847 const struct got_error *err;
1848 struct got_object_qid *parent_id;
1849 struct tog_view *diff_view;
1850
1851 diff_view = view_open(0, 0, 0, begin_x, TOG_VIEW_DIFF);
1852 if (diff_view == NULL((void*)0))
1853 return got_error_from_errno("view_open");
1854
1855 parent_id = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit))((got_object_commit_get_parent_ids(commit))->sqh_first);
1856 err = open_diff_view(diff_view, parent_id ? parent_id->id : NULL((void*)0),
1857 commit_id, NULL((void*)0), NULL((void*)0), 3, 0, 0, log_view, repo);
1858 if (err == NULL((void*)0))
1859 *new_view = diff_view;
1860 return err;
1861}
1862
1863static const struct got_error *
1864tree_view_visit_subtree(struct tog_tree_view_state *s,
1865 struct got_tree_object *subtree)
1866{
1867 struct tog_parent_tree *parent;
1868
1869 parent = calloc(1, sizeof(*parent));
1870 if (parent == NULL((void*)0))
1871 return got_error_from_errno("calloc");
1872
1873 parent->tree = s->tree;
1874 parent->first_displayed_entry = s->first_displayed_entry;
1875 parent->selected_entry = s->selected_entry;
1876 parent->selected = s->selected;
1877 TAILQ_INSERT_HEAD(&s->parents, parent, entry)do { if (((parent)->entry.tqe_next = (&s->parents)->
tqh_first) != ((void*)0)) (&s->parents)->tqh_first->
entry.tqe_prev = &(parent)->entry.tqe_next; else (&
s->parents)->tqh_last = &(parent)->entry.tqe_next
; (&s->parents)->tqh_first = (parent); (parent)->
entry.tqe_prev = &(&s->parents)->tqh_first; } while
(0)
;
1878 s->tree = subtree;
1879 s->selected = 0;
1880 s->first_displayed_entry = NULL((void*)0);
1881 return NULL((void*)0);
1882}
1883
1884static const struct got_error *
1885tree_view_walk_path(struct tog_tree_view_state *s,
1886 struct got_object_id *commit_id, const char *path)
1887{
1888 const struct got_error *err = NULL((void*)0);
1889 struct got_tree_object *tree = NULL((void*)0);
1890 const char *p;
1891 char *slash, *subpath = NULL((void*)0);
1892
1893 /* Walk the path and open corresponding tree objects. */
1894 p = path;
1895 while (*p) {
1896 struct got_tree_entry *te;
1897 struct got_object_id *tree_id;
1898 char *te_name;
1899
1900 while (p[0] == '/')
1901 p++;
1902
1903 /* Ensure the correct subtree entry is selected. */
1904 slash = strchr(p, '/');
1905 if (slash == NULL((void*)0))
1906 te_name = strdup(p);
1907 else
1908 te_name = strndup(p, slash - p);
1909 if (te_name == NULL((void*)0)) {
1910 err = got_error_from_errno("strndup");
1911 break;
1912 }
1913 te = got_object_tree_find_entry(s->tree, te_name);
1914 if (te == NULL((void*)0)) {
1915 err = got_error_path(te_name, GOT_ERR_NO_TREE_ENTRY50);
1916 free(te_name);
1917 break;
1918 }
1919 free(te_name);
1920 s->first_displayed_entry = s->selected_entry = te;
1921
1922 if (!S_ISDIR(got_tree_entry_get_mode(s->selected_entry))((got_tree_entry_get_mode(s->selected_entry) & 0170000
) == 0040000)
)
1923 break; /* jump to this file's entry */
1924
1925 slash = strchr(p, '/');
1926 if (slash)
1927 subpath = strndup(path, slash - path);
1928 else
1929 subpath = strdup(path);
1930 if (subpath == NULL((void*)0)) {
1931 err = got_error_from_errno("strdup");
1932 break;
1933 }
1934
1935 err = got_object_id_by_path(&tree_id, s->repo, commit_id,
1936 subpath);
1937 if (err)
1938 break;
1939
1940 err = got_object_open_as_tree(&tree, s->repo, tree_id);
1941 free(tree_id);
1942 if (err)
1943 break;
1944
1945 err = tree_view_visit_subtree(s, tree);
1946 if (err) {
1947 got_object_tree_close(tree);
1948 break;
1949 }
1950 if (slash == NULL((void*)0))
1951 break;
1952 free(subpath);
1953 subpath = NULL((void*)0);
1954 p = slash;
1955 }
1956
1957 free(subpath);
1958 return err;
1959}
1960
1961static const struct got_error *
1962browse_commit_tree(struct tog_view **new_view, int begin_x,
1963 struct commit_queue_entry *entry, const char *path,
1964 const char *head_ref_name, struct got_repository *repo)
1965{
1966 const struct got_error *err = NULL((void*)0);
1967 struct got_tree_object *tree;
1968 struct tog_tree_view_state *s;
1969 struct tog_view *tree_view;
1970
1971 err = got_object_open_as_tree(&tree, repo,
1972 got_object_commit_get_tree_id(entry->commit));
1973 if (err)
1974 return err;
1975
1976 tree_view = view_open(0, 0, 0, begin_x, TOG_VIEW_TREE);
1977 if (tree_view == NULL((void*)0))
1978 return got_error_from_errno("view_open");
1979
1980 err = open_tree_view(tree_view, tree, entry->id, head_ref_name, repo);
1981 if (err) {
1982 got_object_tree_close(tree);
1983 return err;
1984 }
1985 s = &tree_view->state.tree;
1986
1987 *new_view = tree_view;
1988
1989 if (got_path_is_root_dir(path))
1990 return NULL((void*)0);
1991
1992 return tree_view_walk_path(s, entry->id, path);
1993}
1994
1995static const struct got_error *
1996block_signals_used_by_main_thread(void)
1997{
1998 sigset_t sigset;
1999 int errcode;
2000
2001 if (sigemptyset(&sigset) == -1)
2002 return got_error_from_errno("sigemptyset");
2003
2004 /* tog handles SIGWINCH and SIGCONT */
2005 if (sigaddset(&sigset, SIGWINCH28) == -1)
2006 return got_error_from_errno("sigaddset");
2007 if (sigaddset(&sigset, SIGCONT19) == -1)
2008 return got_error_from_errno("sigaddset");
2009
2010 /* ncurses handles SIGTSTP */
2011 if (sigaddset(&sigset, SIGTSTP18) == -1)
2012 return got_error_from_errno("sigaddset");
2013
2014 errcode = pthread_sigmask(SIG_BLOCK1, &sigset, NULL((void*)0));
2015 if (errcode)
2016 return got_error_set_errno(errcode, "pthread_sigmask");
2017
2018 return NULL((void*)0);
2019}
2020
2021static void *
2022log_thread(void *arg)
2023{
2024 const struct got_error *err = NULL((void*)0);
2025 int errcode = 0;
2026 struct tog_log_thread_args *a = arg;
2027 int done = 0;
2028
2029 err = block_signals_used_by_main_thread();
2030 if (err)
2031 return (void *)err;
2032
2033 while (!done && !err && !tog_sigpipe_received) {
2034 err = queue_commits(a);
2035 if (err) {
2036 if (err->code != GOT_ERR_ITER_COMPLETED46)
2037 return (void *)err;
2038 err = NULL((void*)0);
2039 done = 1;
2040 } else if (a->commits_needed > 0)
2041 a->commits_needed--;
2042
2043 errcode = pthread_mutex_lock(&tog_mutex);
2044 if (errcode) {
2045 err = got_error_set_errno(errcode,
2046 "pthread_mutex_lock");
2047 break;
2048 } else if (*a->quit)
2049 done = 1;
2050 else if (*a->first_displayed_entry == NULL((void*)0)) {
2051 *a->first_displayed_entry =
2052 TAILQ_FIRST(&a->commits->head)((&a->commits->head)->tqh_first);
2053 *a->selected_entry = *a->first_displayed_entry;
2054 }
2055
2056 errcode = pthread_cond_signal(&a->commit_loaded);
2057 if (errcode) {
2058 err = got_error_set_errno(errcode,
2059 "pthread_cond_signal");
2060 pthread_mutex_unlock(&tog_mutex);
2061 break;
2062 }
2063
2064 if (done)
2065 a->commits_needed = 0;
2066 else {
2067 if (a->commits_needed == 0) {
2068 errcode = pthread_cond_wait(&a->need_commits,
2069 &tog_mutex);
2070 if (errcode)
2071 err = got_error_set_errno(errcode,
2072 "pthread_cond_wait");
2073 if (*a->quit)
2074 done = 1;
2075 }
2076 }
2077
2078 errcode = pthread_mutex_unlock(&tog_mutex);
2079 if (errcode && err == NULL((void*)0))
2080 err = got_error_set_errno(errcode,
2081 "pthread_mutex_unlock");
2082 }
2083 a->log_complete = 1;
2084 return (void *)err;
2085}
2086
2087static const struct got_error *
2088stop_log_thread(struct tog_log_view_state *s)
2089{
2090 const struct got_error *err = NULL((void*)0);
2091 int errcode;
2092
2093 if (s->thread) {
2094 s->quit = 1;
2095 errcode = pthread_cond_signal(&s->thread_args.need_commits);
2096 if (errcode)
2097 return got_error_set_errno(errcode,
2098 "pthread_cond_signal");
2099 errcode = pthread_mutex_unlock(&tog_mutex);
2100 if (errcode)
2101 return got_error_set_errno(errcode,
2102 "pthread_mutex_unlock");
2103 errcode = pthread_join(s->thread, (void **)&err);
2104 if (errcode)
2105 return got_error_set_errno(errcode, "pthread_join");
2106 errcode = pthread_mutex_lock(&tog_mutex);
2107 if (errcode)
2108 return got_error_set_errno(errcode,
2109 "pthread_mutex_lock");
2110 s->thread = NULL((void*)0);
2111 }
2112
2113 if (s->thread_args.repo) {
2114 got_repo_close(s->thread_args.repo);
2115 s->thread_args.repo = NULL((void*)0);
2116 }
2117
2118 if (s->thread_args.graph) {
2119 got_commit_graph_close(s->thread_args.graph);
2120 s->thread_args.graph = NULL((void*)0);
2121 }
2122
2123 return err;
2124}
2125
2126static const struct got_error *
2127close_log_view(struct tog_view *view)
2128{
2129 const struct got_error *err = NULL((void*)0);
2130 struct tog_log_view_state *s = &view->state.log;
2131 int errcode;
2132
2133 err = stop_log_thread(s);
2134
2135 errcode = pthread_cond_destroy(&s->thread_args.need_commits);
2136 if (errcode && err == NULL((void*)0))
2137 err = got_error_set_errno(errcode, "pthread_cond_destroy");
2138
2139 errcode = pthread_cond_destroy(&s->thread_args.commit_loaded);
2140 if (errcode && err == NULL((void*)0))
2141 err = got_error_set_errno(errcode, "pthread_cond_destroy");
2142
2143 free_commits(&s->commits);
2144 free(s->in_repo_path);
2145 s->in_repo_path = NULL((void*)0);
2146 free(s->start_id);
2147 s->start_id = NULL((void*)0);
2148 free(s->head_ref_name);
2149 s->head_ref_name = NULL((void*)0);
2150 return err;
2151}
2152
2153static const struct got_error *
2154search_start_log_view(struct tog_view *view)
2155{
2156 struct tog_log_view_state *s = &view->state.log;
2157
2158 s->matched_entry = NULL((void*)0);
2159 s->search_entry = NULL((void*)0);
2160 return NULL((void*)0);
2161}
2162
2163static const struct got_error *
2164search_next_log_view(struct tog_view *view)
2165{
2166 const struct got_error *err = NULL((void*)0);
2167 struct tog_log_view_state *s = &view->state.log;
2168 struct commit_queue_entry *entry;
2169
2170 /* Display progress update in log view. */
2171 show_log_view(view);
2172 update_panels();
2173 doupdate();
2174
2175 if (s->search_entry) {
2176 int errcode, ch;
2177 errcode = pthread_mutex_unlock(&tog_mutex);
2178 if (errcode)
2179 return got_error_set_errno(errcode,
2180 "pthread_mutex_unlock");
2181 ch = wgetch(view->window);
2182 errcode = pthread_mutex_lock(&tog_mutex);
2183 if (errcode)
2184 return got_error_set_errno(errcode,
2185 "pthread_mutex_lock");
2186 if (ch == KEY_BACKSPACE0407) {
2187 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
2188 return NULL((void*)0);
2189 }
2190 if (view->searching == TOG_SEARCH_FORWARD1)
2191 entry = TAILQ_NEXT(s->search_entry, entry)((s->search_entry)->entry.tqe_next);
2192 else
2193 entry = TAILQ_PREV(s->search_entry,(*(((struct commit_queue_head *)((s->search_entry)->entry
.tqe_prev))->tqh_last))
2194 commit_queue_head, entry)(*(((struct commit_queue_head *)((s->search_entry)->entry
.tqe_prev))->tqh_last))
;
2195 } else if (s->matched_entry) {
2196 if (view->searching == TOG_SEARCH_FORWARD1)
2197 entry = TAILQ_NEXT(s->matched_entry, entry)((s->matched_entry)->entry.tqe_next);
2198 else
2199 entry = TAILQ_PREV(s->matched_entry,(*(((struct commit_queue_head *)((s->matched_entry)->entry
.tqe_prev))->tqh_last))
2200 commit_queue_head, entry)(*(((struct commit_queue_head *)((s->matched_entry)->entry
.tqe_prev))->tqh_last))
;
2201 } else {
2202 if (view->searching == TOG_SEARCH_FORWARD1)
2203 entry = TAILQ_FIRST(&s->commits.head)((&s->commits.head)->tqh_first);
2204 else
2205 entry = TAILQ_LAST(&s->commits.head, commit_queue_head)(*(((struct commit_queue_head *)((&s->commits.head)->
tqh_last))->tqh_last))
;
2206 }
2207
2208 while (1) {
2209 int have_match = 0;
2210
2211 if (entry == NULL((void*)0)) {
2212 if (s->thread_args.log_complete ||
2213 view->searching == TOG_SEARCH_BACKWARD2) {
2214 view->search_next_done =
2215 (s->matched_entry == NULL((void*)0) ?
2216 TOG_SEARCH_HAVE_NONE3 : TOG_SEARCH_NO_MORE2);
2217 s->search_entry = NULL((void*)0);
2218 return NULL((void*)0);
2219 }
2220 /*
2221 * Poke the log thread for more commits and return,
2222 * allowing the main loop to make progress. Search
2223 * will resume at s->search_entry once we come back.
2224 */
2225 s->thread_args.commits_needed++;
2226 return trigger_log_thread(view, 0);
2227 }
2228
2229 err = match_commit(&have_match, entry->id, entry->commit,
2230 &view->regex);
2231 if (err)
2232 break;
2233 if (have_match) {
2234 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
2235 s->matched_entry = entry;
2236 break;
2237 }
2238
2239 s->search_entry = entry;
2240 if (view->searching == TOG_SEARCH_FORWARD1)
2241 entry = TAILQ_NEXT(entry, entry)((entry)->entry.tqe_next);
2242 else
2243 entry = TAILQ_PREV(entry, commit_queue_head, entry)(*(((struct commit_queue_head *)((entry)->entry.tqe_prev))
->tqh_last))
;
2244 }
2245
2246 if (s->matched_entry) {
2247 int cur = s->selected_entry->idx;
2248 while (cur < s->matched_entry->idx) {
2249 err = input_log_view(NULL((void*)0), view, KEY_DOWN0402);
2250 if (err)
2251 return err;
2252 cur++;
2253 }
2254 while (cur > s->matched_entry->idx) {
2255 err = input_log_view(NULL((void*)0), view, KEY_UP0403);
2256 if (err)
2257 return err;
2258 cur--;
2259 }
2260 }
2261
2262 s->search_entry = NULL((void*)0);
2263
2264 return NULL((void*)0);
2265}
2266
2267static const struct got_error *
2268open_log_view(struct tog_view *view, struct got_object_id *start_id,
2269 struct got_repository *repo, const char *head_ref_name,
2270 const char *in_repo_path, int log_branches)
2271{
2272 const struct got_error *err = NULL((void*)0);
2273 struct tog_log_view_state *s = &view->state.log;
2274 struct got_repository *thread_repo = NULL((void*)0);
2275 struct got_commit_graph *thread_graph = NULL((void*)0);
2276 int errcode;
2277
2278 if (in_repo_path != s->in_repo_path) {
2279 free(s->in_repo_path);
2280 s->in_repo_path = strdup(in_repo_path);
2281 if (s->in_repo_path == NULL((void*)0))
2282 return got_error_from_errno("strdup");
2283 }
2284
2285 /* The commit queue only contains commits being displayed. */
2286 TAILQ_INIT(&s->commits.head)do { (&s->commits.head)->tqh_first = ((void*)0); (&
s->commits.head)->tqh_last = &(&s->commits.head
)->tqh_first; } while (0)
;
2287 s->commits.ncommits = 0;
2288
2289 s->repo = repo;
2290 if (head_ref_name) {
2291 s->head_ref_name = strdup(head_ref_name);
2292 if (s->head_ref_name == NULL((void*)0)) {
2293 err = got_error_from_errno("strdup");
2294 goto done;
2295 }
2296 }
2297 s->start_id = got_object_id_dup(start_id);
2298 if (s->start_id == NULL((void*)0)) {
2299 err = got_error_from_errno("got_object_id_dup");
2300 goto done;
2301 }
2302 s->log_branches = log_branches;
2303
2304 SIMPLEQ_INIT(&s->colors)do { (&s->colors)->sqh_first = ((void*)0); (&s->
colors)->sqh_last = &(&s->colors)->sqh_first
; } while (0)
;
2305 if (has_colors() && getenv("TOG_COLORS") != NULL((void*)0)) {
2306 err = add_color(&s->colors, "^$", TOG_COLOR_COMMIT9,
2307 get_color_value("TOG_COLOR_COMMIT"));
2308 if (err)
2309 goto done;
2310 err = add_color(&s->colors, "^$", TOG_COLOR_AUTHOR10,
2311 get_color_value("TOG_COLOR_AUTHOR"));
2312 if (err) {
2313 free_colors(&s->colors);
2314 goto done;
2315 }
2316 err = add_color(&s->colors, "^$", TOG_COLOR_DATE11,
2317 get_color_value("TOG_COLOR_DATE"));
2318 if (err) {
2319 free_colors(&s->colors);
2320 goto done;
2321 }
2322 }
2323
2324 view->show = show_log_view;
2325 view->input = input_log_view;
2326 view->close = close_log_view;
2327 view->search_start = search_start_log_view;
2328 view->search_next = search_next_log_view;
2329
2330 err = got_repo_open(&thread_repo, got_repo_get_path(repo), NULL((void*)0));
2331 if (err)
2332 goto done;
2333 err = got_commit_graph_open(&thread_graph, s->in_repo_path,
2334 !s->log_branches);
2335 if (err)
2336 goto done;
2337 err = got_commit_graph_iter_start(thread_graph, s->start_id,
2338 s->repo, NULL((void*)0), NULL((void*)0));
2339 if (err)
2340 goto done;
2341
2342 errcode = pthread_cond_init(&s->thread_args.need_commits, NULL((void*)0));
2343 if (errcode) {
2344 err = got_error_set_errno(errcode, "pthread_cond_init");
2345 goto done;
2346 }
2347 errcode = pthread_cond_init(&s->thread_args.commit_loaded, NULL((void*)0));
2348 if (errcode) {
2349 err = got_error_set_errno(errcode, "pthread_cond_init");
2350 goto done;
2351 }
2352
2353 s->thread_args.commits_needed = view->nlines;
2354 s->thread_args.graph = thread_graph;
2355 s->thread_args.commits = &s->commits;
2356 s->thread_args.in_repo_path = s->in_repo_path;
2357 s->thread_args.start_id = s->start_id;
2358 s->thread_args.repo = thread_repo;
2359 s->thread_args.log_complete = 0;
2360 s->thread_args.quit = &s->quit;
2361 s->thread_args.first_displayed_entry = &s->first_displayed_entry;
2362 s->thread_args.selected_entry = &s->selected_entry;
2363 s->thread_args.searching = &view->searching;
2364 s->thread_args.search_next_done = &view->search_next_done;
2365 s->thread_args.regex = &view->regex;
2366done:
2367 if (err)
2368 close_log_view(view);
2369 return err;
2370}
2371
2372static const struct got_error *
2373show_log_view(struct tog_view *view)
2374{
2375 const struct got_error *err;
2376 struct tog_log_view_state *s = &view->state.log;
2377
2378 if (s->thread == NULL((void*)0)) {
2379 int errcode = pthread_create(&s->thread, NULL((void*)0), log_thread,
2380 &s->thread_args);
2381 if (errcode)
2382 return got_error_set_errno(errcode, "pthread_create");
2383 if (s->thread_args.commits_needed > 0) {
2384 err = trigger_log_thread(view, 1);
2385 if (err)
2386 return err;
2387 }
2388 }
2389
2390 return draw_commits(view);
2391}
2392
2393static const struct got_error *
2394input_log_view(struct tog_view **new_view, struct tog_view *view, int ch)
2395{
2396 const struct got_error *err = NULL((void*)0);
2397 struct tog_log_view_state *s = &view->state.log;
2398 struct tog_view *diff_view = NULL((void*)0), *tree_view = NULL((void*)0);
2399 struct tog_view *ref_view = NULL((void*)0);
2400 int begin_x = 0;
2401
2402 switch (ch) {
2403 case 'q':
2404 s->quit = 1;
2405 break;
2406 case 'k':
2407 case KEY_UP0403:
2408 case '<':
2409 case ',':
2410 if (s->first_displayed_entry == NULL((void*)0))
2411 break;
2412 if (s->selected > 0)
2413 s->selected--;
2414 else
2415 log_scroll_up(s, 1);
2416 select_commit(s);
2417 break;
2418 case KEY_PPAGE0523:
2419 case CTRL('b')(('b') & 0x1f):
2420 if (s->first_displayed_entry == NULL((void*)0))
2421 break;
2422 if (TAILQ_FIRST(&s->commits.head)((&s->commits.head)->tqh_first) == s->first_displayed_entry)
2423 s->selected = 0;
2424 else
2425 log_scroll_up(s, view->nlines - 1);
2426 select_commit(s);
2427 break;
2428 case 'j':
2429 case KEY_DOWN0402:
2430 case '>':
2431 case '.':
2432 if (s->first_displayed_entry == NULL((void*)0))
2433 break;
2434 if (s->selected < MIN(view->nlines - 2,((view->nlines - 2) < (s->commits.ncommits - 1) ? (view
->nlines - 2) : (s->commits.ncommits - 1))
2435 s->commits.ncommits - 1)((view->nlines - 2) < (s->commits.ncommits - 1) ? (view
->nlines - 2) : (s->commits.ncommits - 1))
)
2436 s->selected++;
2437 else {
2438 err = log_scroll_down(view, 1);
2439 if (err)
2440 break;
2441 }
2442 select_commit(s);
2443 break;
2444 case KEY_NPAGE0522:
2445 case CTRL('f')(('f') & 0x1f): {
2446 struct commit_queue_entry *first;
2447 first = s->first_displayed_entry;
2448 if (first == NULL((void*)0))
2449 break;
2450 err = log_scroll_down(view, view->nlines - 1);
2451 if (err)
2452 break;
2453 if (first == s->first_displayed_entry &&
2454 s->selected < MIN(view->nlines - 2,((view->nlines - 2) < (s->commits.ncommits - 1) ? (view
->nlines - 2) : (s->commits.ncommits - 1))
2455 s->commits.ncommits - 1)((view->nlines - 2) < (s->commits.ncommits - 1) ? (view
->nlines - 2) : (s->commits.ncommits - 1))
) {
2456 /* can't scroll further down */
2457 s->selected = MIN(view->nlines - 2,((view->nlines - 2) < (s->commits.ncommits - 1) ? (view
->nlines - 2) : (s->commits.ncommits - 1))
2458 s->commits.ncommits - 1)((view->nlines - 2) < (s->commits.ncommits - 1) ? (view
->nlines - 2) : (s->commits.ncommits - 1))
;
2459 }
2460 select_commit(s);
2461 break;
2462 }
2463 case KEY_RESIZE0632:
2464 if (s->selected > view->nlines - 2)
2465 s->selected = view->nlines - 2;
2466 if (s->selected > s->commits.ncommits - 1)
2467 s->selected = s->commits.ncommits - 1;
2468 select_commit(s);
2469 if (s->commits.ncommits < view->nlines - 1 &&
2470 !s->thread_args.log_complete) {
2471 s->thread_args.commits_needed += (view->nlines - 1) -
2472 s->commits.ncommits;
2473 err = trigger_log_thread(view, 1);
2474 }
2475 break;
2476 case KEY_ENTER0527:
2477 case ' ':
2478 case '\r':
2479 if (s->selected_entry == NULL((void*)0))
2480 break;
2481 if (view_is_parent_view(view))
2482 begin_x = view_split_begin_x(view->begin_x);
2483 err = open_diff_view_for_commit(&diff_view, begin_x,
2484 s->selected_entry->commit, s->selected_entry->id,
2485 view, s->repo);
2486 if (err)
2487 break;
2488 view->focussed = 0;
2489 diff_view->focussed = 1;
2490 if (view_is_parent_view(view)) {
2491 err = view_close_child(view);
2492 if (err)
2493 return err;
2494 view_set_child(view, diff_view);
2495 view->focus_child = 1;
2496 } else
2497 *new_view = diff_view;
2498 break;
2499 case 't':
2500 if (s->selected_entry == NULL((void*)0))
2501 break;
2502 if (view_is_parent_view(view))
2503 begin_x = view_split_begin_x(view->begin_x);
2504 err = browse_commit_tree(&tree_view, begin_x,
2505 s->selected_entry, s->in_repo_path, s->head_ref_name,
2506 s->repo);
2507 if (err)
2508 break;
2509 view->focussed = 0;
2510 tree_view->focussed = 1;
2511 if (view_is_parent_view(view)) {
2512 err = view_close_child(view);
2513 if (err)
2514 return err;
2515 view_set_child(view, tree_view);
2516 view->focus_child = 1;
2517 } else
2518 *new_view = tree_view;
2519 break;
2520 case KEY_BACKSPACE0407:
2521 case CTRL('l')(('l') & 0x1f):
2522 case 'B':
2523 if (ch == KEY_BACKSPACE0407 &&
2524 got_path_is_root_dir(s->in_repo_path))
2525 break;
2526 err = stop_log_thread(s);
2527 if (err)
2528 return err;
2529 if (ch == KEY_BACKSPACE0407) {
2530 char *parent_path;
2531 err = got_path_dirname(&parent_path, s->in_repo_path);
2532 if (err)
2533 return err;
2534 free(s->in_repo_path);
2535 s->in_repo_path = parent_path;
2536 s->thread_args.in_repo_path = s->in_repo_path;
2537 } else if (ch == CTRL('l')(('l') & 0x1f)) {
2538 struct got_object_id *start_id;
2539 err = got_repo_match_object_id(&start_id, NULL((void*)0),
2540 s->head_ref_name ? s->head_ref_name : GOT_REF_HEAD"HEAD",
2541 GOT_OBJ_TYPE_COMMIT1, &tog_refs, s->repo);
2542 if (err)
2543 return err;
2544 free(s->start_id);
2545 s->start_id = start_id;
2546 s->thread_args.start_id = s->start_id;
2547 } else /* 'B' */
2548 s->log_branches = !s->log_branches;
2549
2550 err = got_repo_open(&s->thread_args.repo,
2551 got_repo_get_path(s->repo), NULL((void*)0));
2552 if (err)
2553 return err;
2554 tog_free_refs();
2555 err = tog_load_refs(s->repo);
2556 if (err)
2557 return err;
2558 err = got_commit_graph_open(&s->thread_args.graph,
2559 s->in_repo_path, !s->log_branches);
2560 if (err)
2561 return err;
2562 err = got_commit_graph_iter_start(s->thread_args.graph,
2563 s->start_id, s->repo, NULL((void*)0), NULL((void*)0));
2564 if (err)
2565 return err;
2566 free_commits(&s->commits);
2567 s->first_displayed_entry = NULL((void*)0);
2568 s->last_displayed_entry = NULL((void*)0);
2569 s->selected_entry = NULL((void*)0);
2570 s->selected = 0;
2571 s->thread_args.log_complete = 0;
2572 s->quit = 0;
2573 s->thread_args.commits_needed = view->nlines;
2574 break;
2575 case 'r':
2576 if (view_is_parent_view(view))
2577 begin_x = view_split_begin_x(view->begin_x);
2578 ref_view = view_open(view->nlines, view->ncols,
2579 view->begin_y, begin_x, TOG_VIEW_REF);
2580 if (ref_view == NULL((void*)0))
2581 return got_error_from_errno("view_open");
2582 err = open_ref_view(ref_view, s->repo);
2583 if (err) {
2584 view_close(ref_view);
2585 return err;
2586 }
2587 view->focussed = 0;
2588 ref_view->focussed = 1;
2589 if (view_is_parent_view(view)) {
2590 err = view_close_child(view);
2591 if (err)
2592 return err;
2593 view_set_child(view, ref_view);
2594 view->focus_child = 1;
2595 } else
2596 *new_view = ref_view;
2597 break;
2598 default:
2599 break;
2600 }
2601
2602 return err;
2603}
2604
2605static const struct got_error *
2606apply_unveil(const char *repo_path, const char *worktree_path)
2607{
2608 const struct got_error *error;
2609
2610#ifdef PROFILE
2611 if (unveil("gmon.out", "rwc") != 0)
2612 return got_error_from_errno2("unveil", "gmon.out");
2613#endif
2614 if (repo_path && unveil(repo_path, "r") != 0)
2615 return got_error_from_errno2("unveil", repo_path);
2616
2617 if (worktree_path && unveil(worktree_path, "rwc") != 0)
2618 return got_error_from_errno2("unveil", worktree_path);
2619
2620 if (unveil(GOT_TMPDIR_STR"/tmp", "rwc") != 0)
2621 return got_error_from_errno2("unveil", GOT_TMPDIR_STR"/tmp");
2622
2623 error = got_privsep_unveil_exec_helpers();
2624 if (error != NULL((void*)0))
2625 return error;
2626
2627 if (unveil(NULL((void*)0), NULL((void*)0)) != 0)
2628 return got_error_from_errno("unveil");
2629
2630 return NULL((void*)0);
2631}
2632
2633static void
2634init_curses(void)
2635{
2636 initscr();
2637 cbreak();
2638 halfdelay(1); /* Do fast refresh while initial view is loading. */
2639 noecho();
2640 nonl();
2641 intrflush(stdscr, FALSE0);
2642 keypad(stdscr, TRUE1);
2643 curs_set(0);
2644 if (getenv("TOG_COLORS") != NULL((void*)0)) {
2645 start_color();
2646 use_default_colors();
2647 }
2648 signal(SIGWINCH28, tog_sigwinch);
2649 signal(SIGPIPE13, tog_sigpipe);
2650 signal(SIGCONT19, tog_sigcont);
2651}
2652
2653static const struct got_error *
2654get_in_repo_path_from_argv0(char **in_repo_path, int argc, char *argv[],
2655 struct got_repository *repo, struct got_worktree *worktree)
2656{
2657 const struct got_error *err = NULL((void*)0);
2658
2659 if (argc == 0) {
2660 *in_repo_path = strdup("/");
2661 if (*in_repo_path == NULL((void*)0))
2662 return got_error_from_errno("strdup");
2663 return NULL((void*)0);
2664 }
2665
2666 if (worktree) {
2667 const char *prefix = got_worktree_get_path_prefix(worktree);
2668 char *p;
2669
2670 err = got_worktree_resolve_path(&p, worktree, argv[0]);
2671 if (err)
2672 return err;
2673 if (asprintf(in_repo_path, "%s%s%s", prefix,
2674 (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "",
2675 p) == -1) {
2676 err = got_error_from_errno("asprintf");
2677 *in_repo_path = NULL((void*)0);
2678 }
2679 free(p);
2680 } else
2681 err = got_repo_map_path(in_repo_path, repo, argv[0]);
2682
2683 return err;
2684}
2685
2686static const struct got_error *
2687cmd_log(int argc, char *argv[])
2688{
2689 const struct got_error *error;
2690 struct got_repository *repo = NULL((void*)0);
2691 struct got_worktree *worktree = NULL((void*)0);
2692 struct got_object_id *start_id = NULL((void*)0);
2693 char *in_repo_path = NULL((void*)0), *repo_path = NULL((void*)0), *cwd = NULL((void*)0);
2694 char *start_commit = NULL((void*)0), *label = NULL((void*)0);
2695 struct got_reference *ref = NULL((void*)0);
2696 const char *head_ref_name = NULL((void*)0);
2697 int ch, log_branches = 0;
2698 struct tog_view *view;
2699
2700 while ((ch = getopt(argc, argv, "bc:r:")) != -1) {
2701 switch (ch) {
2702 case 'b':
2703 log_branches = 1;
2704 break;
2705 case 'c':
2706 start_commit = optarg;
2707 break;
2708 case 'r':
2709 repo_path = realpath(optarg, NULL((void*)0));
2710 if (repo_path == NULL((void*)0))
2711 return got_error_from_errno2("realpath",
2712 optarg);
2713 break;
2714 default:
2715 usage_log();
2716 /* NOTREACHED */
2717 }
2718 }
2719
2720 argc -= optind;
2721 argv += optind;
2722
2723 if (argc > 1)
2724 usage_log();
2725
2726 if (repo_path == NULL((void*)0)) {
2727 cwd = getcwd(NULL((void*)0), 0);
2728 if (cwd == NULL((void*)0))
2729 return got_error_from_errno("getcwd");
2730 error = got_worktree_open(&worktree, cwd);
2731 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
2732 goto done;
2733 if (worktree)
2734 repo_path =
2735 strdup(got_worktree_get_repo_path(worktree));
2736 else
2737 repo_path = strdup(cwd);
2738 if (repo_path == NULL((void*)0)) {
2739 error = got_error_from_errno("strdup");
2740 goto done;
2741 }
2742 }
2743
2744 error = got_repo_open(&repo, repo_path, NULL((void*)0));
2745 if (error != NULL((void*)0))
2746 goto done;
2747
2748 error = get_in_repo_path_from_argv0(&in_repo_path, argc, argv,
2749 repo, worktree);
2750 if (error)
2751 goto done;
2752
2753 init_curses();
2754
2755 error = apply_unveil(got_repo_get_path(repo),
2756 worktree ? got_worktree_get_root_path(worktree) : NULL((void*)0));
2757 if (error)
2758 goto done;
2759
2760 /* already loaded by tog_log_with_path()? */
2761 if (TAILQ_EMPTY(&tog_refs)(((&tog_refs)->tqh_first) == ((void*)0))) {
2762 error = tog_load_refs(repo);
2763 if (error)
2764 goto done;
2765 }
2766
2767 if (start_commit == NULL((void*)0)) {
2768 error = got_repo_match_object_id(&start_id, &label,
2769 worktree ? got_worktree_get_head_ref_name(worktree) :
2770 GOT_REF_HEAD"HEAD", GOT_OBJ_TYPE_COMMIT1, &tog_refs, repo);
2771 if (error)
2772 goto done;
2773 head_ref_name = label;
2774 } else {
2775 error = got_ref_open(&ref, repo, start_commit, 0);
2776 if (error == NULL((void*)0))
2777 head_ref_name = got_ref_get_name(ref);
2778 else if (error->code != GOT_ERR_NOT_REF5)
2779 goto done;
2780 error = got_repo_match_object_id(&start_id, NULL((void*)0),
2781 start_commit, GOT_OBJ_TYPE_COMMIT1, &tog_refs, repo);
2782 if (error)
2783 goto done;
2784 }
2785
2786 view = view_open(0, 0, 0, 0, TOG_VIEW_LOG);
2787 if (view == NULL((void*)0)) {
2788 error = got_error_from_errno("view_open");
2789 goto done;
2790 }
2791 error = open_log_view(view, start_id, repo, head_ref_name,
2792 in_repo_path, log_branches);
2793 if (error)
2794 goto done;
2795 if (worktree) {
2796 /* Release work tree lock. */
2797 got_worktree_close(worktree);
2798 worktree = NULL((void*)0);
2799 }
2800 error = view_loop(view);
2801done:
2802 free(in_repo_path);
2803 free(repo_path);
2804 free(cwd);
2805 free(start_id);
2806 free(label);
2807 if (ref)
2808 got_ref_close(ref);
2809 if (repo)
2810 got_repo_close(repo);
2811 if (worktree)
2812 got_worktree_close(worktree);
2813 tog_free_refs();
2814 return error;
2815}
2816
2817__dead__attribute__((__noreturn__)) static void
2818usage_diff(void)
2819{
2820 endwin();
2821 fprintf(stderr(&__sF[2]), "usage: %s diff [-a] [-C number] [-r repository-path] "
2822 "[-w] object1 object2\n", getprogname());
2823 exit(1);
2824}
2825
2826static int
2827match_line(const char *line, regex_t *regex, size_t nmatch,
2828 regmatch_t *regmatch)
2829{
2830 return regexec(regex, line, nmatch, regmatch, 0) == 0;
2831}
2832
2833struct tog_color *
2834match_color(struct tog_colors *colors, const char *line)
2835{
2836 struct tog_color *tc = NULL((void*)0);
2837
2838 SIMPLEQ_FOREACH(tc, colors, entry)for((tc) = ((colors)->sqh_first); (tc) != ((void*)0); (tc)
= ((tc)->entry.sqe_next))
{
2839 if (match_line(line, &tc->regex, 0, NULL((void*)0)))
2840 return tc;
2841 }
2842
2843 return NULL((void*)0);
2844}
2845
2846static const struct got_error *
2847add_matched_line(int *wtotal, const char *line, int wlimit, int col_tab_align,
2848 WINDOW *window, regmatch_t *regmatch)
2849{
2850 const struct got_error *err = NULL((void*)0);
2851 wchar_t *wline;
2852 int width;
2853 char *s;
2854
2855 *wtotal = 0;
2856
2857 s = strndup(line, regmatch->rm_so);
2858 if (s == NULL((void*)0))
2859 return got_error_from_errno("strndup");
2860
2861 err = format_line(&wline, &width, s, wlimit, col_tab_align);
2862 if (err) {
2863 free(s);
2864 return err;
2865 }
2866 waddwstr(window, wline)waddnwstr(window,wline,-1);
2867 free(wline);
2868 free(s);
2869 wlimit -= width;
2870 *wtotal += width;
2871
2872 if (wlimit > 0) {
2873 s = strndup(line + regmatch->rm_so,
2874 regmatch->rm_eo - regmatch->rm_so);
2875 if (s == NULL((void*)0)) {
2876 err = got_error_from_errno("strndup");
2877 free(s);
2878 return err;
2879 }
2880 err = format_line(&wline, &width, s, wlimit, col_tab_align);
2881 if (err) {
2882 free(s);
2883 return err;
2884 }
2885 wattr_on(window, A_STANDOUT((1U) << ((8) + 8)), NULL((void*)0));
2886 waddwstr(window, wline)waddnwstr(window,wline,-1);
2887 wattr_off(window, A_STANDOUT((1U) << ((8) + 8)), NULL((void*)0));
2888 free(wline);
2889 free(s);
2890 wlimit -= width;
2891 *wtotal += width;
2892 }
2893
2894 if (wlimit > 0 && strlen(line) > regmatch->rm_eo) {
2895 err = format_line(&wline, &width,
2896 line + regmatch->rm_eo, wlimit, col_tab_align);
2897 if (err)
2898 return err;
2899 waddwstr(window, wline)waddnwstr(window,wline,-1);
2900 free(wline);
2901 *wtotal += width;
2902 }
2903
2904 return NULL((void*)0);
2905}
2906
2907static const struct got_error *
2908draw_file(struct tog_view *view, const char *header)
2909{
2910 struct tog_diff_view_state *s = &view->state.diff;
2911 regmatch_t *regmatch = &view->regmatch;
2912 const struct got_error *err;
2913 int nprinted = 0;
2914 char *line;
2915 size_t linesize = 0;
2916 ssize_t linelen;
2917 struct tog_color *tc;
2918 wchar_t *wline;
2919 int width;
2920 int max_lines = view->nlines;
2921 int nlines = s->nlines;
2922 off_t line_offset;
2923
2924 line_offset = s->line_offsets[s->first_displayed_line - 1];
2925 if (fseeko(s->f, line_offset, SEEK_SET0) == -1)
2926 return got_error_from_errno("fseek");
2927
2928 werase(view->window);
2929
2930 if (header) {
2931 if (asprintf(&line, "[%d/%d] %s",
2932 s->first_displayed_line - 1 + s->selected_line, nlines,
2933 header) == -1)
2934 return got_error_from_errno("asprintf");
2935 err = format_line(&wline, &width, line, view->ncols, 0);
2936 free(line);
2937 if (err)
2938 return err;
2939
2940 if (view_needs_focus_indication(view))
2941 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
2942 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
2943 free(wline);
2944 wline = NULL((void*)0);
2945 if (view_needs_focus_indication(view))
2946 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
2947 if (width <= view->ncols - 1)
2948 waddch(view->window, '\n');
2949
2950 if (max_lines <= 1)
2951 return NULL((void*)0);
2952 max_lines--;
2953 }
2954
2955 s->eof = 0;
2956 line = NULL((void*)0);
2957 while (max_lines > 0 && nprinted < max_lines) {
2958 linelen = getline(&line, &linesize, s->f);
2959 if (linelen == -1) {
2960 if (feof(s->f)(!__isthreaded ? (((s->f)->_flags & 0x0020) != 0) :
(feof)(s->f))
) {
2961 s->eof = 1;
2962 break;
2963 }
2964 free(line);
2965 return got_ferror(s->f, GOT_ERR_IO6);
2966 }
2967
2968 tc = match_color(&s->colors, line);
2969 if (tc)
2970 wattr_on(view->window,
2971 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
2972 if (s->first_displayed_line + nprinted == s->matched_line &&
2973 regmatch->rm_so >= 0 && regmatch->rm_so < regmatch->rm_eo) {
2974 err = add_matched_line(&width, line, view->ncols, 0,
2975 view->window, regmatch);
2976 if (err) {
2977 free(line);
2978 return err;
2979 }
2980 } else {
2981 err = format_line(&wline, &width, line, view->ncols, 0);
2982 if (err) {
2983 free(line);
2984 return err;
2985 }
2986 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
2987 free(wline);
2988 wline = NULL((void*)0);
2989 }
2990 if (tc)
2991 wattr_off(view->window,
2992 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
2993 if (width <= view->ncols - 1)
2994 waddch(view->window, '\n');
2995 nprinted++;
2996 }
2997 free(line);
2998 if (nprinted >= 1)
2999 s->last_displayed_line = s->first_displayed_line +
3000 (nprinted - 1);
3001 else
3002 s->last_displayed_line = s->first_displayed_line;
3003
3004 view_vborder(view);
3005
3006 if (s->eof) {
3007 while (nprinted < view->nlines) {
3008 waddch(view->window, '\n');
3009 nprinted++;
3010 }
3011
3012 err = format_line(&wline, &width, TOG_EOF_STRING"(END)", view->ncols, 0);
3013 if (err) {
3014 return err;
3015 }
3016
3017 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
3018 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
3019 free(wline);
3020 wline = NULL((void*)0);
3021 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
3022 }
3023
3024 return NULL((void*)0);
3025}
3026
3027static char *
3028get_datestr(time_t *time, char *datebuf)
3029{
3030 struct tm mytm, *tm;
3031 char *p, *s;
3032
3033 tm = gmtime_r(time, &mytm);
3034 if (tm == NULL((void*)0))
3035 return NULL((void*)0);
3036 s = asctime_r(tm, datebuf);
3037 if (s == NULL((void*)0))
3038 return NULL((void*)0);
3039 p = strchr(s, '\n');
3040 if (p)
3041 *p = '\0';
3042 return s;
3043}
3044
3045static const struct got_error *
3046get_changed_paths(struct got_pathlist_head *paths,
3047 struct got_commit_object *commit, struct got_repository *repo)
3048{
3049 const struct got_error *err = NULL((void*)0);
3050 struct got_object_id *tree_id1 = NULL((void*)0), *tree_id2 = NULL((void*)0);
3051 struct got_tree_object *tree1 = NULL((void*)0), *tree2 = NULL((void*)0);
3052 struct got_object_qid *qid;
3053
3054 qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit))((got_object_commit_get_parent_ids(commit))->sqh_first);
3055 if (qid != NULL((void*)0)) {
3056 struct got_commit_object *pcommit;
3057 err = got_object_open_as_commit(&pcommit, repo,
3058 qid->id);
3059 if (err)
3060 return err;
3061
3062 tree_id1 = got_object_commit_get_tree_id(pcommit);
3063 got_object_commit_close(pcommit);
3064
3065 }
3066
3067 if (tree_id1) {
3068 err = got_object_open_as_tree(&tree1, repo, tree_id1);
3069 if (err)
3070 goto done;
3071 }
3072
3073 tree_id2 = got_object_commit_get_tree_id(commit);
3074 err = got_object_open_as_tree(&tree2, repo, tree_id2);
3075 if (err)
3076 goto done;
3077
3078 err = got_diff_tree(tree1, tree2, "", "", repo,
3079 got_diff_tree_collect_changed_paths, paths, 0);
3080done:
3081 if (tree1)
3082 got_object_tree_close(tree1);
3083 if (tree2)
3084 got_object_tree_close(tree2);
3085 return err;
3086}
3087
3088static const struct got_error *
3089add_line_offset(off_t **line_offsets, size_t *nlines, off_t off)
3090{
3091 off_t *p;
3092
3093 p = reallocarray(*line_offsets, *nlines + 1, sizeof(off_t));
3094 if (p == NULL((void*)0))
3095 return got_error_from_errno("reallocarray");
3096 *line_offsets = p;
3097 (*line_offsets)[*nlines] = off;
3098 (*nlines)++;
3099 return NULL((void*)0);
3100}
3101
3102static const struct got_error *
3103write_commit_info(off_t **line_offsets, size_t *nlines,
3104 struct got_object_id *commit_id, struct got_reflist_head *refs,
3105 struct got_repository *repo, FILE *outfile)
3106{
3107 const struct got_error *err = NULL((void*)0);
3108 char datebuf[26], *datestr;
3109 struct got_commit_object *commit;
3110 char *id_str = NULL((void*)0), *logmsg = NULL((void*)0), *s = NULL((void*)0), *line;
3111 time_t committer_time;
3112 const char *author, *committer;
3113 char *refs_str = NULL((void*)0);
3114 struct got_pathlist_head changed_paths;
3115 struct got_pathlist_entry *pe;
3116 off_t outoff = 0;
3117 int n;
3118
3119 TAILQ_INIT(&changed_paths)do { (&changed_paths)->tqh_first = ((void*)0); (&changed_paths
)->tqh_last = &(&changed_paths)->tqh_first; } while
(0)
;
3120
3121 if (refs) {
3122 err = build_refs_str(&refs_str, refs, commit_id, repo);
3123 if (err)
3124 return err;
3125 }
3126
3127 err = got_object_open_as_commit(&commit, repo, commit_id);
3128 if (err)
3129 return err;
3130
3131 err = got_object_id_str(&id_str, commit_id);
3132 if (err) {
3133 err = got_error_from_errno("got_object_id_str");
3134 goto done;
3135 }
3136
3137 err = add_line_offset(line_offsets, nlines, 0);
3138 if (err)
3139 goto done;
3140
3141 n = fprintf(outfile, "commit %s%s%s%s\n", id_str, refs_str ? " (" : "",
3142 refs_str ? refs_str : "", refs_str ? ")" : "");
3143 if (n < 0) {
3144 err = got_error_from_errno("fprintf");
3145 goto done;
3146 }
3147 outoff += n;
3148 err = add_line_offset(line_offsets, nlines, outoff);
3149 if (err)
3150 goto done;
3151
3152 n = fprintf(outfile, "from: %s\n",
3153 got_object_commit_get_author(commit));
3154 if (n < 0) {
3155 err = got_error_from_errno("fprintf");
3156 goto done;
3157 }
3158 outoff += n;
3159 err = add_line_offset(line_offsets, nlines, outoff);
3160 if (err)
3161 goto done;
3162
3163 committer_time = got_object_commit_get_committer_time(commit);
3164 datestr = get_datestr(&committer_time, datebuf);
3165 if (datestr) {
3166 n = fprintf(outfile, "date: %s UTC\n", datestr);
3167 if (n < 0) {
3168 err = got_error_from_errno("fprintf");
3169 goto done;
3170 }
3171 outoff += n;
3172 err = add_line_offset(line_offsets, nlines, outoff);
3173 if (err)
3174 goto done;
3175 }
3176 author = got_object_commit_get_author(commit);
3177 committer = got_object_commit_get_committer(commit);
3178 if (strcmp(author, committer) != 0) {
3179 n = fprintf(outfile, "via: %s\n", committer);
3180 if (n < 0) {
3181 err = got_error_from_errno("fprintf");
3182 goto done;
3183 }
3184 outoff += n;
3185 err = add_line_offset(line_offsets, nlines, outoff);
3186 if (err)
3187 goto done;
3188 }
3189 err = got_object_commit_get_logmsg(&logmsg, commit);
3190 if (err)
3191 goto done;
3192 s = logmsg;
3193 while ((line = strsep(&s, "\n")) != NULL((void*)0)) {
3194 n = fprintf(outfile, "%s\n", line);
3195 if (n < 0) {
3196 err = got_error_from_errno("fprintf");
3197 goto done;
3198 }
3199 outoff += n;
3200 err = add_line_offset(line_offsets, nlines, outoff);
3201 if (err)
3202 goto done;
3203 }
3204
3205 err = get_changed_paths(&changed_paths, commit, repo);
3206 if (err)
3207 goto done;
3208 TAILQ_FOREACH(pe, &changed_paths, entry)for((pe) = ((&changed_paths)->tqh_first); (pe) != ((void
*)0); (pe) = ((pe)->entry.tqe_next))
{
3209 struct got_diff_changed_path *cp = pe->data;
3210 n = fprintf(outfile, "%c %s\n", cp->status, pe->path);
3211 if (n < 0) {
3212 err = got_error_from_errno("fprintf");
3213 goto done;
3214 }
3215 outoff += n;
3216 err = add_line_offset(line_offsets, nlines, outoff);
3217 if (err)
3218 goto done;
3219 free((char *)pe->path);
3220 free(pe->data);
3221 }
3222
3223 fputc('\n', outfile);
3224 outoff++;
3225 err = add_line_offset(line_offsets, nlines, outoff);
3226done:
3227 got_pathlist_free(&changed_paths);
3228 free(id_str);
3229 free(logmsg);
3230 free(refs_str);
3231 got_object_commit_close(commit);
3232 if (err) {
3233 free(*line_offsets);
3234 *line_offsets = NULL((void*)0);
3235 *nlines = 0;
3236 }
3237 return err;
3238}
3239
3240static const struct got_error *
3241create_diff(struct tog_diff_view_state *s)
3242{
3243 const struct got_error *err = NULL((void*)0);
3244 FILE *f = NULL((void*)0);
3245 int obj_type;
3246
3247 free(s->line_offsets);
3248 s->line_offsets = malloc(sizeof(off_t));
3249 if (s->line_offsets == NULL((void*)0))
3250 return got_error_from_errno("malloc");
3251 s->nlines = 0;
3252
3253 f = got_opentemp();
3254 if (f == NULL((void*)0)) {
3255 err = got_error_from_errno("got_opentemp");
3256 goto done;
3257 }
3258 if (s->f && fclose(s->f) == EOF(-1)) {
3259 err = got_error_from_errno("fclose");
3260 goto done;
3261 }
3262 s->f = f;
3263
3264 if (s->id1)
3265 err = got_object_get_type(&obj_type, s->repo, s->id1);
3266 else
3267 err = got_object_get_type(&obj_type, s->repo, s->id2);
3268 if (err)
3269 goto done;
3270
3271 switch (obj_type) {
3272 case GOT_OBJ_TYPE_BLOB3:
3273 err = got_diff_objects_as_blobs(&s->line_offsets, &s->nlines,
3274 s->id1, s->id2, s->label1, s->label2, s->diff_context,
3275 s->ignore_whitespace, s->force_text_diff, s->repo, s->f);
3276 break;
3277 case GOT_OBJ_TYPE_TREE2:
3278 err = got_diff_objects_as_trees(&s->line_offsets, &s->nlines,
3279 s->id1, s->id2, "", "", s->diff_context,
3280 s->ignore_whitespace, s->force_text_diff, s->repo, s->f);
3281 break;
3282 case GOT_OBJ_TYPE_COMMIT1: {
3283 const struct got_object_id_queue *parent_ids;
3284 struct got_object_qid *pid;
3285 struct got_commit_object *commit2;
3286 struct got_reflist_head *refs;
3287
3288 err = got_object_open_as_commit(&commit2, s->repo, s->id2);
3289 if (err)
3290 goto done;
3291 refs = got_reflist_object_id_map_lookup(tog_refs_idmap, s->id2);
3292 /* Show commit info if we're diffing to a parent/root commit. */
3293 if (s->id1 == NULL((void*)0)) {
3294 err = write_commit_info(&s->line_offsets, &s->nlines,
3295 s->id2, refs, s->repo, s->f);
3296 if (err)
3297 goto done;
3298 } else {
3299 parent_ids = got_object_commit_get_parent_ids(commit2);
3300 SIMPLEQ_FOREACH(pid, parent_ids, entry)for((pid) = ((parent_ids)->sqh_first); (pid) != ((void*)0)
; (pid) = ((pid)->entry.sqe_next))
{
3301 if (got_object_id_cmp(s->id1, pid->id) == 0) {
3302 err = write_commit_info(
3303 &s->line_offsets, &s->nlines,
3304 s->id2, refs, s->repo, s->f);
3305 if (err)
3306 goto done;
3307 break;
3308 }
3309 }
3310 }
3311 got_object_commit_close(commit2);
3312
3313 err = got_diff_objects_as_commits(&s->line_offsets, &s->nlines,
3314 s->id1, s->id2, s->diff_context, s->ignore_whitespace,
3315 s->force_text_diff, s->repo, s->f);
3316 break;
3317 }
3318 default:
3319 err = got_error(GOT_ERR_OBJ_TYPE11);
3320 break;
3321 }
3322 if (err)
3323 goto done;
3324done:
3325 if (s->f && fflush(s->f) != 0 && err == NULL((void*)0))
3326 err = got_error_from_errno("fflush");
3327 return err;
3328}
3329
3330static void
3331diff_view_indicate_progress(struct tog_view *view)
3332{
3333 mvwaddstr(view->window, 0, 0, "diffing...")(wmove(view->window,0,0) == (-1) ? (-1) : waddnstr(view->
window,"diffing...",-1))
;
3334 update_panels();
3335 doupdate();
3336}
3337
3338static const struct got_error *
3339search_start_diff_view(struct tog_view *view)
3340{
3341 struct tog_diff_view_state *s = &view->state.diff;
3342
3343 s->matched_line = 0;
3344 return NULL((void*)0);
3345}
3346
3347static const struct got_error *
3348search_next_diff_view(struct tog_view *view)
3349{
3350 struct tog_diff_view_state *s = &view->state.diff;
3351 int lineno;
3352 char *line = NULL((void*)0);
3353 size_t linesize = 0;
3354 ssize_t linelen;
3355
3356 if (!view->searching) {
3357 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
3358 return NULL((void*)0);
3359 }
3360
3361 if (s->matched_line) {
3362 if (view->searching == TOG_SEARCH_FORWARD1)
3363 lineno = s->matched_line + 1;
3364 else
3365 lineno = s->matched_line - 1;
3366 } else {
3367 if (view->searching == TOG_SEARCH_FORWARD1)
3368 lineno = 1;
3369 else
3370 lineno = s->nlines;
3371 }
3372
3373 while (1) {
3374 off_t offset;
3375
3376 if (lineno <= 0 || lineno > s->nlines) {
3377 if (s->matched_line == 0) {
3378 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
3379 break;
3380 }
3381
3382 if (view->searching == TOG_SEARCH_FORWARD1)
3383 lineno = 1;
3384 else
3385 lineno = s->nlines;
3386 }
3387
3388 offset = s->line_offsets[lineno - 1];
3389 if (fseeko(s->f, offset, SEEK_SET0) != 0) {
3390 free(line);
3391 return got_error_from_errno("fseeko");
3392 }
3393 linelen = getline(&line, &linesize, s->f);
3394 if (linelen != -1 &&
3395 match_line(line, &view->regex, 1, &view->regmatch)) {
3396 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
3397 s->matched_line = lineno;
3398 break;
3399 }
3400 if (view->searching == TOG_SEARCH_FORWARD1)
3401 lineno++;
3402 else
3403 lineno--;
3404 }
3405 free(line);
3406
3407 if (s->matched_line) {
3408 s->first_displayed_line = s->matched_line;
3409 s->selected_line = 1;
3410 }
3411
3412 return NULL((void*)0);
3413}
3414
3415static const struct got_error *
3416open_diff_view(struct tog_view *view, struct got_object_id *id1,
3417 struct got_object_id *id2, const char *label1, const char *label2,
3418 int diff_context, int ignore_whitespace, int force_text_diff,
3419 struct tog_view *log_view, struct got_repository *repo)
3420{
3421 const struct got_error *err;
3422 struct tog_diff_view_state *s = &view->state.diff;
3423
3424 if (id1 != NULL((void*)0) && id2 != NULL((void*)0)) {
3425 int type1, type2;
3426 err = got_object_get_type(&type1, repo, id1);
3427 if (err)
3428 return err;
3429 err = got_object_get_type(&type2, repo, id2);
3430 if (err)
3431 return err;
3432
3433 if (type1 != type2)
3434 return got_error(GOT_ERR_OBJ_TYPE11);
3435 }
3436 s->first_displayed_line = 1;
3437 s->last_displayed_line = view->nlines;
3438 s->selected_line = 1;
3439 s->repo = repo;
3440 s->id1 = id1;
3441 s->id2 = id2;
3442 s->label1 = label1;
3443 s->label2 = label2;
3444
3445 if (id1) {
3446 s->id1 = got_object_id_dup(id1);
3447 if (s->id1 == NULL((void*)0))
3448 return got_error_from_errno("got_object_id_dup");
3449 } else
3450 s->id1 = NULL((void*)0);
3451
3452 s->id2 = got_object_id_dup(id2);
3453 if (s->id2 == NULL((void*)0)) {
3454 free(s->id1);
3455 s->id1 = NULL((void*)0);
3456 return got_error_from_errno("got_object_id_dup");
3457 }
3458 s->f = NULL((void*)0);
3459 s->first_displayed_line = 1;
3460 s->last_displayed_line = view->nlines;
3461 s->diff_context = diff_context;
3462 s->ignore_whitespace = ignore_whitespace;
3463 s->force_text_diff = force_text_diff;
3464 s->log_view = log_view;
3465 s->repo = repo;
3466
3467 SIMPLEQ_INIT(&s->colors)do { (&s->colors)->sqh_first = ((void*)0); (&s->
colors)->sqh_last = &(&s->colors)->sqh_first
; } while (0)
;
3468 if (has_colors() && getenv("TOG_COLORS") != NULL((void*)0)) {
3469 err = add_color(&s->colors,
3470 "^-", TOG_COLOR_DIFF_MINUS1,
3471 get_color_value("TOG_COLOR_DIFF_MINUS"));
3472 if (err)
3473 return err;
3474 err = add_color(&s->colors, "^\\+",
3475 TOG_COLOR_DIFF_PLUS2,
3476 get_color_value("TOG_COLOR_DIFF_PLUS"));
3477 if (err) {
3478 free_colors(&s->colors);
3479 return err;
3480 }
3481 err = add_color(&s->colors,
3482 "^@@", TOG_COLOR_DIFF_CHUNK_HEADER3,
3483 get_color_value("TOG_COLOR_DIFF_CHUNK_HEADER"));
3484 if (err) {
3485 free_colors(&s->colors);
3486 return err;
3487 }
3488
3489 err = add_color(&s->colors,
3490 "^(commit [0-9a-f]|(blob|file) [-+] |[MDmA] [^ ])",
3491 TOG_COLOR_DIFF_META4,
3492 get_color_value("TOG_COLOR_DIFF_META"));
3493 if (err) {
3494 free_colors(&s->colors);
3495 return err;
3496 }
3497
3498 err = add_color(&s->colors,
3499 "^(from|via): ", TOG_COLOR_AUTHOR10,
3500 get_color_value("TOG_COLOR_AUTHOR"));
3501 if (err) {
3502 free_colors(&s->colors);
3503 return err;
3504 }
3505
3506 err = add_color(&s->colors,
3507 "^date: ", TOG_COLOR_DATE11,
3508 get_color_value("TOG_COLOR_DATE"));
3509 if (err) {
3510 free_colors(&s->colors);
3511 return err;
3512 }
3513 }
3514
3515 if (log_view && view_is_splitscreen(view))
3516 show_log_view(log_view); /* draw vborder */
3517 diff_view_indicate_progress(view);
3518
3519 s->line_offsets = NULL((void*)0);
3520 s->nlines = 0;
3521 err = create_diff(s);
3522 if (err) {
3523 free(s->id1);
3524 s->id1 = NULL((void*)0);
3525 free(s->id2);
3526 s->id2 = NULL((void*)0);
3527 free_colors(&s->colors);
3528 return err;
3529 }
3530
3531 view->show = show_diff_view;
3532 view->input = input_diff_view;
3533 view->close = close_diff_view;
3534 view->search_start = search_start_diff_view;
3535 view->search_next = search_next_diff_view;
3536
3537 return NULL((void*)0);
3538}
3539
3540static const struct got_error *
3541close_diff_view(struct tog_view *view)
3542{
3543 const struct got_error *err = NULL((void*)0);
3544 struct tog_diff_view_state *s = &view->state.diff;
3545
3546 free(s->id1);
3547 s->id1 = NULL((void*)0);
3548 free(s->id2);
3549 s->id2 = NULL((void*)0);
3550 if (s->f && fclose(s->f) == EOF(-1))
3551 err = got_error_from_errno("fclose");
3552 free_colors(&s->colors);
3553 free(s->line_offsets);
3554 s->line_offsets = NULL((void*)0);
3555 s->nlines = 0;
3556 return err;
3557}
3558
3559static const struct got_error *
3560show_diff_view(struct tog_view *view)
3561{
3562 const struct got_error *err;
3563 struct tog_diff_view_state *s = &view->state.diff;
3564 char *id_str1 = NULL((void*)0), *id_str2, *header;
3565 const char *label1, *label2;
3566
3567 if (s->id1) {
3568 err = got_object_id_str(&id_str1, s->id1);
3569 if (err)
3570 return err;
3571 label1 = s->label1 ? : id_str1;
3572 } else
3573 label1 = "/dev/null";
3574
3575 err = got_object_id_str(&id_str2, s->id2);
3576 if (err)
3577 return err;
3578 label2 = s->label2 ? : id_str2;
3579
3580 if (asprintf(&header, "diff %s %s", label1, label2) == -1) {
3581 err = got_error_from_errno("asprintf");
3582 free(id_str1);
3583 free(id_str2);
3584 return err;
3585 }
3586 free(id_str1);
3587 free(id_str2);
3588
3589 return draw_file(view, header);
3590}
3591
3592static const struct got_error *
3593set_selected_commit(struct tog_diff_view_state *s,
3594 struct commit_queue_entry *entry)
3595{
3596 const struct got_error *err;
3597 const struct got_object_id_queue *parent_ids;
3598 struct got_commit_object *selected_commit;
3599 struct got_object_qid *pid;
3600
3601 free(s->id2);
3602 s->id2 = got_object_id_dup(entry->id);
3603 if (s->id2 == NULL((void*)0))
3604 return got_error_from_errno("got_object_id_dup");
3605
3606 err = got_object_open_as_commit(&selected_commit, s->repo, entry->id);
3607 if (err)
3608 return err;
3609 parent_ids = got_object_commit_get_parent_ids(selected_commit);
3610 free(s->id1);
3611 pid = SIMPLEQ_FIRST(parent_ids)((parent_ids)->sqh_first);
3612 s->id1 = pid ? got_object_id_dup(pid->id) : NULL((void*)0);
3613 got_object_commit_close(selected_commit);
3614 return NULL((void*)0);
3615}
3616
3617static const struct got_error *
3618input_diff_view(struct tog_view **new_view, struct tog_view *view, int ch)
3619{
3620 const struct got_error *err = NULL((void*)0);
3621 struct tog_diff_view_state *s = &view->state.diff;
3622 struct tog_log_view_state *ls;
3623 struct commit_queue_entry *old_selected_entry;
3624 char *line = NULL((void*)0);
3625 size_t linesize = 0;
3626 ssize_t linelen;
3627 int i;
3628
3629 switch (ch) {
3630 case 'a':
3631 case 'w':
3632 if (ch == 'a')
3633 s->force_text_diff = !s->force_text_diff;
3634 if (ch == 'w')
3635 s->ignore_whitespace = !s->ignore_whitespace;
3636 wclear(view->window);
3637 s->first_displayed_line = 1;
3638 s->last_displayed_line = view->nlines;
3639 diff_view_indicate_progress(view);
3640 err = create_diff(s);
3641 break;
3642 case 'k':
3643 case KEY_UP0403:
3644 if (s->first_displayed_line > 1)
3645 s->first_displayed_line--;
3646 break;
3647 case KEY_PPAGE0523:
3648 case CTRL('b')(('b') & 0x1f):
3649 if (s->first_displayed_line == 1)
3650 break;
3651 i = 0;
3652 while (i++ < view->nlines - 1 &&
3653 s->first_displayed_line > 1)
3654 s->first_displayed_line--;
3655 break;
3656 case 'j':
3657 case KEY_DOWN0402:
3658 if (!s->eof)
3659 s->first_displayed_line++;
3660 break;
3661 case KEY_NPAGE0522:
3662 case CTRL('f')(('f') & 0x1f):
3663 case ' ':
3664 if (s->eof)
3665 break;
3666 i = 0;
3667 while (!s->eof && i++ < view->nlines - 1) {
3668 linelen = getline(&line, &linesize, s->f);
3669 s->first_displayed_line++;
3670 if (linelen == -1) {
3671 if (feof(s->f)(!__isthreaded ? (((s->f)->_flags & 0x0020) != 0) :
(feof)(s->f))
) {
3672 s->eof = 1;
3673 } else
3674 err = got_ferror(s->f, GOT_ERR_IO6);
3675 break;
3676 }
3677 }
3678 free(line);
3679 break;
3680 case '[':
3681 if (s->diff_context > 0) {
3682 s->diff_context--;
3683 diff_view_indicate_progress(view);
3684 err = create_diff(s);
3685 if (s->first_displayed_line + view->nlines - 1 >
3686 s->nlines) {
3687 s->first_displayed_line = 1;
3688 s->last_displayed_line = view->nlines;
3689 }
3690 }
3691 break;
3692 case ']':
3693 if (s->diff_context < GOT_DIFF_MAX_CONTEXT64) {
3694 s->diff_context++;
3695 diff_view_indicate_progress(view);
3696 err = create_diff(s);
3697 }
3698 break;
3699 case '<':
3700 case ',':
3701 if (s->log_view == NULL((void*)0))
3702 break;
3703 ls = &s->log_view->state.log;
3704 old_selected_entry = ls->selected_entry;
3705
3706 err = input_log_view(NULL((void*)0), s->log_view, KEY_UP0403);
3707 if (err)
3708 break;
3709
3710 if (old_selected_entry == ls->selected_entry)
3711 break;
3712
3713 err = set_selected_commit(s, ls->selected_entry);
3714 if (err)
3715 break;
3716
3717 s->first_displayed_line = 1;
3718 s->last_displayed_line = view->nlines;
3719
3720 diff_view_indicate_progress(view);
3721 err = create_diff(s);
3722 break;
3723 case '>':
3724 case '.':
3725 if (s->log_view == NULL((void*)0))
3726 break;
3727 ls = &s->log_view->state.log;
3728 old_selected_entry = ls->selected_entry;
3729
3730 err = input_log_view(NULL((void*)0), s->log_view, KEY_DOWN0402);
3731 if (err)
3732 break;
3733
3734 if (old_selected_entry == ls->selected_entry)
3735 break;
3736
3737 err = set_selected_commit(s, ls->selected_entry);
3738 if (err)
3739 break;
3740
3741 s->first_displayed_line = 1;
3742 s->last_displayed_line = view->nlines;
3743
3744 diff_view_indicate_progress(view);
3745 err = create_diff(s);
3746 break;
3747 default:
3748 break;
3749 }
3750
3751 return err;
3752}
3753
3754static const struct got_error *
3755cmd_diff(int argc, char *argv[])
3756{
3757 const struct got_error *error = NULL((void*)0);
3758 struct got_repository *repo = NULL((void*)0);
3759 struct got_worktree *worktree = NULL((void*)0);
3760 struct got_object_id *id1 = NULL((void*)0), *id2 = NULL((void*)0);
3761 char *repo_path = NULL((void*)0), *cwd = NULL((void*)0);
3762 char *id_str1 = NULL((void*)0), *id_str2 = NULL((void*)0);
3763 char *label1 = NULL((void*)0), *label2 = NULL((void*)0);
3764 int diff_context = 3, ignore_whitespace = 0;
3765 int ch, force_text_diff = 0;
3766 const char *errstr;
3767 struct tog_view *view;
3768
3769 while ((ch = getopt(argc, argv, "aC:r:w")) != -1) {
3770 switch (ch) {
3771 case 'a':
3772 force_text_diff = 1;
3773 break;
3774 case 'C':
3775 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT64,
3776 &errstr);
3777 if (errstr != NULL((void*)0))
3778 err(1, "-C option %s", errstr);
3779 break;
3780 case 'r':
3781 repo_path = realpath(optarg, NULL((void*)0));
3782 if (repo_path == NULL((void*)0))
3783 return got_error_from_errno2("realpath",
3784 optarg);
3785 got_path_strip_trailing_slashes(repo_path);
3786 break;
3787 case 'w':
3788 ignore_whitespace = 1;
3789 break;
3790 default:
3791 usage_diff();
3792 /* NOTREACHED */
3793 }
3794 }
3795
3796 argc -= optind;
3797 argv += optind;
3798
3799 if (argc == 0) {
3800 usage_diff(); /* TODO show local worktree changes */
3801 } else if (argc == 2) {
3802 id_str1 = argv[0];
3803 id_str2 = argv[1];
3804 } else
3805 usage_diff();
3806
3807 if (repo_path == NULL((void*)0)) {
3808 cwd = getcwd(NULL((void*)0), 0);
3809 if (cwd == NULL((void*)0))
3810 return got_error_from_errno("getcwd");
3811 error = got_worktree_open(&worktree, cwd);
3812 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
3813 goto done;
3814 if (worktree)
3815 repo_path =
3816 strdup(got_worktree_get_repo_path(worktree));
3817 else
3818 repo_path = strdup(cwd);
3819 if (repo_path == NULL((void*)0)) {
3820 error = got_error_from_errno("strdup");
3821 goto done;
3822 }
3823 }
3824
3825 error = got_repo_open(&repo, repo_path, NULL((void*)0));
3826 if (error)
3827 goto done;
3828
3829 init_curses();
3830
3831 error = apply_unveil(got_repo_get_path(repo), NULL((void*)0));
3832 if (error)
3833 goto done;
3834
3835 error = tog_load_refs(repo);
3836 if (error)
3837 goto done;
3838
3839 error = got_repo_match_object_id(&id1, &label1, id_str1,
3840 GOT_OBJ_TYPE_ANY0, &tog_refs, repo);
3841 if (error)
3842 goto done;
3843
3844 error = got_repo_match_object_id(&id2, &label2, id_str2,
3845 GOT_OBJ_TYPE_ANY0, &tog_refs, repo);
3846 if (error)
3847 goto done;
3848
3849 view = view_open(0, 0, 0, 0, TOG_VIEW_DIFF);
3850 if (view == NULL((void*)0)) {
3851 error = got_error_from_errno("view_open");
3852 goto done;
3853 }
3854 error = open_diff_view(view, id1, id2, label1, label2, diff_context,
3855 ignore_whitespace, force_text_diff, NULL((void*)0), repo);
3856 if (error)
3857 goto done;
3858 error = view_loop(view);
3859done:
3860 free(label1);
3861 free(label2);
3862 free(repo_path);
3863 free(cwd);
3864 if (repo)
3865 got_repo_close(repo);
3866 if (worktree)
3867 got_worktree_close(worktree);
3868 tog_free_refs();
3869 return error;
3870}
3871
3872__dead__attribute__((__noreturn__)) static void
3873usage_blame(void)
3874{
3875 endwin();
3876 fprintf(stderr(&__sF[2]), "usage: %s blame [-c commit] [-r repository-path] path\n",
3877 getprogname());
3878 exit(1);
3879}
3880
3881struct tog_blame_line {
3882 int annotated;
3883 struct got_object_id *id;
3884};
3885
3886static const struct got_error *
3887draw_blame(struct tog_view *view)
3888{
3889 struct tog_blame_view_state *s = &view->state.blame;
3890 struct tog_blame *blame = &s->blame;
3891 regmatch_t *regmatch = &view->regmatch;
3892 const struct got_error *err;
3893 int lineno = 0, nprinted = 0;
3894 char *line = NULL((void*)0);
3895 size_t linesize = 0;
3896 ssize_t linelen;
3897 wchar_t *wline;
3898 int width;
3899 struct tog_blame_line *blame_line;
3900 struct got_object_id *prev_id = NULL((void*)0);
3901 char *id_str;
3902 struct tog_color *tc;
3903
3904 err = got_object_id_str(&id_str, s->blamed_commit->id);
3905 if (err)
5
Assuming 'err' is null
6
Taking false branch
3906 return err;
3907
3908 rewind(blame->f);
3909 werase(view->window);
3910
3911 if (asprintf(&line, "commit %s", id_str) == -1) {
7
Assuming the condition is false
8
Taking false branch
3912 err = got_error_from_errno("asprintf");
3913 free(id_str);
3914 return err;
3915 }
3916
3917 err = format_line(&wline, &width, line, view->ncols, 0);
3918 free(line);
3919 line = NULL((void*)0);
3920 if (err)
9
Assuming 'err' is null
10
Taking false branch
3921 return err;
3922 if (view_needs_focus_indication(view))
11
Taking false branch
3923 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
3924 tc = get_color(&s->colors, TOG_COLOR_COMMIT9);
3925 if (tc)
12
Assuming 'tc' is null
13
Taking false branch
3926 wattr_on(view->window,
3927 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
3928 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
3929 if (tc
13.1
'tc' is null
)
14
Taking false branch
3930 wattr_off(view->window,
3931 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
3932 if (view_needs_focus_indication(view))
15
Taking false branch
3933 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
3934 free(wline);
3935 wline = NULL((void*)0);
3936 if (width < view->ncols - 1)
16
Assuming the condition is false
17
Taking false branch
3937 waddch(view->window, '\n');
3938
3939 if (asprintf(&line, "[%d/%d] %s%s",
20
Assuming the condition is false
21
Taking false branch
3940 s->first_displayed_line - 1 + s->selected_line, blame->nlines,
3941 s->blame_complete ? "" : "annotating... ", s->path) == -1) {
18
Assuming field 'blame_complete' is 0
19
'?' condition is false
3942 free(id_str);
3943 return got_error_from_errno("asprintf");
3944 }
3945 free(id_str);
3946 err = format_line(&wline, &width, line, view->ncols, 0);
3947 free(line);
3948 line = NULL((void*)0);
3949 if (err)
22
Assuming 'err' is null
23
Taking false branch
3950 return err;
3951 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
3952 free(wline);
3953 wline = NULL((void*)0);
3954 if (width < view->ncols - 1)
24
Assuming the condition is false
25
Taking false branch
3955 waddch(view->window, '\n');
3956
3957 s->eof = 0;
3958 while (nprinted < view->nlines - 2) {
26
Assuming the condition is true
27
Loop condition is true. Entering loop body
43
Assuming the condition is false
44
Loop condition is false. Execution continues on line 4043
3959 linelen = getline(&line, &linesize, blame->f);
3960 if (linelen == -1) {
28
Assuming the condition is false
29
Taking false branch
3961 if (feof(blame->f)(!__isthreaded ? (((blame->f)->_flags & 0x0020) != 0
) : (feof)(blame->f))
) {
3962 s->eof = 1;
3963 break;
3964 }
3965 free(line);
3966 return got_ferror(blame->f, GOT_ERR_IO6);
3967 }
3968 if (++lineno < s->first_displayed_line)
30
Assuming the condition is false
31
Taking false branch
3969 continue;
3970
3971 if (view->focussed && nprinted == s->selected_line - 1)
32
Assuming field 'focussed' is 0
3972 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
3973
3974 if (blame->nlines > 0) {
33
Assuming field 'nlines' is <= 0
34
Taking false branch
3975 blame_line = &blame->lines[lineno - 1];
3976 if (blame_line->annotated && prev_id &&
3977 got_object_id_cmp(prev_id, blame_line->id) == 0 &&
3978 !(view->focussed &&
3979 nprinted == s->selected_line - 1)) {
3980 waddstr(view->window, " ")waddnstr(view->window," ",-1);
3981 } else if (blame_line->annotated) {
3982 char *id_str;
3983 err = got_object_id_str(&id_str, blame_line->id);
3984 if (err) {
3985 free(line);
3986 return err;
3987 }
3988 tc = get_color(&s->colors, TOG_COLOR_COMMIT9);
3989 if (tc)
3990 wattr_on(view->window,
3991 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
3992 wprintw(view->window, "%.8s", id_str);
3993 if (tc)
3994 wattr_off(view->window,
3995 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
3996 free(id_str);
3997 prev_id = blame_line->id;
3998 } else {
3999 waddstr(view->window, "........")waddnstr(view->window,"........",-1);
4000 prev_id = NULL((void*)0);
4001 }
4002 } else {
4003 waddstr(view->window, "........")waddnstr(view->window,"........",-1);
4004 prev_id = NULL((void*)0);
4005 }
4006
4007 if (view->focussed
34.1
Field 'focussed' is 0
&& nprinted == s->selected_line - 1)
4008 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
4009 waddstr(view->window, " ")waddnstr(view->window," ",-1);
4010
4011 if (view->ncols <= 9) {
35
Assuming field 'ncols' is <= 9
36
Taking true branch
4012 width = 9;
4013 wline = wcsdup(L"");
37
Memory is allocated
4014 if (wline == NULL((void*)0)) {
38
Assuming 'wline' is not equal to NULL
39
Taking false branch
4015 err = got_error_from_errno("wcsdup");
4016 free(line);
4017 return err;
4018 }
4019 } else if (s->first_displayed_line + nprinted ==
4020 s->matched_line &&
4021 regmatch->rm_so >= 0 && regmatch->rm_so < regmatch->rm_eo) {
4022 err = add_matched_line(&width, line, view->ncols - 9, 9,
4023 view->window, regmatch);
4024 if (err) {
4025 free(line);
4026 return err;
4027 }
4028 width += 9;
4029 } else {
4030 err = format_line(&wline, &width, line,
4031 view->ncols - 9, 9);
4032 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
4033 free(wline);
4034 wline = NULL((void*)0);
4035 width += 9;
4036 }
4037
4038 if (width <= view->ncols - 1)
40
Assuming the condition is false
41
Taking false branch
4039 waddch(view->window, '\n');
4040 if (++nprinted == 1)
42
Taking true branch
4041 s->first_displayed_line = lineno;
4042 }
4043 free(line);
45
Potential leak of memory pointed to by 'wline'
4044 s->last_displayed_line = lineno;
4045
4046 view_vborder(view);
4047
4048 return NULL((void*)0);
4049}
4050
4051static const struct got_error *
4052blame_cb(void *arg, int nlines, int lineno, struct got_object_id *id)
4053{
4054 const struct got_error *err = NULL((void*)0);
4055 struct tog_blame_cb_args *a = arg;
4056 struct tog_blame_line *line;
4057 int errcode;
4058
4059 if (nlines != a->nlines ||
4060 (lineno != -1 && lineno < 1) || lineno > a->nlines)
4061 return got_error(GOT_ERR_RANGE47);
4062
4063 errcode = pthread_mutex_lock(&tog_mutex);
4064 if (errcode)
4065 return got_error_set_errno(errcode, "pthread_mutex_lock");
4066
4067 if (*a->quit) { /* user has quit the blame view */
4068 err = got_error(GOT_ERR_ITER_COMPLETED46);
4069 goto done;
4070 }
4071
4072 if (lineno == -1)
4073 goto done; /* no change in this commit */
4074
4075 line = &a->lines[lineno - 1];
4076 if (line->annotated)
4077 goto done;
4078
4079 line->id = got_object_id_dup(id);
4080 if (line->id == NULL((void*)0)) {
4081 err = got_error_from_errno("got_object_id_dup");
4082 goto done;
4083 }
4084 line->annotated = 1;
4085done:
4086 errcode = pthread_mutex_unlock(&tog_mutex);
4087 if (errcode)
4088 err = got_error_set_errno(errcode, "pthread_mutex_unlock");
4089 return err;
4090}
4091
4092static void *
4093blame_thread(void *arg)
4094{
4095 const struct got_error *err;
4096 struct tog_blame_thread_args *ta = arg;
4097 struct tog_blame_cb_args *a = ta->cb_args;
4098 int errcode;
4099
4100 err = block_signals_used_by_main_thread();
4101 if (err)
4102 return (void *)err;
4103
4104 err = got_blame(ta->path, a->commit_id, ta->repo,
4105 blame_cb, ta->cb_args, ta->cancel_cb, ta->cancel_arg);
4106 if (err && err->code == GOT_ERR_CANCELLED49)
4107 err = NULL((void*)0);
4108
4109 errcode = pthread_mutex_lock(&tog_mutex);
4110 if (errcode)
4111 return (void *)got_error_set_errno(errcode,
4112 "pthread_mutex_lock");
4113
4114 got_repo_close(ta->repo);
4115 ta->repo = NULL((void*)0);
4116 *ta->complete = 1;
4117
4118 errcode = pthread_mutex_unlock(&tog_mutex);
4119 if (errcode && err == NULL((void*)0))
4120 err = got_error_set_errno(errcode, "pthread_mutex_unlock");
4121
4122 return (void *)err;
4123}
4124
4125static struct got_object_id *
4126get_selected_commit_id(struct tog_blame_line *lines, int nlines,
4127 int first_displayed_line, int selected_line)
4128{
4129 struct tog_blame_line *line;
4130
4131 if (nlines <= 0)
4132 return NULL((void*)0);
4133
4134 line = &lines[first_displayed_line - 1 + selected_line - 1];
4135 if (!line->annotated)
4136 return NULL((void*)0);
4137
4138 return line->id;
4139}
4140
4141static const struct got_error *
4142stop_blame(struct tog_blame *blame)
4143{
4144 const struct got_error *err = NULL((void*)0);
4145 int i;
4146
4147 if (blame->thread) {
4148 int errcode;
4149 errcode = pthread_mutex_unlock(&tog_mutex);
4150 if (errcode)
4151 return got_error_set_errno(errcode,
4152 "pthread_mutex_unlock");
4153 errcode = pthread_join(blame->thread, (void **)&err);
4154 if (errcode)
4155 return got_error_set_errno(errcode, "pthread_join");
4156 errcode = pthread_mutex_lock(&tog_mutex);
4157 if (errcode)
4158 return got_error_set_errno(errcode,
4159 "pthread_mutex_lock");
4160 if (err && err->code == GOT_ERR_ITER_COMPLETED46)
4161 err = NULL((void*)0);
4162 blame->thread = NULL((void*)0);
4163 }
4164 if (blame->thread_args.repo) {
4165 got_repo_close(blame->thread_args.repo);
4166 blame->thread_args.repo = NULL((void*)0);
4167 }
4168 if (blame->f) {
4169 if (fclose(blame->f) == EOF(-1) && err == NULL((void*)0))
4170 err = got_error_from_errno("fclose");
4171 blame->f = NULL((void*)0);
4172 }
4173 if (blame->lines) {
4174 for (i = 0; i < blame->nlines; i++)
4175 free(blame->lines[i].id);
4176 free(blame->lines);
4177 blame->lines = NULL((void*)0);
4178 }
4179 free(blame->cb_args.commit_id);
4180 blame->cb_args.commit_id = NULL((void*)0);
4181
4182 return err;
4183}
4184
4185static const struct got_error *
4186cancel_blame_view(void *arg)
4187{
4188 const struct got_error *err = NULL((void*)0);
4189 int *done = arg;
4190 int errcode;
4191
4192 errcode = pthread_mutex_lock(&tog_mutex);
4193 if (errcode)
4194 return got_error_set_errno(errcode,
4195 "pthread_mutex_unlock");
4196
4197 if (*done)
4198 err = got_error(GOT_ERR_CANCELLED49);
4199
4200 errcode = pthread_mutex_unlock(&tog_mutex);
4201 if (errcode)
4202 return got_error_set_errno(errcode,
4203 "pthread_mutex_lock");
4204
4205 return err;
4206}
4207
4208static const struct got_error *
4209run_blame(struct tog_view *view)
4210{
4211 struct tog_blame_view_state *s = &view->state.blame;
4212 struct tog_blame *blame = &s->blame;
4213 const struct got_error *err = NULL((void*)0);
4214 struct got_blob_object *blob = NULL((void*)0);
4215 struct got_repository *thread_repo = NULL((void*)0);
4216 struct got_object_id *obj_id = NULL((void*)0);
4217 int obj_type;
4218
4219 err = got_object_id_by_path(&obj_id, s->repo, s->blamed_commit->id,
4220 s->path);
4221 if (err)
4222 return err;
4223
4224 err = got_object_get_type(&obj_type, s->repo, obj_id);
4225 if (err)
4226 goto done;
4227
4228 if (obj_type != GOT_OBJ_TYPE_BLOB3) {
4229 err = got_error(GOT_ERR_OBJ_TYPE11);
4230 goto done;
4231 }
4232
4233 err = got_object_open_as_blob(&blob, s->repo, obj_id, 8192);
4234 if (err)
4235 goto done;
4236 blame->f = got_opentemp();
4237 if (blame->f == NULL((void*)0)) {
4238 err = got_error_from_errno("got_opentemp");
4239 goto done;
4240 }
4241 err = got_object_blob_dump_to_file(&blame->filesize, &blame->nlines,
4242 &blame->line_offsets, blame->f, blob);
4243 if (err)
4244 goto done;
4245 if (blame->nlines == 0) {
4246 s->blame_complete = 1;
4247 goto done;
4248 }
4249
4250 /* Don't include \n at EOF in the blame line count. */
4251 if (blame->line_offsets[blame->nlines - 1] == blame->filesize)
4252 blame->nlines--;
4253
4254 blame->lines = calloc(blame->nlines, sizeof(*blame->lines));
4255 if (blame->lines == NULL((void*)0)) {
4256 err = got_error_from_errno("calloc");
4257 goto done;
4258 }
4259
4260 err = got_repo_open(&thread_repo, got_repo_get_path(s->repo), NULL((void*)0));
4261 if (err)
4262 goto done;
4263
4264 blame->cb_args.view = view;
4265 blame->cb_args.lines = blame->lines;
4266 blame->cb_args.nlines = blame->nlines;
4267 blame->cb_args.commit_id = got_object_id_dup(s->blamed_commit->id);
4268 if (blame->cb_args.commit_id == NULL((void*)0)) {
4269 err = got_error_from_errno("got_object_id_dup");
4270 goto done;
4271 }
4272 blame->cb_args.quit = &s->done;
4273
4274 blame->thread_args.path = s->path;
4275 blame->thread_args.repo = thread_repo;
4276 blame->thread_args.cb_args = &blame->cb_args;
4277 blame->thread_args.complete = &s->blame_complete;
4278 blame->thread_args.cancel_cb = cancel_blame_view;
4279 blame->thread_args.cancel_arg = &s->done;
4280 s->blame_complete = 0;
4281
4282 if (s->first_displayed_line + view->nlines - 1 > blame->nlines) {
4283 s->first_displayed_line = 1;
4284 s->last_displayed_line = view->nlines;
4285 s->selected_line = 1;
4286 }
4287
4288done:
4289 if (blob)
4290 got_object_blob_close(blob);
4291 free(obj_id);
4292 if (err)
4293 stop_blame(blame);
4294 return err;
4295}
4296
4297static const struct got_error *
4298open_blame_view(struct tog_view *view, char *path,
4299 struct got_object_id *commit_id, struct got_repository *repo)
4300{
4301 const struct got_error *err = NULL((void*)0);
4302 struct tog_blame_view_state *s = &view->state.blame;
4303
4304 SIMPLEQ_INIT(&s->blamed_commits)do { (&s->blamed_commits)->sqh_first = ((void*)0); (
&s->blamed_commits)->sqh_last = &(&s->blamed_commits
)->sqh_first; } while (0)
;
4305
4306 s->path = strdup(path);
4307 if (s->path == NULL((void*)0))
4308 return got_error_from_errno("strdup");
4309
4310 err = got_object_qid_alloc(&s->blamed_commit, commit_id);
4311 if (err) {
4312 free(s->path);
4313 return err;
4314 }
4315
4316 SIMPLEQ_INSERT_HEAD(&s->blamed_commits, s->blamed_commit, entry)do { if (((s->blamed_commit)->entry.sqe_next = (&s->
blamed_commits)->sqh_first) == ((void*)0)) (&s->blamed_commits
)->sqh_last = &(s->blamed_commit)->entry.sqe_next
; (&s->blamed_commits)->sqh_first = (s->blamed_commit
); } while (0)
;
4317 s->first_displayed_line = 1;
4318 s->last_displayed_line = view->nlines;
4319 s->selected_line = 1;
4320 s->blame_complete = 0;
4321 s->repo = repo;
4322 s->commit_id = commit_id;
4323 memset(&s->blame, 0, sizeof(s->blame));
4324
4325 SIMPLEQ_INIT(&s->colors)do { (&s->colors)->sqh_first = ((void*)0); (&s->
colors)->sqh_last = &(&s->colors)->sqh_first
; } while (0)
;
4326 if (has_colors() && getenv("TOG_COLORS") != NULL((void*)0)) {
4327 err = add_color(&s->colors, "^", TOG_COLOR_COMMIT9,
4328 get_color_value("TOG_COLOR_COMMIT"));
4329 if (err)
4330 return err;
4331 }
4332
4333 view->show = show_blame_view;
4334 view->input = input_blame_view;
4335 view->close = close_blame_view;
4336 view->search_start = search_start_blame_view;
4337 view->search_next = search_next_blame_view;
4338
4339 return run_blame(view);
4340}
4341
4342static const struct got_error *
4343close_blame_view(struct tog_view *view)
4344{
4345 const struct got_error *err = NULL((void*)0);
4346 struct tog_blame_view_state *s = &view->state.blame;
4347
4348 if (s->blame.thread)
4349 err = stop_blame(&s->blame);
4350
4351 while (!SIMPLEQ_EMPTY(&s->blamed_commits)(((&s->blamed_commits)->sqh_first) == ((void*)0))) {
4352 struct got_object_qid *blamed_commit;
4353 blamed_commit = SIMPLEQ_FIRST(&s->blamed_commits)((&s->blamed_commits)->sqh_first);
4354 SIMPLEQ_REMOVE_HEAD(&s->blamed_commits, entry)do { if (((&s->blamed_commits)->sqh_first = (&s
->blamed_commits)->sqh_first->entry.sqe_next) == ((void
*)0)) (&s->blamed_commits)->sqh_last = &(&s
->blamed_commits)->sqh_first; } while (0)
;
4355 got_object_qid_free(blamed_commit);
4356 }
4357
4358 free(s->path);
4359 free_colors(&s->colors);
4360
4361 return err;
4362}
4363
4364static const struct got_error *
4365search_start_blame_view(struct tog_view *view)
4366{
4367 struct tog_blame_view_state *s = &view->state.blame;
4368
4369 s->matched_line = 0;
4370 return NULL((void*)0);
4371}
4372
4373static const struct got_error *
4374search_next_blame_view(struct tog_view *view)
4375{
4376 struct tog_blame_view_state *s = &view->state.blame;
4377 int lineno;
4378 char *line = NULL((void*)0);
4379 size_t linesize = 0;
4380 ssize_t linelen;
4381
4382 if (!view->searching) {
4383 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
4384 return NULL((void*)0);
4385 }
4386
4387 if (s->matched_line) {
4388 if (view->searching == TOG_SEARCH_FORWARD1)
4389 lineno = s->matched_line + 1;
4390 else
4391 lineno = s->matched_line - 1;
4392 } else {
4393 if (view->searching == TOG_SEARCH_FORWARD1)
4394 lineno = 1;
4395 else
4396 lineno = s->blame.nlines;
4397 }
4398
4399 while (1) {
4400 off_t offset;
4401
4402 if (lineno <= 0 || lineno > s->blame.nlines) {
4403 if (s->matched_line == 0) {
4404 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
4405 break;
4406 }
4407
4408 if (view->searching == TOG_SEARCH_FORWARD1)
4409 lineno = 1;
4410 else
4411 lineno = s->blame.nlines;
4412 }
4413
4414 offset = s->blame.line_offsets[lineno - 1];
4415 if (fseeko(s->blame.f, offset, SEEK_SET0) != 0) {
4416 free(line);
4417 return got_error_from_errno("fseeko");
4418 }
4419 linelen = getline(&line, &linesize, s->blame.f);
4420 if (linelen != -1 &&
4421 match_line(line, &view->regex, 1, &view->regmatch)) {
4422 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
4423 s->matched_line = lineno;
4424 break;
4425 }
4426 if (view->searching == TOG_SEARCH_FORWARD1)
4427 lineno++;
4428 else
4429 lineno--;
4430 }
4431 free(line);
4432
4433 if (s->matched_line) {
4434 s->first_displayed_line = s->matched_line;
4435 s->selected_line = 1;
4436 }
4437
4438 return NULL((void*)0);
4439}
4440
4441static const struct got_error *
4442show_blame_view(struct tog_view *view)
4443{
4444 const struct got_error *err = NULL((void*)0);
4445 struct tog_blame_view_state *s = &view->state.blame;
4446 int errcode;
4447
4448 if (s->blame.thread == NULL((void*)0) && !s->blame_complete) {
1
Assuming field 'thread' is not equal to NULL
4449 errcode = pthread_create(&s->blame.thread, NULL((void*)0), blame_thread,
4450 &s->blame.thread_args);
4451 if (errcode)
4452 return got_error_set_errno(errcode, "pthread_create");
4453
4454 halfdelay(1); /* fast refresh while annotating */
4455 }
4456
4457 if (s->blame_complete)
2
Assuming field 'blame_complete' is 0
3
Taking false branch
4458 halfdelay(10); /* disable fast refresh */
4459
4460 err = draw_blame(view);
4
Calling 'draw_blame'
4461
4462 view_vborder(view);
4463 return err;
4464}
4465
4466static const struct got_error *
4467input_blame_view(struct tog_view **new_view, struct tog_view *view, int ch)
4468{
4469 const struct got_error *err = NULL((void*)0), *thread_err = NULL((void*)0);
4470 struct tog_view *diff_view;
4471 struct tog_blame_view_state *s = &view->state.blame;
4472 int begin_x = 0;
4473
4474 switch (ch) {
4475 case 'q':
4476 s->done = 1;
4477 break;
4478 case 'k':
4479 case KEY_UP0403:
4480 if (s->selected_line > 1)
4481 s->selected_line--;
4482 else if (s->selected_line == 1 &&
4483 s->first_displayed_line > 1)
4484 s->first_displayed_line--;
4485 break;
4486 case KEY_PPAGE0523:
4487 case CTRL('b')(('b') & 0x1f):
4488 if (s->first_displayed_line == 1) {
4489 s->selected_line = 1;
4490 break;
4491 }
4492 if (s->first_displayed_line > view->nlines - 2)
4493 s->first_displayed_line -=
4494 (view->nlines - 2);
4495 else
4496 s->first_displayed_line = 1;
4497 break;
4498 case 'j':
4499 case KEY_DOWN0402:
4500 if (s->selected_line < view->nlines - 2 &&
4501 s->first_displayed_line +
4502 s->selected_line <= s->blame.nlines)
4503 s->selected_line++;
4504 else if (s->last_displayed_line <
4505 s->blame.nlines)
4506 s->first_displayed_line++;
4507 break;
4508 case 'b':
4509 case 'p': {
4510 struct got_object_id *id = NULL((void*)0);
4511 id = get_selected_commit_id(s->blame.lines, s->blame.nlines,
4512 s->first_displayed_line, s->selected_line);
4513 if (id == NULL((void*)0))
4514 break;
4515 if (ch == 'p') {
4516 struct got_commit_object *commit;
4517 struct got_object_qid *pid;
4518 struct got_object_id *blob_id = NULL((void*)0);
4519 int obj_type;
4520 err = got_object_open_as_commit(&commit,
4521 s->repo, id);
4522 if (err)
4523 break;
4524 pid = SIMPLEQ_FIRST(((got_object_commit_get_parent_ids(commit))->sqh_first)
4525 got_object_commit_get_parent_ids(commit))((got_object_commit_get_parent_ids(commit))->sqh_first);
4526 if (pid == NULL((void*)0)) {
4527 got_object_commit_close(commit);
4528 break;
4529 }
4530 /* Check if path history ends here. */
4531 err = got_object_id_by_path(&blob_id, s->repo,
4532 pid->id, s->path);
4533 if (err) {
4534 if (err->code == GOT_ERR_NO_TREE_ENTRY50)
4535 err = NULL((void*)0);
4536 got_object_commit_close(commit);
4537 break;
4538 }
4539 err = got_object_get_type(&obj_type, s->repo,
4540 blob_id);
4541 free(blob_id);
4542 /* Can't blame non-blob type objects. */
4543 if (obj_type != GOT_OBJ_TYPE_BLOB3) {
4544 got_object_commit_close(commit);
4545 break;
4546 }
4547 err = got_object_qid_alloc(&s->blamed_commit,
4548 pid->id);
4549 got_object_commit_close(commit);
4550 } else {
4551 if (got_object_id_cmp(id,
4552 s->blamed_commit->id) == 0)
4553 break;
4554 err = got_object_qid_alloc(&s->blamed_commit,
4555 id);
4556 }
4557 if (err)
4558 break;
4559 s->done = 1;
4560 thread_err = stop_blame(&s->blame);
4561 s->done = 0;
4562 if (thread_err)
4563 break;
4564 SIMPLEQ_INSERT_HEAD(&s->blamed_commits,do { if (((s->blamed_commit)->entry.sqe_next = (&s->
blamed_commits)->sqh_first) == ((void*)0)) (&s->blamed_commits
)->sqh_last = &(s->blamed_commit)->entry.sqe_next
; (&s->blamed_commits)->sqh_first = (s->blamed_commit
); } while (0)
4565 s->blamed_commit, entry)do { if (((s->blamed_commit)->entry.sqe_next = (&s->
blamed_commits)->sqh_first) == ((void*)0)) (&s->blamed_commits
)->sqh_last = &(s->blamed_commit)->entry.sqe_next
; (&s->blamed_commits)->sqh_first = (s->blamed_commit
); } while (0)
;
4566 err = run_blame(view);
4567 if (err)
4568 break;
4569 break;
4570 }
4571 case 'B': {
4572 struct got_object_qid *first;
4573 first = SIMPLEQ_FIRST(&s->blamed_commits)((&s->blamed_commits)->sqh_first);
4574 if (!got_object_id_cmp(first->id, s->commit_id))
4575 break;
4576 s->done = 1;
4577 thread_err = stop_blame(&s->blame);
4578 s->done = 0;
4579 if (thread_err)
4580 break;
4581 SIMPLEQ_REMOVE_HEAD(&s->blamed_commits, entry)do { if (((&s->blamed_commits)->sqh_first = (&s
->blamed_commits)->sqh_first->entry.sqe_next) == ((void
*)0)) (&s->blamed_commits)->sqh_last = &(&s
->blamed_commits)->sqh_first; } while (0)
;
4582 got_object_qid_free(s->blamed_commit);
4583 s->blamed_commit =
4584 SIMPLEQ_FIRST(&s->blamed_commits)((&s->blamed_commits)->sqh_first);
4585 err = run_blame(view);
4586 if (err)
4587 break;
4588 break;
4589 }
4590 case KEY_ENTER0527:
4591 case '\r': {
4592 struct got_object_id *id = NULL((void*)0);
4593 struct got_object_qid *pid;
4594 struct got_commit_object *commit = NULL((void*)0);
4595 id = get_selected_commit_id(s->blame.lines, s->blame.nlines,
4596 s->first_displayed_line, s->selected_line);
4597 if (id == NULL((void*)0))
4598 break;
4599 err = got_object_open_as_commit(&commit, s->repo, id);
4600 if (err)
4601 break;
4602 pid = SIMPLEQ_FIRST(((got_object_commit_get_parent_ids(commit))->sqh_first)
4603 got_object_commit_get_parent_ids(commit))((got_object_commit_get_parent_ids(commit))->sqh_first);
4604 if (view_is_parent_view(view))
4605 begin_x = view_split_begin_x(view->begin_x);
4606 diff_view = view_open(0, 0, 0, begin_x, TOG_VIEW_DIFF);
4607 if (diff_view == NULL((void*)0)) {
4608 got_object_commit_close(commit);
4609 err = got_error_from_errno("view_open");
4610 break;
4611 }
4612 err = open_diff_view(diff_view, pid ? pid->id : NULL((void*)0),
4613 id, NULL((void*)0), NULL((void*)0), 3, 0, 0, NULL((void*)0), s->repo);
4614 got_object_commit_close(commit);
4615 if (err) {
4616 view_close(diff_view);
4617 break;
4618 }
4619 view->focussed = 0;
4620 diff_view->focussed = 1;
4621 if (view_is_parent_view(view)) {
4622 err = view_close_child(view);
4623 if (err)
4624 break;
4625 view_set_child(view, diff_view);
4626 view->focus_child = 1;
4627 } else
4628 *new_view = diff_view;
4629 if (err)
4630 break;
4631 break;
4632 }
4633 case KEY_NPAGE0522:
4634 case CTRL('f')(('f') & 0x1f):
4635 case ' ':
4636 if (s->last_displayed_line >= s->blame.nlines &&
4637 s->selected_line >= MIN(s->blame.nlines,((s->blame.nlines) < (view->nlines - 2) ? (s->blame
.nlines) : (view->nlines - 2))
4638 view->nlines - 2)((s->blame.nlines) < (view->nlines - 2) ? (s->blame
.nlines) : (view->nlines - 2))
) {
4639 break;
4640 }
4641 if (s->last_displayed_line >= s->blame.nlines &&
4642 s->selected_line < view->nlines - 2) {
4643 s->selected_line = MIN(s->blame.nlines,((s->blame.nlines) < (view->nlines - 2) ? (s->blame
.nlines) : (view->nlines - 2))
4644 view->nlines - 2)((s->blame.nlines) < (view->nlines - 2) ? (s->blame
.nlines) : (view->nlines - 2))
;
4645 break;
4646 }
4647 if (s->last_displayed_line + view->nlines - 2
4648 <= s->blame.nlines)
4649 s->first_displayed_line +=
4650 view->nlines - 2;
4651 else
4652 s->first_displayed_line =
4653 s->blame.nlines -
4654 (view->nlines - 3);
4655 break;
4656 case KEY_RESIZE0632:
4657 if (s->selected_line > view->nlines - 2) {
4658 s->selected_line = MIN(s->blame.nlines,((s->blame.nlines) < (view->nlines - 2) ? (s->blame
.nlines) : (view->nlines - 2))
4659 view->nlines - 2)((s->blame.nlines) < (view->nlines - 2) ? (s->blame
.nlines) : (view->nlines - 2))
;
4660 }
4661 break;
4662 default:
4663 break;
4664 }
4665 return thread_err ? thread_err : err;
4666}
4667
4668static const struct got_error *
4669cmd_blame(int argc, char *argv[])
4670{
4671 const struct got_error *error;
4672 struct got_repository *repo = NULL((void*)0);
4673 struct got_worktree *worktree = NULL((void*)0);
4674 char *cwd = NULL((void*)0), *repo_path = NULL((void*)0), *in_repo_path = NULL((void*)0);
4675 char *link_target = NULL((void*)0);
4676 struct got_object_id *commit_id = NULL((void*)0);
4677 char *commit_id_str = NULL((void*)0);
4678 int ch;
4679 struct tog_view *view;
4680
4681 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
4682 switch (ch) {
4683 case 'c':
4684 commit_id_str = optarg;
4685 break;
4686 case 'r':
4687 repo_path = realpath(optarg, NULL((void*)0));
4688 if (repo_path == NULL((void*)0))
4689 return got_error_from_errno2("realpath",
4690 optarg);
4691 break;
4692 default:
4693 usage_blame();
4694 /* NOTREACHED */
4695 }
4696 }
4697
4698 argc -= optind;
4699 argv += optind;
4700
4701 if (argc != 1)
4702 usage_blame();
4703
4704 if (repo_path == NULL((void*)0)) {
4705 cwd = getcwd(NULL((void*)0), 0);
4706 if (cwd == NULL((void*)0))
4707 return got_error_from_errno("getcwd");
4708 error = got_worktree_open(&worktree, cwd);
4709 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
4710 goto done;
4711 if (worktree)
4712 repo_path =
4713 strdup(got_worktree_get_repo_path(worktree));
4714 else
4715 repo_path = strdup(cwd);
4716 if (repo_path == NULL((void*)0)) {
4717 error = got_error_from_errno("strdup");
4718 goto done;
4719 }
4720 }
4721
4722 error = got_repo_open(&repo, repo_path, NULL((void*)0));
4723 if (error != NULL((void*)0))
4724 goto done;
4725
4726 error = get_in_repo_path_from_argv0(&in_repo_path, argc, argv, repo,
4727 worktree);
4728 if (error)
4729 goto done;
4730
4731 init_curses();
4732
4733 error = apply_unveil(got_repo_get_path(repo), NULL((void*)0));
4734 if (error)
4735 goto done;
4736
4737 error = tog_load_refs(repo);
4738 if (error)
4739 goto done;
4740
4741 if (commit_id_str == NULL((void*)0)) {
4742 struct got_reference *head_ref;
4743 error = got_ref_open(&head_ref, repo, worktree ?
4744 got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD"HEAD", 0);
4745 if (error != NULL((void*)0))
4746 goto done;
4747 error = got_ref_resolve(&commit_id, repo, head_ref);
4748 got_ref_close(head_ref);
4749 } else {
4750 error = got_repo_match_object_id(&commit_id, NULL((void*)0),
4751 commit_id_str, GOT_OBJ_TYPE_COMMIT1, &tog_refs, repo);
4752 }
4753 if (error != NULL((void*)0))
4754 goto done;
4755
4756 view = view_open(0, 0, 0, 0, TOG_VIEW_BLAME);
4757 if (view == NULL((void*)0)) {
4758 error = got_error_from_errno("view_open");
4759 goto done;
4760 }
4761
4762 error = got_object_resolve_symlinks(&link_target, in_repo_path,
4763 commit_id, repo);
4764 if (error)
4765 goto done;
4766
4767 error = open_blame_view(view, link_target ? link_target : in_repo_path,
4768 commit_id, repo);
4769 if (error)
4770 goto done;
4771 if (worktree) {
4772 /* Release work tree lock. */
4773 got_worktree_close(worktree);
4774 worktree = NULL((void*)0);
4775 }
4776 error = view_loop(view);
4777done:
4778 free(repo_path);
4779 free(in_repo_path);
4780 free(link_target);
4781 free(cwd);
4782 free(commit_id);
4783 if (worktree)
4784 got_worktree_close(worktree);
4785 if (repo)
4786 got_repo_close(repo);
4787 tog_free_refs();
4788 return error;
4789}
4790
4791static const struct got_error *
4792draw_tree_entries(struct tog_view *view, const char *parent_path)
4793{
4794 struct tog_tree_view_state *s = &view->state.tree;
4795 const struct got_error *err = NULL((void*)0);
4796 struct got_tree_entry *te;
4797 wchar_t *wline;
4798 struct tog_color *tc;
4799 int width, n, i, nentries;
4800 int limit = view->nlines;
4801
4802 s->ndisplayed = 0;
4803
4804 werase(view->window);
4805
4806 if (limit == 0)
4807 return NULL((void*)0);
4808
4809 err = format_line(&wline, &width, s->tree_label, view->ncols, 0);
4810 if (err)
4811 return err;
4812 if (view_needs_focus_indication(view))
4813 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
4814 tc = get_color(&s->colors, TOG_COLOR_COMMIT9);
4815 if (tc)
4816 wattr_on(view->window,
4817 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
4818 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
4819 if (tc)
4820 wattr_off(view->window,
4821 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
4822 if (view_needs_focus_indication(view))
4823 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
4824 free(wline);
4825 wline = NULL((void*)0);
4826 if (width < view->ncols - 1)
4827 waddch(view->window, '\n');
4828 if (--limit <= 0)
4829 return NULL((void*)0);
4830 err = format_line(&wline, &width, parent_path, view->ncols, 0);
4831 if (err)
4832 return err;
4833 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
4834 free(wline);
4835 wline = NULL((void*)0);
4836 if (width < view->ncols - 1)
4837 waddch(view->window, '\n');
4838 if (--limit <= 0)
4839 return NULL((void*)0);
4840 waddch(view->window, '\n');
4841 if (--limit <= 0)
4842 return NULL((void*)0);
4843
4844 if (s->first_displayed_entry == NULL((void*)0)) {
4845 te = got_object_tree_get_first_entry(s->tree);
4846 if (s->selected == 0) {
4847 if (view->focussed)
4848 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
4849 s->selected_entry = NULL((void*)0);
4850 }
4851 waddstr(view->window, " ..\n")waddnstr(view->window," ..\n",-1); /* parent directory */
4852 if (s->selected == 0 && view->focussed)
4853 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
4854 s->ndisplayed++;
4855 if (--limit <= 0)
4856 return NULL((void*)0);
4857 n = 1;
4858 } else {
4859 n = 0;
4860 te = s->first_displayed_entry;
4861 }
4862
4863 nentries = got_object_tree_get_nentries(s->tree);
4864 for (i = got_tree_entry_get_index(te); i < nentries; i++) {
4865 char *line = NULL((void*)0), *id_str = NULL((void*)0), *link_target = NULL((void*)0);
4866 const char *modestr = "";
4867 mode_t mode;
4868
4869 te = got_object_tree_get_entry(s->tree, i);
4870 mode = got_tree_entry_get_mode(te);
4871
4872 if (s->show_ids) {
4873 err = got_object_id_str(&id_str,
4874 got_tree_entry_get_id(te));
4875 if (err)
4876 return got_error_from_errno(
4877 "got_object_id_str");
4878 }
4879 if (got_object_tree_entry_is_submodule(te))
4880 modestr = "$";
4881 else if (S_ISLNK(mode)((mode & 0170000) == 0120000)) {
4882 int i;
4883
4884 err = got_tree_entry_get_symlink_target(&link_target,
4885 te, s->repo);
4886 if (err) {
4887 free(id_str);
4888 return err;
4889 }
4890 for (i = 0; i < strlen(link_target); i++) {
4891 if (!isprint((unsigned char)link_target[i]))
4892 link_target[i] = '?';
4893 }
4894 modestr = "@";
4895 }
4896 else if (S_ISDIR(mode)((mode & 0170000) == 0040000))
4897 modestr = "/";
4898 else if (mode & S_IXUSR0000100)
4899 modestr = "*";
4900 if (asprintf(&line, "%s %s%s%s%s", id_str ? id_str : "",
4901 got_tree_entry_get_name(te), modestr,
4902 link_target ? " -> ": "",
4903 link_target ? link_target : "") == -1) {
4904 free(id_str);
4905 free(link_target);
4906 return got_error_from_errno("asprintf");
4907 }
4908 free(id_str);
4909 free(link_target);
4910 err = format_line(&wline, &width, line, view->ncols, 0);
4911 if (err) {
4912 free(line);
4913 break;
4914 }
4915 if (n == s->selected) {
4916 if (view->focussed)
4917 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
4918 s->selected_entry = te;
4919 }
4920 tc = match_color(&s->colors, line);
4921 if (tc)
4922 wattr_on(view->window,
4923 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
4924 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
4925 if (tc)
4926 wattr_off(view->window,
4927 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
4928 if (width < view->ncols - 1)
4929 waddch(view->window, '\n');
4930 if (n == s->selected && view->focussed)
4931 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
4932 free(line);
4933 free(wline);
4934 wline = NULL((void*)0);
4935 n++;
4936 s->ndisplayed++;
4937 s->last_displayed_entry = te;
4938 if (--limit <= 0)
4939 break;
4940 }
4941
4942 return err;
4943}
4944
4945static void
4946tree_scroll_up(struct tog_tree_view_state *s, int maxscroll)
4947{
4948 struct got_tree_entry *te;
4949 int isroot = s->tree == s->root;
4950 int i = 0;
4951
4952 if (s->first_displayed_entry == NULL((void*)0))
4953 return;
4954
4955 te = got_tree_entry_get_prev(s->tree, s->first_displayed_entry);
4956 while (i++ < maxscroll) {
4957 if (te == NULL((void*)0)) {
4958 if (!isroot)
4959 s->first_displayed_entry = NULL((void*)0);
4960 break;
4961 }
4962 s->first_displayed_entry = te;
4963 te = got_tree_entry_get_prev(s->tree, te);
4964 }
4965}
4966
4967static void
4968tree_scroll_down(struct tog_tree_view_state *s, int maxscroll)
4969{
4970 struct got_tree_entry *next, *last;
4971 int n = 0;
4972
4973 if (s->first_displayed_entry)
4974 next = got_tree_entry_get_next(s->tree,
4975 s->first_displayed_entry);
4976 else
4977 next = got_object_tree_get_first_entry(s->tree);
4978
4979 last = s->last_displayed_entry;
4980 while (next && last && n++ < maxscroll) {
4981 last = got_tree_entry_get_next(s->tree, last);
4982 if (last) {
4983 s->first_displayed_entry = next;
4984 next = got_tree_entry_get_next(s->tree, next);
4985 }
4986 }
4987}
4988
4989static const struct got_error *
4990tree_entry_path(char **path, struct tog_parent_trees *parents,
4991 struct got_tree_entry *te)
4992{
4993 const struct got_error *err = NULL((void*)0);
4994 struct tog_parent_tree *pt;
4995 size_t len = 2; /* for leading slash and NUL */
4996
4997 TAILQ_FOREACH(pt, parents, entry)for((pt) = ((parents)->tqh_first); (pt) != ((void*)0); (pt
) = ((pt)->entry.tqe_next))
4998 len += strlen(got_tree_entry_get_name(pt->selected_entry))
4999 + 1 /* slash */;
5000 if (te)
5001 len += strlen(got_tree_entry_get_name(te));
5002
5003 *path = calloc(1, len);
5004 if (path == NULL((void*)0))
5005 return got_error_from_errno("calloc");
5006
5007 (*path)[0] = '/';
5008 pt = TAILQ_LAST(parents, tog_parent_trees)(*(((struct tog_parent_trees *)((parents)->tqh_last))->
tqh_last))
;
5009 while (pt) {
5010 const char *name = got_tree_entry_get_name(pt->selected_entry);
5011 if (strlcat(*path, name, len) >= len) {
5012 err = got_error(GOT_ERR_NO_SPACE9);
5013 goto done;
5014 }
5015 if (strlcat(*path, "/", len) >= len) {
5016 err = got_error(GOT_ERR_NO_SPACE9);
5017 goto done;
5018 }
5019 pt = TAILQ_PREV(pt, tog_parent_trees, entry)(*(((struct tog_parent_trees *)((pt)->entry.tqe_prev))->
tqh_last))
;
5020 }
5021 if (te) {
5022 if (strlcat(*path, got_tree_entry_get_name(te), len) >= len) {
5023 err = got_error(GOT_ERR_NO_SPACE9);
5024 goto done;
5025 }
5026 }
5027done:
5028 if (err) {
5029 free(*path);
5030 *path = NULL((void*)0);
5031 }
5032 return err;
5033}
5034
5035static const struct got_error *
5036blame_tree_entry(struct tog_view **new_view, int begin_x,
5037 struct got_tree_entry *te, struct tog_parent_trees *parents,
5038 struct got_object_id *commit_id, struct got_repository *repo)
5039{
5040 const struct got_error *err = NULL((void*)0);
5041 char *path;
5042 struct tog_view *blame_view;
5043
5044 *new_view = NULL((void*)0);
5045
5046 err = tree_entry_path(&path, parents, te);
5047 if (err)
5048 return err;
5049
5050 blame_view = view_open(0, 0, 0, begin_x, TOG_VIEW_BLAME);
5051 if (blame_view == NULL((void*)0)) {
5052 err = got_error_from_errno("view_open");
5053 goto done;
5054 }
5055
5056 err = open_blame_view(blame_view, path, commit_id, repo);
5057 if (err) {
5058 if (err->code == GOT_ERR_CANCELLED49)
5059 err = NULL((void*)0);
5060 view_close(blame_view);
5061 } else
5062 *new_view = blame_view;
5063done:
5064 free(path);
5065 return err;
5066}
5067
5068static const struct got_error *
5069log_selected_tree_entry(struct tog_view **new_view, int begin_x,
5070 struct tog_tree_view_state *s)
5071{
5072 struct tog_view *log_view;
5073 const struct got_error *err = NULL((void*)0);
5074 char *path;
5075
5076 *new_view = NULL((void*)0);
5077
5078 log_view = view_open(0, 0, 0, begin_x, TOG_VIEW_LOG);
5079 if (log_view == NULL((void*)0))
5080 return got_error_from_errno("view_open");
5081
5082 err = tree_entry_path(&path, &s->parents, s->selected_entry);
5083 if (err)
5084 return err;
5085
5086 err = open_log_view(log_view, s->commit_id, s->repo, s->head_ref_name,
5087 path, 0);
5088 if (err)
5089 view_close(log_view);
5090 else
5091 *new_view = log_view;
5092 free(path);
5093 return err;
5094}
5095
5096static const struct got_error *
5097open_tree_view(struct tog_view *view, struct got_tree_object *root,
5098 struct got_object_id *commit_id, const char *head_ref_name,
5099 struct got_repository *repo)
5100{
5101 const struct got_error *err = NULL((void*)0);
5102 char *commit_id_str = NULL((void*)0);
5103 struct tog_tree_view_state *s = &view->state.tree;
5104
5105 TAILQ_INIT(&s->parents)do { (&s->parents)->tqh_first = ((void*)0); (&s
->parents)->tqh_last = &(&s->parents)->tqh_first
; } while (0)
;
5106
5107 err = got_object_id_str(&commit_id_str, commit_id);
5108 if (err != NULL((void*)0))
5109 goto done;
5110
5111 if (asprintf(&s->tree_label, "commit %s", commit_id_str) == -1) {
5112 err = got_error_from_errno("asprintf");
5113 goto done;
5114 }
5115
5116 s->root = s->tree = root;
5117 s->first_displayed_entry = got_object_tree_get_entry(s->tree, 0);
5118 s->selected_entry = got_object_tree_get_entry(s->tree, 0);
5119 s->commit_id = got_object_id_dup(commit_id);
5120 if (s->commit_id == NULL((void*)0)) {
5121 err = got_error_from_errno("got_object_id_dup");
5122 goto done;
5123 }
5124 if (head_ref_name) {
5125 s->head_ref_name = strdup(head_ref_name);
5126 if (s->head_ref_name == NULL((void*)0)) {
5127 err = got_error_from_errno("strdup");
5128 goto done;
5129 }
5130 }
5131 s->repo = repo;
5132
5133 SIMPLEQ_INIT(&s->colors)do { (&s->colors)->sqh_first = ((void*)0); (&s->
colors)->sqh_last = &(&s->colors)->sqh_first
; } while (0)
;
5134
5135 if (has_colors() && getenv("TOG_COLORS") != NULL((void*)0)) {
5136 err = add_color(&s->colors, "\\$$",
5137 TOG_COLOR_TREE_SUBMODULE5,
5138 get_color_value("TOG_COLOR_TREE_SUBMODULE"));
5139 if (err)
5140 goto done;
5141 err = add_color(&s->colors, "@$", TOG_COLOR_TREE_SYMLINK6,
5142 get_color_value("TOG_COLOR_TREE_SYMLINK"));
5143 if (err) {
5144 free_colors(&s->colors);
5145 goto done;
5146 }
5147 err = add_color(&s->colors, "/$",
5148 TOG_COLOR_TREE_DIRECTORY7,
5149 get_color_value("TOG_COLOR_TREE_DIRECTORY"));
5150 if (err) {
5151 free_colors(&s->colors);
5152 goto done;
5153 }
5154
5155 err = add_color(&s->colors, "\\*$",
5156 TOG_COLOR_TREE_EXECUTABLE8,
5157 get_color_value("TOG_COLOR_TREE_EXECUTABLE"));
5158 if (err) {
5159 free_colors(&s->colors);
5160 goto done;
5161 }
5162
5163 err = add_color(&s->colors, "^$", TOG_COLOR_COMMIT9,
5164 get_color_value("TOG_COLOR_COMMIT"));
5165 if (err) {
5166 free_colors(&s->colors);
5167 goto done;
5168 }
5169 }
5170
5171 view->show = show_tree_view;
5172 view->input = input_tree_view;
5173 view->close = close_tree_view;
5174 view->search_start = search_start_tree_view;
5175 view->search_next = search_next_tree_view;
5176done:
5177 free(commit_id_str);
5178 if (err) {
5179 free(s->tree_label);
5180 s->tree_label = NULL((void*)0);
5181 }
5182 return err;
5183}
5184
5185static const struct got_error *
5186close_tree_view(struct tog_view *view)
5187{
5188 struct tog_tree_view_state *s = &view->state.tree;
5189
5190 free_colors(&s->colors);
5191 free(s->tree_label);
5192 s->tree_label = NULL((void*)0);
5193 free(s->commit_id);
5194 s->commit_id = NULL((void*)0);
5195 free(s->head_ref_name);
5196 s->head_ref_name = NULL((void*)0);
5197 while (!TAILQ_EMPTY(&s->parents)(((&s->parents)->tqh_first) == ((void*)0))) {
5198 struct tog_parent_tree *parent;
5199 parent = TAILQ_FIRST(&s->parents)((&s->parents)->tqh_first);
5200 TAILQ_REMOVE(&s->parents, parent, entry)do { if (((parent)->entry.tqe_next) != ((void*)0)) (parent
)->entry.tqe_next->entry.tqe_prev = (parent)->entry.
tqe_prev; else (&s->parents)->tqh_last = (parent)->
entry.tqe_prev; *(parent)->entry.tqe_prev = (parent)->entry
.tqe_next; ; ; } while (0)
;
5201 free(parent);
5202
5203 }
5204 if (s->tree != s->root)
5205 got_object_tree_close(s->tree);
5206 got_object_tree_close(s->root);
5207 return NULL((void*)0);
5208}
5209
5210static const struct got_error *
5211search_start_tree_view(struct tog_view *view)
5212{
5213 struct tog_tree_view_state *s = &view->state.tree;
5214
5215 s->matched_entry = NULL((void*)0);
5216 return NULL((void*)0);
5217}
5218
5219static int
5220match_tree_entry(struct got_tree_entry *te, regex_t *regex)
5221{
5222 regmatch_t regmatch;
5223
5224 return regexec(regex, got_tree_entry_get_name(te), 1, &regmatch,
5225 0) == 0;
5226}
5227
5228static const struct got_error *
5229search_next_tree_view(struct tog_view *view)
5230{
5231 struct tog_tree_view_state *s = &view->state.tree;
5232 struct got_tree_entry *te = NULL((void*)0);
5233
5234 if (!view->searching) {
5235 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
5236 return NULL((void*)0);
5237 }
5238
5239 if (s->matched_entry) {
5240 if (view->searching == TOG_SEARCH_FORWARD1) {
5241 if (s->selected_entry)
5242 te = got_tree_entry_get_next(s->tree,
5243 s->selected_entry);
5244 else
5245 te = got_object_tree_get_first_entry(s->tree);
5246 } else {
5247 if (s->selected_entry == NULL((void*)0))
5248 te = got_object_tree_get_last_entry(s->tree);
5249 else
5250 te = got_tree_entry_get_prev(s->tree,
5251 s->selected_entry);
5252 }
5253 } else {
5254 if (view->searching == TOG_SEARCH_FORWARD1)
5255 te = got_object_tree_get_first_entry(s->tree);
5256 else
5257 te = got_object_tree_get_last_entry(s->tree);
5258 }
5259
5260 while (1) {
5261 if (te == NULL((void*)0)) {
5262 if (s->matched_entry == NULL((void*)0)) {
5263 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
5264 return NULL((void*)0);
5265 }
5266 if (view->searching == TOG_SEARCH_FORWARD1)
5267 te = got_object_tree_get_first_entry(s->tree);
5268 else
5269 te = got_object_tree_get_last_entry(s->tree);
5270 }
5271
5272 if (match_tree_entry(te, &view->regex)) {
5273 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
5274 s->matched_entry = te;
5275 break;
5276 }
5277
5278 if (view->searching == TOG_SEARCH_FORWARD1)
5279 te = got_tree_entry_get_next(s->tree, te);
5280 else
5281 te = got_tree_entry_get_prev(s->tree, te);
5282 }
5283
5284 if (s->matched_entry) {
5285 s->first_displayed_entry = s->matched_entry;
5286 s->selected = 0;
5287 }
5288
5289 return NULL((void*)0);
5290}
5291
5292static const struct got_error *
5293show_tree_view(struct tog_view *view)
5294{
5295 const struct got_error *err = NULL((void*)0);
5296 struct tog_tree_view_state *s = &view->state.tree;
5297 char *parent_path;
5298
5299 err = tree_entry_path(&parent_path, &s->parents, NULL((void*)0));
5300 if (err)
5301 return err;
5302
5303 err = draw_tree_entries(view, parent_path);
5304 free(parent_path);
5305
5306 view_vborder(view);
5307 return err;
5308}
5309
5310static const struct got_error *
5311input_tree_view(struct tog_view **new_view, struct tog_view *view, int ch)
5312{
5313 const struct got_error *err = NULL((void*)0);
5314 struct tog_tree_view_state *s = &view->state.tree;
5315 struct tog_view *log_view, *ref_view;
5316 int begin_x = 0;
5317
5318 switch (ch) {
5319 case 'i':
5320 s->show_ids = !s->show_ids;
5321 break;
5322 case 'l':
5323 if (!s->selected_entry)
5324 break;
5325 if (view_is_parent_view(view))
5326 begin_x = view_split_begin_x(view->begin_x);
5327 err = log_selected_tree_entry(&log_view, begin_x, s);
5328 view->focussed = 0;
5329 log_view->focussed = 1;
5330 if (view_is_parent_view(view)) {
5331 err = view_close_child(view);
5332 if (err)
5333 return err;
5334 view_set_child(view, log_view);
5335 view->focus_child = 1;
5336 } else
5337 *new_view = log_view;
5338 break;
5339 case 'r':
5340 if (view_is_parent_view(view))
5341 begin_x = view_split_begin_x(view->begin_x);
5342 ref_view = view_open(view->nlines, view->ncols,
5343 view->begin_y, begin_x, TOG_VIEW_REF);
5344 if (ref_view == NULL((void*)0))
5345 return got_error_from_errno("view_open");
5346 err = open_ref_view(ref_view, s->repo);
5347 if (err) {
5348 view_close(ref_view);
5349 return err;
5350 }
5351 view->focussed = 0;
5352 ref_view->focussed = 1;
5353 if (view_is_parent_view(view)) {
5354 err = view_close_child(view);
5355 if (err)
5356 return err;
5357 view_set_child(view, ref_view);
5358 view->focus_child = 1;
5359 } else
5360 *new_view = ref_view;
5361 break;
5362 case 'k':
5363 case KEY_UP0403:
5364 if (s->selected > 0) {
5365 s->selected--;
5366 break;
5367 }
5368 tree_scroll_up(s, 1);
5369 break;
5370 case KEY_PPAGE0523:
5371 case CTRL('b')(('b') & 0x1f):
5372 if (s->tree == s->root) {
5373 if (got_object_tree_get_first_entry(s->tree) ==
5374 s->first_displayed_entry)
5375 s->selected = 0;
5376 } else {
5377 if (s->first_displayed_entry == NULL((void*)0))
5378 s->selected = 0;
5379 }
5380 tree_scroll_up(s, MAX(0, view->nlines - 3)((0) > (view->nlines - 3) ? (0) : (view->nlines - 3)
)
);
5381 break;
5382 case 'j':
5383 case KEY_DOWN0402:
5384 if (s->selected < s->ndisplayed - 1) {
5385 s->selected++;
5386 break;
5387 }
5388 if (got_tree_entry_get_next(s->tree, s->last_displayed_entry)
5389 == NULL((void*)0))
5390 /* can't scroll any further */
5391 break;
5392 tree_scroll_down(s, 1);
5393 break;
5394 case KEY_NPAGE0522:
5395 case CTRL('f')(('f') & 0x1f):
5396 if (got_tree_entry_get_next(s->tree, s->last_displayed_entry)
5397 == NULL((void*)0)) {
5398 /* can't scroll any further; move cursor down */
5399 if (s->selected < s->ndisplayed - 1)
5400 s->selected = s->ndisplayed - 1;
5401 break;
5402 }
5403 tree_scroll_down(s, view->nlines - 3);
5404 break;
5405 case KEY_ENTER0527:
5406 case '\r':
5407 case KEY_BACKSPACE0407:
5408 if (s->selected_entry == NULL((void*)0) || ch == KEY_BACKSPACE0407) {
5409 struct tog_parent_tree *parent;
5410 /* user selected '..' */
5411 if (s->tree == s->root)
5412 break;
5413 parent = TAILQ_FIRST(&s->parents)((&s->parents)->tqh_first);
5414 TAILQ_REMOVE(&s->parents, parent,do { if (((parent)->entry.tqe_next) != ((void*)0)) (parent
)->entry.tqe_next->entry.tqe_prev = (parent)->entry.
tqe_prev; else (&s->parents)->tqh_last = (parent)->
entry.tqe_prev; *(parent)->entry.tqe_prev = (parent)->entry
.tqe_next; ; ; } while (0)
5415 entry)do { if (((parent)->entry.tqe_next) != ((void*)0)) (parent
)->entry.tqe_next->entry.tqe_prev = (parent)->entry.
tqe_prev; else (&s->parents)->tqh_last = (parent)->
entry.tqe_prev; *(parent)->entry.tqe_prev = (parent)->entry
.tqe_next; ; ; } while (0)
;
5416 got_object_tree_close(s->tree);
5417 s->tree = parent->tree;
5418 s->first_displayed_entry =
5419 parent->first_displayed_entry;
5420 s->selected_entry =
5421 parent->selected_entry;
5422 s->selected = parent->selected;
5423 free(parent);
5424 } else if (S_ISDIR(got_tree_entry_get_mode(((got_tree_entry_get_mode( s->selected_entry) & 0170000
) == 0040000)
5425 s->selected_entry))((got_tree_entry_get_mode( s->selected_entry) & 0170000
) == 0040000)
) {
5426 struct got_tree_object *subtree;
5427 err = got_object_open_as_tree(&subtree, s->repo,
5428 got_tree_entry_get_id(s->selected_entry));
5429 if (err)
5430 break;
5431 err = tree_view_visit_subtree(s, subtree);
5432 if (err) {
5433 got_object_tree_close(subtree);
5434 break;
5435 }
5436 } else if (S_ISREG(got_tree_entry_get_mode(((got_tree_entry_get_mode( s->selected_entry) & 0170000
) == 0100000)
5437 s->selected_entry))((got_tree_entry_get_mode( s->selected_entry) & 0170000
) == 0100000)
) {
5438 struct tog_view *blame_view;
5439 int begin_x = view_is_parent_view(view) ?
5440 view_split_begin_x(view->begin_x) : 0;
5441
5442 err = blame_tree_entry(&blame_view, begin_x,
5443 s->selected_entry, &s->parents,
5444 s->commit_id, s->repo);
5445 if (err)
5446 break;
5447 view->focussed = 0;
5448 blame_view->focussed = 1;
5449 if (view_is_parent_view(view)) {
5450 err = view_close_child(view);
5451 if (err)
5452 return err;
5453 view_set_child(view, blame_view);
5454 view->focus_child = 1;
5455 } else
5456 *new_view = blame_view;
5457 }
5458 break;
5459 case KEY_RESIZE0632:
5460 if (view->nlines >= 4 && s->selected >= view->nlines - 3)
5461 s->selected = view->nlines - 4;
5462 break;
5463 default:
5464 break;
5465 }
5466
5467 return err;
5468}
5469
5470__dead__attribute__((__noreturn__)) static void
5471usage_tree(void)
5472{
5473 endwin();
5474 fprintf(stderr(&__sF[2]), "usage: %s tree [-c commit] [-r repository-path] [path]\n",
5475 getprogname());
5476 exit(1);
5477}
5478
5479static const struct got_error *
5480cmd_tree(int argc, char *argv[])
5481{
5482 const struct got_error *error;
5483 struct got_repository *repo = NULL((void*)0);
5484 struct got_worktree *worktree = NULL((void*)0);
5485 char *cwd = NULL((void*)0), *repo_path = NULL((void*)0), *in_repo_path = NULL((void*)0);
5486 struct got_object_id *commit_id = NULL((void*)0);
5487 const char *commit_id_arg = NULL((void*)0);
5488 char *label = NULL((void*)0);
5489 struct got_commit_object *commit = NULL((void*)0);
5490 struct got_tree_object *tree = NULL((void*)0);
5491 struct got_reference *ref = NULL((void*)0);
5492 const char *head_ref_name = NULL((void*)0);
5493 int ch;
5494 struct tog_view *view;
5495
5496 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
5497 switch (ch) {
5498 case 'c':
5499 commit_id_arg = optarg;
5500 break;
5501 case 'r':
5502 repo_path = realpath(optarg, NULL((void*)0));
5503 if (repo_path == NULL((void*)0))
5504 return got_error_from_errno2("realpath",
5505 optarg);
5506 break;
5507 default:
5508 usage_tree();
5509 /* NOTREACHED */
5510 }
5511 }
5512
5513 argc -= optind;
5514 argv += optind;
5515
5516 if (argc > 1)
5517 usage_tree();
5518
5519 if (repo_path == NULL((void*)0)) {
5520 cwd = getcwd(NULL((void*)0), 0);
5521 if (cwd == NULL((void*)0))
5522 return got_error_from_errno("getcwd");
5523 error = got_worktree_open(&worktree, cwd);
5524 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
5525 goto done;
5526 if (worktree)
5527 repo_path =
5528 strdup(got_worktree_get_repo_path(worktree));
5529 else
5530 repo_path = strdup(cwd);
5531 if (repo_path == NULL((void*)0)) {
5532 error = got_error_from_errno("strdup");
5533 goto done;
5534 }
5535 }
5536
5537 error = got_repo_open(&repo, repo_path, NULL((void*)0));
5538 if (error != NULL((void*)0))
5539 goto done;
5540
5541 error = get_in_repo_path_from_argv0(&in_repo_path, argc, argv,
5542 repo, worktree);
5543 if (error)
5544 goto done;
5545
5546 init_curses();
5547
5548 error = apply_unveil(got_repo_get_path(repo), NULL((void*)0));
5549 if (error)
5550 goto done;
5551
5552 error = tog_load_refs(repo);
5553 if (error)
5554 goto done;
5555
5556 if (commit_id_arg == NULL((void*)0)) {
5557 error = got_repo_match_object_id(&commit_id, &label,
5558 worktree ? got_worktree_get_head_ref_name(worktree) :
5559 GOT_REF_HEAD"HEAD", GOT_OBJ_TYPE_COMMIT1, &tog_refs, repo);
5560 if (error)
5561 goto done;
5562 head_ref_name = label;
5563 } else {
5564 error = got_ref_open(&ref, repo, commit_id_arg, 0);
5565 if (error == NULL((void*)0))
5566 head_ref_name = got_ref_get_name(ref);
5567 else if (error->code != GOT_ERR_NOT_REF5)
5568 goto done;
5569 error = got_repo_match_object_id(&commit_id, NULL((void*)0),
5570 commit_id_arg, GOT_OBJ_TYPE_COMMIT1, &tog_refs, repo);
5571 if (error)
5572 goto done;
5573 }
5574
5575 error = got_object_open_as_commit(&commit, repo, commit_id);
5576 if (error)
5577 goto done;
5578
5579 error = got_object_open_as_tree(&tree, repo,
5580 got_object_commit_get_tree_id(commit));
5581 if (error)
5582 goto done;
5583
5584 view = view_open(0, 0, 0, 0, TOG_VIEW_TREE);
5585 if (view == NULL((void*)0)) {
5586 error = got_error_from_errno("view_open");
5587 goto done;
5588 }
5589 error = open_tree_view(view, tree, commit_id, head_ref_name, repo);
5590 if (error)
5591 goto done;
5592 if (!got_path_is_root_dir(in_repo_path)) {
5593 error = tree_view_walk_path(&view->state.tree, commit_id,
5594 in_repo_path);
5595 if (error)
5596 goto done;
5597 }
5598
5599 if (worktree) {
5600 /* Release work tree lock. */
5601 got_worktree_close(worktree);
5602 worktree = NULL((void*)0);
5603 }
5604 error = view_loop(view);
5605done:
5606 free(repo_path);
5607 free(cwd);
5608 free(commit_id);
5609 free(label);
5610 if (ref)
5611 got_ref_close(ref);
5612 if (commit)
5613 got_object_commit_close(commit);
5614 if (tree)
5615 got_object_tree_close(tree);
5616 if (repo)
5617 got_repo_close(repo);
5618 tog_free_refs();
5619 return error;
5620}
5621
5622static const struct got_error *
5623ref_view_load_refs(struct tog_ref_view_state *s)
5624{
5625 struct got_reflist_entry *sre;
5626 struct tog_reflist_entry *re;
5627
5628 s->nrefs = 0;
5629 TAILQ_FOREACH(sre, &tog_refs, entry)for((sre) = ((&tog_refs)->tqh_first); (sre) != ((void*
)0); (sre) = ((sre)->entry.tqe_next))
{
5630 if (strncmp(got_ref_get_name(sre->ref), "refs/got/", 9) == 0)
5631 continue;
5632
5633 re = malloc(sizeof(*re));
5634 if (re == NULL((void*)0))
5635 return got_error_from_errno("malloc");
5636
5637 re->ref = got_ref_dup(sre->ref);
5638 if (re->ref == NULL((void*)0))
5639 return got_error_from_errno("got_ref_dup");
5640 re->idx = s->nrefs++;
5641 TAILQ_INSERT_TAIL(&s->refs, re, entry)do { (re)->entry.tqe_next = ((void*)0); (re)->entry.tqe_prev
= (&s->refs)->tqh_last; *(&s->refs)->tqh_last
= (re); (&s->refs)->tqh_last = &(re)->entry
.tqe_next; } while (0)
;
5642 }
5643
5644 s->first_displayed_entry = TAILQ_FIRST(&s->refs)((&s->refs)->tqh_first);
5645 return NULL((void*)0);
5646}
5647
5648void
5649ref_view_free_refs(struct tog_ref_view_state *s)
5650{
5651 struct tog_reflist_entry *re;
5652
5653 while (!TAILQ_EMPTY(&s->refs)(((&s->refs)->tqh_first) == ((void*)0))) {
5654 re = TAILQ_FIRST(&s->refs)((&s->refs)->tqh_first);
5655 TAILQ_REMOVE(&s->refs, re, entry)do { if (((re)->entry.tqe_next) != ((void*)0)) (re)->entry
.tqe_next->entry.tqe_prev = (re)->entry.tqe_prev; else (
&s->refs)->tqh_last = (re)->entry.tqe_prev; *(re
)->entry.tqe_prev = (re)->entry.tqe_next; ; ; } while (
0)
;
5656 got_ref_close(re->ref);
5657 free(re);
5658 }
5659}
5660
5661static const struct got_error *
5662open_ref_view(struct tog_view *view, struct got_repository *repo)
5663{
5664 const struct got_error *err = NULL((void*)0);
5665 struct tog_ref_view_state *s = &view->state.ref;
5666
5667 s->selected_entry = 0;
5668 s->repo = repo;
5669
5670 TAILQ_INIT(&s->refs)do { (&s->refs)->tqh_first = ((void*)0); (&s->
refs)->tqh_last = &(&s->refs)->tqh_first; } while
(0)
;
5671 SIMPLEQ_INIT(&s->colors)do { (&s->colors)->sqh_first = ((void*)0); (&s->
colors)->sqh_last = &(&s->colors)->sqh_first
; } while (0)
;
5672
5673 err = ref_view_load_refs(s);
5674 if (err)
5675 return err;
5676
5677 if (has_colors() && getenv("TOG_COLORS") != NULL((void*)0)) {
5678 err = add_color(&s->colors, "^refs/heads/",
5679 TOG_COLOR_REFS_HEADS12,
5680 get_color_value("TOG_COLOR_REFS_HEADS"));
5681 if (err)
5682 goto done;
5683
5684 err = add_color(&s->colors, "^refs/tags/",
5685 TOG_COLOR_REFS_TAGS13,
5686 get_color_value("TOG_COLOR_REFS_TAGS"));
5687 if (err)
5688 goto done;
5689
5690 err = add_color(&s->colors, "^refs/remotes/",
5691 TOG_COLOR_REFS_REMOTES14,
5692 get_color_value("TOG_COLOR_REFS_REMOTES"));
5693 if (err)
5694 goto done;
5695 }
5696
5697 view->show = show_ref_view;
5698 view->input = input_ref_view;
5699 view->close = close_ref_view;
5700 view->search_start = search_start_ref_view;
5701 view->search_next = search_next_ref_view;
5702done:
5703 if (err)
5704 free_colors(&s->colors);
5705 return err;
5706}
5707
5708static const struct got_error *
5709close_ref_view(struct tog_view *view)
5710{
5711 struct tog_ref_view_state *s = &view->state.ref;
5712
5713 ref_view_free_refs(s);
5714 free_colors(&s->colors);
5715
5716 return NULL((void*)0);
5717}
5718
5719static const struct got_error *
5720resolve_reflist_entry(struct got_object_id **commit_id,
5721 struct tog_reflist_entry *re, struct got_repository *repo)
5722{
5723 const struct got_error *err = NULL((void*)0);
5724 struct got_object_id *obj_id;
5725 struct got_tag_object *tag = NULL((void*)0);
5726 int obj_type;
5727
5728 *commit_id = NULL((void*)0);
5729
5730 err = got_ref_resolve(&obj_id, repo, re->ref);
5731 if (err)
5732 return err;
5733
5734 err = got_object_get_type(&obj_type, repo, obj_id);
5735 if (err)
5736 goto done;
5737
5738 switch (obj_type) {
5739 case GOT_OBJ_TYPE_COMMIT1:
5740 *commit_id = obj_id;
5741 break;
5742 case GOT_OBJ_TYPE_TAG4:
5743 err = got_object_open_as_tag(&tag, repo, obj_id);
5744 if (err)
5745 goto done;
5746 free(obj_id);
5747 err = got_object_get_type(&obj_type, repo,
5748 got_object_tag_get_object_id(tag));
5749 if (err)
5750 goto done;
5751 if (obj_type != GOT_OBJ_TYPE_COMMIT1) {
5752 err = got_error(GOT_ERR_OBJ_TYPE11);
5753 goto done;
5754 }
5755 *commit_id = got_object_id_dup(
5756 got_object_tag_get_object_id(tag));
5757 if (*commit_id == NULL((void*)0)) {
5758 err = got_error_from_errno("got_object_id_dup");
5759 goto done;
5760 }
5761 break;
5762 default:
5763 err = got_error(GOT_ERR_OBJ_TYPE11);
5764 break;
5765 }
5766
5767done:
5768 if (tag)
5769 got_object_tag_close(tag);
5770 if (err) {
5771 free(*commit_id);
5772 *commit_id = NULL((void*)0);
5773 }
5774 return err;
5775}
5776
5777static const struct got_error *
5778log_ref_entry(struct tog_view **new_view, int begin_x,
5779 struct tog_reflist_entry *re, struct got_repository *repo)
5780{
5781 struct tog_view *log_view;
5782 const struct got_error *err = NULL((void*)0);
5783 struct got_object_id *commit_id = NULL((void*)0);
5784
5785 *new_view = NULL((void*)0);
5786
5787 err = resolve_reflist_entry(&commit_id, re, repo);
5788 if (err) {
5789 if (err->code != GOT_ERR_OBJ_TYPE11)
5790 return err;
5791 else
5792 return NULL((void*)0);
5793 }
5794
5795 log_view = view_open(0, 0, 0, begin_x, TOG_VIEW_LOG);
5796 if (log_view == NULL((void*)0)) {
5797 err = got_error_from_errno("view_open");
5798 goto done;
5799 }
5800
5801 err = open_log_view(log_view, commit_id, repo,
5802 got_ref_get_name(re->ref), "", 0);
5803done:
5804 if (err)
5805 view_close(log_view);
5806 else
5807 *new_view = log_view;
5808 free(commit_id);
5809 return err;
5810}
5811
5812static void
5813ref_scroll_up(struct tog_ref_view_state *s, int maxscroll)
5814{
5815 struct tog_reflist_entry *re;
5816 int i = 0;
5817
5818 if (s->first_displayed_entry == TAILQ_FIRST(&s->refs)((&s->refs)->tqh_first))
5819 return;
5820
5821 re = TAILQ_PREV(s->first_displayed_entry, tog_reflist_head, entry)(*(((struct tog_reflist_head *)((s->first_displayed_entry)
->entry.tqe_prev))->tqh_last))
;
5822 while (i++ < maxscroll) {
5823 if (re == NULL((void*)0))
5824 break;
5825 s->first_displayed_entry = re;
5826 re = TAILQ_PREV(re, tog_reflist_head, entry)(*(((struct tog_reflist_head *)((re)->entry.tqe_prev))->
tqh_last))
;
5827 }
5828}
5829
5830static void
5831ref_scroll_down(struct tog_ref_view_state *s, int maxscroll)
5832{
5833 struct tog_reflist_entry *next, *last;
5834 int n = 0;
5835
5836 if (s->first_displayed_entry)
5837 next = TAILQ_NEXT(s->first_displayed_entry, entry)((s->first_displayed_entry)->entry.tqe_next);
5838 else
5839 next = TAILQ_FIRST(&s->refs)((&s->refs)->tqh_first);
5840
5841 last = s->last_displayed_entry;
5842 while (next && last && n++ < maxscroll) {
5843 last = TAILQ_NEXT(last, entry)((last)->entry.tqe_next);
5844 if (last) {
5845 s->first_displayed_entry = next;
5846 next = TAILQ_NEXT(next, entry)((next)->entry.tqe_next);
5847 }
5848 }
5849}
5850
5851static const struct got_error *
5852search_start_ref_view(struct tog_view *view)
5853{
5854 struct tog_ref_view_state *s = &view->state.ref;
5855
5856 s->matched_entry = NULL((void*)0);
5857 return NULL((void*)0);
5858}
5859
5860static int
5861match_reflist_entry(struct tog_reflist_entry *re, regex_t *regex)
5862{
5863 regmatch_t regmatch;
5864
5865 return regexec(regex, got_ref_get_name(re->ref), 1, &regmatch,
5866 0) == 0;
5867}
5868
5869static const struct got_error *
5870search_next_ref_view(struct tog_view *view)
5871{
5872 struct tog_ref_view_state *s = &view->state.ref;
5873 struct tog_reflist_entry *re = NULL((void*)0);
5874
5875 if (!view->searching) {
5876 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
5877 return NULL((void*)0);
5878 }
5879
5880 if (s->matched_entry) {
5881 if (view->searching == TOG_SEARCH_FORWARD1) {
5882 if (s->selected_entry)
5883 re = TAILQ_NEXT(s->selected_entry, entry)((s->selected_entry)->entry.tqe_next);
5884 else
5885 re = TAILQ_PREV(s->selected_entry,(*(((struct tog_reflist_head *)((s->selected_entry)->entry
.tqe_prev))->tqh_last))
5886 tog_reflist_head, entry)(*(((struct tog_reflist_head *)((s->selected_entry)->entry
.tqe_prev))->tqh_last))
;
5887 } else {
5888 if (s->selected_entry == NULL((void*)0))
5889 re = TAILQ_LAST(&s->refs, tog_reflist_head)(*(((struct tog_reflist_head *)((&s->refs)->tqh_last
))->tqh_last))
;
5890 else
5891 re = TAILQ_PREV(s->selected_entry,(*(((struct tog_reflist_head *)((s->selected_entry)->entry
.tqe_prev))->tqh_last))
5892 tog_reflist_head, entry)(*(((struct tog_reflist_head *)((s->selected_entry)->entry
.tqe_prev))->tqh_last))
;
5893 }
5894 } else {
5895 if (view->searching == TOG_SEARCH_FORWARD1)
5896 re = TAILQ_FIRST(&s->refs)((&s->refs)->tqh_first);
5897 else
5898 re = TAILQ_LAST(&s->refs, tog_reflist_head)(*(((struct tog_reflist_head *)((&s->refs)->tqh_last
))->tqh_last))
;
5899 }
5900
5901 while (1) {
5902 if (re == NULL((void*)0)) {
5903 if (s->matched_entry == NULL((void*)0)) {
5904 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
5905 return NULL((void*)0);
5906 }
5907 if (view->searching == TOG_SEARCH_FORWARD1)
5908 re = TAILQ_FIRST(&s->refs)((&s->refs)->tqh_first);
5909 else
5910 re = TAILQ_LAST(&s->refs, tog_reflist_head)(*(((struct tog_reflist_head *)((&s->refs)->tqh_last
))->tqh_last))
;
5911 }
5912
5913 if (match_reflist_entry(re, &view->regex)) {
5914 view->search_next_done = TOG_SEARCH_HAVE_MORE1;
5915 s->matched_entry = re;
5916 break;
5917 }
5918
5919 if (view->searching == TOG_SEARCH_FORWARD1)
5920 re = TAILQ_NEXT(re, entry)((re)->entry.tqe_next);
5921 else
5922 re = TAILQ_PREV(re, tog_reflist_head, entry)(*(((struct tog_reflist_head *)((re)->entry.tqe_prev))->
tqh_last))
;
5923 }
5924
5925 if (s->matched_entry) {
5926 s->first_displayed_entry = s->matched_entry;
5927 s->selected = 0;
5928 }
5929
5930 return NULL((void*)0);
5931}
5932
5933static const struct got_error *
5934show_ref_view(struct tog_view *view)
5935{
5936 const struct got_error *err = NULL((void*)0);
5937 struct tog_ref_view_state *s = &view->state.ref;
5938 struct tog_reflist_entry *re;
5939 char *line = NULL((void*)0);
5940 wchar_t *wline;
5941 struct tog_color *tc;
5942 int width, n;
5943 int limit = view->nlines;
5944
5945 werase(view->window);
5946
5947 s->ndisplayed = 0;
5948
5949 if (limit == 0)
5950 return NULL((void*)0);
5951
5952 re = s->first_displayed_entry;
5953
5954 if (asprintf(&line, "references [%d/%d]", re->idx + s->selected + 1,
5955 s->nrefs) == -1)
5956 return got_error_from_errno("asprintf");
5957
5958 err = format_line(&wline, &width, line, view->ncols, 0);
5959 if (err) {
5960 free(line);
5961 return err;
5962 }
5963 if (view_needs_focus_indication(view))
5964 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
5965 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
5966 if (view_needs_focus_indication(view))
5967 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
5968 free(wline);
5969 wline = NULL((void*)0);
5970 free(line);
5971 line = NULL((void*)0);
5972 if (width < view->ncols - 1)
5973 waddch(view->window, '\n');
5974 if (--limit <= 0)
5975 return NULL((void*)0);
5976
5977 n = 0;
5978 while (re && limit > 0) {
5979 char *line = NULL((void*)0);
5980
5981 if (got_ref_is_symbolic(re->ref)) {
5982 if (asprintf(&line, "%s -> %s",
5983 got_ref_get_name(re->ref),
5984 got_ref_get_symref_target(re->ref)) == -1)
5985 return got_error_from_errno("asprintf");
5986 } else if (s->show_ids) {
5987 struct got_object_id *id;
5988 char *id_str;
5989 err = got_ref_resolve(&id, s->repo, re->ref);
5990 if (err)
5991 return err;
5992 err = got_object_id_str(&id_str, id);
5993 if (err) {
5994 free(id);
5995 return err;
5996 }
5997 if (asprintf(&line, "%s: %s",
5998 got_ref_get_name(re->ref), id_str) == -1) {
5999 err = got_error_from_errno("asprintf");
6000 free(id);
6001 free(id_str);
6002 return err;
6003 }
6004 free(id);
6005 free(id_str);
6006 } else {
6007 line = strdup(got_ref_get_name(re->ref));
6008 if (line == NULL((void*)0))
6009 return got_error_from_errno("strdup");
6010 }
6011
6012 err = format_line(&wline, &width, line, view->ncols, 0);
6013 if (err) {
6014 free(line);
6015 return err;
6016 }
6017 if (n == s->selected) {
6018 if (view->focussed)
6019 wstandout(view->window)(((view->window)->_attrs = (((1U) << ((8) + 8))))
)
;
6020 s->selected_entry = re;
6021 }
6022 tc = match_color(&s->colors, got_ref_get_name(re->ref));
6023 if (tc)
6024 wattr_on(view->window,
6025 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
6026 waddwstr(view->window, wline)waddnwstr(view->window,wline,-1);
6027 if (tc)
6028 wattr_off(view->window,
6029 COLOR_PAIR(tc->colorpair)((tc->colorpair) << ((0) + 8)), NULL((void*)0));
6030 if (width < view->ncols - 1)
6031 waddch(view->window, '\n');
6032 if (n == s->selected && view->focussed)
6033 wstandend(view->window)(((view->window)->_attrs = ((1U - 1U))));
6034 free(line);
6035 free(wline);
6036 wline = NULL((void*)0);
6037 n++;
6038 s->ndisplayed++;
6039 s->last_displayed_entry = re;
6040
6041 limit--;
6042 re = TAILQ_NEXT(re, entry)((re)->entry.tqe_next);
6043 }
6044
6045 view_vborder(view);
6046 return err;
6047}
6048
6049static const struct got_error *
6050browse_ref_tree(struct tog_view **new_view, int begin_x,
6051 struct tog_reflist_entry *re, struct got_repository *repo)
6052{
6053 const struct got_error *err = NULL((void*)0);
6054 struct got_object_id *commit_id = NULL((void*)0), *tree_id = NULL((void*)0);
6055 struct got_tree_object *tree = NULL((void*)0);
6056 struct tog_view *tree_view;
6057
6058 *new_view = NULL((void*)0);
6059
6060 err = resolve_reflist_entry(&commit_id, re, repo);
6061 if (err) {
6062 if (err->code != GOT_ERR_OBJ_TYPE11)
6063 return err;
6064 else
6065 return NULL((void*)0);
6066 }
6067
6068 err = got_object_id_by_path(&tree_id, repo, commit_id, "/");
6069 if (err)
6070 goto done;
6071
6072 err = got_object_open_as_tree(&tree, repo, tree_id);
6073 if (err)
6074 goto done;
6075
6076 tree_view = view_open(0, 0, 0, begin_x, TOG_VIEW_TREE);
6077 if (tree_view == NULL((void*)0)) {
6078 err = got_error_from_errno("view_open");
6079 goto done;
6080 }
6081
6082 err = open_tree_view(tree_view, tree, commit_id,
6083 got_ref_get_name(re->ref), repo);
6084 if (err)
6085 goto done;
6086
6087 *new_view = tree_view;
6088done:
6089 free(commit_id);
6090 free(tree_id);
6091 if (err) {
6092 if (tree)
6093 got_object_tree_close(tree);
6094 }
6095 return err;
6096}
6097static const struct got_error *
6098input_ref_view(struct tog_view **new_view, struct tog_view *view, int ch)
6099{
6100 const struct got_error *err = NULL((void*)0);
6101 struct tog_ref_view_state *s = &view->state.ref;
6102 struct tog_view *log_view, *tree_view;
6103 int begin_x = 0;
6104
6105 switch (ch) {
6106 case 'i':
6107 s->show_ids = !s->show_ids;
6108 break;
6109 case KEY_ENTER0527:
6110 case '\r':
6111 if (!s->selected_entry)
6112 break;
6113 if (view_is_parent_view(view))
6114 begin_x = view_split_begin_x(view->begin_x);
6115 err = log_ref_entry(&log_view, begin_x, s->selected_entry,
6116 s->repo);
6117 view->focussed = 0;
6118 log_view->focussed = 1;
6119 if (view_is_parent_view(view)) {
6120 err = view_close_child(view);
6121 if (err)
6122 return err;
6123 view_set_child(view, log_view);
6124 view->focus_child = 1;
6125 } else
6126 *new_view = log_view;
6127 break;
6128 case 't':
6129 if (!s->selected_entry)
6130 break;
6131 if (view_is_parent_view(view))
6132 begin_x = view_split_begin_x(view->begin_x);
6133 err = browse_ref_tree(&tree_view, begin_x, s->selected_entry,
6134 s->repo);
6135 if (err || tree_view == NULL((void*)0))
6136 break;
6137 view->focussed = 0;
6138 tree_view->focussed = 1;
6139 if (view_is_parent_view(view)) {
6140 err = view_close_child(view);
6141 if (err)
6142 return err;
6143 view_set_child(view, tree_view);
6144 view->focus_child = 1;
6145 } else
6146 *new_view = tree_view;
6147 break;
6148 case 'k':
6149 case KEY_UP0403:
6150 if (s->selected > 0) {
6151 s->selected--;
6152 break;
6153 }
6154 ref_scroll_up(s, 1);
6155 break;
6156 case KEY_PPAGE0523:
6157 case CTRL('b')(('b') & 0x1f):
6158 if (s->first_displayed_entry == TAILQ_FIRST(&s->refs)((&s->refs)->tqh_first))
6159 s->selected = 0;
6160 ref_scroll_up(s, MAX(0, view->nlines - 1)((0) > (view->nlines - 1) ? (0) : (view->nlines - 1)
)
);
6161 break;
6162 case 'j':
6163 case KEY_DOWN0402:
6164 if (s->selected < s->ndisplayed - 1) {
6165 s->selected++;
6166 break;
6167 }
6168 if (TAILQ_NEXT(s->last_displayed_entry, entry)((s->last_displayed_entry)->entry.tqe_next) == NULL((void*)0))
6169 /* can't scroll any further */
6170 break;
6171 ref_scroll_down(s, 1);
6172 break;
6173 case KEY_NPAGE0522:
6174 case CTRL('f')(('f') & 0x1f):
6175 if (TAILQ_NEXT(s->last_displayed_entry, entry)((s->last_displayed_entry)->entry.tqe_next) == NULL((void*)0)) {
6176 /* can't scroll any further; move cursor down */
6177 if (s->selected < s->ndisplayed - 1)
6178 s->selected = s->ndisplayed - 1;
6179 break;
6180 }
6181 ref_scroll_down(s, view->nlines - 1);
6182 break;
6183 case CTRL('l')(('l') & 0x1f):
6184 tog_free_refs();
6185 err = tog_load_refs(s->repo);
6186 if (err)
6187 break;
6188 ref_view_free_refs(s);
6189 err = ref_view_load_refs(s);
6190 break;
6191 case KEY_RESIZE0632:
6192 if (view->nlines >= 2 && s->selected >= view->nlines - 1)
6193 s->selected = view->nlines - 2;
6194 break;
6195 default:
6196 break;
6197 }
6198
6199 return err;
6200}
6201
6202__dead__attribute__((__noreturn__)) static void
6203usage_ref(void)
6204{
6205 endwin();
6206 fprintf(stderr(&__sF[2]), "usage: %s ref [-r repository-path]\n",
6207 getprogname());
6208 exit(1);
6209}
6210
6211static const struct got_error *
6212cmd_ref(int argc, char *argv[])
6213{
6214 const struct got_error *error;
6215 struct got_repository *repo = NULL((void*)0);
6216 struct got_worktree *worktree = NULL((void*)0);
6217 char *cwd = NULL((void*)0), *repo_path = NULL((void*)0);
6218 int ch;
6219 struct tog_view *view;
6220
6221 while ((ch = getopt(argc, argv, "r:")) != -1) {
6222 switch (ch) {
6223 case 'r':
6224 repo_path = realpath(optarg, NULL((void*)0));
6225 if (repo_path == NULL((void*)0))
6226 return got_error_from_errno2("realpath",
6227 optarg);
6228 break;
6229 default:
6230 usage_ref();
6231 /* NOTREACHED */
6232 }
6233 }
6234
6235 argc -= optind;
6236 argv += optind;
6237
6238 if (argc > 1)
6239 usage_ref();
6240
6241 if (repo_path == NULL((void*)0)) {
6242 cwd = getcwd(NULL((void*)0), 0);
6243 if (cwd == NULL((void*)0))
6244 return got_error_from_errno("getcwd");
6245 error = got_worktree_open(&worktree, cwd);
6246 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
6247 goto done;
6248 if (worktree)
6249 repo_path =
6250 strdup(got_worktree_get_repo_path(worktree));
6251 else
6252 repo_path = strdup(cwd);
6253 if (repo_path == NULL((void*)0)) {
6254 error = got_error_from_errno("strdup");
6255 goto done;
6256 }
6257 }
6258
6259 error = got_repo_open(&repo, repo_path, NULL((void*)0));
6260 if (error != NULL((void*)0))
6261 goto done;
6262
6263 init_curses();
6264
6265 error = apply_unveil(got_repo_get_path(repo), NULL((void*)0));
6266 if (error)
6267 goto done;
6268
6269 error = tog_load_refs(repo);
6270 if (error)
6271 goto done;
6272
6273 view = view_open(0, 0, 0, 0, TOG_VIEW_REF);
6274 if (view == NULL((void*)0)) {
6275 error = got_error_from_errno("view_open");
6276 goto done;
6277 }
6278
6279 error = open_ref_view(view, repo);
6280 if (error)
6281 goto done;
6282
6283 if (worktree) {
6284 /* Release work tree lock. */
6285 got_worktree_close(worktree);
6286 worktree = NULL((void*)0);
6287 }
6288 error = view_loop(view);
6289done:
6290 free(repo_path);
6291 free(cwd);
6292 if (repo)
6293 got_repo_close(repo);
6294 tog_free_refs();
6295 return error;
6296}
6297
6298static void
6299list_commands(FILE *fp)
6300{
6301 size_t i;
6302
6303 fprintf(fp, "commands:");
6304 for (i = 0; i < nitems(tog_commands)(sizeof((tog_commands)) / sizeof((tog_commands)[0])); i++) {
6305 struct tog_cmd *cmd = &tog_commands[i];
6306 fprintf(fp, " %s", cmd->name);
6307 }
6308 fputc('\n', fp);
6309}
6310
6311__dead__attribute__((__noreturn__)) static void
6312usage(int hflag, int status)
6313{
6314 FILE *fp = (status == 0) ? stdout(&__sF[1]) : stderr(&__sF[2]);
6315
6316 fprintf(fp, "usage: %s [-h] [-V | --version] [command] [arg ...]\n",
6317 getprogname());
6318 if (hflag) {
6319 fprintf(fp, "lazy usage: %s path\n", getprogname());
6320 list_commands(fp);
6321 }
6322 exit(status);
6323}
6324
6325static char **
6326make_argv(int argc, ...)
6327{
6328 va_list ap;
6329 char **argv;
6330 int i;
6331
6332 va_start(ap, argc)__builtin_va_start(ap, argc);
6333
6334 argv = calloc(argc, sizeof(char *));
6335 if (argv == NULL((void*)0))
6336 err(1, "calloc");
6337 for (i = 0; i < argc; i++) {
6338 argv[i] = strdup(va_arg(ap, char *)__builtin_va_arg(ap, char *));
6339 if (argv[i] == NULL((void*)0))
6340 err(1, "strdup");
6341 }
6342
6343 va_end(ap)__builtin_va_end(ap);
6344 return argv;
6345}
6346
6347/*
6348 * Try to convert 'tog path' into a 'tog log path' command.
6349 * The user could simply have mistyped the command rather than knowingly
6350 * provided a path. So check whether argv[0] can in fact be resolved
6351 * to a path in the HEAD commit and print a special error if not.
6352 * This hack is for mpi@ <3
6353 */
6354static const struct got_error *
6355tog_log_with_path(int argc, char *argv[])
6356{
6357 const struct got_error *error = NULL((void*)0);
6358 struct tog_cmd *cmd = NULL((void*)0);
6359 struct got_repository *repo = NULL((void*)0);
6360 struct got_worktree *worktree = NULL((void*)0);
6361 struct got_object_id *commit_id = NULL((void*)0), *id = NULL((void*)0);
6362 char *cwd = NULL((void*)0), *repo_path = NULL((void*)0), *in_repo_path = NULL((void*)0);
6363 char *commit_id_str = NULL((void*)0), **cmd_argv = NULL((void*)0);
6364
6365 cwd = getcwd(NULL((void*)0), 0);
6366 if (cwd == NULL((void*)0))
6367 return got_error_from_errno("getcwd");
6368
6369 error = got_worktree_open(&worktree, cwd);
6370 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
6371 goto done;
6372
6373 if (worktree)
6374 repo_path = strdup(got_worktree_get_repo_path(worktree));
6375 else
6376 repo_path = strdup(cwd);
6377 if (repo_path == NULL((void*)0)) {
6378 error = got_error_from_errno("strdup");
6379 goto done;
6380 }
6381
6382 error = got_repo_open(&repo, repo_path, NULL((void*)0));
6383 if (error != NULL((void*)0))
6384 goto done;
6385
6386 error = get_in_repo_path_from_argv0(&in_repo_path, argc, argv,
6387 repo, worktree);
6388 if (error)
6389 goto done;
6390
6391 error = tog_load_refs(repo);
6392 if (error)
6393 goto done;
6394 error = got_repo_match_object_id(&commit_id, NULL((void*)0), worktree ?
6395 got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD"HEAD",
6396 GOT_OBJ_TYPE_COMMIT1, &tog_refs, repo);
6397 if (error)
6398 goto done;
6399
6400 if (worktree) {
6401 got_worktree_close(worktree);
6402 worktree = NULL((void*)0);
6403 }
6404
6405 error = got_object_id_by_path(&id, repo, commit_id, in_repo_path);
6406 if (error) {
6407 if (error->code != GOT_ERR_NO_TREE_ENTRY50)
6408 goto done;
6409 fprintf(stderr(&__sF[2]), "%s: '%s' is no known command or path\n",
6410 getprogname(), argv[0]);
6411 usage(1, 1);
6412 /* not reached */
6413 }
6414
6415 got_repo_close(repo);
6416 repo = NULL((void*)0);
6417
6418 error = got_object_id_str(&commit_id_str, commit_id);
6419 if (error)
6420 goto done;
6421
6422 cmd = &tog_commands[0]; /* log */
6423 argc = 4;
6424 cmd_argv = make_argv(argc, cmd->name, "-c", commit_id_str, argv[0]);
6425 error = cmd->cmd_main(argc, cmd_argv);
6426done:
6427 if (repo)
6428 got_repo_close(repo);
6429 if (worktree)
6430 got_worktree_close(worktree);
6431 free(id);
6432 free(commit_id_str);
6433 free(commit_id);
6434 free(cwd);
6435 free(repo_path);
6436 free(in_repo_path);
6437 if (cmd_argv) {
6438 int i;
6439 for (i = 0; i < argc; i++)
6440 free(cmd_argv[i]);
6441 free(cmd_argv);
6442 }
6443 tog_free_refs();
6444 return error;
6445}
6446
6447int
6448main(int argc, char *argv[])
6449{
6450 const struct got_error *error = NULL((void*)0);
6451 struct tog_cmd *cmd = NULL((void*)0);
6452 int ch, hflag = 0, Vflag = 0;
6453 char **cmd_argv = NULL((void*)0);
6454 static struct option longopts[] = {
6455 { "version", no_argument0, NULL((void*)0), 'V' },
6456 { NULL((void*)0), 0, NULL((void*)0), 0}
6457 };
6458
6459 setlocale(LC_CTYPE2, "");
6460
6461 while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL((void*)0))) != -1) {
6462 switch (ch) {
6463 case 'h':
6464 hflag = 1;
6465 break;
6466 case 'V':
6467 Vflag = 1;
6468 break;
6469 default:
6470 usage(hflag, 1);
6471 /* NOTREACHED */
6472 }
6473 }
6474
6475 argc -= optind;
6476 argv += optind;
6477 optind = 1;
6478 optreset = 1;
6479
6480 if (Vflag) {
6481 got_version_print_str();
6482 return 0;
6483 }
6484
6485#ifndef PROFILE
6486 if (pledge("stdio rpath wpath cpath flock proc tty exec sendfd unveil",
6487 NULL((void*)0)) == -1)
6488 err(1, "pledge");
6489#endif
6490
6491 if (argc == 0) {
6492 if (hflag)
6493 usage(hflag, 0);
6494 /* Build an argument vector which runs a default command. */
6495 cmd = &tog_commands[0];
6496 argc = 1;
6497 cmd_argv = make_argv(argc, cmd->name);
6498 } else {
6499 size_t i;
6500
6501 /* Did the user specify a command? */
6502 for (i = 0; i < nitems(tog_commands)(sizeof((tog_commands)) / sizeof((tog_commands)[0])); i++) {
6503 if (strncmp(tog_commands[i].name, argv[0],
6504 strlen(argv[0])) == 0) {
6505 cmd = &tog_commands[i];
6506 break;
6507 }
6508 }
6509 }
6510
6511 if (cmd == NULL((void*)0)) {
6512 if (argc != 1)
6513 usage(0, 1);
6514 /* No command specified; try log with a path */
6515 error = tog_log_with_path(argc, argv);
6516 } else {
6517 if (hflag)
6518 cmd->cmd_usage();
6519 else
6520 error = cmd->cmd_main(argc, cmd_argv ? cmd_argv : argv);
6521 }
6522
6523 endwin();
6524 putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
;
6525 if (cmd_argv) {
6526 int i;
6527 for (i = 0; i < argc; i++)
6528 free(cmd_argv[i]);
6529 free(cmd_argv);
6530 }
6531
6532 if (error && error->code != GOT_ERR_CANCELLED49)
6533 fprintf(stderr(&__sF[2]), "%s: %s\n", getprogname(), error->msg);
6534 return 0;
6535}