1 /* Copyright (c) 2006-2013 Jonas Fonseca <fonseca@diku.dk>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
17 DEFINE_ALLOCATOR(realloc_graph_columns
, struct graph_column
, 32)
18 DEFINE_ALLOCATOR(realloc_graph_symbols
, struct graph_symbol
, 1)
26 id_color_new(const char *id
, size_t color
)
28 struct id_color
*node
= malloc(sizeof(struct id_color
));
30 node
->id
= (char *) malloc(strlen(id
) + 1);
38 id_color_delete(struct id_color
*node
)
45 id_color_eq(const void *entry
, const void *element
)
47 return strcmp(((const struct id_color
*) entry
)->id
, ((const struct id_color
*) element
)->id
) == 0;
53 id_color_delete((struct id_color
*) key
);
57 id_color_hash(const void *node
)
59 return htab_hash_string(((const struct id_color
*) node
)->id
);
63 colors_add_id(struct colors
*colors
, const char *id
, const size_t color
)
65 struct id_color
*node
= id_color_new(id
, color
);
66 void **slot
= htab_find_slot(colors
->id_map
, node
, INSERT
);
68 if (slot
!= NULL
&& *slot
== NULL
) {
70 colors
->count
[color
]++;
72 id_color_delete(node
);
77 colors_remove_id(struct colors
*colors
, const char *id
)
79 struct id_color
*node
= id_color_new(id
, 0);
80 void **slot
= htab_find_slot(colors
->id_map
, node
, NO_INSERT
);
82 if (slot
!= NULL
&& *slot
!= NULL
) {
83 colors
->count
[((struct id_color
*) *slot
)->color
]--;
84 htab_clear_slot(colors
->id_map
, slot
);
87 id_color_delete(node
);
91 colors_get_color(struct colors
*colors
, const char *id
)
93 struct id_color
*key
= id_color_new(id
, 0);
94 struct id_color
*node
= (struct id_color
*) htab_find(colors
->id_map
, key
);
99 return (size_t) -1; // Max value of size_t. ID not found.
105 colors_get_free_color(struct colors
*colors
)
107 size_t free_color
= 0;
108 size_t lowest
= (size_t) -1; // Max value of size_t
111 for (i
= 0; i
< ARRAY_SIZE(colors
->count
); i
++) {
112 if (colors
->count
[i
] < lowest
) {
113 lowest
= colors
->count
[i
];
121 colors_init(struct colors
*colors
)
123 if (colors
->id_map
== NULL
) {
126 colors
->id_map
= htab_create_alloc(size
, id_color_hash
, id_color_eq
, key_del
, calloc
, free
);
131 get_color(struct graph
*graph
, char *new_id
)
135 colors_init(&graph
->colors
);
136 color
= colors_get_color(&graph
->colors
, new_id
);
138 if (color
< (size_t) -1) {
142 color
= colors_get_free_color(&graph
->colors
);
143 colors_add_id(&graph
->colors
, new_id
, color
);
149 done_graph(struct graph
*graph
)
151 free(graph
->prev_row
.columns
);
152 free(graph
->row
.columns
);
153 free(graph
->next_row
.columns
);
154 free(graph
->parents
.columns
);
155 memset(graph
, 0, sizeof(*graph
));
158 #define graph_column_has_commit(col) ((col)->id[0])
161 graph_find_column_by_id(struct graph_row
*row
, const char *id
)
163 size_t free_column
= row
->size
;
166 for (i
= 0; i
< row
->size
; i
++) {
167 if (!graph_column_has_commit(&row
->columns
[i
]) && free_column
== row
->size
)
169 else if (!strcmp(row
->columns
[i
].id
, id
))
177 graph_find_free_column(struct graph_row
*row
)
181 for (i
= 0; i
< row
->size
; i
++) {
182 if (!graph_column_has_commit(&row
->columns
[i
]))
189 static struct graph_column
*
190 graph_insert_column(struct graph
*graph
, struct graph_row
*row
, size_t pos
, const char *id
)
192 struct graph_column
*column
;
194 if (!realloc_graph_columns(&row
->columns
, row
->size
, 1))
197 column
= &row
->columns
[pos
];
198 if (pos
< row
->size
) {
199 memmove(column
+ 1, column
, sizeof(*column
) * (row
->size
- pos
));
203 memset(column
, 0, sizeof(*column
));
204 string_copy_rev(column
->id
, id
);
205 column
->symbol
.boundary
= !!graph
->is_boundary
;
210 struct graph_column
*
211 graph_add_parent(struct graph
*graph
, const char *parent
)
213 return graph_insert_column(graph
, &graph
->parents
, graph
->parents
.size
, parent
);
217 graph_needs_expansion(struct graph
*graph
)
219 return graph
->position
+ graph
->parents
.size
> graph
->row
.size
;
223 graph_expand(struct graph
*graph
)
225 while (graph_needs_expansion(graph
)) {
226 if (!graph_insert_column(graph
, &graph
->prev_row
, graph
->prev_row
.size
, ""))
229 if (!graph_insert_column(graph
, &graph
->row
, graph
->row
.size
, ""))
232 if (!graph_insert_column(graph
, &graph
->next_row
, graph
->next_row
.size
, ""))
240 graph_needs_collapsing(struct graph
*graph
)
242 return graph
->row
.size
> 1
243 && !graph_column_has_commit(&graph
->row
.columns
[graph
->row
.size
- 1]);
247 graph_collapse(struct graph
*graph
)
249 while (graph_needs_collapsing(graph
)) {
250 graph
->prev_row
.size
--;
252 graph
->next_row
.size
--;
259 graph_canvas_append_symbol(struct graph
*graph
, struct graph_symbol
*symbol
)
261 struct graph_canvas
*canvas
= graph
->canvas
;
263 if (realloc_graph_symbols(&canvas
->symbols
, canvas
->size
, 1))
264 canvas
->symbols
[canvas
->size
++] = *symbol
;
268 graph_row_clear_commit(struct graph_row
*row
, const char *id
)
272 for (i
= 0; i
< row
->size
; i
++) {
273 if (strcmp(row
->columns
[i
].id
, id
) == 0) {
274 row
->columns
[i
].id
[0] = 0;
280 graph_insert_parents(struct graph
*graph
)
282 struct graph_row
*prev_row
= &graph
->prev_row
;
283 struct graph_row
*row
= &graph
->row
;
284 struct graph_row
*next_row
= &graph
->next_row
;
285 struct graph_row
*parents
= &graph
->parents
;
288 for (i
= 0; i
< parents
->size
; i
++) {
289 struct graph_column
*new = &parents
->columns
[i
];
291 if (graph_column_has_commit(new)) {
292 size_t match
= graph_find_free_column(next_row
);
294 if (match
== next_row
->size
&& next_row
->columns
[next_row
->size
- 1].id
) {
295 graph_insert_column(graph
, next_row
, next_row
->size
, new->id
);
296 graph_insert_column(graph
, row
, row
->size
, "");
297 graph_insert_column(graph
, prev_row
, prev_row
->size
, "");
299 next_row
->columns
[match
] = *new;
306 commit_is_in_row(const char *id
, struct graph_row
*row
)
310 for (i
= 0; i
< row
->size
; i
++) {
311 if (!graph_column_has_commit(&row
->columns
[i
]))
314 if (strcmp(id
, row
->columns
[i
].id
) == 0)
321 graph_remove_collapsed_columns(struct graph
*graph
)
323 struct graph_row
*row
= &graph
->next_row
;
326 for (i
= row
->size
- 1; i
> 0; i
--) {
327 if (i
== graph
->position
)
330 if (i
== graph
->position
+ 1)
333 if (strcmp(row
->columns
[i
].id
, graph
->id
) == 0)
336 if (strcmp(row
->columns
[i
].id
, row
->columns
[i
- 1].id
) != 0)
339 if (commit_is_in_row(row
->columns
[i
].id
, &graph
->parents
) && !graph_column_has_commit(&graph
->prev_row
.columns
[i
]))
342 if (strcmp(row
->columns
[i
- 1].id
, graph
->prev_row
.columns
[i
- 1].id
) != 0 || graph
->prev_row
.columns
[i
- 1].symbol
.shift_left
)
343 row
->columns
[i
] = row
->columns
[i
+ 1];
348 graph_fill_empty_columns(struct graph
*graph
)
350 struct graph_row
*row
= &graph
->next_row
;
353 for (i
= row
->size
- 2; i
>= 0; i
--) {
354 if (!graph_column_has_commit(&row
->columns
[i
])) {
355 row
->columns
[i
] = row
->columns
[i
+ 1];
361 graph_generate_next_row(struct graph
*graph
)
363 graph_row_clear_commit(&graph
->next_row
, graph
->id
);
364 graph_insert_parents(graph
);
365 graph_remove_collapsed_columns(graph
);
366 graph_fill_empty_columns(graph
);
370 commits_in_row(struct graph_row
*row
)
375 for (i
= 0; i
< row
->size
;i
++) {
376 if (graph_column_has_commit(&row
->columns
[i
]))
384 graph_commit_next_row(struct graph
*graph
)
388 for (i
= 0; i
< graph
->row
.size
; i
++) {
389 graph
->prev_row
.columns
[i
] = graph
->row
.columns
[i
];
391 if (i
== graph
->position
&& commits_in_row(&graph
->parents
) > 0)
392 graph
->prev_row
.columns
[i
] = graph
->next_row
.columns
[i
];
394 if (!graph_column_has_commit(&graph
->prev_row
.columns
[i
]))
395 graph
->prev_row
.columns
[i
] = graph
->next_row
.columns
[i
];
397 graph
->row
.columns
[i
] = graph
->next_row
.columns
[i
];
400 graph
->prev_position
= graph
->position
;
404 continued_down(struct graph_row
*row
, struct graph_row
*next_row
, int pos
)
406 if (strcmp(row
->columns
[pos
].id
, next_row
->columns
[pos
].id
) != 0)
409 if (row
->columns
[pos
].symbol
.shift_left
)
416 shift_left(struct graph_row
*row
, struct graph_row
*prev_row
, int pos
)
420 if (!graph_column_has_commit(&row
->columns
[pos
]))
423 for (i
= pos
- 1; i
>= 0; i
--) {
424 if (!graph_column_has_commit(&row
->columns
[i
]))
427 if (strcmp(row
->columns
[i
].id
, row
->columns
[pos
].id
) != 0)
430 if (!continued_down(prev_row
, row
, i
))
440 new_column(struct graph_row
*row
, struct graph_row
*prev_row
, int pos
)
444 if (!graph_column_has_commit(&prev_row
->columns
[pos
]))
447 for (i
= pos
; i
< row
->size
; i
++) {
448 if (strcmp(row
->columns
[pos
].id
, prev_row
->columns
[i
].id
) == 0)
456 continued_right(struct graph_row
*row
, int pos
, int commit_pos
)
460 if (pos
< commit_pos
)
465 for (i
= pos
+ 1; i
< end
; i
++) {
466 if (strcmp(row
->columns
[pos
].id
, row
->columns
[i
].id
) == 0)
474 continued_left(struct graph_row
*row
, int pos
, int commit_pos
)
478 if (pos
< commit_pos
)
483 for (i
= start
; i
< pos
; i
++) {
484 if (!graph_column_has_commit(&row
->columns
[i
]))
487 if (strcmp(row
->columns
[pos
].id
, row
->columns
[i
].id
) == 0)
495 parent_down(struct graph_row
*parents
, struct graph_row
*next_row
, int pos
)
499 for (parent
= 0; parent
< parents
->size
; parent
++) {
500 if (!graph_column_has_commit(&parents
->columns
[parent
]))
503 if (strcmp(parents
->columns
[parent
].id
, next_row
->columns
[pos
].id
) == 0)
511 parent_right(struct graph_row
*parents
, struct graph_row
*row
, struct graph_row
*next_row
, int pos
)
515 for (parent
= 0; parent
< parents
->size
; parent
++) {
516 if (!graph_column_has_commit(&parents
->columns
[parent
]))
519 for (i
= pos
+ 1; i
< next_row
->size
; i
++) {
520 if (strcmp(parents
->columns
[parent
].id
, next_row
->columns
[i
].id
) != 0)
523 if (strcmp(parents
->columns
[parent
].id
, row
->columns
[i
].id
) != 0)
532 flanked(struct graph_row
*row
, int pos
, int commit_pos
, const char *commit_id
)
536 if (pos
< commit_pos
) {
544 for (i
= start
; i
< end
; i
++) {
545 if (strcmp(row
->columns
[i
].id
, commit_id
) == 0)
553 below_commit(int pos
, struct graph
*graph
)
555 if (!pos
== graph
->prev_position
)
558 if (!strcmp(graph
->row
.columns
[pos
].id
, graph
->prev_row
.columns
[pos
].id
) == 0)
565 graph_generate_symbols(struct graph
*graph
)
567 struct graph_row
*prev_row
= &graph
->prev_row
;
568 struct graph_row
*row
= &graph
->row
;
569 struct graph_row
*next_row
= &graph
->next_row
;
570 struct graph_row
*parents
= &graph
->parents
;
573 for (pos
= 0; pos
< row
->size
; pos
++) {
574 struct graph_column
*column
= &row
->columns
[pos
];
575 struct graph_symbol
*symbol
= &column
->symbol
;
576 char *id
= next_row
->columns
[pos
].id
;
578 symbol
->commit
= (pos
== graph
->position
);
579 symbol
->boundary
= (pos
== graph
->position
&& next_row
->columns
[pos
].symbol
.boundary
);
580 symbol
->initial
= (commits_in_row(parents
) < 1);
581 symbol
->merge
= (commits_in_row(parents
) > 1);
583 symbol
->continued_down
= continued_down(row
, next_row
, pos
);
584 symbol
->continued_up
= continued_down(prev_row
, row
, pos
);
585 symbol
->continued_right
= continued_right(row
, pos
, graph
->position
);
586 symbol
->continued_left
= continued_left(row
, pos
, graph
->position
);
587 symbol
->continued_up_left
= continued_left(prev_row
, pos
, prev_row
->size
);
589 symbol
->parent_down
= parent_down(parents
, next_row
, pos
);
590 symbol
->parent_right
= (pos
> graph
->position
&& parent_right(parents
, row
, next_row
, pos
));
592 symbol
->below_commit
= below_commit(pos
, graph
);
593 symbol
->flanked
= flanked(row
, pos
, graph
->position
, graph
->id
);
594 symbol
->next_right
= continued_right(next_row
, pos
, 0);
595 symbol
->matches_commit
= (strcmp(column
->id
, graph
->id
) == 0);
597 symbol
->shift_left
= shift_left(row
, prev_row
, pos
);
598 symbol
->continue_shift
= shift_left(row
, prev_row
, pos
+ 1);
599 symbol
->below_shift
= prev_row
->columns
[pos
].symbol
.shift_left
;
601 symbol
->new_column
= new_column(row
, prev_row
, pos
);
602 symbol
->empty
= (!graph_column_has_commit(&row
->columns
[pos
]));
604 if (graph_column_has_commit(column
)) {
607 symbol
->color
= get_color(graph
, id
);
609 graph_canvas_append_symbol(graph
, symbol
);
612 colors_remove_id(&graph
->colors
, graph
->id
);
616 graph_render_parents(struct graph
*graph
)
618 if (!graph_expand(graph
))
621 graph_generate_next_row(graph
);
622 graph_generate_symbols(graph
);
623 graph_commit_next_row(graph
);
625 graph
->parents
.size
= graph
->position
= 0;
627 if (!graph_collapse(graph
))
634 graph_add_commit(struct graph
*graph
, struct graph_canvas
*canvas
,
635 const char *id
, const char *parents
, bool is_boundary
)
637 graph
->position
= graph_find_column_by_id(&graph
->row
, id
);
638 string_copy_rev(graph
->id
, id
);
639 graph
->canvas
= canvas
;
640 graph
->is_boundary
= is_boundary
;
642 while ((parents
= strchr(parents
, ' '))) {
644 if (!graph_add_parent(graph
, parents
))
646 graph
->has_parents
= TRUE
;
649 if (graph
->parents
.size
== 0 &&
650 !graph_add_parent(graph
, ""))
657 graph_symbol_forks(struct graph_symbol
*symbol
)
659 if (!symbol
->continued_down
)
662 if (!symbol
->continued_right
)
665 if (!symbol
->continued_up
)
672 graph_symbol_cross_over(struct graph_symbol
*symbol
)
677 if (!symbol
->continued_down
)
680 if (!symbol
->continued_up
&& !symbol
->new_column
&& !symbol
->below_commit
)
683 if (symbol
->shift_left
)
686 if (symbol
->parent_right
&& symbol
->merge
)
696 graph_symbol_turn_left(struct graph_symbol
*symbol
)
698 if (symbol
->matches_commit
&& symbol
->continued_right
&& !symbol
->continued_down
)
701 if (symbol
->continue_shift
)
704 if (symbol
->continued_up
|| symbol
->new_column
|| symbol
->below_commit
) {
705 if (symbol
->matches_commit
)
708 if (symbol
->shift_left
)
716 graph_symbol_turn_down_cross_over(struct graph_symbol
*symbol
)
718 if (!symbol
->continued_down
)
721 if (!symbol
->continued_right
)
734 graph_symbol_turn_down(struct graph_symbol
*symbol
)
736 if (!symbol
->continued_down
)
739 if (!symbol
->continued_right
)
746 graph_symbol_merge(struct graph_symbol
*symbol
)
748 if (symbol
->continued_down
)
751 if (!symbol
->parent_down
)
754 if (symbol
->parent_right
)
761 graph_symbol_multi_merge(struct graph_symbol
*symbol
)
763 if (!symbol
->parent_down
)
766 if (!symbol
->parent_right
)
773 graph_symbol_vertical_bar(struct graph_symbol
*symbol
)
778 if (symbol
->shift_left
)
781 if (!symbol
->continued_down
)
784 if (symbol
->continued_up
)
787 if (symbol
->parent_right
)
793 if (symbol
->continued_right
)
800 graph_symbol_horizontal_bar(struct graph_symbol
*symbol
)
802 if (symbol
->shift_left
)
805 if (symbol
->continued_down
)
808 if (!symbol
->parent_right
&& !symbol
->continued_right
)
811 if ((symbol
->continued_up
&& !symbol
->continued_up_left
))
814 if (!symbol
->below_commit
)
821 graph_symbol_multi_branch(struct graph_symbol
*symbol
)
823 if (symbol
->continued_down
)
826 if (!symbol
->continued_right
)
829 if (symbol
->below_shift
)
832 if (symbol
->continued_up
|| symbol
->new_column
|| symbol
->below_commit
) {
833 if (symbol
->matches_commit
)
836 if (symbol
->shift_left
)
844 graph_symbol_to_utf8(struct graph_symbol
*symbol
)
846 if (symbol
->commit
) {
847 if (symbol
->boundary
)
849 else if (symbol
->initial
)
851 else if (symbol
->merge
)
856 if (graph_symbol_cross_over(symbol
))
859 if (graph_symbol_vertical_bar(symbol
))
862 if (graph_symbol_turn_left(symbol
))
865 if (graph_symbol_multi_branch(symbol
))
868 if (graph_symbol_horizontal_bar(symbol
))
871 if (graph_symbol_forks(symbol
))
874 if (graph_symbol_turn_down_cross_over(symbol
))
877 if (graph_symbol_turn_down(symbol
))
880 if (graph_symbol_merge(symbol
))
883 if (graph_symbol_multi_merge(symbol
))
890 graph_symbol_to_chtype(struct graph_symbol
*symbol
)
892 static chtype graphics
[2];
894 if (symbol
->commit
) {
896 if (symbol
->boundary
)
898 else if (symbol
->initial
)
900 else if (symbol
->merge
)
903 graphics
[1] = 'o'; //ACS_DIAMOND; //'*';
906 } else if (graph_symbol_cross_over(symbol
)) {
907 graphics
[0] = ACS_HLINE
;
908 graphics
[1] = ACS_VLINE
;
910 } else if (graph_symbol_vertical_bar(symbol
)) {
912 graphics
[1] = ACS_VLINE
;
914 } else if (graph_symbol_turn_left(symbol
)) {
915 graphics
[0] = ACS_HLINE
;
916 graphics
[1] = ACS_LRCORNER
;
918 } else if (graph_symbol_multi_branch(symbol
)) {
919 graphics
[0] = ACS_HLINE
;
920 graphics
[1] = ACS_BTEE
;
922 } else if (graph_symbol_horizontal_bar(symbol
)) {
923 graphics
[0] = graphics
[1] = ACS_HLINE
;
925 } else if (graph_symbol_forks(symbol
)) {
927 graphics
[1] = ACS_LTEE
;
929 } else if (graph_symbol_turn_down_cross_over(symbol
)) {
930 graphics
[0] = ACS_HLINE
;
931 graphics
[1] = ACS_ULCORNER
;
933 } else if (graph_symbol_turn_down(symbol
)) {
935 graphics
[1] = ACS_ULCORNER
;
937 } else if (graph_symbol_merge(symbol
)) {
938 graphics
[0] = ACS_HLINE
;
939 graphics
[1] = ACS_URCORNER
;
941 } else if (graph_symbol_multi_merge(symbol
)) {
942 graphics
[0] = ACS_HLINE
;
943 graphics
[1] = ACS_TTEE
;
946 graphics
[0] = graphics
[1] = ' ';
953 graph_symbol_to_ascii(struct graph_symbol
*symbol
)
955 if (symbol
->commit
) {
956 if (symbol
->boundary
)
958 else if (symbol
->initial
)
960 else if (symbol
->merge
)
965 if (graph_symbol_cross_over(symbol
))
968 if (graph_symbol_vertical_bar(symbol
))
971 if (graph_symbol_turn_left(symbol
))
974 if (graph_symbol_multi_branch(symbol
))
977 if (graph_symbol_horizontal_bar(symbol
))
980 if (graph_symbol_forks(symbol
))
983 if (graph_symbol_turn_down_cross_over(symbol
))
986 if (graph_symbol_turn_down(symbol
))
989 if (graph_symbol_merge(symbol
))
992 if (graph_symbol_multi_merge(symbol
))
998 /* vim: set ts=8 sw=8 noexpandtab: */