cSvn-UI for SVN Repositories

cGit-UI – is a web interface for Subversion (SVN) Repositories. cSvn CGI script is writen in C and therefore it's fast enough

6 Commits   0 Branches   2 Tags
author: kx <kx@radix.pro> 2023-04-12 19:21:06 +0300 committer: kx <kx@radix.pro> 2023-04-12 19:21:06 +0300 commit: c7693c71799633467d154afe65f9b4810cec42f5 parent: c8b2593c941778dcfa307badf981160e19d2932b
Commit Summary:
Added repo-root variable for repositories which placed in subdirectory relative svnserve root
Diffstat:
10 files changed, 470 insertions, 111 deletions
diff --git a/csvncgi/csvn-cgi.c b/csvncgi/csvn-cgi.c
index 3a2688b..5996342 100644
--- a/csvncgi/csvn-cgi.c
+++ b/csvncgi/csvn-cgi.c
@@ -188,6 +188,7 @@ int main( int argc, char *argv[] )
     strbuf_addf( &buf, "ctx.query.search = '%s';\n", ctx.query.search );
     strbuf_addf( &buf, "\n" );
     strbuf_addf( &buf, "ctx.repo.name = %s\n", ctx.repo.name );
+    strbuf_addf( &buf, "ctx.repo.repo_root = %s\n", ctx.repo.repo_root );
     strbuf_addf( &buf, "ctx.repo.info = {\n" );
     switch( ctx.repo.info.kind )
     {
diff --git a/csvncgi/csvn-ui.rc.5 b/csvncgi/csvn-ui.rc.5
index c6e5f41..7931383 100644
--- a/csvncgi/csvn-ui.rc.5
+++ b/csvncgi/csvn-ui.rc.5
@@ -81,6 +81,16 @@ The integer or string value of UTC offset on the SVN server where repositories a
 .EE
 .in
 
+.TP 4
+\fBrepo-root\fR
+The name of directory where some repository is placed related to \fBsvn repositories root\fR directory. For example:
+.PP
+.in +6n
+.EX
+\fIrepo-root = 'tools';\fR
+.EE
+.in
+
 .TP 4
 \fBcheckout-prefix-readonly\fR
 The checkout prefix for readonly access to the repository. The value of this variable should has \fBpath\fR type
diff --git a/csvncgi/ctx.c b/csvncgi/ctx.c
index e3405c4..e7e4e2a 100644
--- a/csvncgi/ctx.c
+++ b/csvncgi/ctx.c
@@ -181,6 +181,7 @@ void csvn_prepare_context( void )
 
   ctx.repo.name = NULL;
   ctx.repo.info = CSVN_INFO_INIT;
+  ctx.repo.repo_root = NULL;
   ctx.repo.relative_path = NULL;
   ctx.repo.relative_info = CSVN_INFO_INIT;
   ctx.repo.relative_html = NULL;
@@ -408,13 +409,19 @@ static void ctx_page_size_from_repo( struct repo *repo )
 
 static void ctx_repo_dirs_from_global( void )
 {
-  struct variable  trunk     = { (unsigned char *)"trunk",    { 0 }, DT_PATH };
-  struct variable  branches  = { (unsigned char *)"branches", { 0 }, DT_PATH };
-  struct variable  tags      = { (unsigned char *)"tags",     { 0 }, DT_PATH };
+  struct variable  repo_root = { (unsigned char *)"repo-root", { 0 }, DT_PATH };
+  struct variable  trunk     = { (unsigned char *)"trunk",     { 0 }, DT_PATH };
+  struct variable  branches  = { (unsigned char *)"branches",  { 0 }, DT_PATH };
+  struct variable  tags      = { (unsigned char *)"tags",      { 0 }, DT_PATH };
   struct variable  ro_prefix = { (unsigned char *)"checkout-prefix-readonly", { 0 }, DT_PATH };
   struct variable  rw_prefix = { (unsigned char *)"checkout-prefix",          { 0 }, DT_PATH };
   struct variable *var = NULL;
 
+  var = lookup_global( lookup_global_section( config ), &repo_root );
+  if( var )
+  {
+    ctx.repo.repo_root = (const char *)var->_v.vptr;
+  }
   var = lookup_global( lookup_global_section( config ), &trunk );
   if( var )
   {
@@ -671,15 +678,21 @@ static void ctx_promo_vars_from_global( void )
 
 static void ctx_repo_dirs_from_repo( struct repo *repo )
 {
-  struct variable  trunk     = { (unsigned char *)"trunk",    { 0 }, DT_PATH };
-  struct variable  branches  = { (unsigned char *)"branches", { 0 }, DT_PATH };
-  struct variable  tags      = { (unsigned char *)"tags",     { 0 }, DT_PATH };
+  struct variable  repo_root = { (unsigned char *)"repo-root", { 0 }, DT_PATH };
+  struct variable  trunk     = { (unsigned char *)"trunk",     { 0 }, DT_PATH };
+  struct variable  branches  = { (unsigned char *)"branches",  { 0 }, DT_PATH };
+  struct variable  tags      = { (unsigned char *)"tags",      { 0 }, DT_PATH };
   struct variable  ro_prefix = { (unsigned char *)"checkout-prefix-readonly", { 0 }, DT_PATH };
   struct variable  rw_prefix = { (unsigned char *)"checkout-prefix",          { 0 }, DT_PATH };
   struct variable *var = NULL;
 
   if( !repo ) return;
 
+  var = lookup( repo, &repo_root );
+  if( var )
+  {
+    ctx.repo.repo_root = (const char *)var->_v.vptr;
+  }
   var = lookup( repo, &trunk );
   if( var )
   {
@@ -1165,45 +1178,92 @@ static void ctx_footer_from_repo( struct repo *repo )
 }
 
 
-static void ctx_repo_name( void )
+static char name[PATH_MAX] = { 0 };
+static char repo_root[PATH_MAX] = { 0 };
+static char relative_path[PATH_MAX] = { 0 };
+
+static char try[PATH_MAX] = { 0 };
+
+static struct repo *ctx_repo_name( void )
 {
-  char *p, *path, *rpath, *name, *rname, *path_info;
+  struct repo *repo = NULL;
+  char *s, *p = NULL, *n, *path, *path_info;
   int   len = 0;
 
   if( strcmp( "/", ctx.env.path_info ) )
   {
     path_info = xstrdup( ctx.env.path_info );
-    name = path_info;
+    s = path_info;
 
-    while( *name && is_dir_sep( *name ) )
-      ++name;
-    p = name;
-    while( *p && !is_dir_sep( *p ) )
-      ++p;
+    while( *s )
+    {
+      while( *s && is_dir_sep( *s ) )
+        ++s;
+      n = p = s;
 
-    if( *p ) { *p = '\0'; path = ++p; }
-    else
-      path = p;
+      while( *p && !is_dir_sep( *p ) )
+        ++p;
+      if( *p )
+      {
+        *p = '\0'; s = ++p;
 
-    len = (int)strlen( name ) + 1;
-    rname = (char *)__sbrk( len );
-    memcpy( (void *)rname, (const void *)name, len );
-    ctx.repo.name = (const char *)rname;
+        /****************************************************************
+          The repo name can be given as a relative path in the git-root:
+         */
+        if( repo_root[0] )
+        {
+          sprintf( try, "%s", repo_root );
+          strcat( try, "/" );
+          strcat( try, n );
+        }
+        else
+        {
+          sprintf( try, "%s", n );
+        }
+
+        if( (repo = lookup_repo( config, try )) )
+        {
+          sprintf( name, "%s", try );
+          {
+            char *rr = strstr( repo_root, try );
+            if( rr )
+              *rr = '\0';
+            else
+              repo_root[0] = '\0';
+          }
+          break;
+        }
+        else
+        {
+          if( repo_root[0] )
+            strcat( repo_root, "/" );
+          strcat( repo_root, n );
+        }
+      }
+      else
+        s = p;
+    }
+
+    ctx.repo.name = (const char *)&name[0];
+    ctx.repo.repo_root = (const char *)&repo_root[0];
+
+    path = p;
 
     if( *path )
     {
       len = (int)strlen( path );
 
-      if( is_dir_sep( path[len-1] ) ) { path[len-1] = '\0'; --len; }
+      if( path[len-1] =='/' ) { path[len-1] = '\0'; --len; }
       len += 1;
 
-      rpath = (char *)__sbrk( len );
-      memcpy( (void *)rpath, (const void *)path, len );
-      ctx.repo.relative_path = (const char *)rpath;
+      sprintf( relative_path, "%s", path );
+      ctx.repo.relative_path = (const char *)&relative_path[0];
     }
 
     free( path_info );
   }
+
+  return repo;
 }
 
 
@@ -1342,9 +1402,12 @@ static void ctx_relative_html( void )
   }
   else if( !strcmp( ctx.vars.page_type, ptype_repo ) )
   {
-    if( ctx.repo.name && *ctx.repo.name && ctx.repo.info.kind != KIND_UNKNOWN )
+    if( ctx.repo.name && *ctx.repo.name && ctx.repo.info.kind != KIND_UNKNOWN  )
     {
-      strbuf_addf( &buf, "<a class='base' href='/%s/'>%s</a>/", ctx.repo.name, ctx.repo.name );
+      if( ctx.repo.repo_root && *ctx.repo.repo_root )
+        strbuf_addf( &buf, "<a class='base' href='/%s/%s/'>%s/%s</a>/", ctx.repo.repo_root, ctx.repo.name, ctx.repo.repo_root, ctx.repo.name );
+      else
+        strbuf_addf( &buf, "<a class='base' href='/%s/'>%s</a>/", ctx.repo.name, ctx.repo.name );
     }
     if( ctx.repo.relative_path && *ctx.repo.relative_path && ctx.repo.relative_info.kind != KIND_UNKNOWN )
     {
@@ -1524,8 +1587,7 @@ void csvn_parse_query( void )
     {
       struct repo *repo = NULL;
 
-      ctx_repo_name();
-      repo = lookup_repo( config, ctx.repo.name );
+      repo = ctx_repo_name();
       if( repo )
       {
         ctx_page_size_from_repo( repo );
@@ -1562,7 +1624,11 @@ void csvn_parse_query( void )
           struct strbuf  buf = STRBUF_INIT;
           char  *stmsg = NULL;
 
-          strbuf_addf( &buf, "Subversion Repository '%s' not found.", ctx.repo.name );
+          /**************************************************************************
+            We slipped to the end of PATH_INFO when we were looking for a repository
+            and now we printout the repo-root:
+           */
+          strbuf_addf( &buf, "Subversion Repository '%s' not found.", ctx.repo.repo_root );
           stmsg = (char *)__sbrk( (int)buf.len + 1 );
           memcpy( (void *)stmsg, (const void *)buf.buf, buf.len + 1 );
           strbuf_release( &buf );
diff --git a/csvncgi/ui-blame.c b/csvncgi/ui-blame.c
index 15fff42..ddd37cb 100644
--- a/csvncgi/ui-blame.c
+++ b/csvncgi/ui-blame.c
@@ -53,7 +53,8 @@
 static void csvn_print_blame( struct strbuf *sb, const char *relative_path, int revision )
 {
   const char *co_prefix = ctx.repo.checkout_ro_prefix;
-  const char *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
 
   if( !sb || !relative_path ) return;
 
@@ -62,20 +63,28 @@ static void csvn_print_blame( struct strbuf *sb, const char *relative_path, int
 
   if( co_prefix )
   {
-    char cmd[1024];
+    char repo_path[PATH_MAX] = { 0 };
+    char cmd[PATH_MAX];
     struct strbuf buf = STRBUF_INIT;
     char  *raw = NULL;
     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 blame --revision %d %s/%s/%s 2>/dev/null",
-                revision, co_prefix, repo_path, relative_path );
+                revision, co_prefix, (char *)&repo_path[0], relative_path );
     else
       snprintf( (char *)&cmd[0], 1024,
                 "svn blame %s/%s/%s 2>/dev/null",
-                co_prefix, repo_path, relative_path );
+                co_prefix, (char *)&repo_path[0], relative_path );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
diff --git a/csvncgi/ui-diff.c b/csvncgi/ui-diff.c
index 7851f00..e74de6c 100644
--- a/csvncgi/ui-diff.c
+++ b/csvncgi/ui-diff.c
@@ -53,7 +53,8 @@
 static void csvn_print_diff( struct strbuf *sb, const char *relative_path, int revision )
 {
   const char *co_prefix = ctx.repo.checkout_ro_prefix;
-  const char *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
 
   if( !sb || !relative_path || !revision ) return;
 
@@ -61,15 +62,23 @@ static void csvn_print_diff( struct strbuf *sb, const char *relative_path, int r
 
   if( co_prefix )
   {
-    char cmd[1024];
+    char repo_path[PATH_MAX] = { 0 };
+    char cmd[PATH_MAX];
     struct strbuf buf = STRBUF_INIT;
     char  *raw = NULL;
     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 );
+
     snprintf( (char *)&cmd[0], 1024,
               "svn diff --revision %d:%d %s/%s/%s 2>/dev/null",
-              revision-1, revision, co_prefix, repo_path, relative_path );
+              revision-1, revision, co_prefix, (char *)&repo_path[0], relative_path );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
diff --git a/csvncgi/ui-file.c b/csvncgi/ui-file.c
index b20d86e..d7fe731 100644
--- a/csvncgi/ui-file.c
+++ b/csvncgi/ui-file.c
@@ -56,21 +56,30 @@
 static int csvn_file_last_changed_revision( const char *relative_path )
 {
   const char *co_prefix = ctx.repo.checkout_ro_prefix;
-  const char *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
   int ret = 0;
 
   if( !relative_path ) return ret;
 
   if( co_prefix )
   {
-    char cmd[1024];
+    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 );
+
     snprintf( (char *)&cmd[0], 1024,
               "svn info --show-item last-changed-revision --no-newline %s/%s/%s 2>/dev/null",
-              co_prefix, repo_path, relative_path );
+              co_prefix, (char *)&repo_path[0], relative_path );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
@@ -109,15 +118,33 @@ static void csvn_print_file_links( struct strbuf *sb, const char *relative_path,
 
   if( query_string && *query_string )
   {
-    strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-scroll\"></span><a href=\"/%s/%s/?op=log&rev=%s&%s\">log</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0], query_string );
-    strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-diff\"></span><a href=\"/%s/%s/?op=diff&rev=%s&%s\">diff</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0], query_string );
-    strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-blame\"></span><a href=\"/%s/%s/?op=blame&rev=%s&%s\">blame</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0], query_string );
+    if( ctx.repo.repo_root && *ctx.repo.repo_root )
+    {
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-scroll\"></span><a href=\"/%s/%s/%s/?op=log&rev=%s&%s\">log</a></div>\n", ctx.repo.repo_root, ctx.repo.name, relative_path, (char *)&rev[0], query_string );
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-diff\"></span><a href=\"/%s/%s/%s/?op=diff&rev=%s&%s\">diff</a></div>\n", ctx.repo.repo_root, ctx.repo.name, relative_path, (char *)&rev[0], query_string );
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-blame\"></span><a href=\"/%s/%s/%s/?op=blame&rev=%s&%s\">blame</a></div>\n", ctx.repo.repo_root, ctx.repo.name, relative_path, (char *)&rev[0], query_string );
+    }
+    else
+    {
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-scroll\"></span><a href=\"/%s/%s/?op=log&rev=%s&%s\">log</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0], query_string );
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-diff\"></span><a href=\"/%s/%s/?op=diff&rev=%s&%s\">diff</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0], query_string );
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-blame\"></span><a href=\"/%s/%s/?op=blame&rev=%s&%s\">blame</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0], query_string );
+    }
   }
   else
   {
-    strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-scroll\"></span><a href=\"/%s/%s/?op=log&rev=%s\">log</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0] );
-    strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-diff\"></span><a href=\"/%s/%s/?op=diff&rev=%s\">diff</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0] );
-    strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-blame\"></span><a href=\"/%s/%s/?op=blame&rev=%s\">blame</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0] );
+    if( ctx.repo.repo_root && *ctx.repo.repo_root )
+    {
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-scroll\"></span><a href=\"/%s/%s/%s/?op=log&rev=%s\">log</a></div>\n", ctx.repo.repo_root, ctx.repo.name, relative_path, (char *)&rev[0] );
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-diff\"></span><a href=\"/%s/%s/%s/?op=diff&rev=%s\">diff</a></div>\n", ctx.repo.repo_root, ctx.repo.name, relative_path, (char *)&rev[0] );
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-blame\"></span><a href=\"/%s/%s/%s/?op=blame&rev=%s\">blame</a></div>\n", ctx.repo.repo_root, ctx.repo.name, relative_path, (char *)&rev[0] );
+    }
+    else
+    {
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-scroll\"></span><a href=\"/%s/%s/?op=log&rev=%s\">log</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0] );
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-diff\"></span><a href=\"/%s/%s/?op=diff&rev=%s\">diff</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0] );
+      strbuf_addf( sb, "    <div class=\"item\"><span class=\"icon las la-blame\"></span><a href=\"/%s/%s/?op=blame&rev=%s\">blame</a></div>\n", ctx.repo.name, relative_path, (char *)&rev[0] );
+    }
   }
 
   strbuf_addf( sb, "  </div>\n" );
@@ -144,7 +171,8 @@ static int csvn_write_markdown_content( struct strbuf *sb, const struct strbuf *
 static void csvn_print_markdown_file( struct strbuf *sb, const char *relative_path, int revision )
 {
   const char *co_prefix = ctx.repo.checkout_ro_prefix;
-  const char *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
 
   if( !sb || !relative_path ) return;
 
@@ -154,19 +182,27 @@ static void csvn_print_markdown_file( struct strbuf *sb, const char *relative_pa
 
   if( co_prefix )
   {
-    char cmd[1024];
+    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 cat --revision %d %s/%s/%s 2>/dev/null",
-                revision, co_prefix, repo_path, relative_path );
+                revision, co_prefix, (char *)&repo_path[0], relative_path );
     else
       snprintf( (char *)&cmd[0], 1024,
                 "svn cat %s/%s/%s 2>/dev/null",
-                co_prefix, repo_path, relative_path );
+                co_prefix, (char *)&repo_path[0], relative_path );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
@@ -191,7 +227,8 @@ static void csvn_print_markdown_file( struct strbuf *sb, const char *relative_pa
 static void csvn_print_file( struct strbuf *sb, const char *relative_path, int revision, const char *lang )
 {
   const char *co_prefix = ctx.repo.checkout_ro_prefix;
-  const char *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
 
   if( !sb || !relative_path ) return;
 
@@ -204,20 +241,28 @@ static void csvn_print_file( struct strbuf *sb, const char *relative_path, int r
 
   if( co_prefix )
   {
-    char cmd[1024];
+    char repo_path[PATH_MAX] = { 0 };
+    char cmd[PATH_MAX];
     struct strbuf buf = STRBUF_INIT;
     char  *raw = NULL;
     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 cat --revision %d %s/%s/%s 2>/dev/null",
-                revision, co_prefix, repo_path, relative_path );
+                revision, co_prefix, (char *)&repo_path[0], relative_path );
     else
       snprintf( (char *)&cmd[0], 1024,
                 "svn cat %s/%s/%s 2>/dev/null",
-                co_prefix, repo_path, relative_path );
+                co_prefix, (char *)&repo_path[0], relative_path );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
@@ -244,25 +289,34 @@ static void csvn_print_file( struct strbuf *sb, const char *relative_path, int r
 static void csvn_print_image_file( struct strbuf *sb, const char *relative_path, int revision )
 {
   const char *co_prefix = ctx.repo.checkout_ro_prefix;
-  const char *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
 
   if( !sb || !relative_path ) return;
 
   if( co_prefix )
   {
-    char cmd[1024];
+    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 cat --revision %d %s/%s/%s 2>/dev/null",
-                revision, co_prefix, repo_path, relative_path );
+                revision, co_prefix, (char *)&repo_path[0], relative_path );
     else
       snprintf( (char *)&cmd[0], 1024,
                 "svn cat %s/%s/%s 2>/dev/null",
-                co_prefix, repo_path, relative_path );
+                co_prefix, (char *)&repo_path[0], relative_path );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
diff --git a/csvncgi/ui-log.c b/csvncgi/ui-log.c
index d0c17b6..a97dd06 100644
--- a/csvncgi/ui-log.c
+++ b/csvncgi/ui-log.c
@@ -167,11 +167,25 @@ static void xml_csvn_log( struct strbuf *sb, const struct strbuf *buf, const cha
             strbuf_addf( sb, "                  <div class=\"col-cmsg\"><div onclick=\"trunc(this)\" class=\"log-cmsg trunc\">%s</div></div>\n", (char *)cmsg );
             if( query_string && *query_string )
             {
-              strbuf_addf( sb, "                  <div class=\"col-rev\"><div onclick=\"trunc(this)\" class=\"log-rev trunc\"><a title=\"Compare with Previous\" href=\"/%s%s/?op=diff&rev=%s&%s\">%s</a></div></div>\n", ctx.repo.name, path, (char *)revision, query_string, (char *)revision );
+              if( ctx.repo.repo_root && *ctx.repo.repo_root )
+              {
+                strbuf_addf( sb, "                  <div class=\"col-rev\"><div onclick=\"trunc(this)\" class=\"log-rev trunc\"><a title=\"Compare with Previous\" href=\"/%s/%s%s/?op=diff&rev=%s&%s\">%s</a></div></div>\n", ctx.repo.repo_root, ctx.repo.name, path, (char *)revision, query_string, (char *)revision );
+              }
+              else
+              {
+                strbuf_addf( sb, "                  <div class=\"col-rev\"><div onclick=\"trunc(this)\" class=\"log-rev trunc\"><a title=\"Compare with Previous\" href=\"/%s%s/?op=diff&rev=%s&%s\">%s</a></div></div>\n", ctx.repo.name, path, (char *)revision, query_string, (char *)revision );
+              }
             }
             else
             {
-              strbuf_addf( sb, "                  <div class=\"col-rev\"><div onclick=\"trunc(this)\" class=\"log-rev trunc\"><a title=\"Compare with Previous\" href=\"/%s%s/?op=diff&rev=%s\">%s</a></div></div>\n", ctx.repo.name, path, (char *)revision, (char *)revision );
+              if( ctx.repo.repo_root && *ctx.repo.repo_root )
+              {
+                strbuf_addf( sb, "                  <div class=\"col-rev\"><div onclick=\"trunc(this)\" class=\"log-rev trunc\"><a title=\"Compare with Previous\" href=\"/%s/%s%s/?op=diff&rev=%s\">%s</a></div></div>\n", ctx.repo.repo_root, path, ctx.repo.name, path, (char *)revision, (char *)revision );
+              }
+              else
+              {
+                strbuf_addf( sb, "                  <div class=\"col-rev\"><div onclick=\"trunc(this)\" class=\"log-rev trunc\"><a title=\"Compare with Previous\" href=\"/%s%s/?op=diff&rev=%s\">%s</a></div></div>\n", ctx.repo.name, path, (char *)revision, (char *)revision );
+              }
             }
             strbuf_addf( sb, "                  <div class=\"col-author\"><div onclick=\"trunc(this)\" class=\"log-author trunc\">%s</div></div>\n", (char *)author );
             strbuf_addf( sb, "                </div>\n\n" );
@@ -215,9 +229,27 @@ static void xml_csvn_log( struct strbuf *sb, const struct strbuf *buf, const cha
         if( prev < 0 ) prev = 0;
 
         if( ctx.env.query_string && *ctx.env.query_string )
-          strbuf_addf( sb, "                      <a href=\"/%s%s/?ofs=%d&%s\">&#x226a;&nbsp; Prev</a>\n", ctx.repo.name, path, prev, ctx.env.query_string );
+        {
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/?ofs=%d&%s\">&#x226a;&nbsp; Prev</a>\n", ctx.repo.repo_root, ctx.repo.name, path, prev, ctx.env.query_string );
+          }
+          else
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s%s/?ofs=%d&%s\">&#x226a;&nbsp; Prev</a>\n", ctx.repo.name, path, prev, ctx.env.query_string );
+          }
+        }
         else
-          strbuf_addf( sb, "                      <a href=\"/%s%s/?ofs=%d\">&#x226a;&nbsp; Prev</a>\n", ctx.repo.name, path, prev );
+        {
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/?ofs=%d\">&#x226a;&nbsp; Prev</a>\n", ctx.repo.repo_root, ctx.repo.name, path, prev );
+          }
+          else
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s%s/?ofs=%d\">&#x226a;&nbsp; Prev</a>\n", ctx.repo.name, path, prev );
+          }
+        }
       }
 
       strbuf_addf( sb, "                    </div>\n" );
@@ -230,9 +262,27 @@ static void xml_csvn_log( struct strbuf *sb, const struct strbuf *buf, const cha
         next = ctx.query.ofs + page_size;
 
         if( ctx.env.query_string && *ctx.env.query_string )
-          strbuf_addf( sb, "                      <a href=\"/%s%s/?ofs=%d&%s\">Next &nbsp;&#x226b;</a>\n", ctx.repo.name, path, next, ctx.env.query_string );
+        {
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/?ofs=%d&%s\">Next &nbsp;&#x226b;</a>\n", ctx.repo.repo_root, ctx.repo.name, path, next, ctx.env.query_string );
+          }
+          else
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s%s/?ofs=%d&%s\">Next &nbsp;&#x226b;</a>\n", ctx.repo.name, path, next, ctx.env.query_string );
+          }
+        }
         else
-          strbuf_addf( sb, "                      <a href=\"/%s%s/?ofs=%d\">Next &nbsp;&#x226b;</a>\n", ctx.repo.name, path, next );
+        {
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/?ofs=%d\">Next &nbsp;&#x226b;</a>\n", ctx.repo.repo_root, ctx.repo.name, path, next );
+          }
+          else
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s%s/?ofs=%d\">Next &nbsp;&#x226b;</a>\n", ctx.repo.name, path, next );
+          }
+        }
       }
 
       strbuf_addf( sb, "                    </div>\n" );
@@ -254,7 +304,8 @@ static void xml_csvn_log( struct strbuf *sb, const struct strbuf *buf, const cha
 static void csvn_print_log( struct strbuf *sb, const char *relative_path, int revision )
 {
   const char *co_prefix = ctx.repo.checkout_ro_prefix;
-  const char *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
   char *path = NULL;
 
   if( !sb ) return;
@@ -273,19 +324,27 @@ static void csvn_print_log( struct strbuf *sb, const char *relative_path, int re
 
   if( co_prefix )
   {
-    char cmd[1024];
+    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 log --revision %d:0 --xml %s/%s%s 2>/dev/null",
-                revision, co_prefix, repo_path, path );
+                revision, co_prefix, (char *)&repo_path[0], path );
     else
       snprintf( (char *)&cmd[0], 1024,
                 "svn log --xml %s/%s%s 2>/dev/null",
-                co_prefix, repo_path, path );
+                co_prefix, (char *)&repo_path[0], path );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
diff --git a/csvncgi/ui-repolist.c b/csvncgi/ui-repolist.c
index 44795c2..728f658 100644
--- a/csvncgi/ui-repolist.c
+++ b/csvncgi/ui-repolist.c
@@ -132,8 +132,10 @@ void csvn_print_section_stop( struct strbuf *sb )
 
 void csvn_print_repo( struct strbuf *sb, struct repo *repo )
 {
-  struct variable owner       = { (unsigned char *)"owner", { 0 }, DT_STRING },
+  struct variable repo_root   = { (unsigned char *)"repo-root",   { 0 }, DT_PATH   },
+                  owner       = { (unsigned char *)"owner",       { 0 }, DT_STRING },
                   description = { (unsigned char *)"description", { 0 }, DT_STRING };
+  struct variable *rroot = NULL;
   struct variable *auth = NULL;
   struct variable *desc = NULL;
 
@@ -144,12 +146,16 @@ void csvn_print_repo( struct strbuf *sb, struct repo *repo )
 
   if( !sb || !repo || !repo->path ) return;
 
-  auth = lookup( repo, &owner );
-  desc = lookup( repo, &description );
-  time = csvn_repo_last_changed_time( repo );
+  rroot = lookup( repo, &repo_root );
+  auth  = lookup( repo, &owner );
+  desc  = lookup( repo, &description );
+  time  = csvn_repo_last_changed_time( repo );
 
   strbuf_addf( sb, "                <div class=\"row\">\n" );
-  strbuf_addf( sb, "                  <div class=\"col-name\"><a href=\"/%s/\"><div class=\"repo-name\">%s</div></a></div>\n", repo->path, repo->path );
+  if( rroot )
+    strbuf_addf( sb, "                  <div class=\"col-name\"><a href=\"/%s/%s/\"><div class=\"repo-name\">%s</div></a></div>\n", (const char *)rroot->_v.vptr, repo->path, repo->path );
+  else
+    strbuf_addf( sb, "                  <div class=\"col-name\"><a href=\"/%s/\"><div class=\"repo-name\">%s</div></a></div>\n", repo->path, repo->path );
 
   if( desc )
     strbuf_addf( sb, "                  <div class=\"col-desc\"><div onclick=\"trunc(this)\" class=\"repo-desc trunc\">%s</div></div>\n", (const char *)desc->_v.vptr );
diff --git a/csvncgi/ui-shared.c b/csvncgi/ui-shared.c
index e34141e..1079486 100644
--- a/csvncgi/ui-shared.c
+++ b/csvncgi/ui-shared.c
@@ -406,23 +406,32 @@ static void xml_csvn_info( struct strbuf *sb, struct csvn_info *info )
 void csvn_repo_info( struct csvn_info *info, int revision )
 {
   const char *co_prefix = ctx.repo.checkout_ro_prefix;
-  const char *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
 
   if( co_prefix )
   {
-    char cmd[1024];
+    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 info --revision %d --xml %s/%s/ 2>/dev/null",
-                revision, co_prefix, repo_path );
+                revision, co_prefix, (char *)&repo_path[0] );
     else
       snprintf( (char *)&cmd[0], 1024,
                 "svn info --xml %s/%s/ 2>/dev/null",
-                co_prefix, repo_path );
+                co_prefix, (char *)&repo_path[0] );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
@@ -650,25 +659,34 @@ static const char *mime_info( struct csvn_info *info, const char *buffer, size_t
 void csvn_rpath_mime_info( struct csvn_info *info, const char *relative_path, int revision )
 {
   const char *co_prefix = ctx.repo.checkout_ro_prefix;
-  const char *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
 
   if( !info || !relative_path ) return;
 
   if( co_prefix )
   {
-    char cmd[1024];
+    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 cat --revision %d %s/%s/%s 2>/dev/null | tr -d '\\0' | head -c 1024",
-                revision, co_prefix, repo_path, relative_path );
+                revision, co_prefix, (char *)&repo_path[0], relative_path );
     else
       snprintf( (char *)&cmd[0], 1024,
                 "svn cat %s/%s/%s 2>/dev/null | tr -d '\\0' | head -c 1024",
-                co_prefix, repo_path, relative_path );
+                co_prefix, (char *)&repo_path[0], relative_path );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
@@ -689,25 +707,34 @@ void csvn_rpath_mime_info( struct csvn_info *info, const char *relative_path, in
 void csvn_rpath_info( struct csvn_info *info, const char *relative_path, int revision )
 {
   const char *co_prefix = ctx.repo.checkout_ro_prefix;
-  const char *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
 
   if( !info || !relative_path ) return;
 
   if( co_prefix )
   {
-    char cmd[1024];
+    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 info --revision %d --xml %s/%s/%s 2>/dev/null",
-                revision, co_prefix, repo_path, relative_path );
+                revision, co_prefix, (char *)&repo_path[0], relative_path );
     else
       snprintf( (char *)&cmd[0], 1024,
                 "svn info --xml %s/%s/%s 2>/dev/null",
-                co_prefix, repo_path, relative_path );
+                co_prefix, (char *)&repo_path[0], relative_path );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
diff --git a/csvncgi/ui-tree.c b/csvncgi/ui-tree.c
index bc46535..007abc2 100644
--- a/csvncgi/ui-tree.c
+++ b/csvncgi/ui-tree.c
@@ -147,7 +147,8 @@ static struct dlist *tree_lines_sort_bytag( struct dlist *lines )
 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_path = ctx.repo.name;
+  const char *repo_name = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
   const char *ret  = "";
   char *path = NULL;
 
@@ -167,19 +168,27 @@ static const char *is_file_executable( const char *relative_path, const char *na
 
   if( co_prefix )
   {
-    char cmd[1024];
+    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, repo_path, path, name );
+                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, repo_path, path, name );
+                co_prefix, (char *)&repo_path[0], path, name );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
@@ -432,9 +441,27 @@ static void dlist_csvn_tree( struct strbuf *sb, const char *relative_path, int r
 
         strbuf_addf( sb, "                <div class=\"row\">\n" );
         if( ctx.env.query_string && *ctx.env.query_string )
-          strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s%s/%s/?%s\"><div class=\"tree-path dir\">%s/</div></a></div>\n", ctx.repo.name, path, line->name, ctx.env.query_string, line->name );
+        {
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s/%s%s/%s/?%s\"><div class=\"tree-path dir\">%s/</div></a></div>\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, ctx.env.query_string, line->name );
+          }
+          else
+          {
+            strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s%s/%s/?%s\"><div class=\"tree-path dir\">%s/</div></a></div>\n", ctx.repo.name, path, line->name, ctx.env.query_string, line->name );
+          }
+        }
         else
-          strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s%s/%s/\"><div class=\"tree-path dir\">%s/</div></a></div>\n", ctx.repo.name, path, line->name, line->name );
+        {
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s/%s%s/%s/\"><div class=\"tree-path dir\">%s/</div></a></div>\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->name );
+          }
+          else
+          {
+            strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s%s/%s/\"><div class=\"tree-path dir\">%s/</div></a></div>\n", ctx.repo.name, path, line->name, line->name );
+          }
+        }
         strbuf_addf( sb, "                  <div class=\"col-size\"><div onclick=\"trunc(this)\" class=\"tree-size trunc\">4096</div></div>\n" );
         strbuf_addf( sb, "                  <div class=\"col-rev\"><div onclick=\"trunc(this)\" class=\"tree-rev trunc\">%s</div></div>\n", line->revision );
         if( time != -1 )
@@ -451,13 +478,29 @@ static void dlist_csvn_tree( struct strbuf *sb, const char *relative_path, int r
         strbuf_addf( sb, "                    <div onclick=\"trunc(this)\" class=\"tree-links trunc\">\n" );
         if( query_string && *query_string )
         {
-          strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=log&rev=%s&%s\">log</a> &nbsp;\n", ctx.repo.name, path, line->name, line->revision, query_string );
-          strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=diff&rev=%s&%s\">diff</a>\n", ctx.repo.name, path, line->name, line->revision, query_string );
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/%s/?op=log&rev=%s&%s\">log</a> &nbsp;\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->revision, query_string );
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/%s/?op=diff&rev=%s&%s\">diff</a>\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->revision, query_string );
+          }
+          else
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=log&rev=%s&%s\">log</a> &nbsp;\n", ctx.repo.name, path, line->name, line->revision, query_string );
+            strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=diff&rev=%s&%s\">diff</a>\n", ctx.repo.name, path, line->name, line->revision, query_string );
+          }
         }
         else
         {
-          strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=log&rev=%s\">log</a> &nbsp;\n", ctx.repo.name, path, line->name, line->revision );
-          strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=diff&rev=%s\">diff</a>\n", ctx.repo.name, path, line->name, line->revision );
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/%s/?op=log&rev=%s\">log</a> &nbsp;\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->revision );
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/%s/?op=diff&rev=%s\">diff</a>\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->revision );
+          }
+          else
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=log&rev=%s\">log</a> &nbsp;\n", ctx.repo.name, path, line->name, line->revision );
+            strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=diff&rev=%s\">diff</a>\n", ctx.repo.name, path, line->name, line->revision );
+          }
         }
         strbuf_addf( sb, "                    </div>\n" );
         strbuf_addf( sb, "                  </div>\n" );
@@ -492,9 +535,27 @@ static void dlist_csvn_tree( struct strbuf *sb, const char *relative_path, int r
 
         strbuf_addf( sb, "                <div class=\"row\">\n" );
         if( ctx.env.query_string && *ctx.env.query_string )
-          strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s%s/%s/?%s\"><div class=\"tree-path file%s\">%s</div></a></div>\n", ctx.repo.name, path, line->name, ctx.env.query_string, exec, line->name );
+        {
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s/%s%s/%s/?%s\"><div class=\"tree-path file%s\">%s</div></a></div>\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, ctx.env.query_string, exec, line->name );
+          }
+          else
+          {
+            strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s%s/%s/?%s\"><div class=\"tree-path file%s\">%s</div></a></div>\n", ctx.repo.name, path, line->name, ctx.env.query_string, exec, line->name );
+          }
+        }
         else
-          strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s%s/%s/\"><div class=\"tree-path file%s\">%s</div></a></div>\n", ctx.repo.name, path, line->name, exec, line->name );
+        {
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s/%s%s/%s/\"><div class=\"tree-path file%s\">%s</div></a></div>\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, exec, line->name );
+          }
+          else
+          {
+            strbuf_addf( sb, "                  <div class=\"col-path\"><a href=\"/%s%s/%s/\"><div class=\"tree-path file%s\">%s</div></a></div>\n", ctx.repo.name, path, line->name, exec, line->name );
+          }
+        }
         strbuf_addf( sb, "                  <div class=\"col-size\"><div onclick=\"trunc(this)\" class=\"tree-size trunc\">%s</div></div>\n", line->size );
         strbuf_addf( sb, "                  <div class=\"col-rev\"><div onclick=\"trunc(this)\" class=\"tree-rev trunc\">%s</div></div>\n", line->revision );
         if( time != -1 )
@@ -511,15 +572,33 @@ static void dlist_csvn_tree( struct strbuf *sb, const char *relative_path, int r
         strbuf_addf( sb, "                    <div onclick=\"trunc(this)\" class=\"tree-links trunc\">\n" );
         if( query_string && *query_string )
         {
-          strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=log&rev=%s&%s\">log</a> &nbsp;\n", ctx.repo.name, path, line->name, line->revision, query_string );
-          strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=diff&rev=%s&%s\">diff &nbsp;</a>\n", ctx.repo.name, path, line->name, line->revision, query_string );
-          strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=blame&rev=%s&%s\">blame</a>\n", ctx.repo.name, path, line->name, line->revision, query_string );
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/%s/?op=log&rev=%s&%s\">log</a> &nbsp;\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->revision, query_string );
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/%s/?op=diff&rev=%s&%s\">diff &nbsp;</a>\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->revision, query_string );
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/%s/?op=blame&rev=%s&%s\">blame</a>\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->revision, query_string );
+          }
+          else
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=log&rev=%s&%s\">log</a> &nbsp;\n", ctx.repo.name, path, line->name, line->revision, query_string );
+            strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=diff&rev=%s&%s\">diff &nbsp;</a>\n", ctx.repo.name, path, line->name, line->revision, query_string );
+            strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=blame&rev=%s&%s\">blame</a>\n", ctx.repo.name, path, line->name, line->revision, query_string );
+          }
         }
         else
         {
-          strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=log&rev=%s\">log</a> &nbsp;\n", ctx.repo.name, path, line->name, line->revision );
-          strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=diff&rev=%s\">diff</a> &nbsp;\n", ctx.repo.name, path, line->name, line->revision );
-          strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=blame&rev=%s\">blame</a>\n", ctx.repo.name, path, line->name, line->revision );
+          if( ctx.repo.repo_root && *ctx.repo.repo_root )
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/%s/?op=log&rev=%s\">log</a> &nbsp;\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->revision );
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/%s/?op=diff&rev=%s\">diff</a> &nbsp;\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->revision );
+            strbuf_addf( sb, "                      <a href=\"/%s/%s%s/%s/?op=blame&rev=%s\">blame</a>\n", ctx.repo.repo_root, ctx.repo.name, path, line->name, line->revision );
+          }
+          else
+          {
+            strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=log&rev=%s\">log</a> &nbsp;\n", ctx.repo.name, path, line->name, line->revision );
+            strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=diff&rev=%s\">diff</a> &nbsp;\n", ctx.repo.name, path, line->name, line->revision );
+            strbuf_addf( sb, "                      <a href=\"/%s%s/%s/?op=blame&rev=%s\">blame</a>\n", ctx.repo.name, path, line->name, line->revision );
+          }
         }
         strbuf_addf( sb, "                    </div>\n" );
         strbuf_addf( sb, "                  </div>\n" );
@@ -539,7 +618,8 @@ static void dlist_csvn_tree( struct strbuf *sb, const char *relative_path, int r
 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 *repo_path = ctx.repo.name;
+  const char *name      = ctx.repo.name;
+  const char *repo_root = ctx.repo.repo_root;
   char *path = NULL;
 
   if( !sb ) return;
@@ -558,19 +638,27 @@ static void csvn_print_tree( struct strbuf *sb, const char *relative_path, int r
 
   if( co_prefix )
   {
-    char cmd[1024];
+    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, repo_path, path );
+                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, repo_path, path );
+                co_prefix, (char *)&repo_path[0], path );
     p = sys_exec_command( &buf, cmd );
     rc = sys_wait_command( p, NULL );
     if( rc != 0 )
@@ -615,9 +703,27 @@ static void csvn_print_checkout_urls( struct strbuf *sb, const char *relative_pa
   strbuf_addf( sb, "                      <div class=\"checkout-perms\">ro:</div>\n" );
 
   if( revision )
-    strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout --revision %d %s/%s%s</div>\n", revision, ctx.repo.checkout_ro_prefix, ctx.repo.name, path );
+  {
+    if( ctx.repo.repo_root && *ctx.repo.repo_root )
+    {
+      strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout --revision %d %s/%s/%s%s</div>\n", revision, ctx.repo.checkout_ro_prefix, ctx.repo.repo_root, ctx.repo.name, path );
+    }
+    else
+    {
+      strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout --revision %d %s/%s%s</div>\n", revision, ctx.repo.checkout_ro_prefix, ctx.repo.name, path );
+    }
+  }
   else
-    strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout %s/%s%s</div>\n", ctx.repo.checkout_ro_prefix, ctx.repo.name, path );
+  {
+    if( ctx.repo.repo_root && *ctx.repo.repo_root )
+    {
+      strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout %s/%s/%s%s</div>\n", ctx.repo.checkout_ro_prefix, ctx.repo.repo_root, ctx.repo.name, path );
+    }
+    else
+    {
+      strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout %s/%s%s</div>\n", ctx.repo.checkout_ro_prefix, ctx.repo.name, path );
+    }
+  }
 
   strbuf_addf( sb, "                    </div>\n" );
   if( ctx.repo.checkout_prefix && *ctx.repo.checkout_prefix )
@@ -626,9 +732,27 @@ static void csvn_print_checkout_urls( struct strbuf *sb, const char *relative_pa
     strbuf_addf( sb, "                      <div class=\"checkout-perms\">rw:</div>\n" );
 
     if( revision )
-      strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout --revision %d %s/%s%s</div>\n", revision, ctx.repo.checkout_prefix, ctx.repo.name, path );
+    {
+      if( ctx.repo.repo_root && *ctx.repo.repo_root )
+      {
+        strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout --revision %d %s/%s/%s%s</div>\n", revision, ctx.repo.checkout_prefix, ctx.repo.repo_root, ctx.repo.name, path );
+      }
+      else
+      {
+        strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout --revision %d %s/%s%s</div>\n", revision, ctx.repo.checkout_prefix, ctx.repo.name, path );
+      }
+    }
     else
-      strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout %s/%s%s</div>\n", ctx.repo.checkout_prefix, ctx.repo.name, path );
+    {
+      if( ctx.repo.repo_root && *ctx.repo.repo_root )
+      {
+        strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout %s/%s/%s%s</div>\n", ctx.repo.checkout_prefix, ctx.repo.repo_root, ctx.repo.name, path );
+      }
+      else
+      {
+        strbuf_addf( sb, "                      <div class=\"checkout-url\">svn checkout %s/%s%s</div>\n", ctx.repo.checkout_prefix, ctx.repo.name, path );
+      }
+    }
 
     strbuf_addf( sb, "                    </div>\n" );
   }