Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Merge in all of the latest clear-title changes from the trunk. |
|---|---|
| Timelines: | family | ancestors | descendants | both | clear-title |
| Files: | files | file ages | folders |
| SHA1: |
7c2577bd637e91b0d3649f75555644cd |
| User & Date: | drh 2010-03-18 14:40:46.000 |
Context
|
2010-04-02
| ||
| 20:09 | Merge all recent trunk changes into the clear-title branch (since we hold clear title on all recent changes.) check-in: 64541535d9 user: drh tags: clear-title | |
|
2010-03-18
| ||
| 14:40 | Merge in all of the latest clear-title changes from the trunk. check-in: 7c2577bd63 user: drh tags: clear-title | |
| 14:28 | Update the script that builds the download page. check-in: d5f6a272e9 user: drh tags: trunk | |
|
2010-03-05
| ||
| 23:14 | Pull in all clear-title changes from trunk. check-in: dfb5fd64cd user: drh tags: clear-title | |
Changes
Changes to COPYRIGHT-GPL2.txt.
1 2 3 4 5 6 7 | GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. | > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | Fossil is licensed under the terms of the GPLv2 shown below. In addition, permission is granted to link Fossil against the OpenSSL project's "OpenSSL" library (or with modified versions of that library that use the same license), and distribute the linked executables. If you modify Fossil, you may extend the exception described in this paragraph to your modifications, or not, at your discretion. The "clear-title" branch of Fossil is available for more liberal licenses such as Berkeley-style licenses or the Apache license. The original text of GPLv2 follows. -------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. |
| ︙ | ︙ |
Added src/attach.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 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 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 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 366 367 368 369 370 371 372 373 |
/*
** Copyright (c) 2010 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code for dealing with attachments.
*/
#include "config.h"
#include "attach.h"
#include <assert.h>
/*
** WEBPAGE: attachlist
**
** tkt=TICKETUUID
** page=WIKIPAGE
** all
**
** List attachments.
*/
void attachlist_page(void){
const char *zPage = P("page");
const char *zTkt = P("tkt");
Blob sql;
Stmt q;
if( zPage && zTkt ) zTkt = 0;
login_check_credentials();
blob_zero(&sql);
blob_append(&sql,
"SELECT datetime(mtime,'localtime'), src, target, filename, comment, user"
" FROM attachment",
-1
);
if( zPage ){
if( g.okRdWiki==0 ) login_needed();
style_header("Attachments To %h", zPage);
blob_appendf(&sql, " WHERE target=%Q", zPage);
}else if( zTkt ){
if( g.okRdTkt==0 ) login_needed();
style_header("Attachments To Ticket %.10s", zTkt);
blob_appendf(&sql, " WHERE target GLOB '%q*'", zTkt);
}else{
if( g.okRdTkt==0 && g.okRdWiki==0 ) login_needed();
style_header("All Attachments");
}
blob_appendf(&sql, " ORDER BY mtime DESC");
db_prepare(&q, "%s", blob_str(&sql));
while( db_step(&q)==SQLITE_ROW ){
const char *zDate = db_column_text(&q, 0);
const char *zSrc = db_column_text(&q, 1);
const char *zTarget = db_column_text(&q, 2);
const char *zFilename = db_column_text(&q, 3);
const char *zComment = db_column_text(&q, 4);
const char *zUser = db_column_text(&q, 5);
int i;
char *zUrlTail;
for(i=0; zFilename[i]; i++){
if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
zFilename = &zFilename[i+1];
i = -1;
}
}
if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
}else{
zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
}
@
@ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
@ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
if( zComment ) while( isspace(zComment[0]) ) zComment++;
if( zComment && zComment[0] ){
@ %w(zComment)<br>
}
if( zPage==0 && zTkt==0 ){
if( zSrc==0 || zSrc[0]==0 ){
zSrc = "Deleted from";
}else {
zSrc = "Added to";
}
if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){
char zShort[20];
memcpy(zShort, zTarget, 10);
zShort[10] = 0;
@ %s(zSrc) ticket <a href="%s(g.zTop)/tktview?name=%s(zTarget)">
@ %s(zShort)</a>
}else{
@ %s(zSrc) wiki page <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
@ %h(zTarget)</a>
}
}else{
if( zSrc==0 || zSrc[0]==0 ){
@ Deleted
}else {
@ Added
}
}
@ by %h(zUser) on
hyperlink_to_date(zDate, ".");
free(zUrlTail);
}
db_finalize(&q);
style_footer();
return;
}
/*
** WEBPAGE: attachdownload
** WEBPAGE: attachimage
** WEBPAGE: attachview
**
** tkt=TICKETUUID
** page=WIKIPAGE
** file=FILENAME
** attachid=ID
**
** List attachments.
*/
void attachview_page(void){
const char *zPage = P("page");
const char *zTkt = P("tkt");
const char *zFile = P("file");
const char *zTarget = 0;
int attachid = atoi(PD("attachid","0"));
char *zUUID;
if( zPage && zTkt ) zTkt = 0;
if( zFile==0 ) fossil_redirect_home();
login_check_credentials();
if( zPage ){
if( g.okRdWiki==0 ) login_needed();
zTarget = zPage;
}else if( zTkt ){
if( g.okRdTkt==0 ) login_needed();
zTarget = zTkt;
}else{
fossil_redirect_home();
}
if( attachid>0 ){
zUUID = db_text(0,
"SELECT coalesce(src,'x') FROM attachment"
" WHERE target=%Q AND attachid=%d",
zTarget, attachid
);
}else{
zUUID = db_text(0,
"SELECT coalesce(src,'x') FROM attachment"
" WHERE target=%Q AND filename=%Q"
" ORDER BY mtime DESC LIMIT 1",
zTarget, zFile
);
}
if( zUUID==0 || zUUID[0]==0 ){
style_header("No Such Attachment");
@ No such attachment....
style_footer();
return;
}else if( zUUID[0]=='x' ){
style_header("Missing");
@ Attachment has been deleted
style_footer();
return;
}
g.okRead = 1;
cgi_replace_parameter("name",zUUID);
if( strcmp(g.zPath,"attachview")==0 ){
artifact_page();
}else{
cgi_replace_parameter("m", mimetype_from_name(zFile));
rawartifact_page();
}
}
/*
** WEBPAGE: attachadd
**
** tkt=TICKETUUID
** page=WIKIPAGE
** from=URL
**
** Add a new attachment.
*/
void attachadd_page(void){
const char *zPage = P("page");
const char *zTkt = P("tkt");
const char *zFrom = P("from");
const char *aContent = P("f");
const char *zName = PD("f:filename","unknown");
const char *zTarget;
const char *zTargetType;
int szContent = atoi(PD("f:bytes","0"));
if( P("cancel") ) cgi_redirect(zFrom);
if( zPage && zTkt ) fossil_redirect_home();
if( zPage==0 && zTkt==0 ) fossil_redirect_home();
login_check_credentials();
if( zPage ){
if( g.okApndWiki==0 || g.okAttach==0 ) login_needed();
if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zPage) ){
fossil_redirect_home();
}
zTarget = zPage;
zTargetType = mprintf("Wiki Page <a href=\"%s/wiki?name=%h\">%h</a>",
g.zTop, zPage, zPage);
}else{
if( g.okApndTkt==0 || g.okAttach==0 ) login_needed();
if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTkt) ){
fossil_redirect_home();
}
zTarget = zTkt;
zTargetType = mprintf("Ticket <a href=\"%s/tktview?name=%.10s\">%.10s</a>",
g.zTop, zTkt, zTkt);
}
if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
if( P("cancel") ){
cgi_redirect(zFrom);
}
if( P("ok") && szContent>0 ){
Blob content;
Blob manifest;
Blob cksum;
char *zUUID;
const char *zComment;
char *zDate;
int rid;
int i, n;
db_begin_transaction();
blob_init(&content, aContent, szContent);
rid = content_put(&content, 0, 0);
zUUID = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
blob_zero(&manifest);
for(i=n=0; zName[i]; i++){
if( zName[i]=='/' || zName[i]=='\\' ) n = i;
}
zName += n;
if( zName[0]==0 ) zName = "unknown";
blob_appendf(&manifest, "A %F %F %s\n", zName, zTarget, zUUID);
zComment = PD("comment", "");
while( isspace(zComment[0]) ) zComment++;
n = strlen(zComment);
while( n>0 && isspace(zComment[n-1]) ){ n--; }
if( n>0 ){
blob_appendf(&manifest, "C %F\n", zComment);
}
zDate = db_text(0, "SELECT datetime('now')");
zDate[10] = 'T';
blob_appendf(&manifest, "D %s\n", zDate);
blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody");
md5sum_blob(&manifest, &cksum);
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("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
**
** tkt=TICKETUUID
** page=WIKIPAGE
** file=FILENAME
**
** "Delete" an attachment. Because objects in Fossil are immutable
** the attachment isn't really deleted. Instead, we change the content
** of the attachment to NULL, which the system understands as being
** deleted. Historical values of the attachment are preserved.
*/
void attachdel_page(void){
const char *zPage = P("page");
const char *zTkt = P("tkt");
const char *zFile = P("file");
const char *zFrom = P("from");
const char *zTarget;
if( zPage && zTkt ) fossil_redirect_home();
if( zPage==0 && zTkt==0 ) fossil_redirect_home();
if( zFile==0 ) fossil_redirect_home();
login_check_credentials();
if( zPage ){
if( g.okWrWiki==0 || g.okAttach==0 ) login_needed();
zTarget = zPage;
}else{
if( g.okWrTkt==0 || g.okAttach==0 ) login_needed();
zTarget = zTkt;
}
if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
if( P("cancel") ){
cgi_redirect(zFrom);
}
if( P("confirm") ){
int i, n, rid;
char *zDate;
Blob manifest;
Blob cksum;
db_begin_transaction();
blob_zero(&manifest);
for(i=n=0; zFile[i]; i++){
if( zFile[i]=='/' || zFile[i]=='\\' ) n = i;
}
zFile += n;
if( zFile[0]==0 ) zFile = "unknown";
blob_appendf(&manifest, "A %F %F\n", zFile, zTarget);
zDate = db_text(0, "SELECT datetime('now')");
zDate[10] = 'T';
blob_appendf(&manifest, "D %s\n", zDate);
blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody");
md5sum_blob(&manifest, &cksum);
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();
}
|
Changes to src/browse.c.
| ︙ | ︙ | |||
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 |
int nCol, nRow;
int cnt, i;
char *zPrefix;
Stmt q;
const char *zCI = P("ci");
int rid = 0;
Blob content;
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 */
if( zD ){
| > > < | < < | < < | | > > > > > | > > > > > > > > > > | 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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
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&name=%T", g.zBaseURL, zUuid, zPrefix);
if( zD ){
style_submenu_element("Top", "Top", "%s/dir?ci=%s", g.zBaseURL, zUuid);
style_submenu_element("All", "All", "%s/dir?name=%t", g.zBaseURL, zD);
}else{
style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
}
}else{
@ <h2>The union of all files from all check-ins
@ %s(blob_str(&dirname))</h2>
zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
if( zD ){
style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip",
g.zBaseURL, zD);
style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk",
g.zBaseURL,zD);
}else{
style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
}
}
/* Compute the temporary table "localfiles" containing the names
** of all files and subdirectories in the zD[] directory.
**
** Subdirectory names begin with "/". This causes them to sort
** first and it also gives us an easy way to distinguish files
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
191 192 193 194 195 196 197 198 199 200 |
}else{
printf("UNCHANGED %s\n", zPathname);
}
free(zFullName);
}
db_finalize(&q);
}
/*
** COMMAND: extras
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > > | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 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 |
}else{
printf("UNCHANGED %s\n", zPathname);
}
free(zFullName);
}
db_finalize(&q);
}
/*
** Construct and return a string which is an SQL expression that will
** be TRUE if value zVal matches any of the GLOB expressions in the list
** zGlobList. For example:
**
** zVal: "x"
** zGlobList: "*.o,*.obj"
**
** Result: "(x GLOB '*.o' OR x GLOB '*.obj')"
**
** Each element of the GLOB list may optionally be enclosed in either '...'
** or "...". This allows commas in the expression. Whitespace at the
** beginning and end of each GLOB pattern is ignored, except when enclosed
** within '...' or "...".
**
** This routine makes no effort to free the memory space it uses.
*/
char *glob_expr(const char *zVal, const char *zGlobList){
Blob expr;
char *zSep = "(";
int nTerm = 0;
int i;
int cTerm;
if( zGlobList==0 || zGlobList[0]==0 ) return "0";
blob_zero(&expr);
while( zGlobList[0] ){
while( isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++;
if( zGlobList[0]==0 ) break;
if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){
cTerm = zGlobList[0];
zGlobList++;
}else{
cTerm = ',';
}
for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){}
if( cTerm==',' ){
while( i>0 && isspace(zGlobList[i-1]) ){ i--; }
}
blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList);
zSep = " OR ";
if( cTerm!=',' && zGlobList[i] ) i++;
zGlobList += i;
if( zGlobList[0] ) zGlobList++;
nTerm++;
}
if( nTerm ){
blob_appendf(&expr, ")");
return blob_str(&expr);
}else{
return "0";
}
}
/*
** COMMAND: extras
** Usage: %fossil extras ?--dotfiles? ?--ignore GLOBPATTERN?
**
** Print a list of all files in the source tree that are not part of
** the current checkout. See also the "clean" command.
**
** Files and subdirectories whose names begin with "." are normally
** ignored but can be included by adding the --dotfiles option.
*/
void extra_cmd(void){
Blob path;
Blob repo;
Stmt q;
int n;
const char *zIgnoreFlag = find_option("ignore",0,1);
int allFlag = find_option("dotfiles",0,0)!=0;
db_must_be_within_tree();
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
n = strlen(g.zLocalRoot);
blob_init(&path, g.zLocalRoot, n-1);
if( zIgnoreFlag==0 ){
zIgnoreFlag = db_get("ignore-glob", 0);
}
vfile_scan(0, &path, blob_size(&path), allFlag);
db_prepare(&q,
"SELECT x FROM sfile"
" WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_')"
" AND NOT %s"
" ORDER BY 1",
glob_expr("x", zIgnoreFlag)
);
if( file_tree_name(g.zRepositoryName, &repo, 0) ){
db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
}
while( db_step(&q)==SQLITE_ROW ){
printf("%s\n", db_column_text(&q, 0));
}
db_finalize(&q);
|
| ︙ | ︙ | |||
681 682 683 684 685 686 687 |
}
blob_appendf(&manifest, "C %F\n", blob_str(&comment));
zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
zDate[10] = 'T';
blob_appendf(&manifest, "D %s\n", zDate);
zDate[10] = ' ';
db_prepare(&q,
| | > > > > > | > > | 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 |
}
blob_appendf(&manifest, "C %F\n", blob_str(&comment));
zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
zDate[10] = 'T';
blob_appendf(&manifest, "D %s\n", zDate);
zDate[10] = ' ';
db_prepare(&q,
"SELECT pathname, uuid, origname, blob.rid, isexe"
" FROM vfile JOIN blob ON vfile.mrid=blob.rid"
" WHERE NOT deleted AND vfile.vid=%d"
" ORDER BY 1", vid);
blob_zero(&filename);
blob_appendf(&filename, "%s", g.zLocalRoot);
nBasename = blob_size(&filename);
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const char *zUuid = db_column_text(&q, 1);
const char *zOrig = db_column_text(&q, 2);
int frid = db_column_int(&q, 3);
int isexe = db_column_int(&q, 4);
const char *zPerm;
blob_append(&filename, zName, -1);
#ifndef __MINGW32__
/* For unix, extract the "executable" permission bit directly from
** the filesystem. On windows, the "executable" bit is retained
** unchanged from the original. */
isexe = file_isexe(blob_str(&filename));
#endif
if( isexe ){
zPerm = " x";
}else{
zPerm = "";
}
blob_resize(&filename, nBasename);
if( zOrig==0 || strcmp(zOrig,zName)==0 ){
blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm);
|
| ︙ | ︙ |
Changes to src/checkout.c.
| ︙ | ︙ | |||
91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
return;
}
content_get(vid, &manifest);
vfile_build(vid, &manifest);
blob_reset(&manifest);
}
/*
** 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;
| > > > > > > > > | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
return;
}
content_get(vid, &manifest);
vfile_build(vid, &manifest);
blob_reset(&manifest);
}
/*
** Set or clear the vfile.isexe flag for a file.
*/
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;
|
| ︙ | ︙ | |||
126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
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);
blob_resize(&filename, baseLen);
}
blob_reset(&filename);
manifest_clear(&m);
}
/*
| > | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
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);
}
/*
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
494 495 496 497 498 499 500 |
** of the result set as a string. Space to hold the string is
** obtained from malloc(). If the result set is empty, return
** zDefault instead.
*/
char *db_text(char *zDefault, const char *zSql, ...){
va_list ap;
Stmt s;
| | > > > > > > > > | 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 |
** of the result set as a string. Space to hold the string is
** obtained from malloc(). If the result set is empty, return
** zDefault instead.
*/
char *db_text(char *zDefault, const char *zSql, ...){
va_list ap;
Stmt s;
char *z;
va_start(ap, zSql);
db_vprepare(&s, zSql, ap);
va_end(ap);
if( db_step(&s)==SQLITE_ROW ){
z = mprintf("%s", sqlite3_column_text(s.pStmt, 0));
}else if( zDefault ){
z = mprintf("%s", zDefault);
}else{
z = 0;
}
db_finalize(&s);
return z;
}
#ifdef __MINGW32__
extern char *sqlite3_win32_mbcs_to_utf8(const char*);
#endif
/*
** Initialize a new database file with the given schema. If anything
** goes wrong, call db_err() to exit.
*/
void db_init_database(
const char *zFileName, /* Name of database file to create */
|
| ︙ | ︙ | |||
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 |
lsize = file_size(zDbName);
if( lsize%1024!=0 || lsize<4096 ) return 0;
db_open_or_attach(zDbName, "localdb");
g.localOpen = 1;
db_open_config(0);
db_open_repository(0);
/* If the "mtime" column is missing from the vfile table, then
** add it now. This code added on 2008-12-06. After all users have
** upgraded, this code can be safely deleted.
*/
rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
sqlite3_finalize(pStmt);
if( rc==SQLITE_ERROR ){
sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
}
/* If the "origname" column is missing from the vfile table, then
** add it now. This code added on 2008-11-09. After all users have
** upgraded, this code can be safely deleted.
*/
rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
sqlite3_finalize(pStmt);
if( rc==SQLITE_ERROR ){
sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
}
return 1;
}
/*
** Locate the root directory of the local repository tree. The root
** directory is found by searching for a file named "_FOSSIL_" or ".fos"
| > > > > > > > > > > > > > > | 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 |
lsize = file_size(zDbName);
if( lsize%1024!=0 || lsize<4096 ) return 0;
db_open_or_attach(zDbName, "localdb");
g.localOpen = 1;
db_open_config(0);
db_open_repository(0);
/* If the "isexe" column is missing from the vfile table, then
** add it now. This code added on 2010-03-06. After all users have
** upgraded, this code can be safely deleted.
*/
rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0);
sqlite3_finalize(pStmt);
if( rc==SQLITE_ERROR ){
sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0);
}
#if 0
/* If the "mtime" column is missing from the vfile table, then
** add it now. This code added on 2008-12-06. After all users have
** upgraded, this code can be safely deleted.
*/
rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
sqlite3_finalize(pStmt);
if( rc==SQLITE_ERROR ){
sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
}
#endif
#if 0
/* If the "origname" column is missing from the vfile table, then
** add it now. This code added on 2008-11-09. After all users have
** upgraded, this code can be safely deleted.
*/
rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
sqlite3_finalize(pStmt);
if( rc==SQLITE_ERROR ){
sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
}
#endif
return 1;
}
/*
** Locate the root directory of the local repository tree. The root
** directory is found by searching for a file named "_FOSSIL_" or ".fos"
|
| ︙ | ︙ | |||
883 884 885 886 887 888 889 | ** new repositories. ** ** The zInitialDate parameter determines the date of the initial check-in ** that is automatically created. If zInitialDate is 0 then no initial ** check-in is created. The makeServerCodes flag determines whether or ** not server and project codes are invented for this repository. */ | | > > > > | 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 |
** new repositories.
**
** The zInitialDate parameter determines the date of the initial check-in
** that is automatically created. If zInitialDate is 0 then no initial
** check-in is created. The makeServerCodes flag determines whether or
** not server and project codes are invented for this repository.
*/
void db_initial_setup(
const char *zInitialDate, /* Initial date of repository. (ex: "now") */
const char *zDefaultUser, /* Default user for the repository */
int makeServerCodes /* True to make new server & project codes */
){
char *zDate;
Blob hash;
Blob manifest;
db_set("content-schema", CONTENT_SCHEMA, 0);
db_set("aux-schema", AUX_SCHEMA, 0);
if( makeServerCodes ){
|
| ︙ | ︙ | |||
940 941 942 943 944 945 946 947 948 949 950 951 952 953 |
** By default, your current login name is used to create the default
** admin user. This can be overridden using the -A|--admin-user
** parameter.
**
** Options:
**
** --admin-user|-A USERNAME
**
*/
void create_repository_cmd(void){
char *zPassword;
const char *zDate; /* Date of the initial check-in */
const char *zDefaultUser; /* Optional name of the default user */
| > | 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 |
** By default, your current login name is used to create the default
** admin user. This can be overridden using the -A|--admin-user
** parameter.
**
** Options:
**
** --admin-user|-A USERNAME
** --date-override DATETIME
**
*/
void create_repository_cmd(void){
char *zPassword;
const char *zDate; /* Date of the initial check-in */
const char *zDefaultUser; /* Optional name of the default user */
|
| ︙ | ︙ | |||
1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 | ** The "unset" command clears a property setting. ** ** ** autosync If enabled, automatically pull prior to commit ** or update and automatically push after commit or ** tag or branch creation. If the the value is "pullonly" ** then only pull operations occur automatically. ** ** clearsign When enabled, fossil will attempt to sign all commits ** with gpg. When disabled (the default), commits will ** be unsigned. ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. ** ** editor Text editor command used for check-in comments. ** ** gdiff-command External command to run when performing a graphical ** diff. If undefined, text diff will be used. ** ** http-port The TCP/IP port number to use by the "server" ** and "ui" commands. Default: 8080 ** ** localauth If enabled, require that HTTP connections from ** 127.0.0.1 be authenticated by password. If ** false, all HTTP requests from localhost have ** unrestricted access to the repository. ** ** mtime-changes Use file modification times (mtimes) to detect when | > > > > > > > > | 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 | ** The "unset" command clears a property setting. ** ** ** autosync If enabled, automatically pull prior to commit ** or update and automatically push after commit or ** tag or branch creation. If the the value is "pullonly" ** then only pull operations occur automatically. ** ** binary-glob The VALUE is a comma-separated list of GLOB patterns ** that should be treated as binary files for merging ** purposes. Example: *.xml ** ** clearsign When enabled, fossil will attempt to sign all commits ** with gpg. When disabled (the default), commits will ** be unsigned. ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. ** ** editor Text editor command used for check-in comments. ** ** gdiff-command External command to run when performing a graphical ** diff. If undefined, text diff will be used. ** ** http-port The TCP/IP port number to use by the "server" ** and "ui" commands. Default: 8080 ** ** ignore-glob The VALUE is a comma-separated list of GLOB patterns ** specifying files that the "extra" command will ignore. ** Example: *.o,*.obj,*.exe ** ** localauth If enabled, require that HTTP connections from ** 127.0.0.1 be authenticated by password. If ** false, all HTTP requests from localhost have ** unrestricted access to the repository. ** ** mtime-changes Use file modification times (mtimes) to detect when |
| ︙ | ︙ | |||
1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 |
** web browser when given a URL as an argument.
** Defaults to "start" on windows, "open" on Mac,
** and "firefox" on Unix.
*/
void setting_cmd(void){
static const char *azName[] = {
"autosync",
"clearsign",
"diff-command",
"dont-push",
"editor",
"gdiff-command",
"http-port",
"localauth",
"mtime-changes",
"pgp-command",
"proxy",
"web-browser",
};
| > > | 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 |
** web browser when given a URL as an argument.
** Defaults to "start" on windows, "open" on Mac,
** and "firefox" on Unix.
*/
void setting_cmd(void){
static const char *azName[] = {
"autosync",
"binary-glob",
"clearsign",
"diff-command",
"dont-push",
"editor",
"gdiff-command",
"ignore-glob",
"http-port",
"localauth",
"mtime-changes",
"pgp-command",
"proxy",
"web-browser",
};
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
47 48 49 50 51 52 53 | } blob_append(pBlob, zIn, -1); } /* ** This function implements a cross-platform "system()" interface. */ | | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
}
blob_append(pBlob, zIn, -1);
}
/*
** This function implements a cross-platform "system()" interface.
*/
int portable_system(const char *zOrigCmd){
int rc;
#ifdef __MINGW32__
/* On windows, we have to put double-quotes around the entire command.
** Who knows why - this is just the way windows works.
*/
char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
rc = system(zNewCmd);
|
| ︙ | ︙ |
Changes to src/file.c.
| ︙ | ︙ | |||
161 162 163 164 165 166 167 |
chmod(zFilename, buf.st_mode | 0111);
}
}else{
if( (buf.st_mode & 0111)!=0 ){
chmod(zFilename, buf.st_mode & ~0111);
}
}
| | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
chmod(zFilename, buf.st_mode | 0111);
}
}else{
if( (buf.st_mode & 0111)!=0 ){
chmod(zFilename, buf.st_mode & ~0111);
}
}
#endif /* __MINGW32__ */
}
/*
** Create the directory named in the argument, if it does not already
** exist. If forceFlag is 1, delete any prior non-directory object
** with the same name.
**
|
| ︙ | ︙ |
Changes to src/finfo.c.
| ︙ | ︙ | |||
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
** Show the complete change history for a single file.
*/
void finfo_page(void){
Stmt q;
const char *zFilename;
char zPrevDate[20];
Blob title;
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
style_header("File History");
login_anonymous_available();
zPrevDate[0] = 0;
zFilename = PD("name","");
db_prepare(&q,
| > > > | | | > > > | > > > > > > > > > > > > > | | > > | > | > > > | > | | | > > > > > > > > > > | 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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
** Show the complete change history for a single file.
*/
void finfo_page(void){
Stmt q;
const char *zFilename;
char zPrevDate[20];
Blob title;
GraphContext *pGraph;
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
style_header("File History");
login_anonymous_available();
zPrevDate[0] = 0;
zFilename = PD("name","");
db_prepare(&q,
"SELECT"
" substr(b.uuid,1,10),"
" datetime(event.mtime,'localtime'),"
" coalesce(event.ecomment, event.comment),"
" coalesce(event.euser, event.user),"
" mlink.pid,"
" mlink.fid,"
" mlink.mid,"
" mlink.fnid,"
" ci.uuid,"
" event.bgcolor,"
" (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
" AND tagxref.rid=mlink.mid)"
" FROM mlink, blob b, event, blob ci"
" WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)"
" AND b.rid=mlink.fid"
" AND event.objid=mlink.mid"
" AND event.objid=ci.rid"
" ORDER BY event.mtime DESC",
TAG_BRANCH,
zFilename
);
blob_zero(&title);
blob_appendf(&title, "History of ");
hyperlinked_path(zFilename, &title);
@ <h2>%b(&title)</h2>
blob_reset(&title);
pGraph = graph_init();
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
@ <table cellspacing=0 border=0 cellpadding=0>
while( db_step(&q)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q, 0);
const char *zDate = db_column_text(&q, 1);
const char *zCom = db_column_text(&q, 2);
const char *zUser = db_column_text(&q, 3);
int fpid = db_column_int(&q, 4);
int frid = db_column_int(&q, 5);
int mid = db_column_int(&q, 6);
int fnid = db_column_int(&q, 7);
const char *zCkin = db_column_text(&q,8);
const char *zBgClr = db_column_text(&q, 9);
const char *zBr = db_column_text(&q, 10);
int gidx;
char zTime[10];
char zShort[20];
char zShortCkin[20];
if( zBr==0 ) zBr = "trunk";
gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr);
if( memcmp(zDate, zPrevDate, 10) ){
sprintf(zPrevDate, "%.10s", zDate);
@ <tr><td>
@ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
@ </td></tr>
}
memcpy(zTime, &zDate[11], 5);
zTime[5] = 0;
@ <tr><td valign="top" align="right">
@ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td>
@ <td width="20" align="left" valign="top"><div id="m%d(gidx)"></div></td>
if( zBgClr && zBgClr[0] ){
@ <td valign="top" align="left" bgcolor="%h(zBgClr)">
}else{
@ <td valign="top" align="left">
}
sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
if( g.okHistory ){
@ <a href="%s(g.zTop)/artifact/%s(zUuid)">[%s(zShort)]</a>
}else{
@ [%s(zShort)]
}
@ part of check-in
hyperlink_to_uuid(zShortCkin);
@ %h(zCom) (user:
hyperlink_to_user(zUser, zDate, "");
@ branch: %h(zBr))
if( g.okHistory ){
if( fpid ){
@ <a href="%s(g.zBaseURL)/fdiff?v1=%d(fpid)&v2=%d(frid)">[diff]</a>
}
@ <a href="%s(g.zBaseURL)/annotate?mid=%d(mid)&fnid=%d(fnid)">
@ [annotate]</a>
@ </td>
}
}
db_finalize(&q);
if( pGraph ){
graph_finish(pGraph, 1);
if( pGraph->nErr ){
graph_free(pGraph);
pGraph = 0;
}else{
@ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
}
}
@ </table>
timeline_output_graph_javascript(pGraph);
style_footer();
}
|
Changes to src/graph.c.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
#define GR_MAX_RAIL 32
/* The graph appears vertically beside a timeline. Each row in the
** timeline corresponds to a row in the graph.
*/
struct GraphRow {
int rid; /* The rid for the check-in */
| < > > > > | 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 |
#define GR_MAX_RAIL 32
/* The graph appears vertically beside a timeline. Each row in the
** timeline corresponds to a row in the graph.
*/
struct GraphRow {
int rid; /* The rid for the check-in */
int nParent; /* Number of parents */
int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/
char *zBranch; /* Branch name */
GraphRow *pNext; /* Next row down in the list of all rows */
GraphRow *pPrev; /* Previous row */
int idx; /* Row index. First is 1. 0 used for "none" */
int isLeaf; /* True if no direct child nodes */
int iRail; /* Which rail this check-in appears on. 0-based.*/
int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */
int bDescender; /* Raiser from bottom of graph to here. */
u32 mergeIn; /* Merge in from other rails */
int mergeOut; /* Merge out to this rail */
int mergeUpto; /* Draw the merge rail up to this level */
u32 railInUse; /* Mask of occupied rails */
};
/* Context while building a graph
*/
struct GraphContext {
int nErr; /* Number of errors encountered */
int mxRail; /* Number of rails required to render the graph */
GraphRow *pFirst; /* First row in the list */
GraphRow *pLast; /* Last row in the list */
int nBranch; /* Number of distinct branches */
char **azBranch; /* Names of the branches */
int nRow; /* Number of rows */
int railMap[GR_MAX_RAIL]; /* Rail order mapping */
int nHash; /* Number of slots in apHash[] */
GraphRow **apHash; /* Hash table of rows */
};
#endif
/*
** Malloc for zeroed space. Panic if unable to provide the
** requested space.
|
| ︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
while( p->pFirst ){
pRow = p->pFirst;
p->pFirst = pRow->pNext;
free(pRow);
}
for(i=0; i<p->nBranch; i++) free(p->azBranch[i]);
free(p->azBranch);
free(p);
}
/*
** Return the canonical pointer for a given branch name.
** Multiple calls to this routine with equivalent strings
** will return the same pointer.
*/
static char *persistBranchName(GraphContext *p, const char *zBranch){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
while( p->pFirst ){
pRow = p->pFirst;
p->pFirst = pRow->pNext;
free(pRow);
}
for(i=0; i<p->nBranch; i++) free(p->azBranch[i]);
free(p->azBranch);
free(p->apHash);
free(p);
}
/*
** Insert a row into the hash table. If there is already another
** row with the same rid, the other row is replaced.
*/
static void hashInsert(GraphContext *p, GraphRow *pRow){
int h;
h = pRow->rid % p->nHash;
while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){
h++;
if( h>=p->nHash ) h = 0;
}
p->apHash[h] = pRow;
}
/*
** Look up the row with rid.
*/
static GraphRow *hashFind(GraphContext *p, int rid){
int h = rid % p->nHash;
while( p->apHash[h] && p->apHash[h]->rid!=rid ){
h++;
if( h>=p->nHash ) h = 0;
}
return p->apHash[h];
}
/*
** Return the canonical pointer for a given branch name.
** Multiple calls to this routine with equivalent strings
** will return the same pointer.
*/
static char *persistBranchName(GraphContext *p, const char *zBranch){
|
| ︙ | ︙ | |||
120 121 122 123 124 125 126 |
p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
return p->azBranch[p->nBranch-1];
}
/*
** Add a new row t the graph context. Rows are added from top to bottom.
*/
| | < | | < > > > | 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 184 185 186 187 188 |
p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
return p->azBranch[p->nBranch-1];
}
/*
** Add a new row t the graph context. Rows are added from top to bottom.
*/
int graph_add_row(
GraphContext *p, /* The context to which the row is added */
int rid, /* RID for the check-in */
int nParent, /* Number of parents */
int *aParent, /* Array of parents */
const char *zBranch /* Branch for this check-in */
){
GraphRow *pRow;
if( p->nErr ) return 0;
if( nParent>GR_MAX_PARENT ){ p->nErr++; return 0; }
pRow = (GraphRow*)safeMalloc( sizeof(GraphRow) );
pRow->rid = rid;
pRow->nParent = nParent;
pRow->zBranch = persistBranchName(p, zBranch);
memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent);
if( p->pFirst==0 ){
p->pFirst = pRow;
}else{
p->pLast->pNext = pRow;
}
p->pLast = pRow;
p->nRow++;
pRow->idx = p->nRow;
return pRow->idx;
}
/*
** Return the index of a rail currently not in use for any row between
** top and bottom, inclusive.
*/
static int findFreeRail(
|
| ︙ | ︙ | |||
186 187 188 189 190 191 192 |
}
/*
** Compute the complete graph
*/
void graph_finish(GraphContext *p, int omitDescenders){
GraphRow *pRow, *pDesc;
| < < < < < | > < | | > > > > > > > > > > | | > > > | | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
}
/*
** Compute the complete graph
*/
void graph_finish(GraphContext *p, int omitDescenders){
GraphRow *pRow, *pDesc;
int i;
u32 mask;
u32 inUse;
if( p==0 || p->pFirst==0 || p->nErr ) return;
/* Initialize all rows */
p->nHash = p->nRow*2 + 1;
p->apHash = safeMalloc( sizeof(p->apHash[0])*p->nHash );
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->pNext ) pRow->pNext->pPrev = pRow;
pRow->iRail = -1;
pRow->mergeOut = -1;
hashInsert(p, pRow);
}
p->mxRail = -1;
/* Purge merge-parents that are out-of-graph
*/
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
for(i=1; i<pRow->nParent; i++){
if( hashFind(p, pRow->aParent[i])==0 ){
pRow->aParent[i] = pRow->aParent[--pRow->nParent];
i--;
}
}
}
/* Figure out which nodes have no direct children (children on
** the same rail). Mark such nodes is isLeaf.
*/
memset(p->apHash, 0, sizeof(p->apHash[0])*p->nHash);
for(pRow=p->pLast; pRow; pRow=pRow->pPrev) pRow->isLeaf = 1;
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
GraphRow *pParent;
hashInsert(p, pRow);
if( pRow->nParent>0
&& (pParent = hashFind(p, pRow->aParent[0]))!=0
&& pRow->zBranch==pParent->zBranch
){
pParent->isLeaf = 0;
}
}
/* Identify rows where the primary parent is off screen. Assign
** each to a rail and draw descenders to the bottom of the screen.
*/
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
if( omitDescenders ){
pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, 0, 0);
}else{
pRow->iRail = ++p->mxRail;
}
mask = 1<<(pRow->iRail);
if( omitDescenders ){
|
| ︙ | ︙ | |||
255 256 257 258 259 260 261 |
*/
inUse = (1<<(p->mxRail+1))-1;
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
int parentRid;
if( pRow->iRail>=0 ) continue;
assert( pRow->nParent>0 );
parentRid = pRow->aParent[0];
| < | | | | 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 320 321 322 323 324 325 |
*/
inUse = (1<<(p->mxRail+1))-1;
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
int parentRid;
if( pRow->iRail>=0 ) continue;
assert( pRow->nParent>0 );
parentRid = pRow->aParent[0];
for(pDesc=pRow->pNext; pDesc && pDesc->rid!=parentRid; pDesc=pDesc->pNext){}
if( pDesc==0 ){
/* Time skew */
pRow->iRail = ++p->mxRail;
pRow->railInUse = 1<<pRow->iRail;
continue;
}
if( pDesc->aiRaiser[pDesc->iRail]==0 && pDesc->zBranch==pRow->zBranch ){
pRow->iRail = pDesc->iRail;
}else{
pRow->iRail = findFreeRail(p, 0, pDesc->idx, inUse, 0);
}
pDesc->aiRaiser[pRow->iRail] = pRow->idx;
mask = 1<<pRow->iRail;
if( pRow->isLeaf ){
inUse &= ~mask;
}else{
inUse |= mask;
}
for(pDesc = pRow; ; pDesc=pDesc->pNext){
assert( pDesc!=0 );
pDesc->railInUse |= mask;
if( pDesc->rid==parentRid ) break;
}
}
|
| ︙ | ︙ | |||
307 308 309 310 311 312 313 |
}
}
pRow->mergeIn |= 1<<pDesc->mergeOut;
}
}
/*
| | < < < < < < < < < < < < < < < | 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
}
}
pRow->mergeIn |= 1<<pDesc->mergeOut;
}
}
/*
** Find the maximum rail number.
*/
for(i=0; i<GR_MAX_RAIL; i++) p->railMap[i] = i;
p->mxRail = 0;
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
}
}
|
Changes to src/info.c.
| ︙ | ︙ | |||
730 731 732 733 734 735 736 737 738 739 740 741 742 743 |
if( pDownloadName && blob_size(pDownloadName)==0 ){
blob_append(pDownloadName, zUuid, -1);
}
cnt++;
}
db_finalize(&q);
}
if( cnt==0 ){
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
@ Control artifact.
if( pDownloadName && blob_size(pDownloadName)==0 ){
blob_append(pDownloadName, zUuid, -1);
}
}else if( linkToView && g.okHistory ){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
if( pDownloadName && blob_size(pDownloadName)==0 ){
blob_append(pDownloadName, zUuid, -1);
}
cnt++;
}
db_finalize(&q);
}
db_prepare(&q,
"SELECT target, filename, datetime(mtime), user, src"
" FROM attachment"
" WHERE src=(SELECT uuid FROM blob WHERE rid=%d)"
" ORDER BY mtime DESC",
rid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zTarget = db_column_text(&q, 0);
const char *zFilename = db_column_text(&q, 1);
const char *zDate = db_column_text(&q, 2);
const char *zUser = db_column_text(&q, 3);
const char *zSrc = db_column_text(&q, 4);
if( cnt>0 ){
@ Also attachment "%h(zFilename)" to
}else{
@ Attachment "%h(zFilename)" to
}
if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
char zShort[20];
memcpy(zShort, zTarget, 10);
if( g.okHistory && g.okRdTkt ){
@ ticket [<a href="%s(g.zTop)/tktview?name=%s(zShort)">%s(zShort)</a>]
}else{
@ ticket [%s(zShort)]
}
}else{
if( g.okHistory && g.okRdWiki ){
@ wiki page [<a href="%s(g.zTop)/wiki?name=%t(zTarget)">%h(zTarget)</a>]
}else{
@ wiki page [%h(zTarget)]
}
}
@ added by
hyperlink_to_user(zUser,zDate," on");
hyperlink_to_date(zDate,".");
cnt++;
if( pDownloadName && blob_size(pDownloadName)==0 ){
blob_append(pDownloadName, zSrc, -1);
}
}
db_finalize(&q);
if( cnt==0 ){
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
@ Control artifact.
if( pDownloadName && blob_size(pDownloadName)==0 ){
blob_append(pDownloadName, zUuid, -1);
}
}else if( linkToView && g.okHistory ){
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
474 475 476 477 478 479 480 |
for(i=0; zCap[i]; i++){
switch( zCap[i] ){
case 's': g.okSetup = 1; /* Fall thru into Admin */
case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip =
g.okRdWiki = g.okWrWiki = g.okNewWiki =
g.okApndWiki = g.okHistory = g.okClone =
g.okNewTkt = g.okPassword = g.okRdAddr =
| > | | 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
for(i=0; zCap[i]; i++){
switch( zCap[i] ){
case 's': g.okSetup = 1; /* Fall thru into Admin */
case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip =
g.okRdWiki = g.okWrWiki = g.okNewWiki =
g.okApndWiki = g.okHistory = g.okClone =
g.okNewTkt = g.okPassword = g.okRdAddr =
g.okTktFmt = g.okAttach = 1;
/* Fall thru into Read/Write */
case 'i': g.okRead = g.okWrite = 1; break;
case 'o': g.okRead = 1; break;
case 'z': g.okZip = 1; break;
case 'd': g.okDelete = 1; break;
case 'h': g.okHistory = 1; break;
case 'g': g.okClone = 1; break;
|
| ︙ | ︙ | |||
496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
case 'e': g.okRdAddr = 1; break;
case 'r': g.okRdTkt = 1; break;
case 'n': g.okNewTkt = 1; break;
case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
g.okApndTkt = 1; break;
case 'c': g.okApndTkt = 1; break;
case 't': g.okTktFmt = 1; break;
/* The "u" privileges is a little different. It recursively
** inherits all privileges of the user named "reader" */
case 'u': {
if( zUser==0 ){
zUser = db_text("", "SELECT cap FROM user WHERE login='reader'");
login_set_capabilities(zUser);
| > | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
case 'e': g.okRdAddr = 1; break;
case 'r': g.okRdTkt = 1; break;
case 'n': g.okNewTkt = 1; break;
case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
g.okApndTkt = 1; break;
case 'c': g.okApndTkt = 1; break;
case 't': g.okTktFmt = 1; break;
case 'b': g.okAttach = 1; break;
/* The "u" privileges is a little different. It recursively
** inherits all privileges of the user named "reader" */
case 'u': {
if( zUser==0 ){
zUser = db_text("", "SELECT cap FROM user WHERE login='reader'");
login_set_capabilities(zUser);
|
| ︙ | ︙ | |||
532 533 534 535 536 537 538 |
int login_has_capability(const char *zCap, int nCap){
int i;
int rc = 1;
if( nCap<0 ) nCap = strlen(zCap);
for(i=0; i<nCap && rc && zCap[i]; i++){
switch( zCap[i] ){
case 'a': rc = g.okAdmin; break;
| | | 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
int login_has_capability(const char *zCap, int nCap){
int i;
int rc = 1;
if( nCap<0 ) nCap = strlen(zCap);
for(i=0; i<nCap && rc && zCap[i]; i++){
switch( zCap[i] ){
case 'a': rc = g.okAdmin; break;
case 'b': rc = g.okAttach; break;
case 'c': rc = g.okApndTkt; break;
case 'd': rc = g.okDelete; break;
case 'e': rc = g.okRdAddr; break;
case 'f': rc = g.okNewWiki; break;
case 'g': rc = g.okClone; break;
case 'h': rc = g.okHistory; break;
case 'i': rc = g.okWrite; break;
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
128 129 130 131 132 133 134 135 136 137 138 139 140 141 | int okNewWiki; /* f: create new wiki via web */ int okApndWiki; /* m: append to wiki via web */ int okWrWiki; /* k: edit wiki via web */ int okRdTkt; /* r: view tickets via web */ int okNewTkt; /* n: create new tickets */ int okApndTkt; /* c: append to tickets via the web */ int okWrTkt; /* w: make changes to tickets via web */ int okTktFmt; /* t: create new ticket report formats */ int okRdAddr; /* e: read email addresses or other private data */ int okZip; /* z: download zipped artifact via /zip URL */ /* For defense against Cross-site Request Forgery attacks */ char zCsrfToken[12]; /* Value of the anti-CSRF token */ int okCsrf; /* Anti-CSRF token is present and valid */ | > | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | int okNewWiki; /* f: create new wiki via web */ int okApndWiki; /* m: append to wiki via web */ int okWrWiki; /* k: edit wiki via web */ int okRdTkt; /* r: view tickets via web */ int okNewTkt; /* n: create new tickets */ int okApndTkt; /* c: append to tickets via the web */ int okWrTkt; /* w: make changes to tickets via web */ int okAttach; /* b: add attachments */ int okTktFmt; /* t: create new ticket report formats */ int okRdAddr; /* e: read email addresses or other private data */ int okZip; /* z: download zipped artifact via /zip URL */ /* For defense against Cross-site Request Forgery attacks */ char zCsrfToken[12]; /* Value of the anti-CSRF token */ int okCsrf; /* Anti-CSRF token is present and valid */ |
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ $(SRCDIR)/browse.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/checkin.c \ | > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ $(SRCDIR)/browse.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/checkin.c \ |
| ︙ | ︙ | |||
81 82 83 84 85 86 87 88 89 90 91 92 93 94 | $(SRCDIR)/winhttp.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/zip.c TRANS_SRC = \ add_.c \ allrepo_.c \ bag_.c \ blob_.c \ branch_.c \ browse_.c \ captcha_.c \ cgi_.c \ checkin_.c \ | > | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | $(SRCDIR)/winhttp.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/zip.c TRANS_SRC = \ add_.c \ allrepo_.c \ attach_.c \ bag_.c \ blob_.c \ branch_.c \ browse_.c \ captcha_.c \ cgi_.c \ checkin_.c \ |
| ︙ | ︙ | |||
151 152 153 154 155 156 157 158 159 160 161 162 163 164 | winhttp_.c \ xfer_.c \ zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ | > | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | winhttp_.c \ xfer_.c \ zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ |
| ︙ | ︙ | |||
262 263 264 265 266 267 268 | # $(SRCDIR)/../manifest: # noop clean: rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h rm -f translate makeheaders mkindex page_index.h headers | | | > > > > > > > | 265 266 267 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 297 298 299 300 301 302 303 304 305 306 307 308 | # $(SRCDIR)/../manifest: # noop clean: rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h rm -f translate makeheaders mkindex page_index.h headers rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h page_index.h: $(TRANS_SRC) mkindex ./mkindex $(TRANS_SRC) >$@ headers: page_index.h makeheaders VERSION.h ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h touch headers headers: Makefile Makefile: add_.c: $(SRCDIR)/add.c translate ./translate $(SRCDIR)/add.c >add_.c $(OBJDIR)/add.o: add_.c add.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/add.o -c add_.c add.h: headers allrepo_.c: $(SRCDIR)/allrepo.c translate ./translate $(SRCDIR)/allrepo.c >allrepo_.c $(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c allrepo.h: headers attach_.c: $(SRCDIR)/attach.c translate ./translate $(SRCDIR)/attach.c >attach_.c $(OBJDIR)/attach.o: attach_.c attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c attach_.c attach.h: headers bag_.c: $(SRCDIR)/bag.c translate ./translate $(SRCDIR)/bag.c >bag_.c $(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bag.o -c bag_.c bag.h: headers |
| ︙ | ︙ |
Changes to src/makemake.tcl.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#!/usr/bin/tclsh
#
# Run this TCL script to generate the "main.mk" makefile.
#
# Basenames of all source files that get preprocessed using
# "translate" and "makeheaders"
#
set src {
add
allrepo
bag
blob
branch
browse
captcha
cgi
checkin
| > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#!/usr/bin/tclsh
#
# Run this TCL script to generate the "main.mk" makefile.
#
# Basenames of all source files that get preprocessed using
# "translate" and "makeheaders"
#
set src {
add
allrepo
attach
bag
blob
branch
browse
captcha
cgi
checkin
|
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 | ** Types of control files */ #define CFTYPE_MANIFEST 1 #define CFTYPE_CLUSTER 2 #define CFTYPE_CONTROL 3 #define CFTYPE_WIKI 4 #define CFTYPE_TICKET 5 | < < < < | < < | < | | | | | | | > > > | | | | | | | | | < < < < < < < < | 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 |
** Types of control files
*/
#define CFTYPE_MANIFEST 1
#define CFTYPE_CLUSTER 2
#define CFTYPE_CONTROL 3
#define CFTYPE_WIKI 4
#define CFTYPE_TICKET 5
#define CFTYPE_ATTACHMENT 6
/*
** A parsed manifest or cluster.
*/
struct Manifest {
Blob content; /* The original content blob */
int type; /* Type of artifact. One of CFTYPE_xxxxx */
char *zComment; /* Decoded comment. The C card. */
double rDate; /* Date and time from D card. 0.0 if no D card. */
char *zUser; /* Name of the user from the U card. */
char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */
char *zWiki; /* Text of the wiki page. W card. */
char *zWikiTitle; /* Name of the wiki page. L card. */
char *zTicketUuid; /* UUID for a ticket. K card. */
char *zAttachName; /* Filename of an attachment. A card. */
char *zAttachSrc; /* UUID of document being attached. A card. */
char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */
int nFile; /* Number of F cards */
int nFileAlloc; /* Slots allocated in aFile[] */
struct {
char *zName; /* Name of a file */
char *zUuid; /* UUID of the file */
char *zPerm; /* File permissions */
char *zPrior; /* Prior name if the name was changed */
int iRename; /* index of renamed name in prior/next manifest */
} *aFile; /* One entry for each F card */
int nParent; /* Number of parents. */
int nParentAlloc; /* Slots allocated in azParent[] */
char **azParent; /* UUIDs of parents. One for each P card argument */
int nCChild; /* Number of cluster children */
int nCChildAlloc; /* Number of closts allocated in azCChild[] */
char **azCChild; /* UUIDs of referenced objects in a cluster. M cards */
int nTag; /* Number of T Cards */
int nTagAlloc; /* Slots allocated in aTag[] */
struct {
char *zName; /* Name of the tag */
char *zUuid; /* UUID that the tag is applied to */
char *zValue; /* Value if the tag is really a property */
} *aTag; /* One for each T card */
int nField; /* Number of J cards */
int nFieldAlloc; /* Slots allocated in aField[] */
struct {
char *zName; /* Key or field name */
char *zValue; /* Value of the field */
} *aField; /* One for each J card */
};
#endif
/*
** Clear the memory allocated in a manifest object
*/
void manifest_clear(Manifest *p){
blob_reset(&p->content);
free(p->aFile);
free(p->azParent);
free(p->azCChild);
free(p->aTag);
free(p->aField);
memset(p, 0, sizeof(*p));
}
/*
** Parse a blob into a Manifest object. The Manifest object
** takes over the input blob and will free it when the
** Manifest object is freed. Zeros are inserted into the blob
|
| ︙ | ︙ | |||
176 177 178 179 180 181 182 |
goto manifest_syntax_error;
}
cPrevType = z[0];
seenHeader = 1;
if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
switch( z[0] ){
/*
| | | > > | | | | > | < < | | | | < < < < < < | > > > > | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
goto manifest_syntax_error;
}
cPrevType = z[0];
seenHeader = 1;
if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error;
switch( z[0] ){
/*
** A <filename> <target> ?<source>?
**
** Identifies an attachment to either a wiki page or a ticket.
** <source> is the artifact that is the attachment. <source>
** is omitted to delete an attachment. <target> is the name of
** a wiki page or ticket to which that attachment is connected.
*/
case 'A': {
char *zName, *zTarget, *zSrc;
md5sum_step_text(blob_buffer(&line), blob_size(&line));
if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error;
if( p->zAttachName!=0 ) goto manifest_syntax_error;
zName = blob_terminate(&a1);
zTarget = blob_terminate(&a2);
blob_token(&line, &a3);
zSrc = blob_terminate(&a3);
defossilize(zName);
if( !file_is_simple_pathname(zName) ){
goto manifest_syntax_error;
}
defossilize(zTarget);
if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
&& !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
goto manifest_syntax_error;
}
if( blob_size(&a3)>0
&& (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
goto manifest_syntax_error;
}
p->zAttachName = (char*)file_tail(zName);
p->zAttachSrc = zSrc;
p->zAttachTarget = zTarget;
break;
}
/*
** C <comment>
**
** Comment text is fossil-encoded. There may be no more than
|
| ︙ | ︙ | |||
245 246 247 248 249 250 251 |
char *zDate;
md5sum_step_text(blob_buffer(&line), blob_size(&line));
if( p->rDate!=0.0 ) goto manifest_syntax_error;
if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
zDate = blob_terminate(&a1);
p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
| < < < < < < < < < < < < < < < < < < < < < < < | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
char *zDate;
md5sum_step_text(blob_buffer(&line), blob_size(&line));
if( p->rDate!=0.0 ) goto manifest_syntax_error;
if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error;
if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
zDate = blob_terminate(&a1);
p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate);
break;
}
/*
** F <filename> <uuid> ?<permissions>? ?<old-name>?
**
** Identifies a file in a manifest. Multiple F lines are
|
| ︙ | ︙ | |||
606 607 608 609 610 611 612 |
if( !seenHeader ) goto manifest_syntax_error;
if( p->nFile>0 || p->zRepoCksum!=0 ){
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->rDate==0.0 ) goto manifest_syntax_error;
if( p->nField>0 ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
| < > < < > < > < > | | | | | > > | > > | | > < > < | 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 |
if( !seenHeader ) goto manifest_syntax_error;
if( p->nFile>0 || p->zRepoCksum!=0 ){
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->rDate==0.0 ) goto manifest_syntax_error;
if( p->nField>0 ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zWiki ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
p->type = CFTYPE_MANIFEST;
}else if( p->nCChild>0 ){
if( p->rDate>0.0 ) goto manifest_syntax_error;
if( p->zComment!=0 ) goto manifest_syntax_error;
if( p->zUser!=0 ) goto manifest_syntax_error;
if( p->nTag>0 ) goto manifest_syntax_error;
if( p->nParent>0 ) goto manifest_syntax_error;
if( p->nField>0 ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zWiki ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_CLUSTER;
}else if( p->nField>0 ){
if( p->rDate==0.0 ) goto manifest_syntax_error;
if( p->zWiki ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->nTag>0 ) goto manifest_syntax_error;
if( p->zTicketUuid==0 ) goto manifest_syntax_error;
if( p->zUser==0 ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_TICKET;
}else if( p->zWiki!=0 ){
if( p->rDate==0.0 ) goto manifest_syntax_error;
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->nTag>0 ) goto manifest_syntax_error;
if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
if( p->zWikiTitle==0 ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_WIKI;
}else if( p->nTag>0 ){
if( p->rDate<=0.0 ) goto manifest_syntax_error;
if( p->nParent>0 ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_CONTROL;
}else if( p->zAttachName ){
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->rDate==0.0 ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( !seenZ ) goto manifest_syntax_error;
p->type = CFTYPE_ATTACHMENT;
}else{
if( p->nCChild>0 ) goto manifest_syntax_error;
if( p->rDate<=0.0 ) goto manifest_syntax_error;
if( p->nParent>0 ) goto manifest_syntax_error;
if( p->nField>0 ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zWiki ) goto manifest_syntax_error;
if( p->zWikiTitle ) goto manifest_syntax_error;
if( p->zTicketUuid ) goto manifest_syntax_error;
if( p->zAttachName ) goto manifest_syntax_error;
p->type = CFTYPE_MANIFEST;
}
md5sum_init();
return 1;
manifest_syntax_error:
/*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
md5sum_init();
manifest_clear(p);
|
| ︙ | ︙ | |||
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 |
if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){
manifest_clear(&m);
return 0;
}
db_begin_transaction();
if( m.type==CFTYPE_MANIFEST ){
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
for(i=0; i<m.nParent; i++){
int pid = uuid_to_rid(m.azParent[i], 1);
db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
"VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate);
if( i==0 ){
add_mlink(pid, 0, rid, &m);
parentid = pid;
| > | 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 |
if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){
manifest_clear(&m);
return 0;
}
db_begin_transaction();
if( m.type==CFTYPE_MANIFEST ){
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
char *zCom;
for(i=0; i<m.nParent; i++){
int pid = uuid_to_rid(m.azParent[i], 1);
db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
"VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate);
if( i==0 ){
add_mlink(pid, 0, rid, &m);
parentid = pid;
|
| ︙ | ︙ | |||
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 |
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
TAG_DATE, rid, m.rDate,
rid, m.zUser, m.zComment,
TAG_BGCOLOR, rid,
TAG_USER, rid,
TAG_COMMENT, rid
);
}
}
if( m.type==CFTYPE_CLUSTER ){
tag_insert("cluster", 1, 0, rid, m.rDate, rid);
for(i=0; i<m.nCChild; i++){
int mid;
mid = uuid_to_rid(m.azCChild[i], 1);
| > > > > | 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 |
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
TAG_DATE, rid, m.rDate,
rid, m.zUser, m.zComment,
TAG_BGCOLOR, rid,
TAG_USER, rid,
TAG_COMMENT, rid
);
zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
" WHERE rowid=last_insert_rowid()");
wiki_extract_links(zCom, rid, 0, m.rDate, 1, WIKI_INLINE);
free(zCom);
}
}
if( m.type==CFTYPE_CLUSTER ){
tag_insert("cluster", 1, 0, rid, m.rDate, rid);
for(i=0; i<m.nCChild; i++){
int mid;
mid = uuid_to_rid(m.azCChild[i], 1);
|
| ︙ | ︙ | |||
1106 1107 1108 1109 1110 1111 1112 |
}
}
if( m.type==CFTYPE_WIKI ){
char *zTag = mprintf("wiki-%s", m.zWikiTitle);
int tagid = tag_findid(zTag, 1);
int prior;
char *zComment;
| > > > > > | > | > > > | 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 |
}
}
if( m.type==CFTYPE_WIKI ){
char *zTag = mprintf("wiki-%s", m.zWikiTitle);
int tagid = tag_findid(zTag, 1);
int prior;
char *zComment;
int nWiki;
char zLength[40];
while( isspace(m.zWiki[0]) ) m.zWiki++;
nWiki = strlen(m.zWiki);
sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
tag_insert(zTag, 1, zLength, rid, m.rDate, rid);
free(zTag);
prior = db_int(0,
"SELECT rid FROM tagxref"
" WHERE tagid=%d AND mtime<%.17g"
" ORDER BY mtime DESC",
tagid, m.rDate
);
if( prior ){
content_deltify(prior, rid, 0);
}
if( nWiki>0 ){
zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
}else{
zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle);
}
db_multi_exec(
"REPLACE INTO event(type,mtime,objid,user,comment,"
" bgcolor,euser,ecomment)"
"VALUES('w',%.17g,%d,%Q,%Q,"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
|
| ︙ | ︙ | |||
1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 |
assert( manifest_crosslink_busy==1 );
zTag = mprintf("tkt-%s", m.zTicketUuid);
tag_insert(zTag, 1, 0, rid, m.rDate, rid);
free(zTag);
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
m.zTicketUuid);
}
db_end_transaction(0);
manifest_clear(&m);
return 1;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 |
assert( manifest_crosslink_busy==1 );
zTag = mprintf("tkt-%s", m.zTicketUuid);
tag_insert(zTag, 1, 0, rid, m.rDate, rid);
free(zTag);
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
m.zTicketUuid);
}
if( m.type==CFTYPE_ATTACHMENT ){
db_multi_exec(
"INSERT INTO attachment(attachid, mtime, src, target,"
"filename, comment, user)"
"VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName,
(m.zComment ? m.zComment : ""), m.zUser
);
db_multi_exec(
"UPDATE attachment SET isLatest = (mtime=="
"(SELECT max(mtime) FROM attachment"
" WHERE target=%Q AND filename=%Q))"
" WHERE target=%Q AND filename=%Q",
m.zAttachTarget, m.zAttachName,
m.zAttachTarget, m.zAttachName
);
if( strlen(m.zAttachTarget)!=UUID_SIZE
|| !validate16(m.zAttachTarget, UUID_SIZE)
){
char *zComment;
if( m.zAttachSrc && m.zAttachSrc[0] ){
zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
m.zAttachName, m.zAttachTarget);
}else{
zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
m.zAttachName, m.zAttachTarget);
}
db_multi_exec(
"REPLACE INTO event(type,mtime,objid,user,comment)"
"VALUES('w',%.17g,%d,%Q,%Q)",
m.rDate, rid, m.zUser, zComment
);
free(zComment);
}else{
char *zComment;
if( m.zAttachSrc && m.zAttachSrc[0] ){
zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
m.zAttachName, m.zAttachTarget);
}else{
zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
m.zAttachName, m.zAttachTarget);
}
db_multi_exec(
"REPLACE INTO event(type,mtime,objid,user,comment)"
"VALUES('t',%.17g,%d,%Q,%Q)",
m.rDate, rid, m.zUser, zComment
);
free(zComment);
}
}
db_end_transaction(0);
manifest_clear(&m);
return 1;
}
|
Changes to src/merge.c.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 | #include "merge.h" #include <assert.h> /* ** COMMAND: merge ** | | | | | > > | > > > > > > > > > > > > > | > > > > > | 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 |
#include "merge.h"
#include <assert.h>
/*
** COMMAND: merge
**
** Usage: %fossil merge [--cherrypick] [--backout] VERSION
**
** The argument is a version that should be merged into the current
** checkout. All changes from VERSION back to the nearest common
** ancestor are merged. Except, if either of the --cherrypick or
** --backout options are used only the changes associated with the
** single check-in VERSION are merged. The --backout option causes
** the changes associated with VERSION to be removed from the current
** checkout rather than added.
**
** Only file content is merged. The result continues to use the
** file and directory names from the current checkout even if those
** names might have been changed in the branch being merged in.
**
** Other options:
**
** --detail Show additional details of the merge
**
** --binary GLOBPATTERN Treat files that match GLOBPATTERN as binary
** and do not try to merge parallel changes. This
** option overrides the "binary-glob" setting.
*/
void merge_cmd(void){
int vid; /* Current version */
int mid; /* Version we are merging against */
int pid; /* The pivot version - most recent common ancestor */
int detailFlag; /* True if the --detail option is present */
int pickFlag; /* True if the --cherrypick option is present */
int backoutFlag; /* True if the --backout optioni is present */
const char *zBinGlob; /* The value of --binary */
Stmt q;
detailFlag = find_option("detail",0,0)!=0;
pickFlag = find_option("cherrypick",0,0)!=0;
backoutFlag = find_option("backout",0,0)!=0;
zBinGlob = find_option("binary",0,1);
if( g.argc!=3 ){
usage("VERSION");
}
db_must_be_within_tree();
if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
vid = db_lget_int("checkout", 0);
if( vid==0 ){
fossil_fatal("nothing is checked out");
}
mid = name_to_rid(g.argv[2]);
if( mid==0 ){
fossil_fatal("not a version: %s", g.argv[2]);
}
if( mid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", mid) ){
fossil_fatal("not a version: %s", g.argv[2]);
}
if( pickFlag || backoutFlag ){
pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
if( pid<=0 ){
fossil_fatal("cannot find an ancestor for %s", g.argv[2]);
}
if( backoutFlag ){
int t = pid;
pid = mid;
mid = t;
}
}else{
pivot_set_primary(mid);
pivot_set_secondary(vid);
db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
while( db_step(&q)==SQLITE_ROW ){
pivot_set_secondary(db_column_int(&q,0));
|
| ︙ | ︙ | |||
227 228 229 230 231 232 233 | } db_finalize(&q); /* ** Do a three-way merge on files that have changes pid->mid and pid->vid */ db_prepare(&q, | | | > > > > > > | > | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 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 |
}
db_finalize(&q);
/*
** Do a three-way merge on files that have changes pid->mid and pid->vid
*/
db_prepare(&q,
"SELECT ridm, idv, ridp, ridv, %s FROM fv"
" WHERE idp>0 AND idv>0 AND idm>0"
" AND ridm!=ridp AND (ridv!=ridp OR chnged)",
glob_expr("fv.fn", zBinGlob)
);
while( db_step(&q)==SQLITE_ROW ){
int ridm = db_column_int(&q, 0);
int idv = db_column_int(&q, 1);
int ridp = db_column_int(&q, 2);
int ridv = db_column_int(&q, 3);
int isBinary = db_column_int(&q, 4);
int rc;
char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
char *zFullPath;
Blob m, p, v, r;
/* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
if( detailFlag ){
printf("MERGE %s (pivot=%d v1=%d v2=%d)\n", zName, ridp, ridm, ridv);
}else{
printf("MERGE %s\n", zName);
}
undo_save(zName);
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
content_get(ridp, &p);
content_get(ridm, &m);
blob_zero(&v);
blob_read_from_file(&v, zFullPath);
if( isBinary ){
rc = -1;
blob_zero(&r);
}else{
rc = blob_merge(&p, &m, &v, &r);
}
if( rc>=0 ){
blob_write_to_file(&r, zFullPath);
if( rc>0 ){
printf("***** %d merge conflicts in %s\n", rc, zName);
}
}else{
printf("***** Cannot merge binary file %s\n", zName);
|
| ︙ | ︙ |
Changes to src/name.c.
| ︙ | ︙ | |||
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 |
db_finalize(&q);
rc = 0;
}else{
rc = 0;
}
return rc;
}
/*
** Convert a symbolic tag name into the UUID of a check-in that contains
** that tag. If the tag appears on multiple check-ins, return the UUID
** of the most recent check-in with the tag.
**
** Memory to hold the returned string comes from malloc() and needs to
** be freed by the caller.
*/
char *tag_to_uuid(const char *zTag){
char *zUuid =
db_text(0,
"SELECT blob.uuid"
" FROM tag, tagxref, event, blob"
" WHERE tag.tagname='sym-'||%Q "
" AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
" AND event.objid=tagxref.rid "
" AND blob.rid=event.objid "
" ORDER BY event.mtime DESC ",
zTag
);
return zUuid;
}
/*
** Convert a date/time string into a UUID.
**
** Input forms accepted:
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
db_finalize(&q);
rc = 0;
}else{
rc = 0;
}
return rc;
}
/*
** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD.
*/
static int is_date(const char *z){
if( !isdigit(z[0]) ) return 0;
if( !isdigit(z[1]) ) return 0;
if( !isdigit(z[2]) ) return 0;
if( !isdigit(z[3]) ) return 0;
if( z[4]!='-') return 0;
if( !isdigit(z[5]) ) return 0;
if( !isdigit(z[6]) ) return 0;
if( z[7]!='-') return 0;
if( !isdigit(z[8]) ) return 0;
if( !isdigit(z[9]) ) return 0;
return 1;
}
/*
** Convert a symbolic tag name into the UUID of a check-in that contains
** that tag. If the tag appears on multiple check-ins, return the UUID
** of the most recent check-in with the tag.
**
** If the input string is of the form:
**
** tag:date
**
** Then return the UUID of the oldest check-in with that tag that is
** not older than 'date'.
**
** An input of "tip" returns the most recent check-in.
**
** Memory to hold the returned string comes from malloc() and needs to
** be freed by the caller.
*/
char *tag_to_uuid(const char *zTag){
char *zUuid =
db_text(0,
"SELECT blob.uuid"
" FROM tag, tagxref, event, blob"
" WHERE tag.tagname='sym-'||%Q "
" AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
" AND event.objid=tagxref.rid "
" AND blob.rid=event.objid "
" ORDER BY event.mtime DESC ",
zTag
);
if( zUuid==0 ){
int nTag = strlen(zTag);
int i;
for(i=0; i<nTag-10; i++){
if( zTag[i]==':' && is_date(&zTag[i+1]) ){
char *zDate = mprintf("%s", &zTag[i+1]);
char *zTagBase = mprintf("%.*s", i, zTag);
int nDate = strlen(zDate);
int useUtc = 0;
if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
nDate -= 3;
zDate[nDate] = 0;
useUtc = 1;
}
zUuid = db_text(0,
"SELECT blob.uuid"
" FROM tag, tagxref, event, blob"
" WHERE tag.tagname='sym-'||%Q "
" AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
" AND event.objid=tagxref.rid "
" AND blob.rid=event.objid "
" AND event.mtime<=julianday(%Q %s)"
" ORDER BY event.mtime DESC ",
zTagBase, zDate, (useUtc ? "" : ",'utc'")
);
break;
}
}
if( zUuid==0 && strcmp(zTag, "tip")==0 ){
zUuid = db_text(0,
"SELECT blob.uuid"
" FROM event, blob"
" WHERE event.type='ci'"
" AND blob.rid=event.objid"
" ORDER BY event.mtime DESC"
);
}
}
return zUuid;
}
/*
** Convert a date/time string into a UUID.
**
** Input forms accepted:
|
| ︙ | ︙ | |||
160 161 162 163 164 165 166 |
}else if( memcmp(zDate, "local:", 6)==0 ){
zDate += 6;
}else if( memcmp(zDate, "utc:", 4)==0 ){
zDate += 4;
useUtc = 1;
}
n = strlen(zDate);
| | | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
}else if( memcmp(zDate, "local:", 6)==0 ){
zDate += 6;
}else if( memcmp(zDate, "utc:", 4)==0 ){
zDate += 4;
useUtc = 1;
}
n = strlen(zDate);
if( n<10 || !is_date(zDate) ) return 0;
if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){
zCopy = mprintf("%s", zDate);
zCopy[n-3] = 0;
zDate = zCopy;
n -= 3;
useUtc = 1;
}
|
| ︙ | ︙ |
Changes to src/schema.c.
| ︙ | ︙ | |||
307 308 309 310 311 312 313 314 315 316 317 318 319 320 | @ value TEXT, -- Value of the tag. Might be NULL. @ mtime TIMESTAMP, -- Time of addition or removal @ rid INTEGER REFERENCE blob, -- Artifact tag is applied to @ UNIQUE(rid, tagid) @ ); @ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime); @ @ -- Template for the TICKET table @ -- @ -- NB: when changing the schema of the TICKET table here, also make the @ -- same change in tktsetup.c. @ -- @ CREATE TABLE ticket( @ -- Do not change any column that begins with tkt_ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 | @ value TEXT, -- Value of the tag. Might be NULL. @ mtime TIMESTAMP, -- Time of addition or removal @ rid INTEGER REFERENCE blob, -- Artifact tag is applied to @ UNIQUE(rid, tagid) @ ); @ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime); @ @ -- When a hyperlink occurs from one artifact to another (for example @ -- when a check-in comment refers to a ticket) an entry is made in @ -- the following table for that hyperlink. This table is used to @ -- facilitate the display of "back links". @ -- @ CREATE TABLE backlink( @ target TEXT, -- Where the hyperlink points to @ srctype INT, -- 0: check-in 1: ticket 2: wiki @ srcid INT, -- rid for checkin or wiki. tkt_id for ticket. @ mtime TIMESTAMP, -- time that the hyperlink was added @ UNIQUE(target, srctype, srcid) @ ); @ CREATE INDEX backlink_src ON backlink(srcid, srctype); @ @ -- Each attachment is an entry in the following table. Only @ -- the most recent attachment (identified by the D card) is saved. @ -- @ CREATE TABLE attachment( @ attachid INTEGER PRIMARY KEY, -- Local id for this attachment @ isLatest BOOLEAN DEFAULT 0, -- True if this is the one to use @ mtime TIMESTAMP, -- Time when attachment last changed @ src TEXT, -- UUID of the attachment. NULL to delete @ target TEXT, -- Object attached to. Wikiname or Tkt UUID @ filename TEXT, -- Filename for the attachment @ comment TEXT, -- Comment associated with this attachment @ user TEXT -- Name of user adding attachment @ ); @ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime); @ CREATE INDEX attachment_idx2 ON attachment(src); @ @ -- Template for the TICKET table @ -- @ -- NB: when changing the schema of the TICKET table here, also make the @ -- same change in tktsetup.c. @ -- @ CREATE TABLE ticket( @ -- Do not change any column that begins with tkt_ |
| ︙ | ︙ | |||
391 392 393 394 395 396 397 398 399 400 401 402 403 404 | @ -- @ -- @ CREATE TABLE vfile( @ id INTEGER PRIMARY KEY, -- ID of the checked out file @ vid INTEGER REFERENCES blob, -- The baseline this file is part of. @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add @ deleted BOOLEAN DEFAULT 0, -- True if deleted @ rid INTEGER, -- Originally from this repository record @ mrid INTEGER, -- Based on this record due to a merge @ mtime INTEGER, -- Modification time of file on disk @ pathname TEXT, -- Full pathname relative to root @ origname TEXT, -- Original pathname. NULL if unchanged @ UNIQUE(pathname,vid) @ ); | > | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 | @ -- @ -- @ CREATE TABLE vfile( @ id INTEGER PRIMARY KEY, -- ID of the checked out file @ vid INTEGER REFERENCES blob, -- The baseline this file is part of. @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add @ deleted BOOLEAN DEFAULT 0, -- True if deleted @ isexe BOOLEAN, -- True if file should be executable @ rid INTEGER, -- Originally from this repository record @ mrid INTEGER, -- Based on this record due to a merge @ mtime INTEGER, -- Modification time of file on disk @ pathname TEXT, -- Full pathname relative to root @ origname TEXT, -- Original pathname. NULL if unchanged @ UNIQUE(pathname,vid) @ ); |
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
@ <td valign="top">
@ <b>Notes:</b>
@ <ol>
@ <li><p>The permission flags are as follows:</p>
@ <table>
@ <tr><td valign="top"><b>a</b></td>
@ <td><i>Admin:</i> Create and delete users</td></tr>
@ <tr><td valign="top"><b>c</b></td>
@ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
@ <tr><td valign="top"><b>d</b></td>
@ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
@ <tr><td valign="top"><b>e</b></td>
@ <td><i>Email:</i> View sensitive data such as EMail addresses</td></tr>
@ <tr><td valign="top"><b>f</b></td>
| > > | 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
@ <td valign="top">
@ <b>Notes:</b>
@ <ol>
@ <li><p>The permission flags are as follows:</p>
@ <table>
@ <tr><td valign="top"><b>a</b></td>
@ <td><i>Admin:</i> Create and delete users</td></tr>
@ <tr><td valign="top"><b>b</b></td>
@ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
@ <tr><td valign="top"><b>c</b></td>
@ <td><i>Append-Tkt:</i> Append to tickets</td></tr>
@ <tr><td valign="top"><b>d</b></td>
@ <td><i>Delete:</i> Delete wiki and tickets</td></tr>
@ <tr><td valign="top"><b>e</b></td>
@ <td><i>Email:</i> View sensitive data such as EMail addresses</td></tr>
@ <tr><td valign="top"><b>f</b></td>
|
| ︙ | ︙ | |||
235 236 237 238 239 240 241 |
/*
** WEBPAGE: /setup_uedit
*/
void user_edit(void){
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
| | | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
/*
** WEBPAGE: /setup_uedit
*/
void user_edit(void){
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
char *oat, *oau, *oav, *oab, *oaz;
const char *inherit[128];
int doWrite;
int uid;
int higherUser = 0; /* True if user being edited is SETUP and the */
/* user doing the editing is ADMIN. Disallow editing */
/* Must have ADMIN privleges to access this page
|
| ︙ | ︙ | |||
272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
** to the page that displays a list of users.
*/
doWrite = cgi_all("login","info","pw") && !higherUser;
if( doWrite ){
char zCap[50];
int i = 0;
int aa = P("aa")!=0;
int ad = P("ad")!=0;
int ae = P("ae")!=0;
int ai = P("ai")!=0;
int aj = P("aj")!=0;
int ak = P("ak")!=0;
int an = P("an")!=0;
int ao = P("ao")!=0;
| > | 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
** to the page that displays a list of users.
*/
doWrite = cgi_all("login","info","pw") && !higherUser;
if( doWrite ){
char zCap[50];
int i = 0;
int aa = P("aa")!=0;
int ab = P("ab")!=0;
int ad = P("ad")!=0;
int ae = P("ae")!=0;
int ai = P("ai")!=0;
int aj = P("aj")!=0;
int ak = P("ak")!=0;
int an = P("an")!=0;
int ao = P("ao")!=0;
|
| ︙ | ︙ | |||
293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
int ah = P("ah")!=0;
int ag = P("ag")!=0;
int at = P("at")!=0;
int au = P("au")!=0;
int av = P("av")!=0;
int az = P("az")!=0;
if( aa ){ zCap[i++] = 'a'; }
if( ac ){ zCap[i++] = 'c'; }
if( ad ){ zCap[i++] = 'd'; }
if( ae ){ zCap[i++] = 'e'; }
if( af ){ zCap[i++] = 'f'; }
if( ah ){ zCap[i++] = 'h'; }
if( ag ){ zCap[i++] = 'g'; }
if( ai ){ zCap[i++] = 'i'; }
| > | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
int ah = P("ah")!=0;
int ag = P("ag")!=0;
int at = P("at")!=0;
int au = P("au")!=0;
int av = P("av")!=0;
int az = P("az")!=0;
if( aa ){ zCap[i++] = 'a'; }
if( ab ){ zCap[i++] = 'b'; }
if( ac ){ zCap[i++] = 'c'; }
if( ad ){ zCap[i++] = 'd'; }
if( ae ){ zCap[i++] = 'e'; }
if( af ){ zCap[i++] = 'f'; }
if( ah ){ zCap[i++] = 'h'; }
if( ag ){ zCap[i++] = 'g'; }
if( ai ){ zCap[i++] = 'i'; }
|
| ︙ | ︙ | |||
349 350 351 352 353 354 355 | /* Load the existing information about the user, if any */ zLogin = ""; zInfo = ""; zCap = ""; zPw = ""; | | > | 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
/* Load the existing information about the user, if any
*/
zLogin = "";
zInfo = "";
zCap = "";
zPw = "";
oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
if( uid ){
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
if( strchr(zCap, 'a') ) oaa = " checked";
if( strchr(zCap, 'b') ) oab = " checked";
if( strchr(zCap, 'c') ) oac = " checked";
if( strchr(zCap, 'd') ) oad = " checked";
if( strchr(zCap, 'e') ) oae = " checked";
if( strchr(zCap, 'f') ) oaf = " checked";
if( strchr(zCap, 'g') ) oag = " checked";
if( strchr(zCap, 'h') ) oah = " checked";
if( strchr(zCap, 'i') ) oai = " checked";
|
| ︙ | ︙ | |||
463 464 465 466 467 468 469 |
@ <input type="checkbox" name="au"%s(oau)/>%s(B('u'))Reader<br>
@ <input type="checkbox" name="av"%s(oav)/>%s(B('v'))Developer<br>
@ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
@ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
@ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
@ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
@ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
| > | | | | | | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
@ <input type="checkbox" name="au"%s(oau)/>%s(B('u'))Reader<br>
@ <input type="checkbox" name="av"%s(oav)/>%s(B('v'))Developer<br>
@ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
@ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
@ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
@ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
@ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
@ <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
@ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
@ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
@ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
@ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
@ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
@ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
@ </td>
@ </tr>
@ <tr>
@ <td align="right">Password:</td>
if( zPw[0] ){
/* Obscure the password for all users */
|
| ︙ | ︙ | |||
560 561 562 563 564 565 566 | @ The <b>Check-out</b> privilege allows remote users to "pull". @ The <b>Clone</b> privilege allows remote users to "clone". @ </li><p> @ @ <li><p> @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and @ <b>Write Wiki</b> privileges control access to wiki pages. The | | | | > > > > > | 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 | @ The <b>Check-out</b> privilege allows remote users to "pull". @ The <b>Clone</b> privilege allows remote users to "clone". @ </li><p> @ @ <li><p> @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and @ <b>Write Wiki</b> privileges control access to wiki pages. The @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and @ <b>Write Ticket</b> privileges control access to trouble tickets. @ The <b>Ticket Report</b> privilege allows the user to create or edit @ ticket report formats. @ </p></li> @ @ <li><p> @ Users with the <b>Password</b> privilege are allowed to change their @ own password. Recommended ON for most users but OFF for special @ users "developer", "anonymous", and "nobody". @ </p></li> @ @ <li><p> @ The <b>EMail</b> privilege allows the display of sensitive information @ such as the email address of users and contact information on tickets. @ Recommended OFF for "anonymous" and for "nobody" but ON for @ "developer". @ </p></li> @ @ <li><p> @ The <b>Attachment</b> privilege is needed in order to add attachments @ to tickets or wiki. Write privilege on the ticket or wiki is also @ required.</p></li> @ @ <li><p> @ Login is prohibited if the password is an empty string. @ </p></li> @ </ul> @ @ <h2>Special Logins</h2> @ |
| ︙ | ︙ |
Changes to src/skins.c.
| ︙ | ︙ | |||
160 161 162 163 164 165 166 |
@ padding: 0.2ex 2ex;
@ }');
@ REPLACE INTO config VALUES('header','<html>
@ <head>
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@ href="$baseurl/timeline.rss">
| | | | | | | | | | | | | | | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
@ padding: 0.2ex 2ex;
@ }');
@ REPLACE INTO config VALUES('header','<html>
@ <head>
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@ href="$baseurl/timeline.rss">
@ <link rel="stylesheet" href="$baseurl/style.css?blackwhite" type="text/css"
@ media="screen">
@ </head>
@ <body>
@ <div class="header">
@ <div class="logo">
@ <nobr>$<project_name></nobr>
@ </div>
@ </div>
@ <div class="header">
@ <div class="title">$<title></div>
@ <div class="status"><nobr><th1>
@ if {[info exists login]} {
@ puts "Logged in as $login"
@ } else {
@ puts "Not logged in"
@ }
@ </th1></nobr></div>
@ </div>
@ <div class="mainmenu"><th1>
@ html "<a href=''$baseurl$index_page''>Home</a> "
@ if {[anycap jor]} {
@ html "<a href=''$baseurl/timeline''>Timeline</a> "
@ }
@ if {[hascap oh]} {
@ html "<a href=''$baseurl/dir?ci=tip''>Files</a> "
@ }
@ if {[hascap o]} {
@ html "<a href=''$baseurl/leaves''>Leaves</a> "
@ html "<a href=''$baseurl/brlist''>Branches</a> "
@ html "<a href=''$baseurl/taglist''>Tags</a> "
@ }
@ if {[hascap r]} {
@ html "<a href=''$baseurl/reportlist''>Tickets</a> "
@ }
@ if {[hascap j]} {
@ html "<a href=''$baseurl/wiki''>Wiki</a> "
@ }
@ if {[hascap s]} {
@ html "<a href=''$baseurl/setup''>Admin</a> "
@ } elseif {[hascap a]} {
@ html "<a href=''$baseurl/setup_ulist''>Users</a> "
@ }
@ if {[info exists login]} {
@ html "<a href=''$baseurl/login''>Logout</a> "
@ } else {
@ html "<a href=''$baseurl/login''>Login</a> "
@ }
@ </th1></div>
@ ');
@ REPLACE INTO config VALUES('footer','<div class="footer">
@ Fossil version $manifest_version $manifest_date
@ </div>
@ </body></html>
|
| ︙ | ︙ | |||
365 366 367 368 369 370 371 |
@ }
@ ');
@ REPLACE INTO config VALUES('header','<html>
@ <head>
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@ href="$baseurl/timeline.rss">
| | | | | | | | | | | | | | | 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 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 |
@ }
@ ');
@ REPLACE INTO config VALUES('header','<html>
@ <head>
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@ href="$baseurl/timeline.rss">
@ <link rel="stylesheet" href="$baseurl/style.css?tan" type="text/css"
@ media="screen">
@ </head>
@ <body>
@ <div class="header">
@ <div class="title">$<title></div>
@ <div class="status">
@ <div class="logo"><nobr>$<project_name></nobr></div><br/>
@ <nobr><th1>
@ if {[info exists login]} {
@ puts "Logged in as $login"
@ } else {
@ puts "Not logged in"
@ }
@ </th1></nobr></div>
@ </div>
@ <div class="mainmenu"><th1>
@ html "<a href=''$baseurl$index_page''>Home</a> "
@ if {[anycap jor]} {
@ html "<a href=''$baseurl/timeline''>Timeline</a> "
@ }
@ if {[hascap oh]} {
@ html "<a href=''$baseurl/dir?ci=tip''>Files</a> "
@ }
@ if {[hascap o]} {
@ html "<a href=''$baseurl/leaves''>Leaves</a> "
@ html "<a href=''$baseurl/brlist''>Branches</a> "
@ html "<a href=''$baseurl/taglist''>Tags</a> "
@ }
@ if {[hascap r]} {
@ html "<a href=''$baseurl/reportlist''>Tickets</a> "
@ }
@ if {[hascap j]} {
@ html "<a href=''$baseurl/wiki''>Wiki</a> "
@ }
@ if {[hascap s]} {
@ html "<a href=''$baseurl/setup''>Admin</a> "
@ } elseif {[hascap a]} {
@ html "<a href=''$baseurl/setup_ulist''>Users</a> "
@ }
@ if {[info exists login]} {
@ html "<a href=''$baseurl/login''>Logout</a> "
@ } else {
@ html "<a href=''$baseurl/login''>Login</a> "
@ }
@ </th1></div>
@ ');
@ REPLACE INTO config VALUES('footer','<div class="footer">
@ Fossil version $manifest_version $manifest_date
@ </div>
@ </body></html>
|
| ︙ | ︙ | |||
512 513 514 515 516 517 518 |
@ text-decoration: none;
@ }
@ div.mainmenu a:hover {
@ color: #eee;
@ background-color: #333;
@ }
@
| | | 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 |
@ text-decoration: none;
@ }
@ div.mainmenu a:hover {
@ color: #eee;
@ background-color: #333;
@ }
@
@ /* Container for the sub-menu and content so they don''t spread
@ ** out underneath the main menu */
@ #container {
@ padding-left: 9em;
@ }
@
@ /* The submenu bar that *sometimes* appears below the main menu */
@ div.submenu {
|
| ︙ | ︙ | |||
598 599 600 601 602 603 604 |
@ padding: 0.2ex 2ex;
@ }');
@ REPLACE INTO config VALUES('header','<html>
@ <head>
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@ href="$baseurl/timeline.rss">
| | | | | | | | | | | | | | | 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 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 |
@ padding: 0.2ex 2ex;
@ }');
@ REPLACE INTO config VALUES('header','<html>
@ <head>
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@ href="$baseurl/timeline.rss">
@ <link rel="stylesheet" href="$baseurl/style.css?black2" type="text/css"
@ media="screen">
@ </head>
@ <body>
@ <div class="header">
@ <div class="logo">
@ <!-- <img src="$baseurl/logo" alt="logo"> -->
@ <br><nobr>$<project_name></nobr>
@ </div>
@ <div class="title">$<title></div>
@ <div class="status"><nobr><th1>
@ if {[info exists login]} {
@ puts "Logged in as $login"
@ } else {
@ puts "Not logged in"
@ }
@ </th1></nobr></div>
@ </div>
@ <div class="mainmenu"><ul><th1>
@ html "<li><a href=''$baseurl$index_page''>Home</a></li>"
@ if {[anycap jor]} {
@ html "<li><a href=''$baseurl/timeline''>Timeline</a></li>"
@ }
@ if {[hascap oh]} {
@ html "<li><a href=''$baseurl/dir?ci=tip''>Files</a></li>"
@ }
@ if {[hascap o]} {
@ html "<li><a href=''$baseurl/leaves''>Leaves</a></li>"
@ html "<li><a href=''$baseurl/brlist''>Branches</a></li>"
@ html "<li><a href=''$baseurl/taglist''>Tags</a></li>"
@ }
@ if {[hascap r]} {
@ html "<li><a href=''$baseurl/reportlist''>Tickets</a></li>"
@ }
@ if {[hascap j]} {
@ html "<li><a href=''$baseurl/wiki''>Wiki</a></li>"
@ }
@ if {[hascap s]} {
@ html "<li><a href=''$baseurl/setup''>Admin</a></li>"
@ } elseif {[hascap a]} {
@ html "<li><a href=''$baseurl/setup_ulist''>Users</a></li>"
@ }
@ if {[info exists login]} {
@ html "<li><a href=''$baseurl/login''>Logout</a></li>"
@ } else {
@ html "<li><a href=''$baseurl/login''>Login</a></li>"
@ }
@ </th1></ul></div>
@ <div id="container">
@ ');
@ REPLACE INTO config VALUES('footer','</div>
@ <div class="footer">
@ Fossil version $manifest_version $manifest_date
|
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
186 187 188 189 190 191 192 | */ const char zDefaultHeader[] = @ <html> @ <head> @ <title>$<project_name>: $<title></title> @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" @ href="$baseurl/timeline.rss"> | | | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | */ const char zDefaultHeader[] = @ <html> @ <head> @ <title>$<project_name>: $<title></title> @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" @ href="$baseurl/timeline.rss"> @ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css" @ media="screen"> @ </head> @ <body> @ <div class="header"> @ <div class="logo"> @ <img src="$baseurl/logo" alt="logo"> @ <br><nobr>$<project_name></nobr> |
| ︙ | ︙ | |||
210 211 212 213 214 215 216 |
@ </div>
@ <div class="mainmenu"><th1>
@ html "<a href='$baseurl$index_page'>Home</a> "
@ if {[anycap jor]} {
@ html "<a href='$baseurl/timeline'>Timeline</a> "
@ }
@ if {[hascap oh]} {
| | | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
@ </div>
@ <div class="mainmenu"><th1>
@ html "<a href='$baseurl$index_page'>Home</a> "
@ if {[anycap jor]} {
@ html "<a href='$baseurl/timeline'>Timeline</a> "
@ }
@ if {[hascap oh]} {
@ html "<a href='$baseurl/dir?ci=tip'>Files</a> "
@ }
@ if {[hascap o]} {
@ html "<a href='$baseurl/leaves'>Leaves</a> "
@ html "<a href='$baseurl/brlist'>Branches</a> "
@ html "<a href='$baseurl/taglist'>Tags</a> "
@ }
@ if {[hascap r]} {
|
| ︙ | ︙ |
Changes to src/tag.c.
| ︙ | ︙ | |||
198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
case TAG_USER: {
zCol = "euser";
break;
}
}
if( zCol ){
db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
}
if( tagid==TAG_DATE ){
db_multi_exec("UPDATE event SET mtime=julianday(%Q) WHERE objid=%d",
zValue, rid);
}
if( tagtype==0 || tagtype==2 ){
tag_propagate(rid, tagid, tagtype, rid, zValue, mtime);
| > > > > > | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
case TAG_USER: {
zCol = "euser";
break;
}
}
if( zCol ){
db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
if( tagid==TAG_COMMENT ){
char *zCopy = mprintf("%s", zValue);
wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE);
free(zCopy);
}
}
if( tagid==TAG_DATE ){
db_multi_exec("UPDATE event SET mtime=julianday(%Q) WHERE objid=%d",
zValue, rid);
}
if( tagtype==0 || tagtype==2 ){
tag_propagate(rid, tagid, tagtype, rid, zValue, mtime);
|
| ︙ | ︙ | |||
561 562 563 564 565 566 567 |
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
style_header("Tagged Check-ins");
style_submenu_element("List", "List", "taglist");
login_anonymous_available();
| | | 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 |
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
style_header("Tagged Check-ins");
style_submenu_element("List", "List", "taglist");
login_anonymous_available();
@ <h2>Check-ins with non-propagating tags:</h2>
db_prepare(&q,
"%s AND blob.rid IN (SELECT rid FROM tagxref"
" WHERE tagtype=1 AND srcid>0"
" AND tagid IN (SELECT tagid FROM tag "
" WHERE tagname GLOB 'sym-*'))"
" ORDER BY event.mtime DESC",
timeline_query_for_www()
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
198 199 200 201 202 203 204 |
wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
}
if( tmFlags & TIMELINE_GRAPH ){
pGraph = graph_init();
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
}
| < < < < | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
}
if( tmFlags & TIMELINE_GRAPH ){
pGraph = graph_init();
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
}
@ <table cellspacing=0 border=0 cellpadding=0>
blob_zero(&comment);
while( db_step(pQuery)==SQLITE_ROW ){
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
int nPChild = db_column_int(pQuery, 5);
int nParent = db_column_int(pQuery, 6);
|
| ︙ | ︙ | |||
239 240 241 242 243 244 245 |
@ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr>
suppressCnt = 0;
}
if( strcmp(zType,"div")==0 ){
@ <tr><td colspan=3><hr></td></tr>
continue;
}
| < > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
@ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr>
suppressCnt = 0;
}
if( strcmp(zType,"div")==0 ){
@ <tr><td colspan=3><hr></td></tr>
continue;
}
if( memcmp(zDate, zPrevDate, 10) ){
sprintf(zPrevDate, "%.10s", zDate);
@ <tr><td>
@ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
@ </td></tr>
}
memcpy(zTime, &zDate[11], 5);
zTime[5] = 0;
@ <tr>
@ <td valign="top" align="right">%s(zTime)</td>
@ <td width="20" align="left" valign="top">
if( pGraph && zType[0]=='c' ){
int nParent = 0;
int aParent[32];
const char *zBr;
int gidx;
static Stmt qparent;
static Stmt qbranch;
db_static_prepare(&qparent,
"SELECT pid FROM plink WHERE cid=:rid ORDER BY isprim DESC"
);
db_static_prepare(&qbranch,
"SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
TAG_BRANCH
);
db_bind_int(&qparent, ":rid", rid);
while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){
aParent[nParent++] = db_column_int(&qparent, 0);
}
db_reset(&qparent);
db_bind_int(&qbranch, ":rid", rid);
if( db_step(&qbranch)==SQLITE_ROW ){
zBr = db_column_text(&qbranch, 0);
}else{
zBr = "trunk";
}
gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr);
db_reset(&qbranch);
@ <div id="m%d(gidx)"></div>
}
if( zBgClr && zBgClr[0] ){
@ <td valign="top" align="left" bgcolor="%h(zBgClr)">
}else{
@ <td valign="top" align="left">
}
if( zType[0]=='c' ){
const char *azTag[5];
|
| ︙ | ︙ | |||
288 289 290 291 292 293 294 |
}
if( nTag>0 ){
int i;
for(i=0; i<nTag; i++){
@ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b>
}
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
}
if( nTag>0 ){
int i;
for(i=0; i<nTag; i++){
@ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b>
}
}
}else if( (tmFlags & TIMELINE_ARTID)!=0 ){
hyperlink_to_uuid(zUuid);
}
db_column_blob(pQuery, commentColumn, &comment);
if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
Blob truncated;
blob_zero(&truncated);
|
| ︙ | ︙ | |||
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
graph_free(pGraph);
pGraph = 0;
}else{
@ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
}
}
@ </table>
if( pGraph && pGraph->nErr==0 ){
GraphRow *pRow;
int i;
char cSep;
@ <script type="text/JavaScript">
cgi_printf("var rowinfo = [\n");
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
cgi_printf("{id:\"m%d\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
| > > > > > > > > | | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
graph_free(pGraph);
pGraph = 0;
}else{
@ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
}
}
@ </table>
timeline_output_graph_javascript(pGraph);
}
/*
** Generate all of the necessary javascript to generate a timeline
** graph.
*/
void timeline_output_graph_javascript(GraphContext *pGraph){
if( pGraph && pGraph->nErr==0 ){
GraphRow *pRow;
int i;
char cSep;
@ <script type="text/JavaScript">
cgi_printf("var rowinfo = [\n");
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
cgi_printf("{id:\"m%d\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
pRow->idx,
pRow->iRail,
pRow->bDescender,
pRow->mergeOut,
pRow->mergeUpto,
pRow->aiRaiser[pRow->iRail]
);
cSep = '[';
|
| ︙ | ︙ | |||
696 697 698 699 700 701 702 |
tagid = 0;
}
if( zType[0]=='a' ){
tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH;
}else{
tmFlags = TIMELINE_GRAPH;
}
| | | 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 |
tagid = 0;
}
if( zType[0]=='a' ){
tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH;
}else{
tmFlags = TIMELINE_GRAPH;
}
if( P("ng")!=0 || zSearch!=0 ){
tmFlags &= ~TIMELINE_GRAPH;
}
style_header("Timeline");
login_anonymous_available();
timeline_temp_table();
blob_zero(&sql);
|
| ︙ | ︙ | |||
889 890 891 892 893 894 895 896 897 898 899 900 901 902 |
if( zAfter ){
blob_appendf(&desc, " occurring on or after %h.<br>", zAfter);
}else if( zBefore ){
blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
}else if( zCirca ){
blob_appendf(&desc, " occurring around %h.<br>", zCirca);
}
if( g.okHistory ){
if( zAfter || n==nEntry ){
zDate = db_text(0, "SELECT min(timestamp) FROM timeline");
timeline_submenu(&url, "Older", "b", zDate, "a");
free(zDate);
}
if( zBefore || (zAfter && n==nEntry) ){
| > > > | 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 |
if( zAfter ){
blob_appendf(&desc, " occurring on or after %h.<br>", zAfter);
}else if( zBefore ){
blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
}else if( zCirca ){
blob_appendf(&desc, " occurring around %h.<br>", zCirca);
}
if( zSearch ){
blob_appendf(&desc, " matching \"%h\"", zSearch);
}
if( g.okHistory ){
if( zAfter || n==nEntry ){
zDate = db_text(0, "SELECT min(timestamp) FROM timeline");
timeline_submenu(&url, "Older", "b", zDate, "a");
free(zDate);
}
if( zBefore || (zAfter && n==nEntry) ){
|
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
163 164 165 166 167 168 169 |
const char *z;
for(i=0; (z = cgi_parameter_name(i))!=0; i++){
Th_Store(z, P(z));
}
}
| < < < < < < < < < < < < < < < < < < < < < < | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
const char *z;
for(i=0; (z = cgi_parameter_name(i))!=0; i++){
Th_Store(z, P(z));
}
}
/*
** Update an entry of the TICKET table according to the information
** in the control file given in p. Attempt to create the appropriate
** TICKET table entry if createFlag is true. If createFlag is false,
** that means we already know the entry exists and so we can save the
** work of trying to create it.
**
** Return TRUE if a new TICKET entry was created and FALSE if an
** existing entry was revised.
*/
int ticket_insert(const Manifest *p, int createFlag, int rid){
Blob sql;
Stmt q;
int i;
const char *zSep;
int rc = 0;
getAllTicketFields();
|
| ︙ | ︙ | |||
222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
if( fieldId(zName)<0 ) continue;
blob_appendf(&sql,", %s=coalesce(%s,'') || %Q",
zName, zName, p->aField[i].zValue);
}else{
if( fieldId(zName)<0 ) continue;
blob_appendf(&sql,", %s=%Q", zName, p->aField[i].zValue);
}
}
blob_appendf(&sql, " WHERE tkt_uuid='%s' AND tkt_mtime<:mtime",
p->zTicketUuid);
db_prepare(&q, "%s", blob_str(&sql));
db_bind_double(&q, ":mtime", p->rDate);
db_step(&q);
db_finalize(&q);
| > > > < < < < < < < < < < | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
if( fieldId(zName)<0 ) continue;
blob_appendf(&sql,", %s=coalesce(%s,'') || %Q",
zName, zName, p->aField[i].zValue);
}else{
if( fieldId(zName)<0 ) continue;
blob_appendf(&sql,", %s=%Q", zName, p->aField[i].zValue);
}
if( rid>0 ){
wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0);
}
}
blob_appendf(&sql, " WHERE tkt_uuid='%s' AND tkt_mtime<:mtime",
p->zTicketUuid);
db_prepare(&q, "%s", blob_str(&sql));
db_bind_double(&q, ":mtime", p->rDate);
db_step(&q);
db_finalize(&q);
blob_reset(&sql);
return rc;
}
/*
** Rebuild an entire entry in the TICKET table
*/
|
| ︙ | ︙ | |||
262 263 264 265 266 267 268 |
"DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
);
db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
content_get(rid, &content);
manifest_parse(&manifest, &content);
| | | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
"DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
);
db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
content_get(rid, &content);
manifest_parse(&manifest, &content);
ticket_insert(&manifest, createFlag, rid);
manifest_ticket_event(rid, &manifest, createFlag, tagid);
manifest_clear(&manifest);
createFlag = 0;
}
db_finalize(&q);
}
|
| ︙ | ︙ | |||
323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
** WEBPAGE: tktview
** URL: tktview?name=UUID
**
** View a ticket.
*/
void tktview_page(void){
const char *zScript;
login_check_credentials();
if( !g.okRdTkt ){ login_needed(); return; }
if( g.okWrTkt || g.okApndTkt ){
style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
g.zTop, PD("name",""));
}
if( g.okHistory ){
| > > > < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 320 321 322 323 324 325 326 327 328 329 330 331 332 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 366 367 368 369 370 371 372 373 374 375 376 |
** WEBPAGE: tktview
** URL: tktview?name=UUID
**
** View a ticket.
*/
void tktview_page(void){
const char *zScript;
char *zFullName;
const char *zUuid = PD("name","");
login_check_credentials();
if( !g.okRdTkt ){ login_needed(); return; }
if( g.okWrTkt || g.okApndTkt ){
style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
g.zTop, PD("name",""));
}
if( g.okHistory ){
style_submenu_element("History", "History Of This Ticket",
"%s/tkthistory/%T", g.zTop, zUuid);
style_submenu_element("Timeline", "Timeline Of This Ticket",
"%s/tkttimeline/%T", g.zTop, zUuid);
style_submenu_element("Check-ins", "Check-ins Of This Ticket",
"%s/tkttimeline/%T?y=ci", g.zTop, zUuid);
}
if( g.okNewTkt ){
style_submenu_element("New Ticket", "Create a new ticket",
"%s/tktnew", g.zTop);
}
if( g.okApndTkt && g.okAttach ){
style_submenu_element("Attach", "Add An Attachment",
"%s/attachadd?tkt=%T&from=%s/tktview%%3fname=%t",
g.zTop, zUuid, g.zTop, zUuid);
}
style_header("View Ticket");
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
ticket_init();
initializeVariablesFromDb();
zScript = ticket_viewpage_code();
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
Th_Render(zScript);
if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
zFullName = db_text(0,
"SELECT tkt_uuid FROM ticket"
" WHERE tkt_uuid GLOB '%q*'", zUuid);
if( zFullName ){
int cnt = 0;
Stmt q;
db_prepare(&q,
"SELECT datetime(mtime,'localtime'), filename, user"
" FROM attachment"
" WHERE isLatest AND src!='' AND target=%Q"
" ORDER BY mtime DESC",
zFullName);
while( db_step(&q)==SQLITE_ROW ){
const char *zDate = db_column_text(&q, 0);
const char *zFile = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
if( cnt==0 ){
@ <hr><h2>Attachments:</h2>
@ <ul>
}
cnt++;
@ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
@ %h(zFile)</a> add by %h(zUser) on
hyperlink_to_date(zDate, ".");
if( g.okWrTkt && g.okAttach ){
@ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
}
}
if( cnt ){
@ </ul>
}
db_finalize(&q);
}
style_footer();
}
/*
** TH command: append_field FIELD STRING
**
** FIELD is the name of a database column to which we might want
|
| ︙ | ︙ | |||
608 609 610 611 612 613 614 |
}
}
return 0;
}
/*
** WEBPAGE: tkttimeline
| | > > > > > > > > > > > > > > | > > > > > > | > > > > > > > > > | > > > > > | | > > | 623 624 625 626 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 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 |
}
}
return 0;
}
/*
** WEBPAGE: tkttimeline
** URL: /tkttimeline?name=TICKETUUID&y=TYPE
**
** Show the change history for a single ticket in timeline format.
*/
void tkttimeline_page(void){
Stmt q;
char *zTitle;
char *zSQL;
const char *zUuid;
char *zFullUuid;
int tagid;
char zGlobPattern[50];
const char *zType;
login_check_credentials();
if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
zUuid = PD("name","");
zType = PD("y","a");
if( zType[0]!='c' ){
style_submenu_element("Check-ins", "Check-ins",
"%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid);
}else{
style_submenu_element("Timeline", "Timeline",
"%s/tkttimeline?name=%T", g.zTop, zUuid);
}
style_submenu_element("History", "History",
"%s/tkthistory/%s", g.zTop, zUuid);
style_submenu_element("Status", "Status",
"%s/info/%s", g.zTop, zUuid);
if( zType[0]=='c' ){
zTitle = mprintf("Check-Ins Associated With Ticket %h", zUuid);
}else{
zTitle = mprintf("Timeline Of Ticket %h", zUuid);
}
style_header(zTitle);
free(zTitle);
sqlite3_snprintf(6, zGlobPattern, "%s", zUuid);
canonical16(zGlobPattern, strlen(zGlobPattern));
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
if( tagid==0 ){
@ No such ticket: %h(zUuid)
style_footer();
return;
}
zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d",
tagid);
if( zType[0]=='c' ){
zSQL = mprintf(
"%s AND event.objid IN "
" (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' "
"AND '%s' GLOB (target||'*')) "
"ORDER BY mtime DESC",
timeline_query_for_www(), zFullUuid, zFullUuid
);
}else{
zSQL = mprintf(
"%s AND event.objid IN "
" (SELECT rid FROM tagxref WHERE tagid=%d"
" UNION SELECT srcid FROM backlink"
" WHERE target GLOB '%.4s*'"
" AND '%s' GLOB (target||'*')"
" UNION SELECT attachid FROM attachment"
" WHERE target=%Q) "
"ORDER BY mtime DESC",
timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
);
}
db_prepare(&q, zSQL);
free(zSQL);
www_print_timeline(&q, TIMELINE_ARTID, 0);
db_finalize(&q);
style_footer();
}
|
| ︙ | ︙ | |||
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 |
login_check_credentials();
if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
zUuid = PD("name","");
zTitle = mprintf("History Of Ticket %h", zUuid);
style_submenu_element("Status", "Status",
"%s/info/%s", g.zTop, zUuid);
style_submenu_element("Timeline", "Timeline",
"%s/tkttimeline?name=%s", g.zTop, zUuid);
style_header(zTitle);
free(zTitle);
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
if( tagid==0 ){
@ No such ticket: %h(zUuid)
style_footer();
return;
}
db_prepare(&q,
| > > > | > > > > > | | > > | | > > > > > > > > > > > > > > > > > > | | < < < < | | | | | | < | > > > < | 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 |
login_check_credentials();
if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
zUuid = PD("name","");
zTitle = mprintf("History Of Ticket %h", zUuid);
style_submenu_element("Status", "Status",
"%s/info/%s", g.zTop, zUuid);
style_submenu_element("Check-ins", "Check-ins",
"%s/tkttimeline?name=%s?y=ci", g.zTop, zUuid);
style_submenu_element("Timeline", "Timeline",
"%s/tkttimeline?name=%s", g.zTop, zUuid);
style_header(zTitle);
free(zTitle);
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
if( tagid==0 ){
@ No such ticket: %h(zUuid)
style_footer();
return;
}
db_prepare(&q,
"SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
" FROM event, blob"
" WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
" AND blob.rid=event.objid"
" UNION "
"SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
" FROM attachment, blob"
" WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
" AND blob.rid=attachid"
" ORDER BY 1 DESC",
tagid, tagid
);
while( db_step(&q)==SQLITE_ROW ){
Blob content;
Manifest m;
char zShort[12];
const char *zDate = db_column_text(&q, 0);
int rid = db_column_int(&q, 1);
const char *zChngUuid = db_column_text(&q, 2);
const char *zFile = db_column_text(&q, 4);
memcpy(zShort, zChngUuid, 10);
zShort[10] = 0;
if( zFile!=0 ){
const char *zSrc = db_column_text(&q, 3);
const char *zUser = db_column_text(&q, 5);
if( zSrc==0 || zSrc[0]==0 ){
@
@ <p>Delete attachment "%h(zFile)"
}else{
@
@ <p>Add attachment "%h(zFile)"
}
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
@ (rid %d(rid)) by
hyperlink_to_user(zUser,zDate," on");
hyperlink_to_date(zDate, ".</p>");
}else{
content_get(rid, &content);
if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
@
@ <p>Ticket change
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
@ (rid %d(rid)) by
hyperlink_to_user(m.zUser,zDate," on");
hyperlink_to_date(zDate, ":");
ticket_output_change_artifact(&m);
@ </p>
}
manifest_clear(&m);
}
}
db_finalize(&q);
style_footer();
}
/*
** Return TRUE if the given BLOB contains a newline character.
|
| ︙ | ︙ |
Changes to src/wiki.c.
| ︙ | ︙ | |||
125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
char *zTag;
int rid = 0;
int isSandbox;
Blob wiki;
Manifest m;
const char *zPageName;
char *zBody = mprintf("%s","<i>Empty Page</i>");
login_check_credentials();
if( !g.okRdWiki ){ login_needed(); return; }
zPageName = P("name");
if( zPageName==0 ){
style_header("Wiki");
@ <ul>
| > > | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
char *zTag;
int rid = 0;
int isSandbox;
Blob wiki;
Manifest m;
const char *zPageName;
char *zBody = mprintf("%s","<i>Empty Page</i>");
Stmt q;
int cnt = 0;
login_check_credentials();
if( !g.okRdWiki ){ login_needed(); return; }
zPageName = P("name");
if( zPageName==0 ){
style_header("Wiki");
@ <ul>
|
| ︙ | ︙ | |||
150 151 152 153 154 155 156 |
@ to experiment.</li>
if( g.okNewWiki ){
@ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
}
@ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
@ available on this server.</li>
@ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
| | > | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
@ to experiment.</li>
if( g.okNewWiki ){
@ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
}
@ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
@ available on this server.</li>
@ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
@ Search wiki titles: <input type="text" name="title"/>
@ <input type="submit" />
@ </li>
@ </ul>
style_footer();
return;
}
if( check_name(zPageName) ) return;
isSandbox = is_sandbox(zPageName);
|
| ︙ | ︙ | |||
174 175 176 177 178 179 180 |
free(zTag);
memset(&m, 0, sizeof(m));
blob_zero(&m.content);
if( rid ){
Blob content;
content_get(rid, &content);
manifest_parse(&m, &content);
| | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
free(zTag);
memset(&m, 0, sizeof(m));
blob_zero(&m.content);
if( rid ){
Blob content;
content_get(rid, &content);
manifest_parse(&m, &content);
if( m.type==CFTYPE_WIKI && m.zWiki ){
while( isspace(m.zWiki[0]) ) m.zWiki++;
if( m.zWiki[0] ) zBody = m.zWiki;
}
}
}
if( !g.isHome ){
if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
g.zTop, zPageName);
}
if( rid && g.okApndWiki && g.okAttach ){
style_submenu_element("Attach", "Add An Attachment",
"%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
g.zTop, zPageName, g.zTop, zPageName);
}
if( rid && g.okApndWiki ){
style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
g.zTop, zPageName);
}
if( g.okHistory ){
style_submenu_element("History", "History", "%s/whistory?name=%T",
g.zTop, zPageName);
}
}
style_header(zPageName);
blob_init(&wiki, zBody, -1);
wiki_convert(&wiki, 0, 0);
blob_reset(&wiki);
db_prepare(&q,
"SELECT datetime(mtime,'localtime'), filename, user"
" FROM attachment"
" WHERE isLatest AND src!='' AND target=%Q"
" ORDER BY mtime DESC",
zPageName);
while( db_step(&q)==SQLITE_ROW ){
const char *zDate = db_column_text(&q, 0);
const char *zFile = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
if( cnt==0 ){
@ <hr><h2>Attachments:</h2>
@ <ul>
}
cnt++;
if( g.okHistory ){
@ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
}else{
@ <li>
}
@ %h(zFile)</a> add by %h(zUser) on
hyperlink_to_date(zDate, ".");
if( g.okWrWiki && g.okAttach ){
@ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
}
}
if( cnt ){
@ </ul>
}
db_finalize(&q);
if( !isSandbox ){
manifest_clear(&m);
}
style_footer();
}
/*
|
| ︙ | ︙ | |||
513 514 515 516 517 518 519 |
static const char *zWikiPageName;
/*
** Function called to output extra text at the end of each line in
** a wiki history listing.
*/
static void wiki_history_extra(int rid){
| > | > | 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 |
static const char *zWikiPageName;
/*
** Function called to output extra text at the end of each line in
** a wiki history listing.
*/
static void wiki_history_extra(int rid){
if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
@ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
}
}
/*
** WEBPAGE: whistory
** URL: /whistory?name=PAGENAME
**
** Show the complete change history for a single wiki page.
|
| ︙ | ︙ | |||
536 537 538 539 540 541 542 |
zPageName = PD("name","");
zTitle = mprintf("History Of %s", zPageName);
style_header(zTitle);
free(zTitle);
zSQL = mprintf("%s AND event.objid IN "
" (SELECT rid FROM tagxref WHERE tagid="
| | > > | | 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 |
zPageName = PD("name","");
zTitle = mprintf("History Of %s", zPageName);
style_header(zTitle);
free(zTitle);
zSQL = mprintf("%s AND event.objid IN "
" (SELECT rid FROM tagxref WHERE tagid="
"(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
" UNION SELECT attachid FROM attachment"
" WHERE target=%Q)"
"ORDER BY mtime DESC",
timeline_query_for_www(), zPageName, zPageName);
db_prepare(&q, zSQL);
free(zSQL);
zWikiPageName = zPageName;
www_print_timeline(&q, TIMELINE_ARTID, wiki_history_extra);
db_finalize(&q);
style_footer();
}
|
| ︙ | ︙ | |||
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 |
@ %h(blob_str(&d))
@ </pre>
style_footer();
}
/*
** WEBPAGE: wcontent
**
** List all available wiki pages with date created and last modified.
*/
void wcontent_page(void){
Stmt q;
login_check_credentials();
if( !g.okRdWiki ){ login_needed(); return; }
style_header("Available Wiki Pages");
@ <ul>
db_prepare(&q,
| > > > > > > > > > > > > | > > | > > > | 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 |
@ %h(blob_str(&d))
@ </pre>
style_footer();
}
/*
** WEBPAGE: wcontent
**
** all=1 Show deleted pages
**
** List all available wiki pages with date created and last modified.
*/
void wcontent_page(void){
Stmt q;
int showAll = P("all")!=0;
login_check_credentials();
if( !g.okRdWiki ){ login_needed(); return; }
style_header("Available Wiki Pages");
if( showAll ){
style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
}else{
style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
}
@ <ul>
db_prepare(&q,
"SELECT"
" substr(tagname, 6),"
" (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC)"
" FROM tag WHERE tagname GLOB 'wiki-*'"
" ORDER BY lower(tagname)"
);
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
int size = db_column_int(&q, 1);
if( size>0 ){
@ <li><a href="%s(g.zTop)/wiki?name=%T(zName)">%h(zName)</a></li>
}else if( showAll ){
@ <li><a href="%s(g.zTop)/wiki?name=%T(zName)"><s>%h(zName)</s></a></li>
}
}
db_finalize(&q);
@ </ul>
style_footer();
}
/*
|
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
struct sStack {
short iCode; /* Markup code */
short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */
const char *zId; /* ID attribute or NULL */
} *aStack;
};
/*
** z points to a "<" character. Check to see if this is the start of
** a valid markup. If it is, return the total number of characters in
** the markup including the initial "<" and the terminating ">". If
** it is not well-formed markup, return 0.
*/
| > > > > > > > > > > > > > | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
struct sStack {
short iCode; /* Markup code */
short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */
const char *zId; /* ID attribute or NULL */
} *aStack;
};
/*
** Return TRUE if HTML should be used as the sole markup language for wiki.
**
** On first invocation, this routine consults the "wiki-use-html" setting.
** It caches the result for subsequent invocations, under the assumption
** that the setting will not change.
*/
static int wikiUsesHtml(void){
static int r = -1;
if( r<0 ) r = db_get_boolean("wiki-use-html", 0);
return r;
}
/*
** z points to a "<" character. Check to see if this is the start of
** a valid markup. If it is, return the total number of characters in
** the markup including the initial "<" and the terminating ">". If
** it is not well-formed markup, return 0.
*/
|
| ︙ | ︙ | |||
618 619 620 621 622 623 624 | /* ** Parse only Wiki links, return everything else as TOKEN_RAW. ** ** z points to the start of a token. Return the number of ** characters in that token. Write the token type into *pTokenType. */ | < | 631 632 633 634 635 636 637 638 639 640 641 642 643 644 |
/*
** Parse only Wiki links, return everything else as TOKEN_RAW.
**
** z points to the start of a token. Return the number of
** characters in that token. Write the token type into *pTokenType.
*/
static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
int n;
if( z[0]=='[' && (n = linkLength(z))>0 ){
*pTokenType = TOKEN_LINK;
return n;
}
*pTokenType = TOKEN_RAW;
|
| ︙ | ︙ | |||
776 777 778 779 780 781 782 |
** output its end tag if it is not a </div> tag.
*/
static void popStack(Renderer *p){
if( p->nStack ){
int iCode;
p->nStack--;
iCode = p->aStack[p->nStack].iCode;
| | | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 |
** output its end tag if it is not a </div> tag.
*/
static void popStack(Renderer *p){
if( p->nStack ){
int iCode;
p->nStack--;
iCode = p->aStack[p->nStack].iCode;
if( iCode!=MARKUP_DIV && p->pOut ){
blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName);
}
}
}
/*
** Push a new markup value onto the stack. Enlarge the stack
|
| ︙ | ︙ | |||
1389 1390 1391 1392 1393 1394 1395 |
renderer.state |= INLINE_MARKUP_ONLY;
}
if( flags & WIKI_INLINE ){
renderer.wantAutoParagraph = 0;
}else{
renderer.wantAutoParagraph = 1;
}
| | | 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 |
renderer.state |= INLINE_MARKUP_ONLY;
}
if( flags & WIKI_INLINE ){
renderer.wantAutoParagraph = 0;
}else{
renderer.wantAutoParagraph = 1;
}
if( wikiUsesHtml() ){
renderer.state |= WIKI_USE_HTML;
}
if( pOut ){
renderer.pOut = pOut;
}else{
renderer.pOut = cgi_output_blob();
}
|
| ︙ | ︙ | |||
1445 1446 1447 1448 1449 1450 1451 |
iStart = i+6;
for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){}
if( z[i]!='<' ) return 0;
blob_init(pTitle, &z[iStart], i-iStart);
blob_init(pTail, &z[i+8], -1);
return 1;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 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 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 |
iStart = i+6;
for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){}
if( z[i]!='<' ) return 0;
blob_init(pTitle, &z[iStart], i-iStart);
blob_init(pTail, &z[i+8], -1);
return 1;
}
/*
** Parse text looking for wiki hyperlinks in one of the formats:
**
** [target]
** [target|...]
**
** Where "target" can be either an artifact ID prefix or a wiki page
** name. For each such hyperlink found, add an entry to the
** backlink table.
*/
void wiki_extract_links(
char *z, /* The wiki text from which to extract links */
int srcid, /* srcid field for new BACKLINK table entries */
int srctype, /* srctype field for new BACKLINK table entries */
double mtime, /* mtime field for new BACKLINK table entries */
int replaceFlag, /* True first delete prior BACKLINK entries */
int flags /* wiki parsing flags */
){
Renderer renderer;
int tokenType;
ParsedMarkup markup;
int n;
int inlineOnly;
int wikiUseHtml = 0;
memset(&renderer, 0, sizeof(renderer));
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
if( flags & WIKI_NOBLOCK ){
renderer.state |= INLINE_MARKUP_ONLY;
}
if( wikiUsesHtml() ){
renderer.state |= WIKI_USE_HTML;
wikiUseHtml = 1;
}
inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
if( replaceFlag ){
db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
srctype, srcid);
}
while( z[0] ){
if( wikiUseHtml ){
n = nextRawToken(z, &renderer, &tokenType);
}else{
n = nextWikiToken(z, &renderer, &tokenType);
}
switch( tokenType ){
case TOKEN_LINK: {
char *zTarget;
int i, c;
char zLink[42];
zTarget = &z[1];
for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){}
while(i>1 && zTarget[i-1]==' '){ i--; }
c = zTarget[i];
zTarget[i] = 0;
if( is_valid_uuid(zTarget) ){
memcpy(zLink, zTarget, i+1);
canonical16(zLink, i);
db_multi_exec(
"REPLACE INTO backlink(target,srctype,srcid,mtime)"
"VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime
);
}
zTarget[i] = c;
break;
}
case TOKEN_MARKUP: {
const char *zId;
int iDiv;
parseMarkup(&markup, z);
/* Markup of the form </div id=ID> where there is a matching
** ID somewhere on the stack. Exit the verbatim if were are in
** it. Pop the stack up to the matching <div>. Discard the
** </div>
*/
if( markup.iCode==MARKUP_DIV && markup.endTag &&
(zId = markupId(&markup))!=0 &&
(iDiv = findTagWithId(&renderer, MARKUP_DIV, zId))>=0
){
if( renderer.inVerbatim ){
renderer.inVerbatim = 0;
renderer.state = renderer.preVerbState;
}
while( renderer.nStack>iDiv+1 ) popStack(&renderer);
if( renderer.aStack[iDiv].allowWiki ){
renderer.state |= ALLOW_WIKI;
}else{
renderer.state &= ~ALLOW_WIKI;
}
renderer.nStack--;
}else
/* If within <verbatim id=ID> ignore everything other than
** </verbatim id=ID> and the </dev id=ID2> above.
*/
if( renderer.inVerbatim ){
if( endVerbatim(&renderer, &markup) ){
renderer.inVerbatim = 0;
renderer.state = renderer.preVerbState;
}else{
n = 1;
}
}else
/* Render invalid markup literally. The markup appears in the
** final output as plain text.
*/
if( markup.iCode==MARKUP_INVALID ){
n = 1;
}else
/* If the markup is not font-change markup ignore it if the
** font-change-only flag is set.
*/
if( (markup.iType&MUTYPE_FONT)==0 &&
(renderer.state & FONT_MARKUP_ONLY)!=0 ){
/* Do nothing */
}else
if( markup.iCode==MARKUP_NOWIKI ){
if( markup.endTag ){
renderer.state |= ALLOW_WIKI;
}else{
renderer.state &= ~ALLOW_WIKI;
}
}else
/* Ignore block markup for in-line rendering.
*/
if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
/* Do nothing */
}else
/* Generate end-tags */
if( markup.endTag ){
popStackToTag(&renderer, markup.iCode);
}else
/* Push <div> markup onto the stack together with the id=ID attribute.
*/
if( markup.iCode==MARKUP_DIV ){
pushStackWithId(&renderer, markup.iCode, markupId(&markup),
(renderer.state & ALLOW_WIKI)!=0);
}else
/* Enter <verbatim> processing. With verbatim enabled, all other
** markup other than the corresponding end-tag with the same ID is
** ignored.
*/
if( markup.iCode==MARKUP_VERBATIM ){
int vAttrIdx, vAttrDidAppend=0;
renderer.zVerbatimId = 0;
renderer.inVerbatim = 1;
renderer.preVerbState = renderer.state;
renderer.state &= ~ALLOW_WIKI;
for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
renderer.zVerbatimId = markup.aAttr[0].zValue;
}else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
vAttrDidAppend=1;
}
}
renderer.wantAutoParagraph = 0;
}
/* Restore the input text to its original configuration
*/
unparseMarkup(&markup);
break;
}
default: {
break;
}
}
z += n;
}
}
|
Changes to src/winhttp.c.
| ︙ | ︙ | |||
135 136 137 138 139 140 141 | /* ** Start a listening socket and process incoming HTTP requests on ** that socket. */ void win32_http_server( int mnPort, int mxPort, /* Range of allowed TCP port numbers */ | | | | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
/*
** Start a listening socket and process incoming HTTP requests on
** that socket.
*/
void win32_http_server(
int mnPort, int mxPort, /* Range of allowed TCP port numbers */
const char *zBrowser, /* Command to launch browser. (Or NULL) */
const char *zStopper, /* Stop server when this file is exists (Or NULL) */
const char *zNotFound /* The --notfound option, or NULL */
){
WSADATA wd;
SOCKET s = INVALID_SOCKET;
SOCKADDR_IN addr;
int idCnt = 0;
int iPort = mnPort;
char *zNotFoundOption;
|
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
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 |
static int send_unclustered(Xfer *pXfer){
Stmt q;
int cnt = 0;
db_prepare(&q,
"SELECT uuid FROM unclustered JOIN blob USING(rid)"
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
);
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
cnt++;
}
db_finalize(&q);
return cnt;
}
/*
** Send an igot message for every artifact.
*/
static void send_all(Xfer *pXfer){
Stmt q;
db_prepare(&q,
"SELECT uuid FROM blob "
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
);
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
}
db_finalize(&q);
}
| > > | 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 |
static int send_unclustered(Xfer *pXfer){
Stmt q;
int cnt = 0;
db_prepare(&q,
"SELECT uuid FROM unclustered JOIN blob USING(rid)"
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
);
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
cnt++;
}
db_finalize(&q);
return cnt;
}
/*
** Send an igot message for every artifact.
*/
static void send_all(Xfer *pXfer){
Stmt q;
db_prepare(&q,
"SELECT uuid FROM blob "
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
);
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
}
db_finalize(&q);
}
|
| ︙ | ︙ | |||
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 |
blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
xfer.nFileSent, xfer.nDeltaSent);
nCardSent = 0;
nCardRcvd = 0;
xfer.nFileSent = 0;
xfer.nDeltaSent = 0;
xfer.nGimmeSent = 0;
fflush(stdout);
http_exchange(&send, &recv, cloneFlag==0 || nCycle>0);
blob_reset(&send);
/* Begin constructing the next message (which might never be
** sent) by beginning with the pull or push cards
*/
| > | 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 |
blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
xfer.nFileSent, xfer.nDeltaSent);
nCardSent = 0;
nCardRcvd = 0;
xfer.nFileSent = 0;
xfer.nDeltaSent = 0;
xfer.nGimmeSent = 0;
xfer.nIGotSent = 0;
fflush(stdout);
http_exchange(&send, &recv, cloneFlag==0 || nCycle>0);
blob_reset(&send);
/* Begin constructing the next message (which might never be
** sent) by beginning with the pull or push cards
*/
|
| ︙ | ︙ |
Changes to src/zip.c.
| ︙ | ︙ | |||
376 377 378 379 380 381 382 |
blob_reset(&file);
}
blob_reset(&filename);
zip_close(pZip);
}
/*
| | > > | > > > > > > > | < > > > > > > > > > > > | | 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 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 |
blob_reset(&file);
}
blob_reset(&filename);
zip_close(pZip);
}
/*
** COMMAND: zip
**
** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME]
**
** Generate a ZIP archive for a specified version. If the --name option is
** used, it argument becomes the name of the top-level directory in the
** resulting ZIP archive. If --name is omitted, the top-level directory
** named is derived from the project name, the check-in date and time, and
** the artifact ID of the check-in.
*/
void baseline_zip_cmd(void){
int rid;
Blob zip;
const char *zName;
zName = find_option("name", 0, 1);
db_find_and_open_repository(1);
if( g.argc!=4 ){
usage("VERSION OUTPUTFILE");
}
rid = name_to_rid(g.argv[2]);
if( zName==0 ){
zName = db_text("default-name",
"SELECT replace(%Q,' ','_') "
" || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) "
" || substr(blob.uuid, 1, 10)"
" FROM event, blob"
" WHERE event.objid=%d"
" AND blob.rid=%d",
db_get("project-name", "unnamed"), rid, rid
);
}
zip_of_baseline(rid, &zip, zName);
blob_write_to_file(&zip, g.argv[3]);
}
/*
** WEBPAGE: zip
** URL: /zip/RID.zip
**
|
| ︙ | ︙ |
Changes to www/branching.wiki.
| ︙ | ︙ |
Added www/build-icons/linux.gif.
cannot compute difference between binary files
Added www/build-icons/linux64.gif.
cannot compute difference between binary files
Added www/build-icons/mac.gif.
cannot compute difference between binary files
Added www/build-icons/src.gif.
cannot compute difference between binary files
Added www/build-icons/win32.gif.
cannot compute difference between binary files
Changes to www/build.wiki.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 | <a href="http://www.fossil-scm.org/fossil/leaves">Leaves</a> link at the top of the page.</p></li> <li><p>Select a version of of fossil you want to download. Click on its link. Note that you must successfully log in as "anonymous" in step 1 above in order to see the link to the detailed version information.</p></li> | < < < | | 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 | <a href="http://www.fossil-scm.org/fossil/leaves">Leaves</a> link at the top of the page.</p></li> <li><p>Select a version of of fossil you want to download. Click on its link. Note that you must successfully log in as "anonymous" in step 1 above in order to see the link to the detailed version information.</p></li> <li><p>Finally, click on the "Zip Archive" link. This link will build a ZIP archive of the complete source code and download it to your browser. </ol> <h2>2.0 Compiling</h2> <p>Follow these steps to compile:</p> <ol> <li value="6"> <p>Create a directory to hold the source code. Then unzip the ZIP archive you downloaded into that directory. You should be in the top-level folder of that directory</p></li> <li><p><b>(Optional:)</b> Edit the Makefile to set it up like you want. You probably do not need to do anything. Do not be intimidated: There are less than 10 variables in the makefile that can be changed. The whole Makefile is only a few dozen lines long and most of those lines are comments.</p> <li><p>Type "<b>make</b>" </ol> <h2>3.0 Installing</h2> |
| ︙ | ︙ |
Changes to www/embeddeddoc.wiki.
| ︙ | ︙ | |||
56 57 58 59 60 61 62 | pull the documentation file from the local source tree on disk, not from the any check-in. The "<b>ckout</b>" keyword normally only works when you start your server using the "<b>fossil server</b>" or "<b>fossil ui</b>" command line and is intended to show what the documentation you are currently editing looks like before you check it in. | | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | pull the documentation file from the local source tree on disk, not from the any check-in. The "<b>ckout</b>" keyword normally only works when you start your server using the "<b>fossil server</b>" or "<b>fossil ui</b>" command line and is intended to show what the documentation you are currently editing looks like before you check it in. Finally, the <i><filename></i> element of the URL is the pathname of the documentation file relative to the root of the source tree. The mimetype (and thus the rendering) of documentation files is determined by the file suffix. Fossil currently understands 192 different file suffixes, including all the popular ones such as ".css", ".gif", ".htm", ".html", ".jpg", ".jpeg", ".png", and ".txt". |
| ︙ | ︙ | |||
108 109 110 111 112 113 114 | recently. If you wanted to see an historical version of this document, you could substitute the name of a check-in for "<b>/tip/</b>". For example, to see the version of this document associated with check-in [9be1b00392], simply replace the "<b>/tip/</b>" with "<b>/9be1b00392/</b>". You can also substitute the symbolic name for a particular version or branch. For example, you might replace "<b>/tip/</b>" with "<b>/trunk/</b>" to get the latest | | > | > > > > > | > > > > | > > > > > | 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 | recently. If you wanted to see an historical version of this document, you could substitute the name of a check-in for "<b>/tip/</b>". For example, to see the version of this document associated with check-in [9be1b00392], simply replace the "<b>/tip/</b>" with "<b>/9be1b00392/</b>". You can also substitute the symbolic name for a particular version or branch. For example, you might replace "<b>/tip/</b>" with "<b>/trunk/</b>" to get the latest version of this document in the "trunk" branch. The symbolic name can also be a date and time string in any of the following formats:</p> <ul> <li> <i>YYYY-MM-DD</i> <li> <i>YYYY-MM-DD</i><b>T</b><i>HH:MM</i> <li> <i>YYYY-MM-DD</i><b>T</b><i>HH:MM:SS</i> </ul> When the symbolic name is a date and time, fossil shows the version of the document that was most recently checked in as of the date and time specified. So, for example, to see what the fossil website looked like at the beginning of 2010, enter: <blockquote> <a href="http://www.fossil-scm.org/index.html/doc/2010-01-01/www/index.wiki"> http://www.fossil-scm.org/index.html/doc/<b>2010-01-01</b>/www/index.wiki </a> </blockquote> The file that encodes this document is stored in the fossil source tree under the name "<b>www/embeddeddoc.wiki</b>" and so that name forms the last part of the URL for this document. As I sit writing this documentation file, I am testing my work by running the "<b>fossil server</b>" command line and viewing <b>http://localhost:8080/doc/ckout/www/embeddeddoc.wiki</b> in Firefox. I am doing this even though I have not yet checked in the "<b>www/embeddeddoc.wiki</b>" file for the first time. Using the special "<b>ckout</b>" version identifier on the "<b>/doc</b>" page it is easy to make multiple changes to multiple files and see how they all look together before committing anything to the repository. |
Changes to www/fileformat.wiki.
1 2 3 4 5 | <title>Fossil File Formats</title> <h1 align="center"> Fossil File Formats </h1> | | | < < < < < < | | | | | | > | | | > | | < < < < < < < < < < < < < < < < < < < < < < < < | > < < < < < < < < | | | < < < < > < | | | | | | | | | > > > > > > > > > | | | | | | > > > | | < | < | < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 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 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 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 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 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 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 | <title>Fossil File Formats</title> <h1 align="center"> Fossil File Formats </h1> The global state of a fossil repository is kept simple so that it can endure in useful form for decades or centuries. A fossil repository is intended to be readable, searchable, and extensible by people not yet born. The global state of a fossil repository is an unordered set of <i>artifacts</i>. An artifact might be a source code file, the text of a wiki page, part of a trouble ticket, or one of several special control artifacts used to show the relationships between other artifacts within the project. Each artifact is normally represented on disk as a separate file. Artifacts can be text or binary. In addition to the global state, each fossil repository also contains local state. The local state consists of web-page formatting preferences, authorized users, ticket display and reporting formats, and so forth. The global state is shared in common among all repositories for the same project, whereas the local state is often different in separate repositories. The local state is not versioned and is not synchronized with the global state. The local state is not composed of artifacts and is not intended to be enduring. This document is concerned with global state only. Local state is only mentioned here in order to distinguish it from global state. Each artifact in the repository is named by its SHA1 hash. No prefixes or meta information is added to a artifact before its hash is computed. The name of a artifact in the repository is exactly the same SHA1 hash that is computed by sha1sum on the file as it exists in your source tree.</p> Some artifacts have a particular format which gives them special meaning to fossil. Fossil recognizes: <ul> <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> <li> [#ctrl | Control Artifacts] </li> <li> [#wikichng | Wiki Pages] </li> <li> [#tktchng | Ticket Changes] </li> <li> [#artifact | Artifacts] </li> </ul> These five artifact types are described in the sequel. In the current implementation (as of 2009-01-25) the artifacts that make up a fossil repository are stored in in as delta- and zlib-compressed blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This is an implementation detail and might change in a future release. For the purpose of this article "file format" means the format of the artifacts, not how the artifacts are stored on disk. It is the artifact format that is intended to be enduring. The specifics of how artifacts are stored on disk, though stable, is not intended to live as long as the artifact format. <a name="manifest"></a> <h2>1.0 The Manifest</h2> A manifest defines a check-in or version of the project source tree. The manifest contains a list of artifacts for each file in the project and the corresponding filenames, as well as information such as parent check-ins, the name of the programmer who created the check-in, the date and time when the check-in was created, and any check-in comments associated with the check-in. Any artifact in the repository that follows the syntactic rules of a manifest is a manifest. Note that a manifest can be both a real manifest and also a content file, though this is rare. A manifest is a text file. Newline characters (ASCII 0x0a) separate the file into "cards". Each card begins with a single character "card type". Zero or more arguments may follow the card type. All arguments are separated from each other and from the card-type character by a single space character. There is no surplus white space between arguments and no leading or trailing whitespace except for the newline character that acts as the card separator. All cards of the manifest occur in strict sorted lexicographical order. No card may be duplicated. The entire manifest may be PGP clear-signed, but otherwise it may contain no additional text or data beyond what is described here. Allowed cards in the manifest are as follows: <blockquote> <b>C</b> <i>checkin-comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br> <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br> <b>P</b> <i>SHA1-hash</i>+<br> <b>R</b> <i>repository-checksum</i><br> <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br> <b>U</b> <i>user-login</i><br> <b>Z</b> <i>manifest-checksum</i> </blockquote> A manifest must have exactly one C-card. The sole argument to the C-card is a check-in comment that describes the check-in that the manifest defines. The check-in comment is text. The following escape sequences are applied to the text: A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash (ASCII 0x5C) is represented as two backslashes "\\". Apart from space and newline, no other whitespace characters are allowed in the check-in comment. Nor are any unprintable characters allowed in the comment. A manifest must have exactly one D-card. The sole argument to the D-card is a date-time stamp in the ISO8601 format. The date and time should be in coordinated universal time (UTC). The format is: <blockquote> <i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i> </blockquote> A manifest has zero or more F-cards. Each F-card defines a file (other than the manifest itself) which is part of the check-in that the manifest defines. There are two, three, or four arguments. The first argument is the pathname of the file in the check-in relative to the root of the project file hierarchy. No ".." or "." directories are allowed within the filename. Space characters are escaped as in C-card comment text. Backslash characters and newlines are not allowed within filenames. The directory separator character is a forward slash (ASCII 0x2F). The second argument to the F-card is the full 40-character lower-case hexadecimal SHA1 hash of the content artifact. The optional 3rd argument defines any special access permissions associated with the file. The only special code currently defined is "x" which means that the file is executable. All files are always readable and writable. This can be expressed by "w" permission if desired but is optional. The optional 4th argument is the name of the same file as it existed in the parent check-in. If the name of the file is unchanged from its parent, then the 4th argument is omitted. A manifest has zero or one P-cards. Most manifests have one P-card. The P-card has a varying number of arguments that defines other manifests from which the current manifest is derived. Each argument is an 40-character lowercase hexadecimal SHA1 of the predecessor manifest. All arguments to the P-card must be unique to that line. The first predecessor is the direct ancestor of the manifest. Other arguments define manifests with which the first was merged to yield the current manifest. Most manifests have a P-card with a single argument. The first manifest in the project has no ancestors and thus has no P-card. A manifest may optionally have a single R-card. The R-card has a single argument which is the MD5 checksum of all files in the check-in except the manifest itself. The checksum is expressed as 32-characters of lowercase hexadecimal. The checksum is computed as follows: For each file in the check-in (except for the manifest itself) in strict sorted lexicographical order, take the pathname of the file relative to the root of the repository, append a single space (ASCII 0x20), the size of the file in ASCII decimal, a single newline character (ASCII 0x0A), and the complete text of the file. Compute the MD5 checksum of the the result. A manifest might contain one or more T-cards used to set tags or properties on the check-in. The format of the T-card is the same as described in <i>Control Artifacts</i> section below, except that the second argument is the single characcter "<b>*</b>" instead of an artifact ID. The <b>*</b> in place of the artifact ID indicates that the tag or property applies to the current artifact. It is not possible to encode the current artifact ID as part of an artifact, since the act of inserting the artifact ID would change the artifact ID, hence a <b>*</b> is used to represent "self". T-cards are typically added to manifests in order to set the <b>branch</b> property and a symbolic name when the check-in is intended to start a new branch. Each manifest has a single U-card. The argument to the U-card is the login of the user who created the manifest. The login name is encoded using the same character escapes as is used for the check-in comment argument to the C-card. A manifest has an option Z-card as its last line. The argument to the Z-card is a 32-character lowercase hexadecimal MD5 hash of all prior lines of the manifest up to and including the newline character that immediately precedes the "Z". The Z-card is just a sanity check to prove that the manifest is well-formed and consistent. A sample manifest from Fossil itself can be seen [/artifact/28987096ac | here]. <a name="cluster"></a> <h2>2.0 Clusters</h2> A cluster is a artifact that declares the existence of other artifacts. Clusters are used during repository synchronization to help reduce network traffic. As such, clusters are an optimization and may be removed from a repository without loss or damage to the underlying project code. Clusters follow a syntax that is very similar to manifests. A Cluster is a line-oriented text file. Newline characters (ASCII 0x0a) separate the artifact into cards. Each card begins with a single character "card type". Zero or more arguments may follow the card type. All arguments are separated from each other and from the card-type character by a single space character. There is no surplus white space between arguments and no leading or trailing whitespace except for the newline character that acts as the card separator. All cards of a cluster occur in strict sorted lexicographical order. No card may be duplicated. The cluster may not contain additional text or data beyond what is described here. Unlike manifests, clusters are never PGP signed. Allowed cards in the cluster are as follows: <blockquote> <b>M</b> <i>artifact-id</i><br /> <b>Z</b> <i>checksum</i> </blockquote> A cluster contains one or more "M" cards followed by a single "Z" line. Each M card has a single argument which is the artifact ID of another artifact in the repository. The Z card work exactly like the Z card of a manifest. The argument to the Z card is the lower-case hexadecimal representation of the MD5 checksum of all prior cards in the cluster. Note that the Z card is required on a cluster. An example cluster from Fossil can be seen [/artifact/d03dbdd73a2a8 | here]. <a name="ctrl"></a> <h2>3.0 Control Artifacts</h2> Control artifacts are used to assign properties to other artifacts within the repository. The basic format of a control artifact is the same as a manifest or cluster. A control artifact is a text files divided into cards by newline characters. Each card has a single-character card type followed by arguments. Spaces separate the card type and the arguments. No surplus whitespace is allowed. All cards must occur in strict lexigraphical order. Allowed cards in a control artifact are as follows: <blockquote> <b>D</b> <i>time-and-date-stamp</i><br /> <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name artifact-id ?value?</i><br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i><br /> </blockquote> A control artifact must have one D card and one Z card and one or more T cards. No other cards or other text is allowed in a control artifact. Control artifacts might be PGP clearsigned. The D card and the Z card of a control artifact are the same as in a manifest. The T card represents a "tag" or property that is applied to some other artifact. The T card has two or three values. The second argument is the 40 character lowercase artifact ID of the artifact to which the tag is to be applied. The first value is the tag name. The first character of the tag is either "+", "-", or "*". A "+" means the tag should be added to the artifact. The "-" means the tag should be removed. The "*" character means the tag should be added to the artifact and all direct descendants (but not branches) of the artifact down to but not including the first descendant that contains a more recent "-" tag with the same name. The optional third argument is the value of the tag. A tag without a value is a boolean. When two or more tags with the same name are applied to the same artifact, the tag with the latest (most recent) date is used. Some tags have special meaning. The "comment" tag when applied to a check-in will override the check-in comment of that check-in for display purposes. The "user" tag overrides the name of the check-in user. The "date" tag overrides the check-in date. The "branch" tag sets the name of the branch that at check-in belongs to. Symbolic tags begin with the "sym-" prefix. The U card is the name of the user that created the control artifact. The Z card is the usual artifact checksum. An example control artifacts can be seen [/info/9d302ccda8 | here]. <a name="wikichng"></a> <h2>4.0 Wiki Pages</h2> A wiki page is an artifact with a format similar to manifests, clusters, and control artifacts. The artifact is divided into cards by newline characters. The format of each card is as in manifests, clusters, and control artifacts. Wiki artifacts accept the following card types: <blockquote> <b>D</b> <i>time-and-date-stamp</i><br /> <b>L</b> <i>wiki-title</i><br /> <b>P</b> <i>parent-artifact-id</i>+<br /> <b>U</b> <i>user-name</i><br /> <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> <b>Z</b> <i>checksum</i> </blockquote> The D card is the date and time when the wiki page was edited. The P card specifies the parent wiki pages, if any. The L card gives the name of the wiki page. The U card specifies the login of the user who made this edit to the wiki page. The Z card is the usual checksum over the either artifact. The W card is used to specify the text of the wiki page. The argument to the W card is an integer which is the number of bytes of text in the wiki page. That text follows the newline character that terminates the W card. The wiki text is always followed by one extra newline. An example wiki artifact can be seen [/artifact/7b2f5fd0e0 | here]. <a name="tktchng"></a> <h2>5.0 Ticket Changes</h2> A ticket-change artifact represents a change to a trouble ticket. The following cards are allowed on a ticket change artifact: <blockquote> <b>D</b> <i>time-and-date-stamp</i><br /> <b>J</b> ?<b>+</b>?<i>name</i> ?<i>value</i>?<br /> <b>K</b> <i>ticket-id</i><br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i> </blockquote> The D card is the usual date and time stamp and represents the point in time when the change was entered. The U card is the login of the programmer who entered this change. The Z card is the checksum over the entire artifact. Every ticket has a unique ID. The ticket to which this change is applied is specified by the K card. A ticket exists if it contains one or more changes. The first "change" to a ticket is what brings the ticket into existence. J cards specify changes to the "value" of "fields" in the ticket. If the <i>value</i> parameter of the J card is omitted, then the field is set to an empty string. Each fossil server has a ticket configuration which specifies the fields its understands. The ticket configuration is part of the local state for the repository and thus can vary from one repository to another. Hence a J card might specify a <i>field</i> that do not exist in the local ticket configuration. If a J card specifies a <i>field</i> that is not in the local configuration, then that J card is simply ignored. The first argument of the J card is the field name. The second value is the field value. If the field name begins with "+" then the value is appended to the prior value. Otherwise, the value on the J card replaces any previous value of the field. The field name and value are both encoded using the character escapes defined for the C card of a manifest. An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <a name="attachment"></a> <h2>6.0 Attachments</h2> An attachment artifact associates some other artifact that is the attachment (the source artifact) with a ticket or wiki page to which the attachment is connected (the target artifact). The following cards are allowed on an attachment artifact: <blockquote> <b>A</b> <i>filename target</i> ?<i>source</i>? <b>C</b> <i>comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i> </blockquote> The A card specifies a filename for the attachment in its first argument. The second argument to the A card is the name of the wiki page or ticket to which the attachment is connected. The third argument is either missing or else it is the 40-character artifact ID of the attachment itself. A missing third argument means that the attachment should be deleted. The C card is an optional comment describing what the attachment is about. The C card is optional, but there can only be one. A single D card is required to give the date and time when the attachment was applied. A single U card gives the name of the user to added the attachment. If an attachment is added anonymously, then the U card may be omitted. The Z card is the usual checksum over the rest of the attachment artifact. <a name="summary"></a> <h2>7.0 Card Summary</h2> The following table summaries the various kinds of cards that appear on Fossil artifacts: <table border=1 width="100%"> <tr> <th rowspan=2 valign=bottom>Card Format</th> <th colspan=6>Used By</th> </tr> <tr> <th>Manifest</th> <th>Cluster</th> <th>Control</th> <th>Wiki</th> <th>Ticket</th> <th>Attachment</th> </tr> <tr> <td><b>A</b> <i>filename target source</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>X</b></td> </tr> <tr> <td><b>C</b> <i>coment-text</i></td> <td align=center><b>X</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>X</b></td> </tr> <tr> <td><b>D</b> <i>date-time-stamp</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> <tr> <td><b>F</b> <i>filename uuid permissions oldname</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>J</b> <i>name value</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> </tr> <tr> <td><b>K</b> <i>ticket-uuid</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> </tr> <tr> <td><b>L</b> <i>wiki-title</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>M</b> <i>uuid</i></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>P</b> <i>uuid ...</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>R</b> <i>md5sum</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <tr> <td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname uuid value</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>U</b> <i>username</i></td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> <tr> <td><b>W</b> <i>size</i></td> <td align=center> </td> <td align=center> </td> <td align=center> </td> <td align=center><b>X</b></td> <td align=center> </td> <td align=center> </td> </tr> <tr> <td><b>Z</b> <i>md5sum</i></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> <td align=center><b>X</b></td> </tr> </table> |
Changes to www/mkdownload.tcl.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 | Click on links below to download prebuilt binaries and source tarballs for recent versions of <a href="/fossil">Fossil</a>. The historical source code is also available in the <a href="/fossil/doc/tip/www/selfhost.wiki">self-hosting Fossil repositories</a>. </p> | > > > > > > > > > > | < > > | > > > > > | > > > > > > > | > > > > | > > > > > > > | < > | | | | | | | | | < | < | < | < | < | < < | < < | < < > < < < < < < > | 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 |
Click on links below to download prebuilt binaries and source tarballs for
recent versions of <a href="/fossil">Fossil</a>.
The historical source code is also available in the
<a href="/fossil/doc/tip/www/selfhost.wiki">self-hosting
Fossil repositories</a>.
</p>
<p>
<u>Important Note:</u>
After upgrading to a newer version of fossil, it is always a good idea
to run:
<blockquote><pre>
<b><big><tt>fossil all rebuild</tt></big></b>
</pre></blockquote>
Running "rebuild" this way is not always necessary, but it never hurts.
</p>
<table cellpadding="10">
}
# Find all all unique timestamps.
#
foreach file [glob -nocomplain download/fossil-*.zip] {
if {[regexp {(\d+).zip$} $file all datetime]
&& [string length $datetime]==14} {
set adate($datetime) 1
}
}
# Do all dates from newest to oldest
#
foreach datetime [lsort -decr [array names adate]] {
set dt [string range $datetime 0 3]-[string range $datetime 4 5]-
append dt "[string range $datetime 6 7] "
append dt "[string range $datetime 8 9]:[string range $datetime 10 11]:"
append dt "[string range $datetime 12 13]"
set link [string map {{ } +} $dt]
set hr http://www.fossil-scm.org/fossil/timeline?c=$link&y=ci
puts "<tr><td colspan=5 align=center><hr>"
puts "<b>Fossil snapshot as of <a href=\"$hr\">$dt</a><td width=30></b>"
puts "</td></tr>"
foreach {prefix suffix img desc} {
fossil-linux-x86 zip linux.gif {Linux x86}
fossil-linux-amd64 zip linux64.gif {Linux x86_64}
fossil-macosx-x86 zip mac.gif {Mac 10.5 x86}
fossil-w32 zip win32.gif {Windows}
fossil-src tar.gz src.gif {Source Tarball}
} {
set filename download/$prefix-$datetime.$suffix
if {[file exists $filename]} {
set size [file size $filename]
set units bytes
if {$size>1024*1024} {
set size [format %.2f [expr {$size/(1024.0*1024.0)}]]
set units MiB
} elseif {$size>1024} {
set size [format %.2f [expr {$size/(1024.0)}]]
set units KiB
}
puts "<td align=center valign=bottom><a href=\"$filename\">"
puts "<img src=\"build-icons/$img\" border=0><br>$desc</a><br>"
puts "$size $units</td>"
} else {
puts "<td> </td>"
}
}
puts "</tr>"
}
puts "<tr><td colspan=5><hr></td></tr>"
puts {</table>
</body>
</html>
}
|
Changes to www/quickstart.wiki.
| ︙ | ︙ | |||
65 66 67 68 69 70 71 |
local configuration. This is easily accomplished using the webserver
that is built into fossil. Start the fossil webserver like this:</p>
<blockquote>
<b>fossil ui </b><i> repository-filename</i>
</blockquote>
| | < < | | > | > | > | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
local configuration. This is easily accomplished using the webserver
that is built into fossil. Start the fossil webserver like this:</p>
<blockquote>
<b>fossil ui </b><i> repository-filename</i>
</blockquote>
<p>This starts a web server then automatically launches your
web browser and makes it point to this web server. If your system
has an unusual configuration, fossil might not be able to figure out
how to start your web browser. In that case, first tell fossil
where to find your web browser using a command like this:</p>
<blockquote>
<b>fossil setting web-browser </b><i> path-to-web-browser</i>
</blockquote>
<p>By default, fossil does not require a login for HTTP connections
coming in from the IP loopback address 127.0.0.1. You can, and perhaps
should, change this after you create a few users.</p>
<p>When you are finished configuring, just press Control-C or use
the <b>kill</b> command to shut down the mini-server.</p>
|
| ︙ | ︙ |
Changes to www/webui.wiki.
| ︙ | ︙ | |||
146 147 148 149 150 151 152 | 80 stream tcp nowait.1000 root /usr/local/bin/fossil \ /usr/local/bin/fossil http /home/www/sample-project.fossil </verbatim> As always, you'll want to adjust the pathnames to whatever is appropriate for your system. The xinetd setup uses a different syntax but follows the same idea. | < < < < < < < < < < < < < | 146 147 148 149 150 151 152 | 80 stream tcp nowait.1000 root /usr/local/bin/fossil \ /usr/local/bin/fossil http /home/www/sample-project.fossil </verbatim> As always, you'll want to adjust the pathnames to whatever is appropriate for your system. The xinetd setup uses a different syntax but follows the same idea. |
Changes to www/wikitheory.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 |
<h1>Wiki In [./index.wiki | Fossil]</h1>
Fossil uses [/wiki_rules | wiki markup] for many things:
* Stand-alone wiki pages.
* Description and comments in [./bugtheory.wiki | bug reports].
* Check-in comments.
* [./embeddeddoc.wiki | Embedded documentation] files whose
name ends in "wiki".
The [/wiki_rules | formatting rules] for fossil wiki
are designed to be simple and intuitive. The idea is that wiki provides
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<h1>Wiki In [./index.wiki | Fossil]</h1>
Fossil uses [/wiki_rules | wiki markup] for many things:
* Stand-alone wiki pages.
* Description and comments in [./bugtheory.wiki | bug reports].
* Check-in comments.
* [./embeddeddoc.wiki | Embedded documentation] files whose
name ends in "wiki".
The [/wiki_rules | formatting rules] for fossil wiki
are designed to be simple and intuitive. The idea is that wiki provides
paragraph breaks, numbered and bulletted lists, and hyperlinking for
simple documents together with a safe subset of HTML for more complex
formatting tasks.
Some commentators feel that the use of HTML is a mistake and that
fossil should use the markup language of the
<i>fill-in-your-favorite</i> wiki engine instead. That approach
was considered but was rejected for the following reasons:
|
| ︙ | ︙ |