#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_INTTYPES_H #include #else #include #endif #include /* offsetof(3) */ #include #include /* chmod(2) */ #include #include #include #include #include /* strdup(3) */ #include /* basename(3) */ #include /* tolower(3) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /******************************** Sorted dlist functions: */ struct tree_line { char *name; char *revision; char *date; char *size; }; static struct dlist *directories = NULL; static struct dlist *files = NULL; static struct tree_line *tree_line_alloc( const char *name, const char *revision, const char *date, const char *size ) { struct tree_line *line = NULL; line = (struct tree_line *)xmalloc( sizeof(struct tree_line) ); if( name ) { line->name = xstrdup( name ); } if( revision ) { line->revision = xstrdup( revision ); } if( date ) { line->date = xstrdup( date ); } if( size ) { line->size = xstrdup( size ); } return line; } static void __line_free( void *data, void *user_data ) { struct tree_line *line = (struct tree_line *)data; if( line ) { if( line->name ) { free( line->name ); line->name = NULL; } if( line->revision ) { free( line->revision ); line->revision = NULL; } if( line->date ) { free( line->date ); line->date = NULL; } if( line->size ) { free( line->size ); line->size = NULL; } free( line ); } } static struct dlist *tree_lines_free( struct dlist *lines ) { if( lines ) { dlist_free( lines, __line_free ); lines = NULL; } return lines; } static int __cmp_tree_lines_bytag( const void *a, const void *b ) { char *v1 = NULL, *v2 = NULL; int ret = -1; struct tree_line *line1 = (struct tree_line *)a; struct tree_line *line2 = (struct tree_line *)b; if( line1->name && line2->name ) { v1 = line1->name; v2 = line2->name; while( *v1 && !isdigit( *v1 ) ) ++v1; while( *v2 && !isdigit( *v2 ) ) ++v2; ret = strcmp( line1->name, line2->name ); } else return ret; if( !*v1 || !*v2 ) { return ret; } /****************************************** sort reversive to show newest tag first: */ return -cmp_version( (const char *)v1, (const char *)v2 ); } static struct dlist *tree_lines_sort_bytag( struct dlist *lines ) { if( lines ) { lines = dlist_sort( lines, __cmp_tree_lines_bytag ); } return lines; } /* End of sorted dlist functions. ********************************/ static const char *is_file_executable( const char *relative_path, const char *name, int revision ) { const char *co_prefix = ctx.repo.checkout_ro_prefix; const char *repo_name = ctx.repo.name; const char *repo_root = ctx.repo.repo_root; const char *ret = ""; char *path = NULL; if( !name ) return ret; if( relative_path && *relative_path ) { path = (char *)xmalloc( strlen( relative_path ) + 2 ); path[0] = '/'; sprintf( (char *)&path[1], relative_path ); } else { path = (char *)xmalloc( 1 ); path[0] = '\0'; } if( co_prefix ) { char repo_path[PATH_MAX] = { 0 }; char cmd[PATH_MAX]; struct strbuf buf = STRBUF_INIT; pid_t p = (pid_t) -1; int rc; if( repo_root && *repo_root ) { strcat( (char *)&repo_path[0], repo_root ); strcat( (char *)&repo_path[0], "/" ); } strcat( (char *)&repo_path[0], repo_name ); if( revision ) snprintf( (char *)&cmd[0], 1024, "svn propget svn:executable --revision %d %s/%s%s/%s 2>/dev/null", revision, co_prefix, (char *)&repo_path[0], path, name ); else snprintf( (char *)&cmd[0], 1024, "svn propget svn:executable %s/%s%s/%s 2>/dev/null", co_prefix, (char *)&repo_path[0], path, name ); p = sys_exec_command( &buf, cmd ); rc = sys_wait_command( p, NULL ); if( rc != 0 ) { strbuf_release( &buf ); free( path ); return ""; } strbuf_trim( &buf ); if( buf.buf[0] == '*' ) ret = " exec"; strbuf_release( &buf ); } free( path ); return ret; } static xmlNode *xml_find_node_by_name( xmlNode *root, const char *name ) { xmlNode *node = NULL; xmlNode *ret = NULL; if( !name || !*name || !root || !root->children ) return ret; for( node = root->children; node; node = node->next ) { if( node->type == XML_ELEMENT_NODE ) { if( !strcmp( (char *)name, (char *)node->name ) ) { return node; } } } return ret; } static void xml_csvn_tree( const struct strbuf *buf ) { xmlDocPtr doc = NULL; xmlNode *root = NULL; if( !buf || !buf->buf ) return; LIBXML_TEST_VERSION doc = xmlReadMemory( buf->buf, buf->len, "list.xml", NULL, 0 ); if( !doc ) { html_fatal( "cannot parse svn list.xml" ); return; } root = xmlDocGetRootElement( doc ); if( !root ) { xmlFreeDoc( doc ); xmlCleanupParser(); return; } if( !strcmp( "lists", (char *)root->name ) ) { xmlNode *list = NULL, *node = NULL; list = xml_find_node_by_name( root, "list" ); if( !list ) { xmlFreeDoc( doc ); xmlCleanupParser(); return; } /**************************** Collect directories first: */ for( node = list->children; node; node = node->next ) { if( node->type == XML_ELEMENT_NODE && !strcmp( "entry", (char *)node->name ) ) { xmlChar *content = NULL; xmlNode *param = NULL; content = xmlGetProp( node, (const unsigned char *)"kind" ); if( !strcmp( (const char *)content, "dir" ) ) { xmlChar *name = NULL, *revision = NULL, *date = NULL; for( param = node->children; param; param= param->next ) { if( param->type == XML_ELEMENT_NODE && !strcmp( "name", (char *)param->name ) ) { name = xmlNodeGetContent( param ); } if( param->type == XML_ELEMENT_NODE && !strcmp( "commit", (char *)param->name ) ) { xmlNode *commit_date = NULL; revision = xmlGetProp( param, (const unsigned char *)"revision" ); commit_date = xml_find_node_by_name( param, "date" ); if( commit_date ) date = xmlNodeGetContent( commit_date ); } } /* End for entry parameters */ if( name && revision && date ) { struct tree_line *line = NULL; line = tree_line_alloc( (const char *)name, (const char *)revision, (const char *)date, NULL ); directories = dlist_append( directories, (void *)line ); xmlFree( name ); xmlFree( revision ); xmlFree( date ); } else { if( name ) xmlFree( name ); if( revision ) xmlFree( revision ); if( date ) xmlFree( date ); } } xmlFree( content ); } } /**************************** Collect files: */ for( node = list->children; node; node = node->next ) { if( node->type == XML_ELEMENT_NODE && !strcmp( "entry", (char *)node->name ) ) { xmlChar *content = NULL; xmlNode *param = NULL; content = xmlGetProp( node, (const unsigned char *)"kind" ); if( !strcmp( (const char *)content, "file" ) ) { xmlChar *name = NULL, *size = NULL, *revision = NULL, *date = NULL; for( param = node->children; param; param= param->next ) { if( param->type == XML_ELEMENT_NODE && !strcmp( "name", (char *)param->name ) ) { name = xmlNodeGetContent( param ); } if( param->type == XML_ELEMENT_NODE && !strcmp( "size", (char *)param->name ) ) { size = xmlNodeGetContent( param ); } if( param->type == XML_ELEMENT_NODE && !strcmp( "commit", (char *)param->name ) ) { xmlNode *commit_date = NULL; revision = xmlGetProp( param, (const unsigned char *)"revision" ); commit_date = xml_find_node_by_name( param, "date" ); if( commit_date ) date = xmlNodeGetContent( commit_date ); } } /* End for entry parameters */ if( name && size && revision && date ) { struct tree_line *line = NULL; line = tree_line_alloc( (const char *)name, (const char *)revision, (const char *)date, (const char *)size ); files = dlist_append( files, (void *)line ); xmlFree( name ); xmlFree( size ); xmlFree( revision ); xmlFree( date ); } else { if( name ) xmlFree( name ); if( size ) xmlFree( size ); if( revision ) xmlFree( revision ); if( date ) xmlFree( date ); } } xmlFree( content ); } } } xmlFreeDoc(doc); xmlCleanupParser(); } static void dlist_csvn_tree( struct strbuf *sb, const char *relative_path, int rev ) { char *path = NULL; if( !sb || !sb->len ) return; if( relative_path && *relative_path ) { path = (char *)xmalloc( strlen( relative_path ) + 2 ); path[0] = '/'; sprintf( (char *)&path[1], relative_path ); } else { path = (char *)xmalloc( 1 ); path[0] = '\0'; } if( path && *path && !strcmp( (char *)&path[1], ctx.repo.tags ) ) { directories = tree_lines_sort_bytag( directories ); } strbuf_addstr( sb, "\n" ); strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
Path
\n" ); strbuf_addf( sb, "
Size
\n" ); strbuf_addf( sb, "
Rev
\n" ); strbuf_addf( sb, "
Date
\n" ); strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
\n\n" ); strbuf_addf( sb, "
\n\n" ); /************************** Print directories first: */ if( directories ) { struct dlist *list = directories; while( list ) { struct tree_line *line = (struct tree_line *)list->data; if( line->name && line->revision && line->date ) { struct tm tm; time_t time = -1; const char *query_string = ctx_remove_query_param( ctx.env.query_string, "rev" ); query_string = ctx_remove_query_param( query_string, "op" ); time = parse_date( &tm, (const char *)line->date ); strbuf_addf( sb, "
\n" ); if( ctx.env.query_string && *ctx.env.query_string ) { if( ctx.repo.repo_root && *ctx.repo.repo_root ) { strbuf_addf( sb, " \n", ctx.repo.repo_root, ctx.repo.name, path, line->name, ctx.env.query_string, line->name ); } else { strbuf_addf( sb, " \n", ctx.repo.name, path, line->name, ctx.env.query_string, line->name ); } } else { if( ctx.repo.repo_root && *ctx.repo.repo_root ) { strbuf_addf( sb, " \n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->name ); } else { strbuf_addf( sb, " \n", ctx.repo.name, path, line->name, line->name ); } } strbuf_addf( sb, "
4096
\n" ); strbuf_addf( sb, "
%s
\n", line->revision ); if( time != -1 ) { strbuf_addf( sb, "
" ); csvn_print_age( sb, time, 0, 0 ); strbuf_addf( sb, "
\n" ); } else { strbuf_addf( sb, "
unknown
\n" ); } strbuf_addf( sb, "
\n" ); strbuf_addf( sb, " \n" ); strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
\n\n" ); } list = dlist_next( list ); } directories = tree_lines_free( directories ); } /************************** Print files: */ if( files ) { struct dlist *list = files; while( list ) { struct tree_line *line = (struct tree_line *)list->data; if( line->name && line->size && line->revision && line->date ) { struct tm tm; time_t time = -1; const char *query_string = ctx_remove_query_param( ctx.env.query_string, "rev" ); const char *exec = is_file_executable( relative_path, (const char *)line->name, rev ); query_string = ctx_remove_query_param( query_string, "op" ); time = parse_date( &tm, (const char *)line->date ); strbuf_addf( sb, "
\n" ); if( ctx.env.query_string && *ctx.env.query_string ) { if( ctx.repo.repo_root && *ctx.repo.repo_root ) { strbuf_addf( sb, " \n", ctx.repo.repo_root, ctx.repo.name, path, line->name, ctx.env.query_string, exec, line->name ); } else { strbuf_addf( sb, " \n", ctx.repo.name, path, line->name, ctx.env.query_string, exec, line->name ); } } else { if( ctx.repo.repo_root && *ctx.repo.repo_root ) { strbuf_addf( sb, " \n", ctx.repo.repo_root, ctx.repo.name, path, line->name, exec, line->name ); } else { strbuf_addf( sb, " \n", ctx.repo.name, path, line->name, exec, line->name ); } } strbuf_addf( sb, "
%s
\n", line->size ); strbuf_addf( sb, "
%s
\n", line->revision ); if( time != -1 ) { strbuf_addf( sb, "
" ); csvn_print_age( sb, time, 0, 0 ); strbuf_addf( sb, "
\n" ); } else { strbuf_addf( sb, "
unknown
\n" ); } strbuf_addf( sb, "
\n" ); strbuf_addf( sb, " \n" ); strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
\n\n" ); } list = dlist_next( list ); } files = tree_lines_free( files ); } strbuf_addf( sb, "
\n\n" ); free( path ); } static void csvn_print_tree( struct strbuf *sb, const char *relative_path, int revision ) { const char *co_prefix = ctx.repo.checkout_ro_prefix; const char *name = ctx.repo.name; const char *repo_root = ctx.repo.repo_root; char *path = NULL; if( !sb ) return; if( relative_path && *relative_path ) { path = (char *)xmalloc( strlen( relative_path ) + 2 ); path[0] = '/'; sprintf( (char *)&path[1], relative_path ); } else { path = (char *)xmalloc( 1 ); path[0] = '\0'; } if( co_prefix ) { char repo_path[PATH_MAX] = { 0 }; char cmd[PATH_MAX]; struct strbuf buf = STRBUF_INIT; pid_t p = (pid_t) -1; int rc; if( repo_root && *repo_root ) { strcat( (char *)&repo_path[0], repo_root ); strcat( (char *)&repo_path[0], "/" ); } strcat( (char *)&repo_path[0], name ); if( revision ) snprintf( (char *)&cmd[0], 1024, "svn list --revision %d --xml %s/%s%s 2>/dev/null", revision, co_prefix, (char *)&repo_path[0], path ); else snprintf( (char *)&cmd[0], 1024, "svn list --xml %s/%s%s 2>/dev/null", co_prefix, (char *)&repo_path[0], path ); p = sys_exec_command( &buf, cmd ); rc = sys_wait_command( p, NULL ); if( rc != 0 ) { strbuf_release( &buf ); free( path ); return; } xml_csvn_tree( (const struct strbuf *)&buf ); strbuf_release( &buf ); dlist_csvn_tree( sb, relative_path, revision ); } free( path ); return; } static void csvn_print_checkout_urls( struct strbuf *sb, const char *relative_path, int revision ) { char *path = NULL; if( !sb ) return; if( relative_path && *relative_path ) { path = (char *)xmalloc( strlen( relative_path ) + 2 ); path[0] = '/'; sprintf( (char *)&path[1], relative_path ); } else { path = (char *)xmalloc( 1 ); path[0] = '\0'; } strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
Checkout
\n" ); strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
ro:
\n" ); if( revision ) { if( ctx.repo.repo_root && *ctx.repo.repo_root ) { strbuf_addf( sb, "
svn checkout --revision %d %s/%s/%s%s
\n", revision, ctx.repo.checkout_ro_prefix, ctx.repo.repo_root, ctx.repo.name, path ); } else { strbuf_addf( sb, "
svn checkout --revision %d %s/%s%s
\n", revision, ctx.repo.checkout_ro_prefix, ctx.repo.name, path ); } } else { if( ctx.repo.repo_root && *ctx.repo.repo_root ) { strbuf_addf( sb, "
svn checkout %s/%s/%s%s
\n", ctx.repo.checkout_ro_prefix, ctx.repo.repo_root, ctx.repo.name, path ); } else { strbuf_addf( sb, "
svn checkout %s/%s%s
\n", ctx.repo.checkout_ro_prefix, ctx.repo.name, path ); } } strbuf_addf( sb, "
\n" ); if( ctx.repo.checkout_prefix && *ctx.repo.checkout_prefix ) { strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
rw:
\n" ); if( revision ) { if( ctx.repo.repo_root && *ctx.repo.repo_root ) { strbuf_addf( sb, "
svn checkout --revision %d %s/%s/%s%s
\n", revision, ctx.repo.checkout_prefix, ctx.repo.repo_root, ctx.repo.name, path ); } else { strbuf_addf( sb, "
svn checkout --revision %d %s/%s%s
\n", revision, ctx.repo.checkout_prefix, ctx.repo.name, path ); } } else { if( ctx.repo.repo_root && *ctx.repo.repo_root ) { strbuf_addf( sb, "
svn checkout %s/%s/%s%s
\n", ctx.repo.checkout_prefix, ctx.repo.repo_root, ctx.repo.name, path ); } else { strbuf_addf( sb, "
svn checkout %s/%s%s
\n", ctx.repo.checkout_prefix, ctx.repo.name, path ); } } strbuf_addf( sb, "
\n" ); } strbuf_addf( sb, "
\n" ); strbuf_addf( sb, "
\n" ); free( path ); } void csvn_print_tree_page( void ) { FILE *fp; struct strbuf buf = STRBUF_INIT; fp = xfopen( ctx.page.header, "r" ); (void)strbuf_env_fread( &buf, fp ); fclose( fp ); strbuf_addf( &buf, "
\n" ); strbuf_addf( &buf, "
\n" ); strbuf_addf( &buf, "
\n" ); if( ctx.repo.relative_info.kind == KIND_DIR ) { if( ctx.repo.name ) { csvn_print_tree( &buf, ctx.repo.relative_path, ctx.query.rev ); csvn_print_checkout_urls( &buf, ctx.repo.relative_path, ctx.query.rev ); } else { strbuf_addf( &buf, "

Requested resource cannot be shown

\n" ); strbuf_addf( &buf, "

Repository '%s' not found.

\n", ctx.repo.name ); } } else { strbuf_addf( &buf, "

Requested resource cannot be shown

\n" ); strbuf_addf( &buf, "

This page assume the repository tree to be shown.

\n" ); } strbuf_addf( &buf, "
\n" ); strbuf_addf( &buf, "
\n" ); strbuf_addf( &buf, "
\n" ); fp = xfopen( ctx.page.footer, "r" ); (void)strbuf_env_fread( &buf, fp ); fclose( fp ); ctx.page.size = buf.len; csvn_print_http_headers(); strbuf_write( &buf, STDOUT_FILENO ); strbuf_release( &buf ); }