Fossil

Check-in [5a48a9b27b]
Login

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

Overview
Comment:Merge all changes from the wolfgangFormat2CSS_2 branch into the trunk.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 5a48a9b27bcf0bc3f1f2f9198dc84d7194fa7e2d
User & Date: drh 2010-09-29 13:30:55.000
Context
2010-09-29
13:32
Update SQLite to the latest version 3.7.3 release candidate. ... (check-in: fa29769a3d user: drh tags: trunk)
13:30
Merge all changes from the wolfgangFormat2CSS_2 branch into the trunk. ... (check-in: 5a48a9b27b user: drh tags: trunk)
13:29
Code formatting tweaks. ... (check-in: 8fce80ea99 user: drh tags: wolfgangFormat2CSS_2)
2010-09-26
11:33
Steps to try to avoid redirect loops on a misconfigured repository. Ticket [99d0791365daaec739c53c8b94059e280efc9628] ... (check-in: 1a0086249b user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/add.c.
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
*/
static void add_one_file(const char *zName, int vid, Blob *pOmit){
  Blob pathname;
  const char *zPath;

  file_tree_name(zName, &pathname, 1);
  zPath = blob_str(&pathname);
  if( strcmp(zPath, "manifest")==0
   || strcmp(zPath, "_FOSSIL_")==0
   || strcmp(zPath, "_FOSSIL_-journal")==0
   || strcmp(zPath, "_FOSSIL_-wal")==0
   || strcmp(zPath, "_FOSSIL_-shm")==0
   || strcmp(zPath, ".fos")==0
   || strcmp(zPath, ".fos-journal")==0
   || strcmp(zPath, ".fos-wal")==0
   || strcmp(zPath, ".fos-shm")==0
   || strcmp(zPath, "manifest.uuid")==0
   || blob_compare(&pathname, pOmit)==0
  ){
    fossil_warning("cannot add %s", zPath);
  }else{
    if( !file_is_simple_pathname(zPath) ){
      fossil_fatal("filename contains illegal characters: %s", zPath);
    }







<
|







<







34
35
36
37
38
39
40

41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
*/
static void add_one_file(const char *zName, int vid, Blob *pOmit){
  Blob pathname;
  const char *zPath;

  file_tree_name(zName, &pathname, 1);
  zPath = blob_str(&pathname);

  if( strcmp(zPath, "_FOSSIL_")==0
   || strcmp(zPath, "_FOSSIL_-journal")==0
   || strcmp(zPath, "_FOSSIL_-wal")==0
   || strcmp(zPath, "_FOSSIL_-shm")==0
   || strcmp(zPath, ".fos")==0
   || strcmp(zPath, ".fos-journal")==0
   || strcmp(zPath, ".fos-wal")==0
   || strcmp(zPath, ".fos-shm")==0

   || blob_compare(&pathname, pOmit)==0
  ){
    fossil_warning("cannot add %s", zPath);
  }else{
    if( !file_is_simple_pathname(zPath) ){
      fossil_fatal("filename contains illegal characters: %s", zPath);
    }
Changes to src/attach.c.
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
    rid = content_put(&manifest, 0, 0);
    manifest_crosslink(rid, &manifest);
    db_end_transaction(0);
    cgi_redirect(zFrom);
  }
  style_header("Add Attachment");
  @ <h2>Add Attachment To %s(zTargetType)</h2>
  @ <form action="%s(g.zBaseURL)/attachadd" method="POST"
  @  enctype="multipart/form-data">
  @ File to Attach:
  @ <input type="file" name="f" size="60"><br />
  @ Description:<br />
  @ <textarea name="comment" cols=80 rows=5 wrap="virtual"></textarea><br />
  if( zTkt ){
    @ <input type="hidden" name="tkt" value="%h(zTkt)">
  }else{
    @ <input type="hidden" name="page" value="%h(zPage)">
  }
  @ <input type="hidden" name="from" value="%h(zFrom)">
  @ <input type="submit" name="ok" value="Add Attachment">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>
  style_footer();
}


/*
** WEBPAGE: attachdelete
**







|
|

|

|

|

|

|
|
|
|







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
    rid = content_put(&manifest, 0, 0);
    manifest_crosslink(rid, &manifest);
    db_end_transaction(0);
    cgi_redirect(zFrom);
  }
  style_header("Add Attachment");
  @ <h2>Add Attachment To %s(zTargetType)</h2>
  @ <form action="%s(g.zBaseURL)/attachadd" method="post"
  @  enctype="multipart/form-data"><div>
  @ File to Attach:
  @ <input type="file" name="f" size="60" /><br />
  @ Description:<br />
  @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
  if( zTkt ){
    @ <input type="hidden" name="tkt" value="%h(zTkt)" />
  }else{
    @ <input type="hidden" name="page" value="%h(zPage)" />
  }
  @ <input type="hidden" name="from" value="%h(zFrom)" />
  @ <input type="submit" name="ok" value="Add Attachment" />
  @ <input type="submit" name="cancel" value="Cancel" />
  @ </div></form>
  style_footer();
}


/*
** WEBPAGE: attachdelete
**
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
    blob_appendf(&manifest, "Z %b\n", &cksum);
    rid = content_put(&manifest, 0, 0);
    manifest_crosslink(rid, &manifest);
    db_end_transaction(0);
    cgi_redirect(zFrom);
  }    
  style_header("Delete Attachment");
  @ <form action="%s(g.zBaseURL)/attachdelete" method="POST">
  @ <p>Confirm that you want to delete the attachment named
  @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br />
  if( zTkt ){
    @ <input type="hidden" name="tkt" value="%h(zTkt)">
  }else{
    @ <input type="hidden" name="page" value="%h(zPage)">
  }
  @ <input type="hidden" name="file" value="%h(zFile)">
  @ <input type="hidden" name="from" value="%h(zFrom)">
  @ <input type="submit" name="confirm" value="Delete">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>
  style_footer();

}







|



|

|

|
|
|
|
|



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
    blob_appendf(&manifest, "Z %b\n", &cksum);
    rid = content_put(&manifest, 0, 0);
    manifest_crosslink(rid, &manifest);
    db_end_transaction(0);
    cgi_redirect(zFrom);
  }    
  style_header("Delete Attachment");
  @ <form action="%s(g.zBaseURL)/attachdelete" method="post"><div>
  @ <p>Confirm that you want to delete the attachment named
  @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br />
  if( zTkt ){
    @ <input type="hidden" name="tkt" value="%h(zTkt)" />
  }else{
    @ <input type="hidden" name="page" value="%h(zPage)" />
  }
  @ <input type="hidden" name="file" value="%h(zFile)" />
  @ <input type="hidden" name="from" value="%h(zFrom)" />
  @ <input type="submit" name="confirm" value="Delete" />
  @ <input type="submit" name="cancel" value="Cancel" />
  @ </div></form>
  style_footer();

}
Changes to src/browse.c.
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125




126

127
128


129
130

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  int mxLen;
  int nCol, nRow;
  int cnt, i;
  char *zPrefix;
  Stmt q;
  const char *zCI = P("ci");
  int rid = 0;

  Blob content;
  Blob dirname;
  Manifest m;
  const char *zSubdirLink;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("File List");
  sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
                          pathelementFunc, 0, 0);

  /* If the name= parameter is an empty string, make it a NULL pointer */
  if( zD && strlen(zD)==0 ){ zD = 0; }

  /* If a specific check-in is requested, fetch and parse it. */




  if( zCI && (rid = name_to_rid(zCI))!=0 && content_get(rid, &content) ){

    if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){
      zCI = 0;


    }
  }


  /* Compute the title of the page */  
  blob_zero(&dirname);
  if( zD ){
    blob_append(&dirname, "in directory ", -1);
    hyperlinked_path(zD, &dirname);
    zPrefix = mprintf("%h/", zD);
  }else{
    blob_append(&dirname, "in the top-level directory", -1);
    zPrefix = "";
  }
  if( zCI ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    char zShort[20];
    memcpy(zShort, zUuid, 10);
    zShort[10] = 0;
    @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
    @ %s(blob_str(&dirname))</h2>
    zSubdirLink = mprintf("%s/dir?ci=%S&amp;name=%T", g.zTop, zUuid, zPrefix);
    if( zD ){







>














|
>
>
>
>
|
>
|
|
>
>


>












<







104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
  int mxLen;
  int nCol, nRow;
  int cnt, i;
  char *zPrefix;
  Stmt q;
  const char *zCI = P("ci");
  int rid = 0;
  char *zUuid = 0;
  Blob content;
  Blob dirname;
  Manifest m;
  const char *zSubdirLink;

  login_check_credentials();
  if( !g.okHistory ){ login_needed(); return; }
  style_header("File List");
  sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
                          pathelementFunc, 0, 0);

  /* If the name= parameter is an empty string, make it a NULL pointer */
  if( zD && strlen(zD)==0 ){ zD = 0; }

  /* If a specific check-in is requested, fetch and parse it.  If the
  ** specific check-in does not exist, clear zCI.  zCI==0 will cause all
  ** files from all check-ins to be displayed.
  */
  if( zCI ){
    if( (rid = name_to_rid(zCI))==0 || content_get(rid, &content)==0 ){
      zCI = 0;  /* No artifact named zCI exists */
    }else if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){
      zCI = 0;  /* The artifact exists but is not a manifest */
    }else{
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    }
  }


  /* Compute the title of the page */  
  blob_zero(&dirname);
  if( zD ){
    blob_append(&dirname, "in directory ", -1);
    hyperlinked_path(zD, &dirname);
    zPrefix = mprintf("%h/", zD);
  }else{
    blob_append(&dirname, "in the top-level directory", -1);
    zPrefix = "";
  }
  if( zCI ){

    char zShort[20];
    memcpy(zShort, zUuid, 10);
    zShort[10] = 0;
    @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
    @ %s(blob_str(&dirname))</h2>
    zSubdirLink = mprintf("%s/dir?ci=%S&amp;name=%T", g.zTop, zUuid, zPrefix);
    if( zD ){
Changes to src/cgi.c.
40
41
42
43
44
45
46



47
48
49
50
51
52
53
#ifdef __EMX__
  typedef int socklen_t;
#endif
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>



#include "cgi.h"

#if INTERFACE
/*
** Shortcuts for cgi_parameter.  P("x") returns the value of query parameter
** or cookie "x", or NULL if there is no such parameter or cookie.  PD("x","y")
** does the same except "y" is returned in place of NULL if there is not match.







>
>
>







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#ifdef __EMX__
  typedef int socklen_t;
#endif
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#if defined (__POCC__)
# undef INTERFACE
#endif
#include "cgi.h"

#if INTERFACE
/*
** Shortcuts for cgi_parameter.  P("x") returns the value of query parameter
** or cookie "x", or NULL if there is no such parameter or cookie.  PD("x","y")
** does the same except "y" is returned in place of NULL if there is not match.
Changes to src/checkin.c.
533
534
535
536
537
538
539













540
541
542
543
544
545
546
                 zInputDate);
  }
  assert( strlen(zDate)==19 );
  assert( zDate[10]==' ' );
  zDate[10] = 'T';
  return zDate;
}














/*
** COMMAND: ci
** COMMAND: commit
**
** Usage: %fossil commit ?OPTIONS? ?FILE...?
**







>
>
>
>
>
>
>
>
>
>
>
>
>







533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
                 zInputDate);
  }
  assert( strlen(zDate)==19 );
  assert( zDate[10]==' ' );
  zDate[10] = 'T';
  return zDate;
}

/*
** Return TRUE (non-zero) if a file named "zFilename" exists in
** the checkout identified by vid.
**
** The original purpose of this routine was to check for the presence of
** a "checked-in" file named "manifest" or "manifest.uuid" so as to avoid
** overwriting that file with automatically generated files.
*/
int file_exists_in_checkout(int vid, const char *zFilename){
  return db_exists("SELECT 1 FROM vfile WHERE vid=%d AND pathname=%Q",
                   vid, zFilename);
}

/*
** COMMAND: ci
** COMMAND: commit
**
** Usage: %fossil commit ?OPTIONS? ?FILE...?
**
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857


858
859
860
861

862
863
864
865
866
867
868
869
870

871
872
873
874
875
876

877
878
879
880
881
882
883
      blob_appendf(&manifest, "T -%F *\n", zTag);
    }
    db_finalize(&q);
  }  
  blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
  md5sum_blob(&manifest, &mcksum);
  blob_appendf(&manifest, "Z %b\n", &mcksum);
  zManifestFile = mprintf("%smanifest", g.zLocalRoot);
  if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
    Blob ans;
    blob_zero(&ans);
    prompt_user("unable to sign manifest.  continue (y/N)? ", &ans);
    if( blob_str(&ans)[0]!='y' ){
      fossil_exit(1);
    }
  }


  blob_write_to_file(&manifest, zManifestFile);
  blob_reset(&manifest);
  blob_read_from_file(&manifest, zManifestFile);
  free(zManifestFile);

  nvid = content_put(&manifest, 0, 0);
  if( nvid==0 ){
    fossil_panic("trouble committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
  manifest_crosslink(nvid, &manifest);
  content_deltify(vid, nvid, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
  printf("New_Version: %s\n", zUuid);

  zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
  blob_zero(&muuid);
  blob_appendf(&muuid, "%s\n", zUuid);
  blob_write_to_file(&muuid, zManifestFile);
  free(zManifestFile);
  blob_reset(&muuid);


  
  /* Update the vfile and vmerge tables */
  db_multi_exec(
    "DELETE FROM vfile WHERE (vid!=%d OR deleted) AND file_is_selected(id);"
    "DELETE FROM vmerge WHERE file_is_selected(id) OR id=0;"
    "UPDATE vfile SET vid=%d;"







<








>
>
|
|
|
|
>









>
|
|
|
|
|
|
>







855
856
857
858
859
860
861

862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
      blob_appendf(&manifest, "T -%F *\n", zTag);
    }
    db_finalize(&q);
  }  
  blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
  md5sum_blob(&manifest, &mcksum);
  blob_appendf(&manifest, "Z %b\n", &mcksum);

  if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
    Blob ans;
    blob_zero(&ans);
    prompt_user("unable to sign manifest.  continue (y/N)? ", &ans);
    if( blob_str(&ans)[0]!='y' ){
      fossil_exit(1);
    }
  }
  if( !file_exists_in_checkout(vid, "manifest") ){
    zManifestFile = mprintf("%smanifest", g.zLocalRoot);
    blob_write_to_file(&manifest, zManifestFile);
    blob_reset(&manifest);
    blob_read_from_file(&manifest, zManifestFile);
    free(zManifestFile);
  }
  nvid = content_put(&manifest, 0, 0);
  if( nvid==0 ){
    fossil_panic("trouble committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
  manifest_crosslink(nvid, &manifest);
  content_deltify(vid, nvid, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
  printf("New_Version: %s\n", zUuid);
  if( !file_exists_in_checkout(vid, "manifest.uuid") ){
    zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
    blob_zero(&muuid);
    blob_appendf(&muuid, "%s\n", zUuid);
    blob_write_to_file(&muuid, zManifestFile);
    free(zManifestFile);
    blob_reset(&muuid);
  }

  
  /* Update the vfile and vmerge tables */
  db_multi_exec(
    "DELETE FROM vfile WHERE (vid!=%d OR deleted) AND file_is_selected(id);"
    "DELETE FROM vmerge WHERE file_is_selected(id) OR id=0;"
    "UPDATE vfile SET vid=%d;"
Changes to src/checkout.c.
95
96
97
98
99
100
101





102
103
104
105
106
107
108
109
110
111


112
113


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136




137
138
139

















140
141
142
143
144
145
146
*/
static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
  db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
                onoff, vid, zFilename);
}

/*





** Read the manifest file given by vid out of the repository
** and store it in the root of the local check-out.
*/
void manifest_to_disk(int vid){
  char *zManFile;
  Blob manifest;
  Blob hash;
  Blob filename;
  int baseLen;
  int i;


  Manifest m;



  blob_zero(&manifest);
  zManFile = mprintf("%smanifest", g.zLocalRoot);
  content_get(vid, &manifest);
  blob_write_to_file(&manifest, zManFile);
  free(zManFile);
  blob_zero(&hash);
  sha1sum_blob(&manifest, &hash);
  zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
  blob_append(&hash, "\n", 1);
  blob_write_to_file(&hash, zManFile);
  free(zManFile);
  blob_reset(&hash);
  manifest_parse(&m, &manifest);
  blob_zero(&filename);
  blob_appendf(&filename, "%s/", g.zLocalRoot);
  baseLen = blob_size(&filename);
  for(i=0; i<m.nFile; i++){ 
    int isExe;
    blob_append(&filename, m.aFile[i].zName, -1);
    isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
    file_setexe(blob_str(&filename), isExe);
    set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
    blob_resize(&filename, baseLen);




  }
  blob_reset(&filename);
  manifest_clear(&m);

















}

/*
** COMMAND: checkout
** COMMAND: co
**
** Usage: %fossil checkout VERSION ?-f|--force? ?--keep?







>
>
>
>
>
|
|








>
>


>
>

<

<
<
<
<
<
<
<
<
<











>
>
>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

124









125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
*/
static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
  db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q",
                onoff, vid, zFilename);
}

/*
** Set or clear the execute permission bit (as appropriate) for all
** files in the current check-out.
**
** If the checkout does not have explicit files named "manifest" and
** "manifest.uuid" then automatically generate files with those names
** containing, respectively, the text of the manifest and the artifact
** ID of the manifest.
*/
void manifest_to_disk(int vid){
  char *zManFile;
  Blob manifest;
  Blob hash;
  Blob filename;
  int baseLen;
  int i;
  int seenManifest = 0;
  int seenManifestUuid = 0;
  Manifest m;

  /* Check the EXE permission status of all files
  */
  blob_zero(&manifest);

  content_get(vid, &manifest);









  manifest_parse(&m, &manifest);
  blob_zero(&filename);
  blob_appendf(&filename, "%s/", g.zLocalRoot);
  baseLen = blob_size(&filename);
  for(i=0; i<m.nFile; i++){ 
    int isExe;
    blob_append(&filename, m.aFile[i].zName, -1);
    isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x");
    file_setexe(blob_str(&filename), isExe);
    set_or_clear_isexe(m.aFile[i].zName, vid, isExe);
    blob_resize(&filename, baseLen);
    if( memcmp(m.aFile[i].zName, "manifest", 8)==0 ){
      if( m.aFile[i].zName[8]==0 ) seenManifest = 1;
      if( strcmp(&m.aFile[i].zName[8], ".uuid")==0 ) seenManifestUuid = 1;
    }
  }
  blob_reset(&filename);
  manifest_clear(&m);

  blob_zero(&manifest);
  content_get(vid, &manifest);
  if( !seenManifest ){
    zManFile = mprintf("%smanifest", g.zLocalRoot);
    blob_write_to_file(&manifest, zManFile);
    free(zManFile);
  }
  if( !seenManifestUuid ){
    blob_zero(&hash);
    sha1sum_blob(&manifest, &hash);
    zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
    blob_append(&hash, "\n", 1);
    blob_write_to_file(&hash, zManFile);
    free(zManFile);
    blob_reset(&hash);
  }
}

/*
** COMMAND: checkout
** COMMAND: co
**
** Usage: %fossil checkout VERSION ?-f|--force? ?--keep?
Changes to src/config.h.
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#if defined( __MINGW32__) ||  defined(__DMC__) || defined(_MSC_VER)
#  if defined(__DMC__)  || defined(_MSC_VER)
     typedef int socklen_t;
#  endif
#  ifndef _WIN32
#    define _WIN32
#  endif
#else
# include <sys/types.h>







|
|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#if defined( __MINGW32__) ||  defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)
#  if defined(__DMC__)  || defined(_MSC_VER) || defined(__POCC__)
     typedef int socklen_t;
#  endif
#  ifndef _WIN32
#    define _WIN32
#  endif
#else
# include <sys/types.h>
Changes to src/info.c.
1304
1305
1306
1307
1308
1309
1310

1311
1312

1313
1314
1315
1316
1317
1318
1319
     { "#c0fff0", "#c0fff0" },
     { "#c0f0ff", "#c0f0ff" },
     { "#d0c0ff", "#d0c0ff" },
     { "#ffc0ff", "#ffc0ff" },
     { "#ffc0d0", "#ffc0d0" },
     { "#fff0c0", "#fff0c0" },
     { "#c0c0c0", "#c0c0c0" },

  };
  int nColor = sizeof(aColor)/sizeof(aColor[0]);

  int i;
  
  login_check_credentials();
  if( !g.okWrite ){ login_needed(); return; }
  rid = name_to_rid(P("r"));
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  zComment = db_text(0, "SELECT coalesce(ecomment,comment)"







>

|
>







1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
     { "#c0fff0", "#c0fff0" },
     { "#c0f0ff", "#c0f0ff" },
     { "#d0c0ff", "#d0c0ff" },
     { "#ffc0ff", "#ffc0ff" },
     { "#ffc0d0", "#ffc0d0" },
     { "#fff0c0", "#fff0c0" },
     { "#c0c0c0", "#c0c0c0" },
     { "custom",  "##"      },
  };
  int nColor = sizeof(aColor)/sizeof(aColor[0])-1;
  int stdClrFound;
  int i;
  
  login_check_credentials();
  if( !g.okWrite ){ login_needed(); return; }
  rid = name_to_rid(P("r"));
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
1330
1331
1332
1333
1334
1335
1336



1337
1338
1339
1340
1341
1342
1343
  zDate = db_text(0, "SELECT datetime(mtime)"
                     "  FROM event WHERE objid=%d", rid);
  if( zDate==0 ) fossil_redirect_home();
  zNewDate = PD("dt",zDate);
  zColor = db_text("", "SELECT bgcolor"
                        "  FROM event WHERE objid=%d", rid);
  zNewColor = PD("clr",zColor);



  fPropagateColor = P("pclr")!=0;
  zNewTagFlag = P("newtag") ? " checked" : "";
  zNewTag = PD("tagname","");
  zNewBrFlag = P("newbr") ? " checked" : "";
  zNewBranch = PD("brname","");
  zCloseFlag = P("close") ? " checked" : "";
  if( P("apply") ){







>
>
>







1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
  zDate = db_text(0, "SELECT datetime(mtime)"
                     "  FROM event WHERE objid=%d", rid);
  if( zDate==0 ) fossil_redirect_home();
  zNewDate = PD("dt",zDate);
  zColor = db_text("", "SELECT bgcolor"
                        "  FROM event WHERE objid=%d", rid);
  zNewColor = PD("clr",zColor);
  if( strcmp(zNewColor,aColor[nColor].zColor)==0 ){
    zNewColor = P("clrcust");
  }
  fPropagateColor = P("pclr")!=0;
  zNewTagFlag = P("newtag") ? " checked" : "";
  zNewTag = PD("tagname","");
  zNewBrFlag = P("newbr") ? " checked" : "";
  zNewBranch = PD("brname","");
  zCloseFlag = P("close") ? " checked" : "";
  if( P("apply") ){
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517


1518
1519
1520
1521
1522
1523
1524
1525













1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
    @ </td></tr></table>
    @ </blockquote>
    @ <hr />
    blob_reset(&suffix);
  }
  @ <p>Make changes to attributes of check-in
  @ [<a href="ci?name=%s(zUuid)">%s(zUuid)</a>]:</p>
  @ <form action="%s(g.zBaseURL)/ci_edit" method="POST">
  login_insert_csrf_secret();
  @ <input type="hidden" name="r" value="%S(zUuid)">
  @ <table border="0" cellspacing="10">

  @ <tr><td align="right" valign="top"><b>User:</b></td>
  @ <td valign="top">
  @   <input type="text" name="u" size="20" value="%h(zNewUser)">
  @ </td></tr>

  @ <tr><td align="right" valign="top"><b>Comment:</b></td>
  @ <td valign="top">
  @ <textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea>
  @ </td></tr>

  @ <tr><td align="right" valign="top"><b>Check-in Time:</b></td>
  @ <td valign="top">
  @   <input type="text" name="dt" size="20" value="%h(zNewDate)">
  @ </td></tr>

  @ <tr><td align="right" valign="top"><b>Background Color:</b></td>
  @ <td valign="top">
  @ <table border=0 cellpadding=0 cellspacing=1>
  @ <tr><td colspan="6" align="left">
  if( fPropagateColor ){
    @ <input type="checkbox" name="pclr" checked>
  }else{
    @ <input type="checkbox" name="pclr">
  }
  @ Propagate color to descendants</input></td></tr>
  @ <tr>
  for(i=0; i<nColor; i++){
    if( aColor[i].zColor[0] ){
      @ <td style="background-color: %h(aColor[i].zColor);">
    }else{
      @ <td>
    }
    if( strcmp(zNewColor, aColor[i].zColor)==0 ){
      @ <input type="radio" name="clr" value="%h(aColor[i].zColor)" checked>


    }else{
      @ <input type="radio" name="clr" value="%h(aColor[i].zColor)">
    }
    @ %h(aColor[i].zCName)</input></td>
    if( (i%6)==5 && i+1<nColor ){
      @ </tr><tr>
    }
  }













  @ </tr>
  @ </table>
  @ </td></tr>

  @ <tr><td align="right" valign="top"><b>Tags:</b></td>
  @ <td valign="top">
  @ <input type="checkbox" name="newtag"%s(zNewTagFlag)>
  @ Add the following new tag name to this check-in:
  @ <input type="text" width="15" name="tagname" value="%h(zNewTag)">
  db_prepare(&q,
     "SELECT tag.tagid, tagname FROM tagxref, tag"
     " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
     " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
     "               ELSE tagname END",
     rid
  );
  while( db_step(&q)==SQLITE_ROW ){
    int tagid = db_column_int(&q, 0);
    const char *zTagName = db_column_text(&q, 1);
    char zLabel[30];
    sprintf(zLabel, "c%d", tagid);
    if( P(zLabel) ){
      @ <br /><input type="checkbox" name="c%d(tagid)" checked>
    }else{
      @ <br /><input type="checkbox" name="c%d(tagid)">
    }
    if( strncmp(zTagName, "sym-", 4)==0 ){
      @ Cancel tag <b>%h(&zTagName[4])</b>
    }else{
      @ Cancel special tag <b>%h(zTagName)</b>
    }
  }
  db_finalize(&q);
  @ </td></tr>

  @ <tr><td align="right" valign="top"><b>Branching:</b></td>
  @ <td valign="top">
  @ <input type="checkbox" name="newbr"%s(zNewBrFlag)>
  @ Make this check-in the start of a new branch named:
  @ <input type="text" width="15" name="brname" value="%h(zNewBranch)">
  @ </td></tr>

  if( is_a_leaf(rid)
   && !db_exists("SELECT 1 FROM tagxref "
                 " WHERE tagid=%d AND rid=%d AND tagtype>0",
                 TAG_CLOSED, rid)
  ){
    @ <tr><td align="right" valign="top"><b>Leaf Closure:</b></td>
    @ <td valign="top">
    @ <input type="checkbox" name="close"%s(zCloseFlag)>
    @ Mark this leaf as "closed" so that it no longer appears on the
    @ "leaves" page and is no longer labeled as a "<b>Leaf</b>".
    @ </td></tr>
  }


  @ <tr><td colspan="2">
  @ <input type="submit" name="preview" value="Preview">
  @ <input type="submit" name="apply" value="Apply Changes">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </td></tr>
  @ </table>
  @ </form>
  style_footer();
}







|

|




|









|




|


|

|

|

|






|
>
>

|

|




>
>
>
>
>
>
>
>
>
>
>
>
>






|

|













|

|












|

|









|







|
|
|


|


1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
    @ </td></tr></table>
    @ </blockquote>
    @ <hr />
    blob_reset(&suffix);
  }
  @ <p>Make changes to attributes of check-in
  @ [<a href="ci?name=%s(zUuid)">%s(zUuid)</a>]:</p>
  @ <form action="%s(g.zBaseURL)/ci_edit" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="hidden" name="r" value="%S(zUuid)" />
  @ <table border="0" cellspacing="10">

  @ <tr><td align="right" valign="top"><b>User:</b></td>
  @ <td valign="top">
  @   <input type="text" name="u" size="20" value="%h(zNewUser)" />
  @ </td></tr>

  @ <tr><td align="right" valign="top"><b>Comment:</b></td>
  @ <td valign="top">
  @ <textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea>
  @ </td></tr>

  @ <tr><td align="right" valign="top"><b>Check-in Time:</b></td>
  @ <td valign="top">
  @   <input type="text" name="dt" size="20" value="%h(zNewDate)" />
  @ </td></tr>

  @ <tr><td align="right" valign="top"><b>Background Color:</b></td>
  @ <td valign="top">
  @ <table border="0" cellpadding="0" cellspacing="1">
  @ <tr><td colspan="6" align="left">
  if( fPropagateColor ){
    @ <input type="checkbox" name="pclr" checked="checked" />
  }else{
    @ <input type="checkbox" name="pclr" />
  }
  @ Propagate color to descendants</td></tr>
  @ <tr>
  for(i=0,stdClrFound=0; i<nColor; i++){
    if( aColor[i].zColor[0] ){
      @ <td style="background-color: %h(aColor[i].zColor);">
    }else{
      @ <td>
    }
    if( strcmp(zNewColor, aColor[i].zColor)==0 ){
      @ <input type="radio" name="clr" value="%h(aColor[i].zColor)"
      @  checked="checked" />
      stdClrFound=1;
    }else{
      @ <input type="radio" name="clr" value="%h(aColor[i].zColor)" />
    }
    @ %h(aColor[i].zCName)</td>
    if( (i%6)==5 && i+1<nColor ){
      @ </tr><tr>
    }
  }
  @ </tr><tr>
  if (stdClrFound){
    @ <td colspan="6">
    @ <input type="radio" name="clr" value="%h(aColor[nColor].zColor)" />
  }else{
    @ <td style="background-color: %h(zNewColor);" colspan="6">
    @ <input type="radio" name="clr" value="%h(aColor[nColor].zColor)"
    @  checked="checked" />
  }
  @ %h(aColor[i].zCName)&nbsp;
  @ <input type="text" name="clrcust" id="clrcust" class="checkinUserColor"
  @  value="%h(stdClrFound?"":zNewColor)" />
  @ </td>
  @ </tr>
  @ </table>
  @ </td></tr>

  @ <tr><td align="right" valign="top"><b>Tags:</b></td>
  @ <td valign="top">
  @ <input type="checkbox" name="newtag"%s(zNewTagFlag) />
  @ Add the following new tag name to this check-in:
  @ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)" />
  db_prepare(&q,
     "SELECT tag.tagid, tagname FROM tagxref, tag"
     " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
     " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
     "               ELSE tagname END",
     rid
  );
  while( db_step(&q)==SQLITE_ROW ){
    int tagid = db_column_int(&q, 0);
    const char *zTagName = db_column_text(&q, 1);
    char zLabel[30];
    sprintf(zLabel, "c%d", tagid);
    if( P(zLabel) ){
      @ <br /><input type="checkbox" name="c%d(tagid)" checked="checked" />
    }else{
      @ <br /><input type="checkbox" name="c%d(tagid)" />
    }
    if( strncmp(zTagName, "sym-", 4)==0 ){
      @ Cancel tag <b>%h(&zTagName[4])</b>
    }else{
      @ Cancel special tag <b>%h(zTagName)</b>
    }
  }
  db_finalize(&q);
  @ </td></tr>

  @ <tr><td align="right" valign="top"><b>Branching:</b></td>
  @ <td valign="top">
  @ <input type="checkbox" name="newbr"%s(zNewBrFlag) />
  @ Make this check-in the start of a new branch named:
  @ <input type="text" style="width:15;" name="brname" value="%h(zNewBranch)" />
  @ </td></tr>

  if( is_a_leaf(rid)
   && !db_exists("SELECT 1 FROM tagxref "
                 " WHERE tagid=%d AND rid=%d AND tagtype>0",
                 TAG_CLOSED, rid)
  ){
    @ <tr><td align="right" valign="top"><b>Leaf Closure:</b></td>
    @ <td valign="top">
    @ <input type="checkbox" name="close"%s(zCloseFlag) />
    @ Mark this leaf as "closed" so that it no longer appears on the
    @ "leaves" page and is no longer labeled as a "<b>Leaf</b>".
    @ </td></tr>
  }


  @ <tr><td colspan="2">
  @ <input type="submit" name="preview" value="Preview" />
  @ <input type="submit" name="apply" value="Apply Changes" />
  @ <input type="submit" name="cancel" value="Cancel" />
  @ </td></tr>
  @ </table>
  @ </div></form>
  style_footer();
}
Changes to src/main.c.
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  static int once = 1;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.cgiOutput && once ){
    once = 0;
    cgi_printf("<p><font color=\"red\">%h</font></p>", z);
    cgi_reply();
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
  db_force_rollback();
  fossil_exit(1);
}
void fossil_fatal(const char *zFormat, ...){
  char *z;
  va_list ap;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.cgiOutput ){
    g.cgiOutput = 0;
    cgi_printf("<p><font color=\"red\">%h</font></p>", z);
    cgi_reply();
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
  db_force_rollback();
  fossil_exit(1);
}







|
















|







288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  static int once = 1;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.cgiOutput && once ){
    once = 0;
    cgi_printf("<p class=\"generalError\">%h</p>", z);
    cgi_reply();
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
  db_force_rollback();
  fossil_exit(1);
}
void fossil_fatal(const char *zFormat, ...){
  char *z;
  va_list ap;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.cgiOutput ){
    g.cgiOutput = 0;
    cgi_printf("<p class=\"generalError\">%h</p>", z);
    cgi_reply();
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
  db_force_rollback();
  fossil_exit(1);
}
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
  if( mainInFatalError ) return;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.cgiOutput ){
    g.cgiOutput = 0;
    cgi_printf("<p><font color=\"red\">%h</font></p>", z);
    cgi_reply();
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
  db_force_rollback();
  fossil_exit(1);
}


/* Print a warning message */
void fossil_warning(const char *zFormat, ...){
  char *z;
  va_list ap;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.cgiOutput ){
    cgi_printf("<p><font color=\"red\">%h</font></p>", z);
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
}

/*
** Return a name for an SQLite error code







|

















|







333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
  if( mainInFatalError ) return;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.cgiOutput ){
    g.cgiOutput = 0;
    cgi_printf("<p class=\"generalError\">%h</p>", z);
    cgi_reply();
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
  db_force_rollback();
  fossil_exit(1);
}


/* Print a warning message */
void fossil_warning(const char *zFormat, ...){
  char *z;
  va_list ap;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.cgiOutput ){
    cgi_printf("<p class=\"generalError\">%h</p>", z);
  }else{
    fprintf(stderr, "%s: %s\n", g.argv[0], z);
  }
}

/*
** Return a name for an SQLite error code
Changes to src/rebuild.c.
399
400
401
402
403
404
405







































406
407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466




















































































    db_end_transaction(0);
    db_multi_exec("VACUUM;");
  }else{
    rebuild_db(0, 1);
    db_end_transaction(0);
  }
}








































/*
** COMMAND: reconstruct
**
** Usage: %fossil reconstruct FILENAME DIRECTORY
**
** This command studies the artifacts (files) in DIRECTORY and
** reconstructs the fossil record from them. It places the new
** fossil repository in FILENAME

**
*/
void reconstruct_cmd(void) {
  char *zPassword;
  DIR *d;
  struct dirent *pEntry;
  Blob aContent; /* content of the just read artifact */
  if( g.argc!=4 ){
    usage("FILENAME DIRECTORY");
  }
  if( file_isdir(g.argv[3])!=1 ){
    printf("\"%s\" is not a directory\n\n", g.argv[3]);
    usage("FILENAME DIRECTORY");
  }
  db_create_repository(g.argv[2]);
  db_open_repository(g.argv[2]);
  db_open_config(0);
  db_begin_transaction();
  db_initial_setup(0, 0, 1);

  d = opendir(g.argv[3]);
  if( d ){
    while( (pEntry=readdir(d))!=0 ){
      Blob path;
      blob_init(&path, 0, 0);
      if( pEntry->d_name[0]=='.' ){
        continue;
      }
      if( file_isdir(pEntry->d_name)==1 ){
        continue;
      }
      blob_appendf(&path, "%s/%s", g.argv[3], pEntry->d_name);
      if( blob_read_from_file(&aContent, blob_str(&path))==-1 ){
        fossil_panic("Some unknown error occurred while reading \"%s\"", blob_str(&path));
      }
      content_put(&aContent, 0, 0);
      blob_reset(&path);
      blob_reset(&aContent);
    }
  }
  else {
    fossil_panic("Encountered error %d while trying to open \"%s\".", errno, g.argv[3]);
  }

  rebuild_db(0, 1);

  db_end_transaction(0);
  printf("project-id: %s\n", db_get("project-code", 0));
  printf("server-id: %s\n", db_get("server-code", 0));
  zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
  printf("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword);
}



























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








|
>




<
<
<













|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458



459
460
461
462
463
464
465
466
467
468
469
470
471
472






















473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
    db_end_transaction(0);
    db_multi_exec("VACUUM;");
  }else{
    rebuild_db(0, 1);
    db_end_transaction(0);
  }
}

/* 
** help function for reconstruct for recursiv directory
** reading.
*/
void recon_read_dir(char * zPath){
  DIR *d;
  struct dirent *pEntry;
  Blob aContent; /* content of the just read artifact */

  d = opendir(zPath);
  if( d ){
    while( (pEntry=readdir(d))!=0 ){
      Blob path;
      char *zSubpath;

      if( pEntry->d_name[0]=='.' ){
        continue;
      }
      zSubpath = mprintf("%s/%s",zPath,pEntry->d_name);
      if( file_isdir(zSubpath)==1 ){
        recon_read_dir(zSubpath);
      }
      blob_init(&path, 0, 0);
      blob_appendf(&path, "%s", zSubpath);
      if( blob_read_from_file(&aContent, blob_str(&path))==-1 ){
        fossil_panic("some unknown error occurred while reading \"%s\"", 
                     blob_str(&path));
      }
      content_put(&aContent, 0, 0);
      blob_reset(&path);
      blob_reset(&aContent);
      free(zSubpath);
    }
  }else {
    fossil_panic("encountered error %d while trying to open \"%s\".",
                  errno, g.argv[3]);
  }
}

/*
** COMMAND: reconstruct
**
** Usage: %fossil reconstruct FILENAME DIRECTORY
**
** This command studies the artifacts (files) in DIRECTORY and
** reconstructs the fossil record from them. It places the new
** fossil repository in FILENAME. Subdirectories are read, files
** with leading '.' in the filename are ignored.
**
*/
void reconstruct_cmd(void) {
  char *zPassword;



  if( g.argc!=4 ){
    usage("FILENAME DIRECTORY");
  }
  if( file_isdir(g.argv[3])!=1 ){
    printf("\"%s\" is not a directory\n\n", g.argv[3]);
    usage("FILENAME DIRECTORY");
  }
  db_create_repository(g.argv[2]);
  db_open_repository(g.argv[2]);
  db_open_config(0);
  db_begin_transaction();
  db_initial_setup(0, 0, 1);

  recon_read_dir(g.argv[3]);























  rebuild_db(0, 1);

  db_end_transaction(0);
  printf("project-id: %s\n", db_get("project-code", 0));
  printf("server-id: %s\n", db_get("server-code", 0));
  zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
  printf("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword);
}

/*
** COMMAND: deconstruct
**
** Usage %fossil deconstruct ?-R|--repository REPOSITORY? ?-L|--prefixlength N? DESTINATION
**
** This command exports all artifacts of o given repository and
** writes all artifacts to the file system. The DESTINATION directory
** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where
** AABBBBBBBBB.. is the 40 character artifact ID, AA the first 2 characters.
** If -L|--prefixlength is given, the length (default 2) of the directory
** prefix can be set to 0,1,..,9 characters.
*/
void deconstruct_cmd(void){
  const char *zDestDir;
  const char *zPrefixOpt;
  int         prefixLength = 0;
  char       *zAFileOutFormat;
  Stmt        q;

  /* check number of arguments */
  if( (g.argc != 3) && (g.argc != 5)  && (g.argc != 7)){
    usage ("?-R|--repository REPOSITORY? ?-L|--prefixlength N? DESTINATION");
  }
  /* get and check argument destination directory */
  zDestDir = g.argv[g.argc-1];
  if( !*zDestDir  || !file_isdir(zDestDir)) {
    fossil_panic("DESTINATION(%s) is not a directory!",zDestDir);
  }
  /* get and check prefix length argument and build format string */
  zPrefixOpt=find_option("prefixlength","L",1);
  if( !zPrefixOpt ){
    prefixLength = 2;
  }else{
    if( zPrefixOpt[0]>='0' && zPrefixOpt[0]<='9' && !zPrefixOpt[1] ){
      prefixLength = (int)(*zPrefixOpt-'0');
    }else{
      fossil_panic("N(%s) is not a a valid prefix length!",zPrefixOpt);
    }
  }
  if( prefixLength ){
    zAFileOutFormat = mprintf("%%s/%%.%ds/%%s",prefixLength);
  }else{
    zAFileOutFormat = mprintf("%%s/%%s");
  }
#ifndef _WIN32
  if( access(zDestDir, W_OK) ){
    fossil_panic("DESTINATION(%s) is not writeable!",zDestDir);
  }
#else
  /* write access on windows is not checked, errors will be
  ** dected on blob_write_to_file
  */
#endif
  /* open repository and open query for all artifacts */
  db_find_and_open_repository(1);
  db_prepare(&q, "SELECT rid,uuid FROM blob");
  /* loop over artifacts and write them to single files */
  while( db_step(&q)==SQLITE_ROW ){
    int         aRid;
    const char *zAUuid;
    char       *zAFName;
    Blob        zACont;

    /* get data from query */
    aRid   = db_column_int (&q, 0);
    zAUuid = db_column_text(&q, 1);

    /* construct output filename */
    zAFName = mprintf(zAFileOutFormat, zDestDir, zAUuid, zAUuid + prefixLength);

    /* read artifact contents from db and write to file */
    content_get(aRid,&zACont);
    blob_write_to_file(&zACont,zAFName);
    blob_reset(&zACont);

    /* free artifact filename string */
    free(zAFName);
  }
  /* close query statement */
  db_finalize(&q);
  /* free filename format string */
  free(zAFileOutFormat);
}
Changes to src/report.c.
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
    return;
  }else if( rn>0 && P("del1") ){
    zTitle = db_text(0, "SELECT title FROM reportfmt "
                         "WHERE rn=%d", rn);
    if( zTitle==0 ) cgi_redirect("reportlist");

    style_header("Are You Sure?");
    @ <form action="rptedit" method="POST">
    @ <p>You are about to delete all traces of the report
    @ <strong>%h(zTitle)</strong> from
    @ the database.  This is an irreversible operation.  All records
    @ related to this report will be removed and cannot be recovered.</p>
    @
    @ <input type="hidden" name="rn" value="%d(rn)">
    login_insert_csrf_secret();







|







326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
    return;
  }else if( rn>0 && P("del1") ){
    zTitle = db_text(0, "SELECT title FROM reportfmt "
                         "WHERE rn=%d", rn);
    if( zTitle==0 ) cgi_redirect("reportlist");

    style_header("Are You Sure?");
    @ <form action="rptedit" method="post">
    @ <p>You are about to delete all traces of the report
    @ <strong>%h(zTitle)</strong> from
    @ the database.  This is an irreversible operation.  All records
    @ related to this report will be removed and cannot be recovered.</p>
    @
    @ <input type="hidden" name="rn" value="%d(rn)">
    login_insert_csrf_secret();
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
  if( zOwner==0 ) zOwner = g.zLogin;
  style_submenu_element("Cancel", "Cancel", "reportlist");
  if( rn>0 ){
    style_submenu_element("Delete", "Delete", "rptedit?rn=%d&amp;del1=1", rn);
  }
  style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
  if( zErr ){
    @ <blockquote><font color="#ff0000"><b>%h(zErr)</b></font></blockquote>
  }
  @ <form action="rptedit" method="POST">
  @ <input type="hidden" name="rn" value="%d(rn)">
  @ <p>Report Title:<br />
  @ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
  @ <p>Enter a complete SQL query statement against the "TICKET" table:<br />
  @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
  @ </p>
  login_insert_csrf_secret();
  if( g.okAdmin ){
    @ <p>Report owner:
    @ <input type="text" name="w" size="20" value="%h(zOwner)">
    @ </p>
  } else {
    @ <input type="hidden" name="w" value="%h(zOwner)">
  }
  @ <p>Enter an optional color key in the following box.  (If blank, no
  @ color key is displayed.)  Each line contains the text for a single
  @ entry in the key.  The first token of each line is the background
  @ color for that line.<br />
  @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
  @ </p>
  if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){
    @ <p>This report format is owned by %h(zOwner).  You are not allowed
    @ to change it.</p>
    @ </form>
    report_format_hints();
    style_footer();
    return;
  }
  @ <input type="submit" value="Apply Changes">
  if( rn>0 ){
    @ <input type="submit" value="Delete This Report" name="del1">
  }
  @ </form>
  report_format_hints();
  style_footer();
}

/*
** Output a bunch of text that provides information about report
** formats







|

|
|

|






|


|















|

|

|







395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
  if( zOwner==0 ) zOwner = g.zLogin;
  style_submenu_element("Cancel", "Cancel", "reportlist");
  if( rn>0 ){
    style_submenu_element("Delete", "Delete", "rptedit?rn=%d&amp;del1=1", rn);
  }
  style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
  if( zErr ){
    @ <blockquote class="reportError">%h(zErr)</blockquote>
  }
  @ <form action="rptedit" method="post"><div>
  @ <input type="hidden" name="rn" value="%d(rn)" />
  @ <p>Report Title:<br />
  @ <input type="text" name="t" value="%h(zTitle)" size="60" /></p>
  @ <p>Enter a complete SQL query statement against the "TICKET" table:<br />
  @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
  @ </p>
  login_insert_csrf_secret();
  if( g.okAdmin ){
    @ <p>Report owner:
    @ <input type="text" name="w" size="20" value="%h(zOwner)" />
    @ </p>
  } else {
    @ <input type="hidden" name="w" value="%h(zOwner)" />
  }
  @ <p>Enter an optional color key in the following box.  (If blank, no
  @ color key is displayed.)  Each line contains the text for a single
  @ entry in the key.  The first token of each line is the background
  @ color for that line.<br />
  @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
  @ </p>
  if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){
    @ <p>This report format is owned by %h(zOwner).  You are not allowed
    @ to change it.</p>
    @ </form>
    report_format_hints();
    style_footer();
    return;
  }
  @ <input type="submit" value="Apply Changes" />
  if( rn>0 ){
    @ <input type="submit" value="Delete This Report" name="del1" />
  }
  @ </div></form>
  report_format_hints();
  style_footer();
}

/*
** Output a bunch of text that provides information about report
** formats
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
  @
  @ <h3>Examples</h3>
  @ <p>In this example, the first column in the result set is named
  @ "bgcolor".  The value of this column is not displayed.  Instead, it
  @ selects the background color of each row based on the TICKET.STATUS
  @ field of the database.  The color key at the right shows the various
  @ color codes.</p>
  @ <table align="right" style="margin: 0 5px;" border=1 cellspacing=0 width=125>
  @ <tr bgcolor="#f2dcdc"><td align="center">new or active</td></tr>
  @ <tr bgcolor="#e8e8bd"><td align="center">review</td></tr>
  @ <tr bgcolor="#cfe8bd"><td align="center">fixed</td></tr>
  @ <tr bgcolor="#bde5d6"><td align="center">tested</td></tr>
  @ <tr bgcolor="#cacae5"><td align="center">defer</td></tr>
  @ <tr bgcolor="#c8c8c8"><td align="center">closed</td></tr>
  @ </table>
  @ <blockquote><pre>
  @ SELECT
  @   CASE WHEN status IN ('new','active') THEN '#f2dcdc'
  @        WHEN status='review' THEN '#e8e8bd'
  @        WHEN status='fixed' THEN '#cfe8bd'
  @        WHEN status='tested' THEN '#bde5d6'







|
|
|
|
|
|
|







476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
  @
  @ <h3>Examples</h3>
  @ <p>In this example, the first column in the result set is named
  @ "bgcolor".  The value of this column is not displayed.  Instead, it
  @ selects the background color of each row based on the TICKET.STATUS
  @ field of the database.  The color key at the right shows the various
  @ color codes.</p>
  @ <table class="rpteditex">
  @ <tr style="background-color:#f2dcdc;"><td class="rpteditex">new or active</td></tr>
  @ <tr style="background-color:#e8e8bd;"><td class="rpteditex">review</td></tr>
  @ <tr style="background-color:#cfe8bd;"><td class="rpteditex">fixed</td></tr>
  @ <tr style="background-color:#bde5d6;"><td class="rpteditex">tested</td></tr>
  @ <tr style="background-color:#cacae5;"><td class="rpteditex">defer</td></tr>
  @ <tr style="background-color:#c8c8c8;"><td class="rpteditex">closed</td></tr>
  @ </table>
  @ <blockquote><pre>
  @ SELECT
  @   CASE WHEN status IN ('new','active') THEN '#f2dcdc'
  @        WHEN status='review' THEN '#e8e8bd'
  @        WHEN status='fixed' THEN '#cfe8bd'
  @        WHEN status='tested' THEN '#bde5d6'
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
  @   priority AS 'Pri',
  @   title AS 'Title'
  @ FROM ticket
  @ </pre></blockquote>
  @ <p>To base the background color on the TICKET.PRIORITY or
  @ TICKET.SEVERITY fields, substitute the following code for the
  @ first column of the query:</p>
  @ <table align="right" style="margin: 0 5px;" border=1 cellspacing=0 width=125>
  @ <tr bgcolor="#f2dcdc"><td align="center">1</td></tr>
  @ <tr bgcolor="#e8e8bd"><td align="center">2</td></tr>
  @ <tr bgcolor="#cfe8bd"><td align="center">3</td></tr>
  @ <tr bgcolor="#cacae5"><td align="center">4</td></tr>
  @ <tr bgcolor="#c8c8c8"><td align="center">5</td></tr>
  @ </table>
  @ <blockquote><pre>
  @ SELECT
  @   CASE priority WHEN 1 THEN '#f2dcdc'
  @        WHEN 2 THEN '#e8e8bd'
  @        WHEN 3 THEN '#cfe8bd'
  @        WHEN 4 THEN '#cacae5'







|
|
|
|
|
|







508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
  @   priority AS 'Pri',
  @   title AS 'Title'
  @ FROM ticket
  @ </pre></blockquote>
  @ <p>To base the background color on the TICKET.PRIORITY or
  @ TICKET.SEVERITY fields, substitute the following code for the
  @ first column of the query:</p>
  @ <table class="rpteditex">
  @ <tr style="background-color:#f2dcdc;"><td class="rpteditex">1</td></tr>
  @ <tr style="background-color:#e8e8bd;"><td class="rpteditex">2</td></tr>
  @ <tr style="background-color:#cfe8bd;"><td class="rpteditex">3</td></tr>
  @ <tr style="background-color:#cacae5;"><td class="rpteditex">4</td></tr>
  @ <tr style="background-color:#c8c8c8;"><td class="rpteditex">5</td></tr>
  @ </table>
  @ <blockquote><pre>
  @ SELECT
  @   CASE priority WHEN 1 THEN '#f2dcdc'
  @        WHEN 2 THEN '#e8e8bd'
  @        WHEN 3 THEN '#cfe8bd'
  @        WHEN 4 THEN '#cacae5'
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
    sState.rn = rn;
    sState.nCount = 0;
    sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
    sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2);
    sqlite3_set_authorizer(g.db, 0, 0);
    @ </table>
    if( zErr1 ){
      @ <p><font color="red"><b>Error: %h(zErr1)</b></font></p>
    }else if( zErr2 ){
      @ <p><font color="red"><b>Error: %h(zErr2)</b></font></p>
    }
    style_footer();
  }else{
    sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
    sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2);
    sqlite3_set_authorizer(g.db, 0, 0);
    cgi_set_content_type("text/plain");
  }
}







|

|









928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
    sState.rn = rn;
    sState.nCount = 0;
    sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
    sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2);
    sqlite3_set_authorizer(g.db, 0, 0);
    @ </table>
    if( zErr1 ){
      @ <p class="reportError">Error: %h(zErr1)</p>
    }else if( zErr2 ){
      @ <p class="reportError">Error: %h(zErr2)</p>
    }
    style_footer();
  }else{
    sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
    sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2);
    sqlite3_set_authorizer(g.db, 0, 0);
    cgi_set_content_type("text/plain");
  }
}
Changes to src/setup.c.
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
  }
  @ </table>
  @ </div></form>
  @ </div>
  @ <h2>Privileges And Capabilities:</h2>
  @ <ul>
  if( higherUser ){
    @ <li><p><font color="blue"><b>
    @ User %h(zLogin) has Setup privileges and you only have Admin privileges
    @ so you are not permitted to make changes to %h(zLogin).
    @ </b></font></p></li>
    @
  }
  @ <li><p>
  @ The <span class="capability">Setup</span> user can make arbitrary
  @ configuration changes. An <span class="usertype">Admin</span> user
  @ can add other users and change user privileges
  @ and reset user passwords.  Both automatically get all other privileges







|


|







505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
  }
  @ </table>
  @ </div></form>
  @ </div>
  @ <h2>Privileges And Capabilities:</h2>
  @ <ul>
  if( higherUser ){
    @ <li><p class=missingPriv">
    @ User %h(zLogin) has Setup privileges and you only have Admin privileges
    @ so you are not permitted to make changes to %h(zLogin).
    @ </p></li>
    @
  }
  @ <li><p>
  @ The <span class="capability">Setup</span> user can make arbitrary
  @ configuration changes. An <span class="usertype">Admin</span> user
  @ can add other users and change user privileges
  @ and reset user passwords.  Both automatically get all other privileges
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
    if( pSet->width==0 ){
      onoff_attribute(pSet->name, pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name,
                      pSet->def[0]=='1');
      @ <br />
    }
  }
  @ </td><td width="30"></td><td valign="top">
  for(pSet=ctrlSettings; pSet->name!=0; pSet++){
    if( pSet->width!=0 ){
      entry_attribute(pSet->name, /*pSet->width*/ 40, pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name,
                      (char*)pSet->def);
      @ <br />
    }
  }
  @ </tr></table>
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ </div></form>
  @ <hr /><p>
  @ These settings work in the same way, as the <kbd>set</kbd> commandline:<br>
  @ <pre>%s(zHelp_setting_cmd)</pre></p>
  db_end_transaction(0);
  style_footer();
}

/*
** WEBPAGE: setup_config
*/







|








|



|
|







879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
    if( pSet->width==0 ){
      onoff_attribute(pSet->name, pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name,
                      pSet->def[0]=='1');
      @ <br />
    }
  }
  @ </td><td style="width: 30;"></td><td valign="top">
  for(pSet=ctrlSettings; pSet->name!=0; pSet++){
    if( pSet->width!=0 ){
      entry_attribute(pSet->name, /*pSet->width*/ 40, pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name,
                      (char*)pSet->def);
      @ <br />
    }
  }
  @ </td></tr></table>
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ </div></form>
  @ <hr /><p>
  @ These settings work in the same way, as the <kbd>set</kbd> commandline:<br />
  @ </p><pre>%s(zHelp_setting_cmd)</pre>
  db_end_transaction(0);
  style_footer();
}

/*
** WEBPAGE: setup_config
*/
Changes to src/shun.c.
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
    }
  }
  style_header("Shunned Artifacts");
  if( zUuid && P("sub") ){
    login_verify_csrf_secret();
    db_multi_exec("DELETE FROM shun WHERE uuid='%s'", zUuid);
    if( db_exists("SELECT 1 FROM blob WHERE uuid='%s'", zUuid) ){
      @ <p><font color="blue">Artifact 
      @ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> is no
      @ longer being shunned.</font></p>
    }else{
      @ <p><font color="blue">Artifact %s(zUuid)</a> will no longer
      @ be shunned.  But it does not exist in the repository.  It
      @ may be necessary to rebuild the repository using the
      @ <b>fossil rebuild</b> command-line before the artifact content
      @ can pulled in from other respositories.</font></p>
    }
  }
  if( zUuid && P("add") ){
    login_verify_csrf_secret();
    db_multi_exec("INSERT OR IGNORE INTO shun VALUES('%s')", zUuid);
    @ <p><font color="blue">Artifact
    @ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> has been
    @ shunned.  It will no longer be pushed.
    @ It will be removed from the repository the next time the respository
    @ is rebuilt using the <b>fossil rebuild</b> command-line</font></p>
  }
  @ <p>A shunned artifact will not be pushed nor accepted in a pull and the
  @ artifact content will be purged from the repository the next time the
  @ repository is rebuilt.  A list of shunned artifacts can be seen at the
  @ bottom of this page.</p>
  @ 
  @ <a name="addshun"></a>







|

|

|



|





|



|







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
    }
  }
  style_header("Shunned Artifacts");
  if( zUuid && P("sub") ){
    login_verify_csrf_secret();
    db_multi_exec("DELETE FROM shun WHERE uuid='%s'", zUuid);
    if( db_exists("SELECT 1 FROM blob WHERE uuid='%s'", zUuid) ){
      @ <p class="noMoreShun">Artifact 
      @ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> is no
      @ longer being shunned.</p>
    }else{
      @ <p class="noMoreShun">Artifact %s(zUuid) will no longer
      @ be shunned.  But it does not exist in the repository.  It
      @ may be necessary to rebuild the repository using the
      @ <b>fossil rebuild</b> command-line before the artifact content
      @ can pulled in from other respositories.</p>
    }
  }
  if( zUuid && P("add") ){
    login_verify_csrf_secret();
    db_multi_exec("INSERT OR IGNORE INTO shun VALUES('%s')", zUuid);
    @ <p class="shunned">Artifact
    @ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> has been
    @ shunned.  It will no longer be pushed.
    @ It will be removed from the repository the next time the respository
    @ is rebuilt using the <b>fossil rebuild</b> command-line</p>
  }
  @ <p>A shunned artifact will not be pushed nor accepted in a pull and the
  @ artifact content will be purged from the repository the next time the
  @ repository is rebuilt.  A list of shunned artifacts can be seen at the
  @ bottom of this page.</p>
  @ 
  @ <a name="addshun"></a>
Changes to src/skins.c.
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zN = db_column_text(&q, 0);
    const char *zV = db_column_text(&q, 1);
    if( strcmp(zV, zCurrent)==0 ){
      @ <li><p>%h(zN).&nbsp;&nbsp;  <b>Currently In Use</b></p>
    }else{
      @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
      @ %h(zN).&nbsp;&nbsp; 
      @ <input type="hidden" name="sn" value="%h(zN)">
      @ <input type="submit" name="load" value="Use This Skin">
      @ <input type="submit" name="del1" value="Delete This Skin">
      @ </form></li>
    }
  }







|







828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zN = db_column_text(&q, 0);
    const char *zV = db_column_text(&q, 1);
    if( strcmp(zV, zCurrent)==0 ){
      @ <li><p>%h(zN).&nbsp;&nbsp;  <b>Currently In Use</b></p>
    }else{
      @ <li><form action="%s(g.zBaseURL)/setup_skin" method="post">
      @ %h(zN).&nbsp;&nbsp; 
      @ <input type="hidden" name="sn" value="%h(zN)">
      @ <input type="submit" name="load" value="Use This Skin">
      @ <input type="submit" name="del1" value="Delete This Skin">
      @ </form></li>
    }
  }
Changes to src/style.c.
38
39
40
41
42
43
44





45
46
47
48
49
50
51

/*
** Remember that the header has been generated.  The footer is omitted
** if an error occurs before the header.
*/
static int headerHasBeenGenerated = 0;






/*
** Add a new element to the submenu
*/
void style_submenu_element(
  const char *zLabel,
  const char *zTitle,
  const char *zLink,







>
>
>
>
>







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

/*
** Remember that the header has been generated.  The footer is omitted
** if an error occurs before the header.
*/
static int headerHasBeenGenerated = 0;

/*
** remember, if a sidebox was used
*/
static int sideboxUsed = 0;

/*
** Add a new element to the submenu
*/
void style_submenu_element(
  const char *zLabel,
  const char *zTitle,
  const char *zLink,
104
105
106
107
108
109
110

111
112
113
114
115
116
117
  if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
  Th_Render(zHeader);
  if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1);
  Th_Unstore("title");   /* Avoid collisions with ticket field names */
  cgi_destination(CGI_BODY);
  g.cgiOutput = 1;
  headerHasBeenGenerated = 1;

}

/*
** Draw the footer at the bottom of the page.
*/
void style_footer(void){
  const char *zFooter;







>







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
  Th_Render(zHeader);
  if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1);
  Th_Unstore("title");   /* Avoid collisions with ticket field names */
  cgi_destination(CGI_BODY);
  g.cgiOutput = 1;
  headerHasBeenGenerated = 1;
  sideboxUsed = 0;
}

/*
** Draw the footer at the bottom of the page.
*/
void style_footer(void){
  const char *zFooter;
136
137
138
139
140
141
142

143
144
145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

168
169
170
171
172
173
174
      }
    }
    @ </div>
  }
  @ <div class="content">
  cgi_destination(CGI_BODY);


  /* Put the footer at the bottom of the page.
  ** the additional clear/both is needed to extend the content
  ** part to the end of an optional sidebox.
  */
  @ <div style="clear: both;"></div>

  @ </div>
  zFooter = db_get("footer", (char*)zDefaultFooter);
  if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
  Th_Render(zFooter);
  if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
  
  /* Render trace log if TH1 tracing is enabled. */
  if( g.thTrace ){
    cgi_append_content("<font color=\"red\"><hr />\n", -1);
    cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
    cgi_append_content("</font>\n", -1);
  }
}

/*
** Begin a side-box on the right-hand side of a page.  The title and
** the width of the box are given as arguments.  The width is usually
** a percentage of total screen width.
*/
void style_sidebox_begin(const char *zTitle, const char *zWidth){

  @ <div class="sidebox" style="width:%s(zWidth)">
  @ <div class="sideboxTitle">%h(zTitle)</div>
}

/* End the side-box
*/
void style_sidebox_end(void){







>
|
|
|
|
|
>








|

|









>







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
      }
    }
    @ </div>
  }
  @ <div class="content">
  cgi_destination(CGI_BODY);

  if (sideboxUsed) {
    /* Put the footer at the bottom of the page.
    ** the additional clear/both is needed to extend the content
    ** part to the end of an optional sidebox.
    */
    @ <div class="endContent"></div>
  }
  @ </div>
  zFooter = db_get("footer", (char*)zDefaultFooter);
  if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
  Th_Render(zFooter);
  if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
  
  /* Render trace log if TH1 tracing is enabled. */
  if( g.thTrace ){
    cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
    cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
    cgi_append_content("</span>\n", -1);
  }
}

/*
** Begin a side-box on the right-hand side of a page.  The title and
** the width of the box are given as arguments.  The width is usually
** a percentage of total screen width.
*/
void style_sidebox_begin(const char *zTitle, const char *zWidth){
  sideboxUsed = 1;
  @ <div class="sidebox" style="width:%s(zWidth)">
  @ <div class="sideboxTitle">%h(zTitle)</div>
}

/* End the side-box
*/
void style_sidebox_end(void){
358
359
360
361
362
363
364

365
366
367
368
369
370
371
@   float: left;
@   clear: left;
@   white-space: nowrap;
@ }
@
@ /* The footer at the very bottom of the page */
@ div.footer {

@   font-size: 0.8em;
@   margin-top: 12px;
@   padding: 5px 10px 5px 10px;
@   text-align: right;
@   background-color: #558195;
@   color: white;
@ }







>







367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
@   float: left;
@   clear: left;
@   white-space: nowrap;
@ }
@
@ /* The footer at the very bottom of the page */
@ div.footer {
@   clear: both;
@   font-size: 0.8em;
@   margin-top: 12px;
@   padding: 5px 10px 5px 10px;
@   text-align: right;
@   background-color: #558195;
@   color: white;
@ }
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
    "captcha display options",
    @   text-align: center;
  },
  { "table.captcha",
    "format for the layout table, used for the captcha display",
    @   margin: auto;
    @   padding: 10px;
    @   border-width: 1;
    @   border-style: double;
    @   bordor-color: black;
  },
  { "td.login_out_label",
    "format for the label cells in the login/out table",
    @   text-align: center;
  },
  { "span.loginError",
    "format for login error messages",







|

|







529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
    "captcha display options",
    @   text-align: center;
  },
  { "table.captcha",
    "format for the layout table, used for the captcha display",
    @   margin: auto;
    @   padding: 10px;
    @   border-width: 4px;
    @   border-style: double;
    @   border-color: black;
  },
  { "td.login_out_label",
    "format for the label cells in the login/out table",
    @   text-align: center;
  },
  { "span.loginError",
    "format for login error messages",
627
628
629
630
631
632
633




634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652


































































653
654
655
656
657
658
659
  { "span.usertype:before",
    "leading text for user types, mentioned on the user edit page",
    @   content:"'";
  },
  { "span.usertype:after",
    "trailing text for user types, mentioned on the user edit page",
    @   content:"'";




  },
  { "span.wikiruleHead",
    "format for leading text in wikirules definitions",
    @   font-weight: bold;
  },
  { "td.tktDspLabel",
    "format for labels on ticket display page",
    @   text-align: right;
  },
  { "td.tktDspValue",
    "format for values on ticket display page",
    @   text-align: left;
    @   vertical-align: top;
    @   background-color: #d0d0d0;
  },
  { "span.tktError",
    "format for ticket error messages",
    @   color: red;
    @   font-weight: bold;


































































  },
  { 0,
    0,
    0
  }
};








>
>
>
>



















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
  { "span.usertype:before",
    "leading text for user types, mentioned on the user edit page",
    @   content:"'";
  },
  { "span.usertype:after",
    "trailing text for user types, mentioned on the user edit page",
    @   content:"'";
  },
  { "p.missingPriv",
    "format for missing priviliges note on user setup page",
    @  color: blue;
  },
  { "span.wikiruleHead",
    "format for leading text in wikirules definitions",
    @   font-weight: bold;
  },
  { "td.tktDspLabel",
    "format for labels on ticket display page",
    @   text-align: right;
  },
  { "td.tktDspValue",
    "format for values on ticket display page",
    @   text-align: left;
    @   vertical-align: top;
    @   background-color: #d0d0d0;
  },
  { "span.tktError",
    "format for ticket error messages",
    @   color: red;
    @   font-weight: bold;
  },
  { "table.rpteditex",
    "format for example tables on the report edit page",
    @   float: right;
    @   margin: 0;
    @   padding: 0;
    @   width: 125px;
    @   text-align: center;
    @   border-collapse: collapse;
    @   border-spacing: 0;
  },
  { "td.rpteditex",
    "format for example table cells on the report edit page",
    @   border-width: thin;
    @   border-color: #000000;
    @   border-style: solid;
  },
  { "input.checkinUserColor",
    "format for user color input on checkin edit page",
    @ # no special definitions, class defined, to enable color pickers, f.e.:
    @ #  add the color picker found at http:jscolor.com  as java script include
    @ #  to the header and configure the java script file with
    @ #   1. use as bindClass :checkinUserColor
    @ #   2. change the default hash adding behaviour to ON
    @ #  or change the class defition of element identified by id="clrcust"
    @ #  to a standard jscolor definition with java script in the footer.
  },
  { "div.endContent",
    "format for end of content area, to be used to clear page flow(sidebox on branch,..",
    @   clear: both;
  },
  { "p.generalError",
    "format for general errors",
    @   color: red;
  },
  { "p.tktsetupError",
    "format for tktsetup errors",
    @   color: red;
    @   font-weight: bold;
  },
  { "p.thmainError",
    "format for th script errors",
    @   color: red;
    @   font-weight: bold;
  },
  { "span.thTrace",
    "format for th script trace messages",
    @   color: red;
  },
  { "p:reportError",
    "format for report configuration errors",
    @   color: red;
    @   font-weight: bold;
  },
  { "blockquote.reportError",
    "format for report configuration errors",
    @   color: red;
    @   font-weight: bold;
  },
  { "p.noMoreShun",
    "format for artifact lines, no longer shunned",
    @   color: blue;
  },
  { "p.shunned",
    "format for artifact lines beeing shunned",
    @   color: blue;
  },
  { 0,
    0,
    0
  }
};

Changes to src/th_main.c.
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
      if( z[0] ){ z += 6; }
      i = 0;
    }else{
      i++;
    }
  }
  if( rc==TH_ERROR ){
    sendText("<hr><p><font color=\"red\"><b>ERROR: ", -1, 0);
    zResult = (char*)Th_GetResult(g.interp, &n);
    sendText((char*)zResult, n, 1);
    sendText("</b></font></p>", -1, 0);
  }else{
    sendText(z, i, 0);
  }
  return rc;
}

/*







|


|







502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
      if( z[0] ){ z += 6; }
      i = 0;
    }else{
      i++;
    }
  }
  if( rc==TH_ERROR ){
    sendText("<hr><p class=\"thmainError\">ERROR: ", -1, 0);
    zResult = (char*)Th_GetResult(g.interp, &n);
    sendText((char*)zResult, n, 1);
    sendText("</p>", -1, 0);
  }else{
    sendText(z, i, 0);
  }
  return rc;
}

/*
Changes to src/tkt.c.
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
                zName);
  if( nRec==0 ){
    @ <span class="tktError">No such ticket: \"%h(zName)\"</span>
    style_footer();
    return;
  }
  if( nRec>1 ){
    @ <span class="tktError"><b>%d(nRec) tickets begin with:
    @ \"%h(zName)\"</span>
    style_footer();
    return;
  }
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  ticket_init();
  getAllTicketFields();







|







565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
                zName);
  if( nRec==0 ){
    @ <span class="tktError">No such ticket: \"%h(zName)\"</span>
    style_footer();
    return;
  }
  if( nRec>1 ){
    @ <span class="tktError">%d(nRec) tickets begin with:
    @ \"%h(zName)\"</span>
    style_footer();
    return;
  }
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  ticket_init();
  getAllTicketFields();
Changes to src/tktsetup.c.
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
    db_unset(zDbField, 0);
    if( xRebuild ) xRebuild();
    z = zDfltValue;
  }else if( isSubmit ){
    char *zErr = 0;
    login_verify_csrf_secret();
    if( xText && (zErr = xText(z))!=0 ){
      @ <p><font color="red"><b>ERROR: %h(zErr)</b></font></p>
    }else{
      db_set(zDbField, z, 0);
      if( xRebuild ) xRebuild();
      cgi_redirect("tktsetup");
    }
  }
  @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="post"><div>







|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
    db_unset(zDbField, 0);
    if( xRebuild ) xRebuild();
    z = zDfltValue;
  }else if( isSubmit ){
    char *zErr = 0;
    login_verify_csrf_secret();
    if( xText && (zErr = xText(z))!=0 ){
      @ <p class="tktsetupError">ERROR: %h(zErr)</p>
    }else{
      db_set(zDbField, z, 0);
      if( xRebuild ) xRebuild();
      cgi_redirect("tktsetup");
    }
  }
  @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="post"><div>
Changes to src/wiki.c.
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
    appendRemark(&preview);
    @ Preview:<hr>
    wiki_convert(&preview, 0, 0);
    @ <hr>
    blob_reset(&preview);
  }
  zUser = PD("u", g.zLogin);
  @ <form method="POST" action="%s(g.zBaseURL)/wikiappend">
  login_insert_csrf_secret();
  @ <input type="hidden" name="name" value="%h(zPageName)">
  @ Your Name:
  @ <input type="text" name="u" size="20" value="%h(zUser)"><br />
  @ Comment to append:<br />
  @ <textarea name="r" class="wikiedit" cols="80" 
  @  rows="10" wrap="virtual">%h(PD("r",""))</textarea>
  @ <br />
  @ <input type="submit" name="preview" value="Preview Your Comment">
  @ <input type="submit" name="submit" value="Append Your Changes">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>
  style_footer();
}

/*
** Name of the wiki history page being generated
*/







|

|

|




|
|
|







531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
    appendRemark(&preview);
    @ Preview:<hr>
    wiki_convert(&preview, 0, 0);
    @ <hr>
    blob_reset(&preview);
  }
  zUser = PD("u", g.zLogin);
  @ <form method="post" action="%s(g.zBaseURL)/wikiappend">
  login_insert_csrf_secret();
  @ <input type="hidden" name="name" value="%h(zPageName)" />
  @ Your Name:
  @ <input type="text" name="u" size="20" value="%h(zUser)" /><br />
  @ Comment to append:<br />
  @ <textarea name="r" class="wikiedit" cols="80" 
  @  rows="10" wrap="virtual">%h(PD("r",""))</textarea>
  @ <br />
  @ <input type="submit" name="preview" value="Preview Your Comment" />
  @ <input type="submit" name="submit" value="Append Your Changes" />
  @ <input type="submit" name="cancel" value="Cancel" />
  @ </form>
  style_footer();
}

/*
** Name of the wiki history page being generated
*/
Added win/Makefile.PellesCGMake.


















































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# ###########################################################################
#
# HowTo
# -----
#
# This is a Makefile to compile fossil with PellesC from
#  http://www.smorgasbordet.com/pellesc/index.htm
# In addition to the Compiler envrionment, you need
#  gmake from http://sourceforge.net/projects/unxutils/, Pelles make version
#        couldn't handle the complex dependencies in this build
#  zlib sources
# Then you do
# 1. create a directory PellesC in the project root directory
# 2. Change the variables PellesCDir/ZLIBSRCDIR to the path of your installation
# 3. open a dos prompt window and change working directory into PellesC (step 1)
# 4. run gmake -f ..\win\Makefile.PellesCGMake
#
# this file is tested with
#   PellesC         5.00.13
#   gmake           3.80
#   zlib sources    1.2.5
#   Windows XP SP 2
# and
#   PellesC         6.00.4
#   gmake           3.80
#   zlib sources    1.2.5
#   Windows 7 Home Premium
#  
# ###########################################################################

#  
PellesCDir=c:\Programme\PellesC

# Select between 32/64 bit code, default is 32 bit
#TARGETVERSION=64

ifeq ($(TARGETVERSION),64)
# 64 bit version
TARGETMACHINE_CC=amd64
TARGETMACHINE_LN=amd64
TARGETEXTEND=64
else
# 32 bit version
TARGETMACHINE_CC=x86
TARGETMACHINE_LN=ix86
TARGETEXTEND=
endif

B=..
SRCDIR=$(B)/src/
WINDIR=$(B)/win/
ZLIBSRCDIR=E:/fossil-w32/zlib/

LINK=$(PellesCDir)/bin/polink.exe
LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib Crtmt$(TARGETEXTEND).lib

CC=$(PellesCDir)\bin\pocc.exe
DEFINES=-DFOSSIL_I18N=0 -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp 
CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES)
INCLUDE=/I $(PellesCDir)\Include\Win /I $(PellesCDir)\Include /I $(ZLIBSRCDIR) /I $(SRCDIR)

UTILS=translate.exe mkindex.exe makeheaders.exe
UTILS_OBJ=$(UTILS:.exe=.obj)

SRC=add.c allrepo.c attach.c bag.c blob.c branch.c browse.c captcha.c cgi.c checkin.c checkout.c clearsign.c clone.c comformat.c configure.c content.c db.c delta.c deltacmd.c descendants.c diff.c diffcmd.c doc.c encode.c file.c finfo.c graph.c http.c http_socket.c http_ssl.c http_transport.c info.c login.c main.c manifest.c md5.c merge.c merge3.c name.c pivot.c popen.c pqueue.c printf.c rebuild.c report.c rss.c schema.c search.c setup.c sha1.c shun.c skins.c stat.c style.c sync.c tag.c th_main.c timeline.c tkt.c tktsetup.c undo.c update.c url.c user.c verify.c vfile.c wiki.c wikiformat.c winhttp.c xfer.c zip.c
ORIGSRC=$(foreach sf,$(SRC),$(SRCDIR)$(sf))
TRANSLATEDSRC=$(SRC:.c=_.c)
TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj)

SQLITESRC=sqlite3.c
ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
SQLITEDEFINES=-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0

THSRC=th.c th_lang.c
ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))

ZLIBSRC=adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c
ORIGZLIBSRC=$(foreach sf,$(ZLIBSRC),$(ZLIBSRCDIR)$(sf))
ZLIBOBJ=$(foreach sf,$(ZLIBSRC),$(sf:.c=.obj))

APPLICATION=fossil.exe

.PHONY:	default
default:	page_index.h headers $(APPLICATION)

$(UTILS):	%.exe:	%.obj
	$(LINK) $(LINKFLAGS) -out:"$@" $<

$(UTILS_OBJ):	%.obj:	$(SRCDIR)%.c
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

$(TRANSLATEDSRC):	%_.c:	$(SRCDIR)%.c translate.exe
	translate.exe $< >$@

page_index.h:	$(TRANSLATEDSRC) mkindex.exe
	mkindex.exe $(TRANSLATEDSRC) >$@

version.exe:	version.obj
	$(LINK) $(LINKFLAGS) -out:"$@" $<

version.obj:	$(WINDIR)version.c
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

VERSION.h:	version.exe ..\manifest.uuid ..\manifest
	version.exe ..\manifest.uuid ..\manifest  > $@

headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h
	makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h
	echo Done >$@

$(TRANSLATEDOBJ):	%_.obj:	%_.c %.h
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

$(SQLITEOBJ):	%.obj:	$(SRCDIR)%.c $(SRCDIR)%.h
	$(CC) $(CCFLAGS) $(SQLITEDEFINES) $(INCLUDE) "$<" -Fo"$@"

$(THOBJ):	%.obj:	$(SRCDIR)%.c $(SRCDIR)th.h
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

$(ZLIBOBJ):	%.obj:	$(ZLIBSRCDIR)%.c
	$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"

$(APPLICATION):	$(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) headers
	$(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ)

.PHONY: clean
clean:
	del /F *.obj
	del /F *.c
	del /F *.h headers

.PHONY: clobber
clobber: clean
	del /F *.exe