Changes On Branch use-fossil_strcmp-everywhere
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch dg-misc Excluding Merge-Ins

This is equivalent to a diff from 7ac0fd9d11 to e4a698bdd2

2013-02-12
16:47
Merge from trunk. Leaf check-in: e4a698bdd2 user: dg tags: use-fossil_strcmp-everywhere, dg-misc
11:53
Replaced a call to realloc() with cson_realloc() (which, in turn, uses the fossil realloc). check-in: a1d2cd84b8 user: stephan tags: trunk
2013-02-06
17:37
Allow filtering by filename, tag or wiki page as well as by ticket UUID. Better handling of invalid values (they now generate empty RSS feeds rather than appending the HTML footer...). check-in: d244452bda user: dg tags: use-fossil_strcmp-everywhere, dg-misc
2013-01-30
13:10
Make sure that "fossil_strcmp" is used everywhere in stead of "strcmp": The "strcmp" function from the C library is not usable in all situations, e.g. with --static on Linux Do an #undef in printf.c, preventing that the fossil_strcmp function is optimized for non-null arguments. check-in: afffe48643 user: jan.nijtmans tags: trunk
12:37
Create new branch named "dg-misc" check-in: 168b42eee1 user: dg tags: use-fossil_strcmp-everywhere, dg-misc
10:03
Make sure that "fossil_strcmp" is used everywhere in stead of "strcmp": The "strcmp" function from the C library is not usable in all situations, e.g. with --static on Linux Closed-Leaf check-in: 7ac0fd9d11 user: jan.nijtmans tags: use-fossil_strcmp-everywhere
2013-01-29
09:15
Rename "unicode-glob" setting to "encoding-glob". Mention the existance of the *-glob settings in the "fossil commit" warnings. Alphabetize the settings list. check-in: 7d237c49f3 user: jan.nijtmans tags: trunk

Changes to Makefile.in.

    35     35   
    36     36   #### Tcl shell for use in running the fossil testsuite.  If you do not
    37     37   #    care about testing the end result, this can be blank.
    38     38   #
    39     39   TCLSH = tclsh
    40     40   
    41     41   LIB =	@LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
    42         -TCC +=	@EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H -Dstrcmp=fossil_strcmp
           42  +TCC +=	@EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H
    43     43   INSTALLDIR = $(DESTDIR)@prefix@/bin
    44     44   USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
    45     45   FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@
    46     46   FOSSIL_ENABLE_TCL_STUBS = @FOSSIL_ENABLE_TCL_STUBS@
    47     47   
    48     48   include $(SRCDIR)/main.mk
    49     49   
    50     50   distclean: clean
    51     51   	rm -f autoconfig.h config.log Makefile

Changes to auto.def.

   231    231   cc-check-function-in-lib iconv iconv
   232    232   
   233    233   # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
   234    234   if {![cc-check-functions getpassphrase]} {
   235    235       # Haiku needs this
   236    236       cc-check-function-in-lib getpass bsd
   237    237   }
          238  +cc-check-function-in-lib dlopen dl
   238    239   
   239    240   make-template Makefile.in
   240    241   make-config-header autoconfig.h -auto {USE_* FOSSIL_*}

Changes to compat/zlib/configure.

   812    812   /^sharedlibdir *=/s#=.*#=$sharedlibdir#
   813    813   /^includedir *=/s#=.*#=$includedir#
   814    814   /^mandir *=/s#=.*#=$mandir#
   815    815   /^LDFLAGS *=/s#=.*#=$LDFLAGS#
   816    816   " | sed -e "
   817    817   s/\@VERSION\@/$VER/g;
   818    818   " > zlib.pc
          819  +#

Changes to src/blob.c.

  1102   1102       struct Blob temp;
  1103   1103       zUtf8 = blob_str(pBlob) + bomSize;
  1104   1104       blob_zero(&temp);
  1105   1105       blob_append(&temp, zUtf8, -1);
  1106   1106       blob_swap(pBlob, &temp);
  1107   1107       blob_reset(&temp);
  1108   1108   #ifdef _WIN32
  1109         -  }else if( starts_with_utf16le_bom(pBlob, &bomSize) ){
  1110         -    /* Make sure the blob contains two terminating 0-bytes */
  1111         -    blob_append(pBlob, "", 1);
  1112         -    zUtf8 = blob_str(pBlob) + bomSize;
  1113         -    zUtf8 = fossil_unicode_to_utf8(zUtf8);
  1114         -    blob_zero(pBlob);
  1115         -    blob_append(pBlob, zUtf8, -1);
  1116         -    fossil_unicode_free(zUtf8);
  1117         -  }else if( starts_with_utf16be_bom(pBlob, &bomSize) ){
  1118         -    unsigned int i = blob_size(pBlob);
         1109  +  }else if( starts_with_utf16_bom(pBlob, &bomSize) ){
  1119   1110       zUtf8 = blob_buffer(pBlob);
  1120         -    while( i > 0 ){
  1121         -      /* swap bytes of unicode representation */
  1122         -      char zTemp = zUtf8[--i];
  1123         -      zUtf8[i] = zUtf8[i-1];
  1124         -      zUtf8[--i] = zTemp;
         1111  +    if (*((unsigned short *)zUtf8) == 0xfffe) {
         1112  +      /* Found BOM, but with reversed bytes */
         1113  +      unsigned int i = blob_size(pBlob);
         1114  +      while( i > 0 ){
         1115  +        /* swap bytes of unicode representation */
         1116  +        char zTemp = zUtf8[--i];
         1117  +        zUtf8[i] = zUtf8[i-1];
         1118  +        zUtf8[--i] = zTemp;
         1119  +      }
  1125   1120       }
  1126   1121       /* Make sure the blob contains two terminating 0-bytes */
  1127   1122       blob_append(pBlob, "", 1);
  1128   1123       zUtf8 = blob_str(pBlob) + bomSize;
  1129   1124       zUtf8 = fossil_unicode_to_utf8(zUtf8);
  1130   1125       blob_zero(pBlob);
  1131   1126       blob_append(pBlob, zUtf8, -1);

Changes to src/checkin.c.

   614    614   **
   615    615   ** The last element of aCommitFile[] is always 0 - indicating the end
   616    616   ** of the array.
   617    617   **
   618    618   ** If there were no arguments passed to [commit], aCommitFile is not
   619    619   ** allocated and remains NULL. Other parts of the code interpret this
   620    620   ** to mean "all files".
          621  +**
          622  +** Returns 1 if there was a warning, 0 otherwise.
   621    623   */
   622         -void select_commit_files(void){
          624  +int select_commit_files(void){
          625  +  int result = 0;
   623    626     if( g.argc>2 ){
   624         -    int ii;
          627  +    int ii, jj=0;
   625    628       Blob b;
   626    629       blob_zero(&b);
   627    630       g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1));
   628    631   
   629    632       for(ii=2; ii<g.argc; ii++){
   630    633         int iId;
   631    634         file_tree_name(g.argv[ii], &b, 1);
   632    635         iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b));
   633    636         if( iId<0 ){
   634         -        fossil_fatal("fossil knows nothing about: %s", g.argv[ii]);
          637  +        fossil_warning("fossil knows nothing about: %s", g.argv[ii]);
          638  +        result = 1;
          639  +      } else {
          640  +        g.aCommitFile[jj++] = iId;
   635    641         }
   636         -      g.aCommitFile[ii-2] = iId;
   637    642         blob_reset(&b);
   638    643       }
   639         -    g.aCommitFile[ii-2] = 0;
          644  +    g.aCommitFile[jj] = 0;
   640    645     }
          646  +  return result;
   641    647   }
   642    648   
   643    649   /*
   644    650   ** Make sure the current check-in with timestamp zDate is younger than its
   645    651   ** ancestor identified rid and zUuid.  Throw a fatal error if not.
   646    652   */
   647    653   static void checkin_verify_younger(
................................................................................
  1197   1203     ** follows "commit", then only those files are committed.
  1198   1204     **
  1199   1205     ** After the following function call has returned, the Global.aCommitFile[]
  1200   1206     ** array is allocated to contain the "id" field from the vfile table
  1201   1207     ** for each file to be committed. Or, if aCommitFile is NULL, all files
  1202   1208     ** should be committed.
  1203   1209     */
  1204         -  select_commit_files();
         1210  +  if ( select_commit_files() ){
         1211  +    blob_zero(&ans);
         1212  +    prompt_user("continue (y/N)? ", &ans);
         1213  +    cReply = blob_str(&ans)[0];
         1214  +    if( cReply!='y' && cReply!='Y' ) fossil_exit(1);;
         1215  +  }
  1205   1216     isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0");
  1206   1217     if( g.aCommitFile && isAMerge ){
  1207   1218       fossil_fatal("cannot do a partial commit of a merge");
  1208   1219     }
  1209   1220   
  1210   1221     /* Doing "fossil mv fileA fileB; fossil add fileA; fossil commit fileA"
  1211   1222     ** will generate a manifest that has two fileA entries, which is illegal.

Changes to src/cson_amalgamation.c.

  4347   4347       }
  4348   4348       else if( buf->capacity >= n )
  4349   4349       {
  4350   4350           return 0;
  4351   4351       }
  4352   4352       else
  4353   4353       {
  4354         -        unsigned char * x = (unsigned char *)realloc( buf->mem, n );
         4354  +        unsigned char * x = (unsigned char *)cson_realloc( buf->mem, n, "cson_buffer::mem" );
  4355   4355           if( ! x ) return cson_rc.AllocError;
  4356   4356           memset( x + buf->used, 0, n - buf->used );
  4357   4357           buf->mem = x;
  4358   4358           buf->capacity = n;
  4359   4359           ++buf->timesExpanded;
  4360   4360           return 0;
  4361   4361       }

Changes to src/diff.c.

   357    357   
   358    358   /*
   359    359   ** This function returns non-zero if the blob starts with a UTF-16le or
   360    360   ** UTF-16be byte-order-mark (BOM).
   361    361   */
   362    362   int starts_with_utf16_bom(const Blob *pContent, int *pnByte){
   363    363     const char *z = blob_buffer(pContent);
   364         -  int c1, c2;
          364  +  int c1;
   365    365   
   366    366     if( pnByte ) *pnByte = 2;
   367         -  if( blob_size(pContent)<2 ) return 0;
   368         -  c1 = z[0]; c2 = z[1];
   369         -  if( (c1==(char)0xff) && (c2==(char)0xfe) ){
   370         -    return 1;
   371         -  }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){
   372         -    return 1;
   373         -  }
   374         -  return 0;
   375         -}
   376         -
   377         -/*
   378         -** This function returns non-zero if the blob starts with a UTF-16le
   379         -** byte-order-mark (BOM).
   380         -*/
   381         -int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){
   382         -  const char *z = blob_buffer(pContent);
   383         -  int c1, c2;
   384         -
   385         -  if( pnByte ) *pnByte = 2;
   386         -  if( blob_size(pContent)<2 ) return 0;
   387         -  c1 = z[0]; c2 = z[1];
   388         -  if( (c1==(char)0xff) && (c2==(char)0xfe) ){
   389         -    return 1;
   390         -  }
   391         -  return 0;
   392         -}
   393         -
   394         -/*
   395         -** This function returns non-zero if the blob starts with a UTF-16be
   396         -** byte-order-mark (BOM).
   397         -*/
   398         -int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){
   399         -  const char *z = blob_buffer(pContent);
   400         -  int c1, c2;
   401         -
   402         -  if( pnByte ) *pnByte = 2;
   403         -  if( blob_size(pContent)<2 ) return 0;
   404         -  c1 = z[0]; c2 = z[1];
   405         -  if( (c1==(char)0xfe) && (c2==(char)0xff) ){
   406         -    return 1;
          367  +  if( (blob_size(pContent)<2) || (blob_size(pContent)&1)) return 0;
          368  +  c1 = ((unsigned short *)z)[0];
          369  +  if( (c1==0xfeff) || (c1==0xfffe) ){
          370  +    if( blob_size(pContent) < 4 ) return 1;
          371  +    c1 = ((unsigned short *)z)[1];
          372  +    if( c1 != 0 ) return 1;
   407    373     }
   408    374     return 0;
   409    375   }
   410    376   
   411    377   /*
   412    378   ** Return true if two DLine elements are identical.
   413    379   */

Changes to src/http.c.

   128    128   ** in pRecv.  pRecv is assumed to be uninitialized when
   129    129   ** this routine is called - this routine will initialize it.
   130    130   **
   131    131   ** The server address is contain in the "g" global structure.  The
   132    132   ** url_parse() routine should have been called prior to this routine
   133    133   ** in order to fill this structure appropriately.
   134    134   */
   135         -int http_exchange(Blob *pSend, Blob *pReply, int useLogin){
          135  +int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){
   136    136     Blob login;           /* The login card */
   137    137     Blob payload;         /* The complete payload including login card */
   138    138     Blob hdr;             /* The HTTP request header */
   139    139     int closeConnection;  /* True to close the connection when done */
   140    140     int iLength;          /* Length of the reply payload */
   141    141     int rc = 0;           /* Result code */
   142    142     int iHttpVersion;     /* Which version of HTTP protocol server uses */
................................................................................
   229    229         if( c=='c' || c=='C' ){
   230    230           closeConnection = 1;
   231    231         }else if( c=='k' || c=='K' ){
   232    232           closeConnection = 0;
   233    233         }
   234    234       }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){
   235    235         int i, j;
          236  +
          237  +      if ( --maxRedirect == 0){
          238  +        fossil_fatal("redirect limit exceeded");
          239  +      }
   236    240         for(i=9; zLine[i] && zLine[i]==' '; i++){}
   237    241         if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine);
   238    242         j = strlen(zLine) - 1; 
   239    243         while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){
   240    244            j -= 4;
   241    245            zLine[j] = 0;
   242    246         }
   243    247         fossil_print("redirect to %s\n", &zLine[i]);
   244    248         url_parse(&zLine[i]);
   245    249         transport_close();
   246         -      return http_exchange(pSend, pReply, useLogin);
          250  +      return http_exchange(pSend, pReply, useLogin, maxRedirect);
   247    251       }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
   248    252         if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
   249    253           isCompressed = 0;
   250    254         }else if( fossil_strnicmp(&zLine[14], 
   251    255                             "application/x-fossil-uncompressed", -1)==0 ){
   252    256           isCompressed = 0;
   253    257         }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){

Changes to src/http_transport.c.

   181    181     if( g.urlIsSsh ){
   182    182       /* Only SSH requires a global initialization.  For SSH we need to create
   183    183       ** and run an SSH command to talk to the remote machine.
   184    184       */
   185    185       const char *zSsh;  /* The base SSH command */
   186    186       Blob zCmd;         /* The SSH command */
   187    187       char *zHost;       /* The host name to contact */
          188  +    int n;             /* Size of prefix string */
   188    189   
   189    190       zSsh = db_get("ssh-command", zDefaultSshCmd);
   190    191       blob_init(&zCmd, zSsh, -1);
   191    192       if( g.urlPort!=g.urlDfltPort ){
   192    193   #ifdef __MINGW32__
   193    194         blob_appendf(&zCmd, " -P %d", g.urlPort);
   194    195   #else
................................................................................
   217    218           blob_reset(&pw);
   218    219           fossil_print(" -pw ********");  /* Do not show the password text */
   219    220         }
   220    221   #endif
   221    222       }else{
   222    223         zHost = mprintf("%s", g.urlName);
   223    224       }
          225  +    n = blob_size(&zCmd);
   224    226       blob_append(&zCmd, " ", 1);
   225    227       shell_escape(&zCmd, zHost);
   226         -    fossil_print(" %s\n", zHost);  /* Show the conclusion of the SSH command */
          228  +    if( g.urlShell ){
          229  +      blob_appendf(&zCmd, " %s", g.urlShell);
          230  +    }else{
          231  +#if defined(FOSSIL_ENABLE_SSH_FAR_SIDE)
          232  +      /* The following works.  But only if the fossil on the remote side
          233  +      ** is recent enough to support the test-ssh-far-side command.  That
          234  +      ** command was added on 2013-02-06.  We will leave this turned off
          235  +      ** until most fossil servers have upgraded to that version or a later
          236  +      ** version.  The sync will still work as long as the shell on the far
          237  +      ** side is bash and not tcsh.  And if the default far side shell is
          238  +      ** tcsh, then the shell=/bin/bash query parameter can be used as a
          239  +      ** work-around.  Enable this code after about a year...
          240  +      */
          241  +      blob_appendf(&zCmd, " exec %s test-ssh-far-side", g.urlFossil);
          242  +#endif
          243  +    }
          244  +    fossil_print("%s\n", blob_str(&zCmd)+n);  /* Show tail of SSH command */
   227    245       free(zHost);
   228    246       popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
   229    247       if( sshPid==0 ){
   230    248         fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
   231    249       }
   232    250       blob_reset(&zCmd);
   233    251       transport_ssh_startup();
   234    252     }
   235    253   }
          254  +
          255  +/*
          256  +** COMMAND: test-ssh-far-side
          257  +**
          258  +** Read lines of input text, one by one, and evaluate each line using
          259  +** system().  The ssh: sync protocol uses this on the far side of the
          260  +** SSH link.
          261  +*/
          262  +void test_ssh_far_side_cmd(void){
          263  +  int i = 0;
          264  +  int got;
          265  +  char zLine[5000];
          266  +  while( i<sizeof(zLine) ){
          267  +    got = read(0, zLine+i, 1);
          268  +    if( got==0 ) return;
          269  +    if( zLine[i]=='\n' ){
          270  +      zLine[i] = 0;
          271  +      system(zLine);
          272  +      i = 0;
          273  +    }else{
          274  +      i++;
          275  +    }
          276  +  }
          277  +}
   236    278   
   237    279   /*
   238    280   ** Open a connection to the server.  The server is defined by the following
   239    281   ** global variables:
   240    282   **
   241    283   **   g.urlName        Name of the server.  Ex: www.fossil-scm.org
   242    284   **   g.urlPort        TCP/IP port.  Ex: 80
................................................................................
   249    291     if( transport.isOpen==0 ){
   250    292       if( g.urlIsSsh ){
   251    293         Blob cmd;
   252    294         blob_zero(&cmd);
   253    295         shell_escape(&cmd, g.urlFossil);
   254    296         blob_append(&cmd, " test-http ", -1);
   255    297         shell_escape(&cmd, g.urlPath);
   256         -      /* printf("%s\n", blob_str(&cmd)); fflush(stdout); */
   257         -      fprintf(sshOut, "%s\n", blob_str(&cmd));
          298  +      fprintf(sshOut, "%s || true\n", blob_str(&cmd));
   258    299         fflush(sshOut);
          300  +      if( g.fSshTrace ) printf("Sent: [%s]\n", blob_str(&cmd));
   259    301         blob_reset(&cmd);
   260    302       }else if( g.urlIsHttps ){
   261    303         #ifdef FOSSIL_ENABLE_SSL
   262    304         rc = ssl_open();
   263    305         if( rc==0 ) transport.isOpen = 1;
   264    306         #else
   265    307         socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
................................................................................
   401    443   */
   402    444   static int transport_fetch(char *zBuf, int N){
   403    445     int got;
   404    446     if( sshIn ){
   405    447       int x;
   406    448       int wanted = N;
   407    449       got = 0;
   408         -    /* printf("want %d bytes...\n", wanted); fflush(stdout); */
   409    450       while( wanted>0 ){
   410    451         x = read(sshIn, &zBuf[got], wanted);
   411    452         if( x<=0 ) break;
   412    453         got += x;
   413    454         wanted -= x;
   414    455       }
   415    456     }else if( g.urlIsHttps ){
................................................................................
   436    477   ** Return the number of bytes actually received.
   437    478   */
   438    479   int transport_receive(char *zBuf, int N){
   439    480     int onHand;       /* Bytes current held in the transport buffer */
   440    481     int nByte = 0;    /* Bytes of content received */
   441    482   
   442    483     onHand = transport.nUsed - transport.iCursor;
   443         -  /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */
          484  +  if( g.fSshTrace){
          485  +    printf("Reading %d bytes with %d on hand...  ", N, onHand);
          486  +    fflush(stdout);
          487  +  }
   444    488     if( onHand>0 ){
   445    489       int toMove = onHand;
   446    490       if( toMove>N ) toMove = N;
   447    491       /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */
   448    492       memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove);
   449    493       transport.iCursor += toMove;
   450    494       if( transport.iCursor>=transport.nUsed ){
................................................................................
   458    502     if( N>0 ){
   459    503       int got = transport_fetch(zBuf, N);
   460    504       if( got>0 ){
   461    505         nByte += got;
   462    506         transport.nRcvd += got;
   463    507       }
   464    508     }
          509  +  if( g.fSshTrace ) printf("Got %d bytes\n", nByte);
   465    510     return nByte;
   466    511   }
   467    512   
   468    513   /*
   469    514   ** Load up to N new bytes of content into the transport.pBuf buffer.
   470    515   ** The buffer itself might be moved.  And the transport.iCursor value
   471    516   ** might be reset to 0.
................................................................................
   530    575           transport.pBuf[i] = 0;
   531    576           i--;
   532    577         }
   533    578         break;
   534    579       }
   535    580       i++;
   536    581     }
   537         -   /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */
          582  +  if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]);
   538    583     return &transport.pBuf[iStart];
   539    584   }
   540    585   
   541    586   void transport_global_shutdown(void){
   542    587     if( g.urlIsSsh && sshPid ){
   543    588       /*printf("Closing SSH tunnel: ");*/
   544    589       fflush(stdout);

Changes to src/json_detail.h.

   197    197     char const * mtime;
   198    198     char const * payload;
   199    199     char const * requestId;
   200    200     char const * resultCode;
   201    201     char const * resultText;
   202    202     char const * timestamp;
   203    203   } FossilJsonKeys_;
   204         -const FossilJsonKeys_ FossilJsonKeys;
          204  +extern const FossilJsonKeys_ FossilJsonKeys;
   205    205   
   206    206   /*
   207    207   ** A page/command dispatch helper for fossil_json_f() implementations.
   208    208   ** pages must be an array of JsonPageDef commands which we can
   209    209   ** dispatch. The final item in the array MUST have a NULL name
   210    210   ** element.
   211    211   **

Changes to src/main.c.

   133    133     int fHttpTrace;         /* Trace outbound HTTP requests */
   134    134     int fSystemTrace;       /* Trace calls to fossil_system(), --systemtrace */
   135    135     int fSshTrace;          /* Trace the SSH setup traffic */
   136    136     int fNoSync;            /* Do not do an autosync ever.  --nosync */
   137    137     char *zPath;            /* Name of webpage being served */
   138    138     char *zExtra;           /* Extra path information past the webpage name */
   139    139     char *zBaseURL;         /* Full text of the URL being served */
          140  +  const char *zAltBase;   /* Alternative URL root.  --baseurl */
   140    141     char *zTop;             /* Parent directory of zPath */
   141    142     const char *zContentType;  /* The content type of the input HTTP request */
   142    143     int iErrPriority;       /* Priority of current error message */
   143    144     char *zErrMsg;          /* Text of an error message */
   144    145     int sslNotAvailable;    /* SSL is not available.  Do not redirect to https: */
   145    146     Blob cgiIn;             /* Input to an xfer www method */
   146    147     int cgiOutput;          /* Write error and status messages to CGI */
................................................................................
   168    169     int urlPort;            /* TCP port number for http: or https: */
   169    170     int urlDfltPort;        /* The default port for the given protocol */
   170    171     char *urlPath;          /* Pathname for http: */
   171    172     char *urlUser;          /* User id for http: */
   172    173     char *urlPasswd;        /* Password for http: */
   173    174     char *urlCanonical;     /* Canonical representation of the URL */
   174    175     char *urlProxyAuth;     /* Proxy-Authorizer: string */
   175         -  char *urlFossil;        /* The path of the ?fossil=path suffix on ssh: */
          176  +  char *urlFossil;        /* The fossil query parameter on ssh: */
          177  +  char *urlShell;         /* The shell query parameter on ssh: */
   176    178     int dontKeepUrl;        /* Do not persist the URL */
   177    179   
   178    180     const char *zLogin;     /* Login name.  "" if not logged in. */
   179    181     const char *zSSLIdentity;  /* Value of --ssl-identity option, filename of SSL client identity */
   180    182     int useLocalauth;       /* No login required if from 127.0.0.1 */
   181    183     int noPswd;             /* Logged in without password (on 127.0.0.1) */
   182    184     int userUid;            /* Integer user id */
................................................................................
  1114   1116   
  1115   1117   /*
  1116   1118   ** Set the g.zBaseURL value to the full URL for the toplevel of
  1117   1119   ** the fossil tree.  Set g.zTop to g.zBaseURL without the
  1118   1120   ** leading "http://" and the host and port.
  1119   1121   **
  1120   1122   ** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME
  1121         -** environment variables.  However, if zAltBase is not NULL then it
         1123  +** environment variables.  However, if g.zAltBase is not NULL then it
  1122   1124   ** is the argument to the --baseurl option command-line option and
  1123   1125   ** g.zBaseURL and g.zTop is set from that instead.
         1126  +**
         1127  +** If g.zAltBase is set, then zRepoName may be a repository name (with a
         1128  +** leading slash ** but without the .fossil) with is concatenated to the
         1129  +** root.
  1124   1130   */
  1125         -static void set_base_url(const char *zAltBase){
         1131  +static void set_base_url(const char* zRepoName){
  1126   1132     int i;
  1127   1133     const char *zHost;
  1128   1134     const char *zMode;
  1129   1135     const char *zCur;
  1130   1136   
  1131         -  if( g.zBaseURL!=0 ) return;
  1132         -  if( zAltBase ){
         1137  +  fossil_free(g.zBaseURL);
         1138  +  if( g.zAltBase ){
  1133   1139       int i, n, c;
  1134         -    g.zTop = g.zBaseURL = mprintf("%s", zAltBase);
         1140  +    if (!zRepoName) zRepoName = "";
         1141  +    if (zRepoName[0] == '/') zRepoName++;
         1142  +    g.zTop = g.zBaseURL = mprintf("%s%s", g.zAltBase, zRepoName);
  1135   1143       if( memcmp(g.zTop, "http://", 7)!=0 && memcmp(g.zTop,"https://",8)!=0 ){
  1136   1144         fossil_fatal("argument to --baseurl should be 'http://host/path'"
  1137   1145                      " or 'https://host/path'");
  1138   1146       }
  1139   1147       for(i=n=0; (c = g.zTop[i])!=0; i++){
  1140   1148         if( c=='/' ){
  1141   1149           n++;
................................................................................
  1271   1279     ** repository based on the first element of PATH_INFO and open it.
  1272   1280     */
  1273   1281     zPathInfo = PD("PATH_INFO","");
  1274   1282     if( !g.repositoryOpen ){
  1275   1283       char *zRepo, *zToFree;
  1276   1284       const char *zOldScript = PD("SCRIPT_NAME", "");
  1277   1285       char *zNewScript;
         1286  +    char *zRepoName;
  1278   1287       int j, k;
  1279   1288       i64 szFile;
  1280   1289   
  1281   1290       i = zPathInfo[0]!=0;
  1282   1291       while( 1 ){
  1283   1292         while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
  1284   1293         zRepo = zToFree = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo);
................................................................................
  1347   1356             cgi_set_status(404, "not found");
  1348   1357             cgi_reply();
  1349   1358           }
  1350   1359           return;
  1351   1360         }
  1352   1361         break;
  1353   1362       }
         1363  +    /* Rebuild the base URL to add the repository name to the end. */
         1364  +    zRepoName = mprintf("%.*s", i, zPathInfo);
         1365  +    set_base_url(zRepoName);
         1366  +    fossil_free(zRepoName);
  1354   1367       zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo);
  1355   1368       cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
  1356   1369       zPathInfo += i;
  1357   1370       cgi_replace_parameter("SCRIPT_NAME", zNewScript);
  1358   1371       db_open_repository(zRepo);
  1359   1372       if( g.fHttpTrace ){
  1360   1373         fprintf(stderr,
  1361   1374             "# repository: [%s]\n"
  1362   1375             "# new PATH_INFO = [%s]\n"
  1363   1376             "# new SCRIPT_NAME = [%s]\n",
  1364   1377             zRepo, zPathInfo, zNewScript);
  1365   1378       }
  1366   1379     }
         1380  +  else
         1381  +    set_base_url(0);
  1367   1382   
  1368   1383     /* Find the page that the user has requested, construct and deliver that
  1369   1384     ** page.
  1370   1385     */
  1371   1386     if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){
  1372   1387       zPathInfo = "/xfer";
  1373   1388     }
  1374         -  set_base_url(0);
  1375   1389     if( zPathInfo==0 || zPathInfo[0]==0
  1376   1390         || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
  1377   1391   #ifdef FOSSIL_ENABLE_JSON
  1378   1392       if(g.json.isJsonMode){
  1379   1393         json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
  1380   1394         fossil_exit(0);
  1381   1395       }
................................................................................
  1738   1752   **
  1739   1753   ** See also: cgi, server, winsrv
  1740   1754   */
  1741   1755   void cmd_http(void){
  1742   1756     const char *zIpAddr;
  1743   1757     const char *zNotFound;
  1744   1758     const char *zHost;
  1745         -  const char *zAltBase;
  1746   1759     const char *zFileGlob;
  1747   1760   
  1748   1761     /* The winhttp module passes the --files option as --files-urlenc with
  1749   1762     ** the argument being URL encoded, to avoid wildcard expansion in the 
  1750   1763     ** shell.  This option is for internal use and is undocumented.
  1751   1764     */
  1752   1765     zFileGlob = find_option("files-urlenc",0,1);
................................................................................
  1756   1769       zFileGlob = z;
  1757   1770     }else{
  1758   1771       zFileGlob = find_option("files",0,1);
  1759   1772     }
  1760   1773     zNotFound = find_option("notfound", 0, 1);
  1761   1774     g.useLocalauth = find_option("localauth", 0, 0)!=0;
  1762   1775     g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
  1763         -  zAltBase = find_option("baseurl", 0, 1);
  1764         -  if( zAltBase ) set_base_url(zAltBase);
         1776  +  g.zAltBase = find_option("baseurl", 0, 1);
         1777  +  set_base_url(0);
  1765   1778     if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on");
  1766   1779     zHost = find_option("host", 0, 1);
  1767   1780     if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);
  1768   1781     g.cgiOutput = 1;
         1782  +  g.httpIn = stdin;
         1783  +  g.httpOut = stdout;
  1769   1784     if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
  1770   1785       fossil_fatal("no repository specified");
  1771   1786     }
  1772   1787     g.fullHttpReply = 1;
  1773   1788     if( g.argc==6 ){
  1774   1789       g.httpIn = fossil_fopen(g.argv[3], "rb");
  1775   1790       g.httpOut = fossil_fopen(g.argv[4], "wb");
  1776   1791       zIpAddr = g.argv[5];
  1777   1792     }else{
  1778         -    g.httpIn = stdin;
  1779         -    g.httpOut = stdout;
  1780   1793       zIpAddr = 0;
  1781   1794     }
  1782   1795     find_server_repository(0);
  1783   1796     g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
  1784   1797     cgi_handle_http_request(zIpAddr);
  1785   1798     process_one_web_page(zNotFound, glob_create(zFileGlob));
  1786   1799   }
................................................................................
  1886   1899     int iPort, mxPort;        /* Range of TCP ports allowed */
  1887   1900     const char *zPort;        /* Value of the --port option */
  1888   1901     const char *zBrowser;     /* Name of web browser program */
  1889   1902     char *zBrowserCmd = 0;    /* Command to launch the web browser */
  1890   1903     int isUiCmd;              /* True if command is "ui", not "server' */
  1891   1904     const char *zNotFound;    /* The --notfound option or NULL */
  1892   1905     int flags = 0;            /* Server flags */
  1893         -  const char *zAltBase;     /* Argument to the --baseurl option */
  1894   1906     const char *zFileGlob;    /* Static content must match this */
  1895   1907   
  1896   1908   #if defined(_WIN32)
  1897   1909     const char *zStopperFile;    /* Name of file used to terminate server */
  1898   1910     zStopperFile = find_option("stopper", 0, 1);
  1899   1911   #endif
  1900   1912   
................................................................................
  1902   1914     g.thTrace = find_option("th-trace", 0, 0)!=0;
  1903   1915     g.useLocalauth = find_option("localauth", 0, 0)!=0;
  1904   1916     if( g.thTrace ){
  1905   1917       blob_zero(&g.thLog);
  1906   1918     }
  1907   1919     zPort = find_option("port", "P", 1);
  1908   1920     zNotFound = find_option("notfound", 0, 1);
  1909         -  zAltBase = find_option("baseurl", 0, 1);
  1910         -  if( zAltBase ){
  1911         -    set_base_url(zAltBase);
  1912         -  }
         1921  +  g.zAltBase = find_option("baseurl", 0, 1);
         1922  +  set_base_url(0);
  1913   1923     if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
  1914   1924     isUiCmd = g.argv[1][0]=='u';
  1915   1925     if( isUiCmd ){
  1916   1926       flags |= HTTP_SERVER_LOCALHOST;
  1917   1927       g.useLocalauth = 1;
  1918   1928     }
  1919   1929     find_server_repository(isUiCmd && zNotFound==0);

Changes to src/rss.c.

    20     20   #include "config.h"
    21     21   #include <time.h>
    22     22   #include "rss.h"
    23     23   #include <assert.h>
    24     24   
    25     25   /*
    26     26   ** WEBPAGE: timeline.rss
           27  +** URL:  /timeline.rss/y=TYPE&n=LIMIT&tkt=UUID&tag=TAG&wiki=NAME&name=FILENAME
           28  +**
           29  +** Produce an RSS feed of the timeline.
           30  +**
           31  +** TYPE may be: all, ci (show checkins only), t (show tickets only),
           32  +** w (show wiki only). LIMIT is the number of items to show.
           33  +**
           34  +** tkt=UUID filters for only those events for the specified ticket. tag=TAG
           35  +** filters for a tag, and wiki=NAME for a wiki page. Only one may be used.
           36  +**
           37  +** In addition, name=FILENAME filters for a specific file. This may be
           38  +** combined with one of the other filters (useful for looking at a specific
           39  +** branch).
    27     40   */
           41  +
    28     42   void page_timeline_rss(void){
    29     43     Stmt q;
    30     44     int nLine=0;
    31     45     char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
    32     46     Blob bSQL;
    33     47     const char *zType = PD("y","all"); /* Type of events.  All if NULL */
           48  +  const char *zTicketUuid = PD("tkt",NULL);
           49  +  const char *zTag = PD("tag",NULL);
           50  +  const char *zFilename = PD("name",NULL);
           51  +  const char *zWiki = PD("wiki",NULL);
    34     52     int nLimit = atoi(PD("n","20"));
           53  +  int nTagId;
    35     54     const char zSQL1[] =
    36     55       @ SELECT
    37     56       @   blob.rid,
    38     57       @   uuid,
    39     58       @   event.mtime,
    40     59       @   coalesce(ecomment,comment),
    41     60       @   coalesce(euser,user),
................................................................................
    60     79       blob_appendf(&bSQL, " AND event.type=%Q", zType);
    61     80     }else{
    62     81       if( !g.perm.Read ){
    63     82         if( g.perm.RdTkt && g.perm.RdWiki ){
    64     83           blob_append(&bSQL, " AND event.type!='ci'", -1);
    65     84         }else if( g.perm.RdTkt ){
    66     85           blob_append(&bSQL, " AND event.type=='t'", -1);
           86  +        
    67     87         }else{
    68     88           blob_append(&bSQL, " AND event.type=='w'", -1);
    69     89         }
    70     90       }else if( !g.perm.RdWiki ){
    71     91         if( g.perm.RdTkt ){
    72     92           blob_append(&bSQL, " AND event.type!='w'", -1);
    73     93         }else{
................................................................................
    74     94           blob_append(&bSQL, " AND event.type=='ci'", -1);
    75     95         }
    76     96       }else if( !g.perm.RdTkt ){
    77     97         assert( !g.perm.RdTkt &&& g.perm.Read && g.perm.RdWiki );
    78     98         blob_append(&bSQL, " AND event.type!='t'", -1);
    79     99       }
    80    100     }
          101  +
          102  +  if( zTicketUuid ){
          103  +    nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",
          104  +      zTicketUuid);
          105  +    if ( nTagId==0 ){
          106  +      nTagId = -1;
          107  +    }
          108  +  }else if( zTag ){
          109  +    nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q*'",
          110  +      zTag);
          111  +    if ( nTagId==0 ){
          112  +      nTagId = -1;
          113  +    }
          114  +  }else if( zWiki ){
          115  +    nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'wiki-%q*'",
          116  +      zWiki);
          117  +    if ( nTagId==0 ){
          118  +      nTagId = -1;
          119  +    }
          120  +  }else{
          121  +    nTagId = 0;
          122  +  }
          123  +
          124  +  if( nTagId==-1 ){
          125  +    blob_appendf(&bSQL, " AND 0");
          126  +  }else if( nTagId!=0 ){
          127  +    blob_appendf(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
          128  +      " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
          129  +  }
          130  +
          131  +  if( zFilename ){
          132  +    blob_appendf(&bSQL,
          133  +      " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)",
          134  +        zFilename, filename_collation()
          135  +    );
          136  +  }
    81    137   
    82    138     blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );
    83    139   
    84    140     cgi_set_content_type("application/rss+xml");
    85    141   
    86    142     zProjectName = db_get("project-name", 0);
    87    143     if( zProjectName==0 ){

Changes to src/url.c.

    61     61     if( strncmp(zUrl, "http://", 7)==0
    62     62      || strncmp(zUrl, "https://", 8)==0
    63     63      || strncmp(zUrl, "ssh://", 6)==0
    64     64     ){
    65     65       int iStart;
    66     66       char *zLogin;
    67     67       char *zExe;
           68  +    char cQuerySep = '?';
    68     69   
    69     70       g.urlIsFile = 0;
    70     71       if( zUrl[4]=='s' ){
    71     72         g.urlIsHttps = 1;
    72     73         g.urlProtocol = "https";
    73     74         g.urlDfltPort = 443;
    74     75         iStart = 8;
    75     76       }else if( zUrl[0]=='s' ){
    76     77         g.urlIsSsh = 1;
    77     78         g.urlProtocol = "ssh";
    78     79         g.urlDfltPort = 22;
    79     80         g.urlFossil = "fossil";
           81  +      g.urlShell = 0;
    80     82         iStart = 6;
    81     83       }else{
    82     84         g.urlIsHttps = 0;
    83     85         g.urlProtocol = "http";
    84     86         g.urlDfltPort = 80;
    85     87         iStart = 7;
    86     88       }
................................................................................
   142    144         if( g.urlPath[i] ){
   143    145           g.urlPath[i] = 0;
   144    146           i++;
   145    147         }
   146    148         if( fossil_strcmp(zName,"fossil")==0 ){
   147    149           g.urlFossil = zValue;
   148    150           dehttpize(g.urlFossil);
   149         -        zExe = mprintf("?fossil=%T", g.urlFossil);
          151  +        zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil);
          152  +        cQuerySep = '&';
          153  +      }
          154  +      if( fossil_strcmp(zName,"shell")==0 ){
          155  +        g.urlShell = zValue;
          156  +        dehttpize(g.urlShell);
          157  +        zExe = mprintf("%cshell=%T", cQuerySep, g.urlFossil);
          158  +        cQuerySep = '&';
   150    159         }
   151    160       }
   152    161   
   153    162       dehttpize(g.urlPath);
   154    163       if( g.urlDfltPort==g.urlPort ){
   155    164         g.urlCanonical = mprintf(
   156    165           "%s://%s%T%T%s", 

Changes to src/wikiformat.c.

   822    822       blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
   823    823     }else{
   824    824       blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
   825    825       for(i=0; i<p->nAttr; i++){
   826    826         blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
   827    827         if( p->aAttr[i].zValue ){
   828    828           const char *zVal = p->aAttr[i].zValue;
   829         -        if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){
          829  +        if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/'
          830  +            && (zVal[0]==0 || zVal[1]!='/') ){
   830    831             blob_appendf(pOut, "=\"%s%s\"", g.zTop, zVal);
   831    832           }else{
   832    833             blob_appendf(pOut, "=\"%s\"", zVal);
   833    834           }
   834    835         }
   835    836       }
   836    837       if (p->iType & MUTYPE_SINGLE){
................................................................................
  1169   1170     const char *z;
  1170   1171   
  1171   1172     assert( nClose>=20 );
  1172   1173     if( strncmp(zTarget, "http:", 5)==0
  1173   1174      || strncmp(zTarget, "https:", 6)==0
  1174   1175      || strncmp(zTarget, "ftp:", 4)==0
  1175   1176      || strncmp(zTarget, "mailto:", 7)==0
         1177  +   || strncmp(zTarget, "//", 2)==0
  1176   1178     ){
  1177   1179       blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
  1178   1180     }else if( zTarget[0]=='/' ){
  1179   1181       blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zTop, zTarget);
  1180   1182     }else if( zTarget[0]=='.'
  1181   1183            && (zTarget[1]=='/' || (zTarget[1]=='.' && zTarget[2]=='/'))
  1182   1184            && (p->state & WIKI_LINKSONLY)==0 ){

Changes to src/xfer.c.

    18     18   ** This file contains code to implement the file transfer protocol.
    19     19   */
    20     20   #include "config.h"
    21     21   #include "xfer.h"
    22     22   
    23     23   #include <time.h>
    24     24   
           25  +/*
           26  +** Maximum number of HTTP redirects that any http_exchange() call will
           27  +** follow before throwing a fatal error. Most browsers use a limit of 20.
           28  +*/
           29  +#define MAX_REDIRECTS 20
           30  +
    25     31   /*
    26     32   ** This structure holds information about the current state of either
    27     33   ** a client or a server that is participating in xfer.
    28     34   */
    29     35   typedef struct Xfer Xfer;
    30     36   struct Xfer {
    31     37     Blob *pIn;          /* Input text from the other side */
................................................................................
  1478   1484       xfer.nDeltaSent = 0;
  1479   1485       xfer.nGimmeSent = 0;
  1480   1486       xfer.nIGotSent = 0;
  1481   1487       if( syncFlags & SYNC_VERBOSE ){
  1482   1488         fossil_print("waiting for server...");
  1483   1489       }
  1484   1490       fflush(stdout);
  1485         -    if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
         1491  +    if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
         1492  +        MAX_REDIRECTS) ){
  1486   1493         nErr++;
  1487   1494         break;
  1488   1495       }
  1489   1496       lastPctDone = -1;
  1490   1497       blob_reset(&send);
  1491   1498       rArrivalTime = db_double(0.0, "SELECT julianday('now')");
  1492   1499   

Changes to www/build.wiki.

    52     52   These link will build a ZIP archive or a gzip-compressed tarball of the 
    53     53   complete source code and download it to your browser.
    54     54   </ol>
    55     55   
    56     56   <h2>2.0 Compiling</h2>
    57     57   
    58     58   <ol>
    59         -<li value="6">
           59  +<li value="5">
    60     60   <p>Unpack the ZIP or tarball you downloaded then
    61     61   <b>cd</b> into the directory created.</p></li>
    62     62   
    63     63   <li><i>(Optional, unix only)</i>
    64     64   Run <b>./configure</b> to construct a makefile.
    65     65   
    66     66   <ol type="a">
................................................................................
    99     99   "<b>nmake /f Makefile.msc</b>".
   100    100   </ol>
   101    101   </ol>
   102    102   
   103    103   <h2>3.0 Installing</h2>
   104    104   
   105    105   <ol>
   106         -<li value="9">
          106  +<li value="8">
   107    107   <p>The finished binary is named "fossil" (or "fossil.exe" on windows).  
   108    108   Put this binary in a 
   109    109   directory that is somewhere on your PATH environment variable.
   110    110   It does not matter where.</p>
   111    111   
   112    112   <li>
   113    113   <p><b>(Optional:)</b>