#ifndef VOLGIT_GIT_PART_C
#define VOLGIT_GIT_PART_C VOLGIT_GIT_PART_C
#include <git_part.h>
static const char *special_chars[256]
=
{
['\"']=""",
['\'']="'",
['&']="&",
['<']="<",
['>']=">",
};
struct Index_File *get_index_file(int dir_fd,const char *name)
{
struct Index_File *ret;
ret=calloc(sizeof(struct Index_File),1);
ret->out=create_file(dir_fd,name);
fprintf(ret->out,"<pre>\n");
return ret;
}
void release_index_file(struct Index_File *index)
{
fprintf(index->out,"</pre>\n");
close_file(index->out);
free(index);
}
int print_diff_line(const git_diff_delta *delta,const git_diff_hunk *hunk,const git_diff_line *line,FILE *out)
{
size_t i;
if(line->origin=='+')
fprintf(out,"<div class=\"origin_added\">");
else if(line->origin=='-')
fprintf(out,"<div class=\"origin_removed\">");
else
fprintf(out,"<div class=\"origin_other\">");
fprintf(out,"%c ",line->origin);
for(i=0;i<line->content_len;++i)
if(special_chars[(unsigned char)line->content[i]])
{
fwrite(special_chars[line->content[i]],1,strlen(special_chars[(unsigned char)line->content[i]]),out);
}else
{
fwrite(line->content+i,1,1,out);
}
fprintf(out,"</div>");
return 0;
}
void print_diff(FILE *out,git_tree *parent_tree,git_tree *current_tree,git_repository *repo)
{
git_diff *diff_from_parent;
size_t number_of_deltas=0;
size_t i;
fprintf(out,"<pre>");
git_diff_tree_to_tree(&diff_from_parent,repo,parent_tree,current_tree,NULL);
git_diff_print(diff_from_parent,GIT_DIFF_FORMAT_PATCH_ID,(git_diff_line_cb)print_diff_line,out);
if(diff_from_parent)
git_diff_free(diff_from_parent);
fprintf(out,"</pre>");
}
void print_headers_and_commit_message(FILE *where,git_commit *current_commit,const git_oid *current,_Bool has_diff)
{
const git_signature *who_commited;
fprintf(where,"<div class=\"commit_header commit_commit_header\">COMMIT: %s",git_oid_tostr_s(current));
if(has_diff)
fprintf(where," <a href=\"diffs/%s.html\">[DIFF]</a></div>\n",git_oid_tostr_s(current));
else
fprintf(where,"</div>\n");
who_commited=git_commit_committer(current_commit);
fprintf(where,"<div class=\"commit_header commit_author_header\">AUTHOR: %s <%s></div>\n",who_commited->name,who_commited->email);
fprintf(where,"<div class=\"commit_header commit_date_header\">DATE: %s</div>\n",ctime(&who_commited->when.time));
fprintf(where,"<pre><div class=\"commit_header commit_message\">\t%s</div></pre>\n",git_commit_message(current_commit));
}
void print_commits(int dir_fd,const git_reference *branch, git_repository *repo)
{
const git_oid *id;
FILE *log_file;
int diff_directory_fd;
int files_directory_fd;
git_revwalk *walker;
git_oid current;
git_commit *current_commit;
git_commit *parent_commit=NULL;
git_revwalk_new(&walker,repo);
id=git_reference_target(branch);
git_revwalk_push(walker,id);
log_file=create_file(dir_fd,"log");
diff_directory_fd=create_dir(dir_fd,"diffs");
files_directory_fd=create_dir(dir_fd,"files");
if(git_revwalk_next(¤t,walker))
return;
git_commit_lookup(¤t_commit,repo,¤t);
print_files_top(files_directory_fd,current_commit,repo);
while(!git_revwalk_next(¤t,walker))
{
git_commit_lookup(&parent_commit,repo,¤t);
print_commit(current_commit,parent_commit,log_file,diff_directory_fd,repo);
git_commit_free(current_commit);
current_commit=parent_commit;
}
if(parent_commit!=NULL)
print_commit(current_commit,NULL,log_file,diff_directory_fd,repo);
git_revwalk_free(walker);
close_file(log_file);
}
void print_commit(git_commit *current_commit,git_commit *parent_commit,FILE *log_file,int diff_directory_fd,git_repository *repo)
{
git_tree *current_tree;
git_tree *parent_tree;
FILE *diff_file;
git_commit_tree(¤t_tree,current_commit);
if(parent_commit==NULL)
parent_tree=NULL;
else
git_commit_tree(&parent_tree,parent_commit);
diff_file=create_file(diff_directory_fd,git_oid_tostr_s(git_commit_id(current_commit)));
print_diff(diff_file,parent_tree,current_tree,repo);
git_tree_free(parent_tree);
print_headers_and_commit_message(log_file,current_commit,git_commit_id(current_commit),1);
close_file(diff_file);
}
void print_branches(git_repository *repo)
{
const char *branch_name;
int branch_dir_fd;
git_branch_iterator *it;
git_reference *ref;
git_branch_t branch_type=GIT_BRANCH_LOCAL;
git_branch_iterator_new(&it,repo,branch_type);
while(git_branch_next(&ref,&branch_type,it)==0)
{
git_branch_name(&branch_name,ref);
if(branch_name)
{
branch_dir_fd=create_branch_dir(branch_name);
print_commits(branch_dir_fd,ref,repo);
close(branch_dir_fd);
}else
{
printf("NULL\n");
}
}
git_branch_iterator_free(it);
}
void print_files(struct Index_File *index_file,int dir_fd,git_tree *tree,git_repository *repo)
{
size_t number_of_entries;
size_t i;
const git_tree_entry *current_entry;
number_of_entries=git_tree_entrycount(tree);
for(i=0;i<number_of_entries;++i)
{
current_entry=git_tree_entry_byindex(tree,i);
if(i<number_of_entries-1)
indentation_set_is_not_final(index_file,1);
else
indentation_set_is_not_final(index_file,0);
if(state.output_is_tree_like)
{
print_indentation_for_treelike_output(index_file);
if(i<number_of_entries-1)
fprintf(index_file->out,"├── ");
else
fprintf(index_file->out,"└── ");
}
print_entry(index_file,current_entry,dir_fd,repo);
if(i<number_of_entries-1)
fprintf(index_file->out,"\n");
}
}
void print_files_top(int dir_fd,git_commit *top_commit,git_repository *repo)
{
struct Index_File *index_file;
git_tree *tree;
git_commit_tree(&tree,top_commit);
index_file=get_index_file(dir_fd,"index");
if(state.output_is_tree_like)
fprintf(index_file->out,"│\n");
print_files(index_file,dir_fd,tree,repo);
release_index_file(index_file);
}
int print_entry(struct Index_File *index_file,const git_tree_entry *entry,int base_dir_fd,git_repository *repo)
{
git_object *obj;
const char *entry_name;
git_tree_entry_to_object(&obj,repo,entry);
entry_name=git_tree_entry_name(entry);
switch(git_object_type(obj))
{
case GIT_OBJECT_TREE:
print_tree_entry(index_file,entry_name,obj,base_dir_fd,repo);
break;
case GIT_OBJECT_BLOB:
print_blob_entry(index_file,entry_name,obj,base_dir_fd,repo);
break;
}
git_object_free(obj);
return 0;
}
static inline void print_tree_entry(struct Index_File *index_file,const char *name,git_object *obj,int base_dir_fd,git_repository *repo)
{
const git_oid *obj_oid;
const char *entry_oid;
git_tree *tree;
obj_oid=git_object_id(obj);
entry_oid=git_oid_tostr_s(obj_oid);
tree=(git_tree*)obj;
if(state.output_is_tree_like)
{
if(index_file->indentation==state.tree_cutoff)
{
push_html_link_for_tree(index_file->out,name,entry_oid);
print_files_in_another_index(base_dir_fd,entry_oid,name,tree,repo);
}else
{
fprintf(index_file->out,"%s",name);
increment_indentation(index_file,name);
fprintf(index_file->out,"\n");
print_files(index_file,base_dir_fd,tree,repo);
decrement_indentation(index_file);
}
}else
{
push_html_link_for_tree(index_file->out,name,entry_oid);
print_files_in_another_index(base_dir_fd,entry_oid,name,tree,repo);
}
}
static inline void print_blob_entry(struct Index_File *index_file,const char *name,git_object *obj,int base_dir_fd,git_repository *repo)
{
const git_oid *obj_oid;
const char *entry_oid;
FILE *blob_file;
const unsigned char *blob_data;
size_t blob_size;
size_t i;
git_blob *blob;
obj_oid=git_object_id(obj);
entry_oid=git_oid_tostr_s(obj_oid);
push_html_link_for_blob(index_file->out,name,entry_oid);
blob=(git_blob*)obj;
blob_file=create_file(base_dir_fd,entry_oid);
blob_size=git_blob_rawsize(blob);
blob_data=git_blob_rawcontent(blob);
fprintf(blob_file,"<code><pre>\n");
for(i=0;i<blob_size;++i)
{
if(special_chars[blob_data[i]])
{
fwrite(special_chars[blob_data[i]],1,strlen(special_chars[blob_data[i]]),blob_file);
}else
{
fwrite(blob_data+i,1,1,blob_file);
}
}
fprintf(blob_file,"</pre></code>\n");
close_file(blob_file);
}
void print_indentation_for_treelike_output(struct Index_File *out)
{
int i;
int j;
for(i=0;i<out->indentation;++i)
{
if(out->branches[i])
fprintf(out->out,"│");
else
fprintf(out->out," ");
for(j=0;j<out->offset[i]+2;++j)
fprintf(out->out," ");
}
}
void increment_indentation(struct Index_File *index,const char *name)
{
index->offset[index->indentation]=strnlen(name,1000);
++index->indentation;
}
void decrement_indentation(struct Index_File *index)
{
--index->indentation;
}
void indentation_set_is_not_final(struct Index_File *index,_Bool is_final)
{
index->branches[index->indentation]=is_final;
}
void print_files_in_another_index(int base_dir_fd,const char *entry_oid,const char *name,git_tree *tree,git_repository *repo)
{
int new_dir_fd;
struct Index_File *new_index;
new_dir_fd=create_dir(base_dir_fd,entry_oid);
new_index=get_index_file(new_dir_fd,"index");
if(state.output_is_tree_like)
fprintf(new_index->out,"%s\n│\n",name);
print_files(new_index,new_dir_fd,tree,repo);
release_index_file(new_index);
close(new_dir_fd);
}
#endif