Index: Makefile.in
==================================================================
--- Makefile.in
+++ Makefile.in
@@ -37,15 +37,15 @@
 #    care about testing the end result, this can be blank.
 #
 TCLSH = tclsh
 
 LIB =	@LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
-TCC +=	@EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H -Dstrcmp=fossil_strcmp
+TCC +=	@EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H
 INSTALLDIR = $(DESTDIR)@prefix@/bin
 USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
 FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@
 FOSSIL_ENABLE_TCL_STUBS = @FOSSIL_ENABLE_TCL_STUBS@
 
 include $(SRCDIR)/main.mk
 
 distclean: clean
 	rm -f autoconfig.h config.log Makefile

Index: auto.def
==================================================================
--- auto.def
+++ auto.def
@@ -233,8 +233,9 @@
 # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
 if {![cc-check-functions getpassphrase]} {
     # Haiku needs this
     cc-check-function-in-lib getpass bsd
 }
+cc-check-function-in-lib dlopen dl
 
 make-template Makefile.in
 make-config-header autoconfig.h -auto {USE_* FOSSIL_*}

Index: compat/zlib/configure
==================================================================
--- compat/zlib/configure
+++ compat/zlib/configure
@@ -814,5 +814,6 @@
 /^mandir *=/s#=.*#=$mandir#
 /^LDFLAGS *=/s#=.*#=$LDFLAGS#
 " | sed -e "
 s/\@VERSION\@/$VER/g;
 " > zlib.pc
+#

Index: src/blob.c
==================================================================
--- src/blob.c
+++ src/blob.c
@@ -1104,26 +1104,21 @@
     blob_zero(&temp);
     blob_append(&temp, zUtf8, -1);
     blob_swap(pBlob, &temp);
     blob_reset(&temp);
 #ifdef _WIN32
-  }else if( starts_with_utf16le_bom(pBlob, &bomSize) ){
-    /* Make sure the blob contains two terminating 0-bytes */
-    blob_append(pBlob, "", 1);
-    zUtf8 = blob_str(pBlob) + bomSize;
-    zUtf8 = fossil_unicode_to_utf8(zUtf8);
-    blob_zero(pBlob);
-    blob_append(pBlob, zUtf8, -1);
-    fossil_unicode_free(zUtf8);
-  }else if( starts_with_utf16be_bom(pBlob, &bomSize) ){
-    unsigned int i = blob_size(pBlob);
+  }else if( starts_with_utf16_bom(pBlob, &bomSize) ){
     zUtf8 = blob_buffer(pBlob);
-    while( i > 0 ){
-      /* swap bytes of unicode representation */
-      char zTemp = zUtf8[--i];
-      zUtf8[i] = zUtf8[i-1];
-      zUtf8[--i] = zTemp;
+    if (*((unsigned short *)zUtf8) == 0xfffe) {
+      /* Found BOM, but with reversed bytes */
+      unsigned int i = blob_size(pBlob);
+      while( i > 0 ){
+        /* swap bytes of unicode representation */
+        char zTemp = zUtf8[--i];
+        zUtf8[i] = zUtf8[i-1];
+        zUtf8[--i] = zTemp;
+      }
     }
     /* Make sure the blob contains two terminating 0-bytes */
     blob_append(pBlob, "", 1);
     zUtf8 = blob_str(pBlob) + bomSize;
     zUtf8 = fossil_unicode_to_utf8(zUtf8);

Index: src/checkin.c
==================================================================
--- src/checkin.c
+++ src/checkin.c
@@ -616,30 +616,36 @@
 ** of the array.
 **
 ** If there were no arguments passed to [commit], aCommitFile is not
 ** allocated and remains NULL. Other parts of the code interpret this
 ** to mean "all files".
+**
+** Returns 1 if there was a warning, 0 otherwise.
 */
-void select_commit_files(void){
+int select_commit_files(void){
+  int result = 0;
   if( g.argc>2 ){
-    int ii;
+    int ii, jj=0;
     Blob b;
     blob_zero(&b);
     g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1));
 
     for(ii=2; ii<g.argc; ii++){
       int iId;
       file_tree_name(g.argv[ii], &b, 1);
       iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b));
       if( iId<0 ){
-        fossil_fatal("fossil knows nothing about: %s", g.argv[ii]);
+        fossil_warning("fossil knows nothing about: %s", g.argv[ii]);
+        result = 1;
+      } else {
+        g.aCommitFile[jj++] = iId;
       }
-      g.aCommitFile[ii-2] = iId;
       blob_reset(&b);
     }
-    g.aCommitFile[ii-2] = 0;
+    g.aCommitFile[jj] = 0;
   }
+  return result;
 }
 
 /*
 ** Make sure the current check-in with timestamp zDate is younger than its
 ** ancestor identified rid and zUuid.  Throw a fatal error if not.
@@ -1199,11 +1205,16 @@
   ** After the following function call has returned, the Global.aCommitFile[]
   ** array is allocated to contain the "id" field from the vfile table
   ** for each file to be committed. Or, if aCommitFile is NULL, all files
   ** should be committed.
   */
-  select_commit_files();
+  if ( select_commit_files() ){
+    blob_zero(&ans);
+    prompt_user("continue (y/N)? ", &ans);
+    cReply = blob_str(&ans)[0];
+    if( cReply!='y' && cReply!='Y' ) fossil_exit(1);;
+  }
   isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0");
   if( g.aCommitFile && isAMerge ){
     fossil_fatal("cannot do a partial commit of a merge");
   }
 

Index: src/cson_amalgamation.c
==================================================================
--- src/cson_amalgamation.c
+++ src/cson_amalgamation.c
@@ -4349,11 +4349,11 @@
     {
         return 0;
     }
     else
     {
-        unsigned char * x = (unsigned char *)realloc( buf->mem, n );
+        unsigned char * x = (unsigned char *)cson_realloc( buf->mem, n, "cson_buffer::mem" );
         if( ! x ) return cson_rc.AllocError;
         memset( x + buf->used, 0, n - buf->used );
         buf->mem = x;
         buf->capacity = n;
         ++buf->timesExpanded;

Index: src/diff.c
==================================================================
--- src/diff.c
+++ src/diff.c
@@ -359,53 +359,19 @@
 ** This function returns non-zero if the blob starts with a UTF-16le or
 ** UTF-16be byte-order-mark (BOM).
 */
 int starts_with_utf16_bom(const Blob *pContent, int *pnByte){
   const char *z = blob_buffer(pContent);
-  int c1, c2;
+  int c1;
 
   if( pnByte ) *pnByte = 2;
-  if( blob_size(pContent)<2 ) return 0;
-  c1 = z[0]; c2 = z[1];
-  if( (c1==(char)0xff) && (c2==(char)0xfe) ){
-    return 1;
-  }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){
-    return 1;
-  }
-  return 0;
-}
-
-/*
-** This function returns non-zero if the blob starts with a UTF-16le
-** byte-order-mark (BOM).
-*/
-int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){
-  const char *z = blob_buffer(pContent);
-  int c1, c2;
-
-  if( pnByte ) *pnByte = 2;
-  if( blob_size(pContent)<2 ) return 0;
-  c1 = z[0]; c2 = z[1];
-  if( (c1==(char)0xff) && (c2==(char)0xfe) ){
-    return 1;
-  }
-  return 0;
-}
-
-/*
-** This function returns non-zero if the blob starts with a UTF-16be
-** byte-order-mark (BOM).
-*/
-int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){
-  const char *z = blob_buffer(pContent);
-  int c1, c2;
-
-  if( pnByte ) *pnByte = 2;
-  if( blob_size(pContent)<2 ) return 0;
-  c1 = z[0]; c2 = z[1];
-  if( (c1==(char)0xfe) && (c2==(char)0xff) ){
-    return 1;
+  if( (blob_size(pContent)<2) || (blob_size(pContent)&1)) return 0;
+  c1 = ((unsigned short *)z)[0];
+  if( (c1==0xfeff) || (c1==0xfffe) ){
+    if( blob_size(pContent) < 4 ) return 1;
+    c1 = ((unsigned short *)z)[1];
+    if( c1 != 0 ) return 1;
   }
   return 0;
 }
 
 /*

Index: src/http.c
==================================================================
--- src/http.c
+++ src/http.c
@@ -130,11 +130,11 @@
 **
 ** The server address is contain in the "g" global structure.  The
 ** url_parse() routine should have been called prior to this routine
 ** in order to fill this structure appropriately.
 */
-int http_exchange(Blob *pSend, Blob *pReply, int useLogin){
+int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){
   Blob login;           /* The login card */
   Blob payload;         /* The complete payload including login card */
   Blob hdr;             /* The HTTP request header */
   int closeConnection;  /* True to close the connection when done */
   int iLength;          /* Length of the reply payload */
@@ -231,10 +231,14 @@
       }else if( c=='k' || c=='K' ){
         closeConnection = 0;
       }
     }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){
       int i, j;
+
+      if ( --maxRedirect == 0){
+        fossil_fatal("redirect limit exceeded");
+      }
       for(i=9; zLine[i] && zLine[i]==' '; i++){}
       if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine);
       j = strlen(zLine) - 1; 
       while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){
          j -= 4;
@@ -241,11 +245,11 @@
          zLine[j] = 0;
       }
       fossil_print("redirect to %s\n", &zLine[i]);
       url_parse(&zLine[i]);
       transport_close();
-      return http_exchange(pSend, pReply, useLogin);
+      return http_exchange(pSend, pReply, useLogin, maxRedirect);
     }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
       if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
         isCompressed = 0;
       }else if( fossil_strnicmp(&zLine[14], 
                           "application/x-fossil-uncompressed", -1)==0 ){

Index: src/http_transport.c
==================================================================
--- src/http_transport.c
+++ src/http_transport.c
@@ -183,10 +183,11 @@
     ** and run an SSH command to talk to the remote machine.
     */
     const char *zSsh;  /* The base SSH command */
     Blob zCmd;         /* The SSH command */
     char *zHost;       /* The host name to contact */
+    int n;             /* Size of prefix string */
 
     zSsh = db_get("ssh-command", zDefaultSshCmd);
     blob_init(&zCmd, zSsh, -1);
     if( g.urlPort!=g.urlDfltPort ){
 #ifdef __MINGW32__
@@ -219,22 +220,63 @@
       }
 #endif
     }else{
       zHost = mprintf("%s", g.urlName);
     }
+    n = blob_size(&zCmd);
     blob_append(&zCmd, " ", 1);
     shell_escape(&zCmd, zHost);
-    fossil_print(" %s\n", zHost);  /* Show the conclusion of the SSH command */
+    if( g.urlShell ){
+      blob_appendf(&zCmd, " %s", g.urlShell);
+    }else{
+#if defined(FOSSIL_ENABLE_SSH_FAR_SIDE)
+      /* The following works.  But only if the fossil on the remote side
+      ** is recent enough to support the test-ssh-far-side command.  That
+      ** command was added on 2013-02-06.  We will leave this turned off
+      ** until most fossil servers have upgraded to that version or a later
+      ** version.  The sync will still work as long as the shell on the far
+      ** side is bash and not tcsh.  And if the default far side shell is
+      ** tcsh, then the shell=/bin/bash query parameter can be used as a
+      ** work-around.  Enable this code after about a year...
+      */
+      blob_appendf(&zCmd, " exec %s test-ssh-far-side", g.urlFossil);
+#endif
+    }
+    fossil_print("%s\n", blob_str(&zCmd)+n);  /* Show tail of SSH command */
     free(zHost);
     popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
     if( sshPid==0 ){
       fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
     }
     blob_reset(&zCmd);
     transport_ssh_startup();
   }
 }
+
+/*
+** COMMAND: test-ssh-far-side
+**
+** Read lines of input text, one by one, and evaluate each line using
+** system().  The ssh: sync protocol uses this on the far side of the
+** SSH link.
+*/
+void test_ssh_far_side_cmd(void){
+  int i = 0;
+  int got;
+  char zLine[5000];
+  while( i<sizeof(zLine) ){
+    got = read(0, zLine+i, 1);
+    if( got==0 ) return;
+    if( zLine[i]=='\n' ){
+      zLine[i] = 0;
+      system(zLine);
+      i = 0;
+    }else{
+      i++;
+    }
+  }
+}
 
 /*
 ** Open a connection to the server.  The server is defined by the following
 ** global variables:
 **
@@ -251,13 +293,13 @@
       Blob cmd;
       blob_zero(&cmd);
       shell_escape(&cmd, g.urlFossil);
       blob_append(&cmd, " test-http ", -1);
       shell_escape(&cmd, g.urlPath);
-      /* printf("%s\n", blob_str(&cmd)); fflush(stdout); */
-      fprintf(sshOut, "%s\n", blob_str(&cmd));
+      fprintf(sshOut, "%s || true\n", blob_str(&cmd));
       fflush(sshOut);
+      if( g.fSshTrace ) printf("Sent: [%s]\n", blob_str(&cmd));
       blob_reset(&cmd);
     }else if( g.urlIsHttps ){
       #ifdef FOSSIL_ENABLE_SSL
       rc = ssl_open();
       if( rc==0 ) transport.isOpen = 1;
@@ -403,11 +445,10 @@
   int got;
   if( sshIn ){
     int x;
     int wanted = N;
     got = 0;
-    /* printf("want %d bytes...\n", wanted); fflush(stdout); */
     while( wanted>0 ){
       x = read(sshIn, &zBuf[got], wanted);
       if( x<=0 ) break;
       got += x;
       wanted -= x;
@@ -438,11 +479,14 @@
 int transport_receive(char *zBuf, int N){
   int onHand;       /* Bytes current held in the transport buffer */
   int nByte = 0;    /* Bytes of content received */
 
   onHand = transport.nUsed - transport.iCursor;
-  /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */
+  if( g.fSshTrace){
+    printf("Reading %d bytes with %d on hand...  ", N, onHand);
+    fflush(stdout);
+  }
   if( onHand>0 ){
     int toMove = onHand;
     if( toMove>N ) toMove = N;
     /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */
     memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove);
@@ -460,10 +504,11 @@
     if( got>0 ){
       nByte += got;
       transport.nRcvd += got;
     }
   }
+  if( g.fSshTrace ) printf("Got %d bytes\n", nByte);
   return nByte;
 }
 
 /*
 ** Load up to N new bytes of content into the transport.pBuf buffer.
@@ -532,11 +577,11 @@
       }
       break;
     }
     i++;
   }
-   /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */
+  if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]);
   return &transport.pBuf[iStart];
 }
 
 void transport_global_shutdown(void){
   if( g.urlIsSsh && sshPid ){

Index: src/json_detail.h
==================================================================
--- src/json_detail.h
+++ src/json_detail.h
@@ -199,11 +199,11 @@
   char const * requestId;
   char const * resultCode;
   char const * resultText;
   char const * timestamp;
 } FossilJsonKeys_;
-const FossilJsonKeys_ FossilJsonKeys;
+extern const FossilJsonKeys_ FossilJsonKeys;
 
 /*
 ** A page/command dispatch helper for fossil_json_f() implementations.
 ** pages must be an array of JsonPageDef commands which we can
 ** dispatch. The final item in the array MUST have a NULL name

Index: src/main.c
==================================================================
--- src/main.c
+++ src/main.c
@@ -135,10 +135,11 @@
   int fSshTrace;          /* Trace the SSH setup traffic */
   int fNoSync;            /* Do not do an autosync ever.  --nosync */
   char *zPath;            /* Name of webpage being served */
   char *zExtra;           /* Extra path information past the webpage name */
   char *zBaseURL;         /* Full text of the URL being served */
+  const char *zAltBase;   /* Alternative URL root.  --baseurl */
   char *zTop;             /* Parent directory of zPath */
   const char *zContentType;  /* The content type of the input HTTP request */
   int iErrPriority;       /* Priority of current error message */
   char *zErrMsg;          /* Text of an error message */
   int sslNotAvailable;    /* SSL is not available.  Do not redirect to https: */
@@ -170,11 +171,12 @@
   char *urlPath;          /* Pathname for http: */
   char *urlUser;          /* User id for http: */
   char *urlPasswd;        /* Password for http: */
   char *urlCanonical;     /* Canonical representation of the URL */
   char *urlProxyAuth;     /* Proxy-Authorizer: string */
-  char *urlFossil;        /* The path of the ?fossil=path suffix on ssh: */
+  char *urlFossil;        /* The fossil query parameter on ssh: */
+  char *urlShell;         /* The shell query parameter on ssh: */
   int dontKeepUrl;        /* Do not persist the URL */
 
   const char *zLogin;     /* Login name.  "" if not logged in. */
   const char *zSSLIdentity;  /* Value of --ssl-identity option, filename of SSL client identity */
   int useLocalauth;       /* No login required if from 127.0.0.1 */
@@ -1116,24 +1118,30 @@
 ** Set the g.zBaseURL value to the full URL for the toplevel of
 ** the fossil tree.  Set g.zTop to g.zBaseURL without the
 ** leading "http://" and the host and port.
 **
 ** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME
-** environment variables.  However, if zAltBase is not NULL then it
+** environment variables.  However, if g.zAltBase is not NULL then it
 ** is the argument to the --baseurl option command-line option and
 ** g.zBaseURL and g.zTop is set from that instead.
+**
+** If g.zAltBase is set, then zRepoName may be a repository name (with a
+** leading slash ** but without the .fossil) with is concatenated to the
+** root.
 */
-static void set_base_url(const char *zAltBase){
+static void set_base_url(const char* zRepoName){
   int i;
   const char *zHost;
   const char *zMode;
   const char *zCur;
 
-  if( g.zBaseURL!=0 ) return;
-  if( zAltBase ){
+  fossil_free(g.zBaseURL);
+  if( g.zAltBase ){
     int i, n, c;
-    g.zTop = g.zBaseURL = mprintf("%s", zAltBase);
+    if (!zRepoName) zRepoName = "";
+    if (zRepoName[0] == '/') zRepoName++;
+    g.zTop = g.zBaseURL = mprintf("%s%s", g.zAltBase, zRepoName);
     if( memcmp(g.zTop, "http://", 7)!=0 && memcmp(g.zTop,"https://",8)!=0 ){
       fossil_fatal("argument to --baseurl should be 'http://host/path'"
                    " or 'https://host/path'");
     }
     for(i=n=0; (c = g.zTop[i])!=0; i++){
@@ -1273,10 +1281,11 @@
   zPathInfo = PD("PATH_INFO","");
   if( !g.repositoryOpen ){
     char *zRepo, *zToFree;
     const char *zOldScript = PD("SCRIPT_NAME", "");
     char *zNewScript;
+    char *zRepoName;
     int j, k;
     i64 szFile;
 
     i = zPathInfo[0]!=0;
     while( 1 ){
@@ -1349,10 +1358,14 @@
         }
         return;
       }
       break;
     }
+    /* Rebuild the base URL to add the repository name to the end. */
+    zRepoName = mprintf("%.*s", i, zPathInfo);
+    set_base_url(zRepoName);
+    fossil_free(zRepoName);
     zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo);
     cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
     zPathInfo += i;
     cgi_replace_parameter("SCRIPT_NAME", zNewScript);
     db_open_repository(zRepo);
@@ -1362,18 +1375,19 @@
           "# new PATH_INFO = [%s]\n"
           "# new SCRIPT_NAME = [%s]\n",
           zRepo, zPathInfo, zNewScript);
     }
   }
+  else
+    set_base_url(0);
 
   /* Find the page that the user has requested, construct and deliver that
   ** page.
   */
   if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){
     zPathInfo = "/xfer";
   }
-  set_base_url(0);
   if( zPathInfo==0 || zPathInfo[0]==0
       || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
 #ifdef FOSSIL_ENABLE_JSON
     if(g.json.isJsonMode){
       json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
@@ -1740,11 +1754,10 @@
 */
 void cmd_http(void){
   const char *zIpAddr;
   const char *zNotFound;
   const char *zHost;
-  const char *zAltBase;
   const char *zFileGlob;
 
   /* The winhttp module passes the --files option as --files-urlenc with
   ** the argument being URL encoded, to avoid wildcard expansion in the 
   ** shell.  This option is for internal use and is undocumented.
@@ -1758,27 +1771,27 @@
     zFileGlob = find_option("files",0,1);
   }
   zNotFound = find_option("notfound", 0, 1);
   g.useLocalauth = find_option("localauth", 0, 0)!=0;
   g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
-  zAltBase = find_option("baseurl", 0, 1);
-  if( zAltBase ) set_base_url(zAltBase);
+  g.zAltBase = find_option("baseurl", 0, 1);
+  set_base_url(0);
   if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on");
   zHost = find_option("host", 0, 1);
   if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);
   g.cgiOutput = 1;
+  g.httpIn = stdin;
+  g.httpOut = stdout;
   if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
     fossil_fatal("no repository specified");
   }
   g.fullHttpReply = 1;
   if( g.argc==6 ){
     g.httpIn = fossil_fopen(g.argv[3], "rb");
     g.httpOut = fossil_fopen(g.argv[4], "wb");
     zIpAddr = g.argv[5];
   }else{
-    g.httpIn = stdin;
-    g.httpOut = stdout;
     zIpAddr = 0;
   }
   find_server_repository(0);
   g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
   cgi_handle_http_request(zIpAddr);
@@ -1888,11 +1901,10 @@
   const char *zBrowser;     /* Name of web browser program */
   char *zBrowserCmd = 0;    /* Command to launch the web browser */
   int isUiCmd;              /* True if command is "ui", not "server' */
   const char *zNotFound;    /* The --notfound option or NULL */
   int flags = 0;            /* Server flags */
-  const char *zAltBase;     /* Argument to the --baseurl option */
   const char *zFileGlob;    /* Static content must match this */
 
 #if defined(_WIN32)
   const char *zStopperFile;    /* Name of file used to terminate server */
   zStopperFile = find_option("stopper", 0, 1);
@@ -1904,14 +1916,12 @@
   if( g.thTrace ){
     blob_zero(&g.thLog);
   }
   zPort = find_option("port", "P", 1);
   zNotFound = find_option("notfound", 0, 1);
-  zAltBase = find_option("baseurl", 0, 1);
-  if( zAltBase ){
-    set_base_url(zAltBase);
-  }
+  g.zAltBase = find_option("baseurl", 0, 1);
+  set_base_url(0);
   if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
   isUiCmd = g.argv[1][0]=='u';
   if( isUiCmd ){
     flags |= HTTP_SERVER_LOCALHOST;
     g.useLocalauth = 1;

Index: src/rss.c
==================================================================
--- src/rss.c
+++ src/rss.c
@@ -22,18 +22,37 @@
 #include "rss.h"
 #include <assert.h>
 
 /*
 ** WEBPAGE: timeline.rss
+** URL:  /timeline.rss/y=TYPE&n=LIMIT&tkt=UUID&tag=TAG&wiki=NAME&name=FILENAME
+**
+** Produce an RSS feed of the timeline.
+**
+** TYPE may be: all, ci (show checkins only), t (show tickets only),
+** w (show wiki only). LIMIT is the number of items to show.
+**
+** tkt=UUID filters for only those events for the specified ticket. tag=TAG
+** filters for a tag, and wiki=NAME for a wiki page. Only one may be used.
+**
+** In addition, name=FILENAME filters for a specific file. This may be
+** combined with one of the other filters (useful for looking at a specific
+** branch).
 */
+
 void page_timeline_rss(void){
   Stmt q;
   int nLine=0;
   char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
   Blob bSQL;
   const char *zType = PD("y","all"); /* Type of events.  All if NULL */
+  const char *zTicketUuid = PD("tkt",NULL);
+  const char *zTag = PD("tag",NULL);
+  const char *zFilename = PD("name",NULL);
+  const char *zWiki = PD("wiki",NULL);
   int nLimit = atoi(PD("n","20"));
+  int nTagId;
   const char zSQL1[] =
     @ SELECT
     @   blob.rid,
     @   uuid,
     @   event.mtime,
@@ -62,10 +81,11 @@
     if( !g.perm.Read ){
       if( g.perm.RdTkt && g.perm.RdWiki ){
         blob_append(&bSQL, " AND event.type!='ci'", -1);
       }else if( g.perm.RdTkt ){
         blob_append(&bSQL, " AND event.type=='t'", -1);
+        
       }else{
         blob_append(&bSQL, " AND event.type=='w'", -1);
       }
     }else if( !g.perm.RdWiki ){
       if( g.perm.RdTkt ){
@@ -76,10 +96,46 @@
     }else if( !g.perm.RdTkt ){
       assert( !g.perm.RdTkt &&& g.perm.Read && g.perm.RdWiki );
       blob_append(&bSQL, " AND event.type!='t'", -1);
     }
   }
+
+  if( zTicketUuid ){
+    nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",
+      zTicketUuid);
+    if ( nTagId==0 ){
+      nTagId = -1;
+    }
+  }else if( zTag ){
+    nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q*'",
+      zTag);
+    if ( nTagId==0 ){
+      nTagId = -1;
+    }
+  }else if( zWiki ){
+    nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'wiki-%q*'",
+      zWiki);
+    if ( nTagId==0 ){
+      nTagId = -1;
+    }
+  }else{
+    nTagId = 0;
+  }
+
+  if( nTagId==-1 ){
+    blob_appendf(&bSQL, " AND 0");
+  }else if( nTagId!=0 ){
+    blob_appendf(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
+      " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
+  }
+
+  if( zFilename ){
+    blob_appendf(&bSQL,
+      " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)",
+        zFilename, filename_collation()
+    );
+  }
 
   blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );
 
   cgi_set_content_type("application/rss+xml");
 

Index: src/url.c
==================================================================
--- src/url.c
+++ src/url.c
@@ -63,10 +63,11 @@
    || strncmp(zUrl, "ssh://", 6)==0
   ){
     int iStart;
     char *zLogin;
     char *zExe;
+    char cQuerySep = '?';
 
     g.urlIsFile = 0;
     if( zUrl[4]=='s' ){
       g.urlIsHttps = 1;
       g.urlProtocol = "https";
@@ -75,10 +76,11 @@
     }else if( zUrl[0]=='s' ){
       g.urlIsSsh = 1;
       g.urlProtocol = "ssh";
       g.urlDfltPort = 22;
       g.urlFossil = "fossil";
+      g.urlShell = 0;
       iStart = 6;
     }else{
       g.urlIsHttps = 0;
       g.urlProtocol = "http";
       g.urlDfltPort = 80;
@@ -144,11 +146,18 @@
         i++;
       }
       if( fossil_strcmp(zName,"fossil")==0 ){
         g.urlFossil = zValue;
         dehttpize(g.urlFossil);
-        zExe = mprintf("?fossil=%T", g.urlFossil);
+        zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil);
+        cQuerySep = '&';
+      }
+      if( fossil_strcmp(zName,"shell")==0 ){
+        g.urlShell = zValue;
+        dehttpize(g.urlShell);
+        zExe = mprintf("%cshell=%T", cQuerySep, g.urlFossil);
+        cQuerySep = '&';
       }
     }
 
     dehttpize(g.urlPath);
     if( g.urlDfltPort==g.urlPort ){

Index: src/wikiformat.c
==================================================================
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -824,11 +824,12 @@
     blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
     for(i=0; i<p->nAttr; i++){
       blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
       if( p->aAttr[i].zValue ){
         const char *zVal = p->aAttr[i].zValue;
-        if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){
+        if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/'
+            && (zVal[0]==0 || zVal[1]!='/') ){
           blob_appendf(pOut, "=\"%s%s\"", g.zTop, zVal);
         }else{
           blob_appendf(pOut, "=\"%s\"", zVal);
         }
       }
@@ -1171,10 +1172,11 @@
   assert( nClose>=20 );
   if( strncmp(zTarget, "http:", 5)==0
    || strncmp(zTarget, "https:", 6)==0
    || strncmp(zTarget, "ftp:", 4)==0
    || strncmp(zTarget, "mailto:", 7)==0
+   || strncmp(zTarget, "//", 2)==0
   ){
     blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
   }else if( zTarget[0]=='/' ){
     blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zTop, zTarget);
   }else if( zTarget[0]=='.'

Index: src/xfer.c
==================================================================
--- src/xfer.c
+++ src/xfer.c
@@ -20,10 +20,16 @@
 #include "config.h"
 #include "xfer.h"
 
 #include <time.h>
 
+/*
+** Maximum number of HTTP redirects that any http_exchange() call will
+** follow before throwing a fatal error. Most browsers use a limit of 20.
+*/
+#define MAX_REDIRECTS 20
+
 /*
 ** This structure holds information about the current state of either
 ** a client or a server that is participating in xfer.
 */
 typedef struct Xfer Xfer;
@@ -1480,11 +1486,12 @@
     xfer.nIGotSent = 0;
     if( syncFlags & SYNC_VERBOSE ){
       fossil_print("waiting for server...");
     }
     fflush(stdout);
-    if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
+    if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
+        MAX_REDIRECTS) ){
       nErr++;
       break;
     }
     lastPctDone = -1;
     blob_reset(&send);

Index: www/build.wiki
==================================================================
--- www/build.wiki
+++ www/build.wiki
@@ -54,11 +54,11 @@
 </ol>
 
 <h2>2.0 Compiling</h2>
 
 <ol>
-<li value="6">
+<li value="5">
 <p>Unpack the ZIP or tarball you downloaded then
 <b>cd</b> into the directory created.</p></li>
 
 <li><i>(Optional, unix only)</i>
 Run <b>./configure</b> to construct a makefile.
@@ -101,11 +101,11 @@
 </ol>
 
 <h2>3.0 Installing</h2>
 
 <ol>
-<li value="9">
+<li value="8">
 <p>The finished binary is named "fossil" (or "fossil.exe" on windows).  
 Put this binary in a 
 directory that is somewhere on your PATH environment variable.
 It does not matter where.</p>