Check-in [7c2577bd63]
Not logged in

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

Overview
SHA1 Hash:7c2577bd637e91b0d3649f75555644cd8b46189d
Date: 2010-03-18 14:40:46
User: drh
Comment:Merge in all of the latest clear-title changes from the trunk.
Tags And Properties
Changes

Changes to COPYRIGHT-GPL2.txt

> 1 Fossil is licensed under the terms of the GPLv2 shown below. In > 2 addition, permission is granted to link Fossil against the OpenSSL > 3 project's "OpenSSL" library (or with modified versions of that > 4 library that use the same license), and distribute the linked > 5 executables. If you modify Fossil, you may extend the exception > 6 described in this paragraph to your modifications, or not, at your > 7 discretion. > 8 > 9 The "clear-title" branch of Fossil is available for more liberal > 10 licenses such as Berkeley-style licenses or the Apache license. > 11 > 12 The original text of GPLv2 follows. > 13 -------------------------------------------------------------------------- > 14 1 GNU GENERAL PUBLIC LICENSE 15 GNU GENERAL PUBLIC LICENSE 2 Version 2, June 1991 16 Version 2, June 1991 3 17 4 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 18 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 Everyone is permitted to copy and distribute verbatim copies 20 Everyone is permitted to copy and distribute verbatim copies 7 of this license document, but changing it is not allowed. 21 of this license document, but changing it is not allowed.

Added src/attach.c

> 1 /* > 2 ** Copyright (c) 2010 D. Richard Hipp > 3 ** > 4 ** This program is free software; you can redistribute it and/or > 5 ** modify it under the terms of the GNU General Public > 6 ** License version 2 as published by the Free Software Foundation. > 7 ** > 8 ** This program is distributed in the hope that it will be useful, > 9 ** but WITHOUT ANY WARRANTY; without even the implied warranty of > 10 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > 11 ** General Public License for more details. > 12 ** > 13 ** You should have received a copy of the GNU General Public > 14 ** License along with this library; if not, write to the > 15 ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, > 16 ** Boston, MA 02111-1307, USA. > 17 ** > 18 ** Author contact information: > 19 ** drh@hwaci.com > 20 ** http://www.hwaci.com/drh/ > 21 ** > 22 ******************************************************************************* > 23 ** > 24 ** This file contains code for dealing with attachments. > 25 */ > 26 #include "config.h" > 27 #include "attach.h" > 28 #include <assert.h> > 29 > 30 /* > 31 ** WEBPAGE: attachlist > 32 ** > 33 ** tkt=TICKETUUID > 34 ** page=WIKIPAGE > 35 ** all > 36 ** > 37 ** List attachments. > 38 */ > 39 void attachlist_page(void){ > 40 const char *zPage = P("page"); > 41 const char *zTkt = P("tkt"); > 42 Blob sql; > 43 Stmt q; > 44 > 45 if( zPage && zTkt ) zTkt = 0; > 46 login_check_credentials(); > 47 blob_zero(&sql); > 48 blob_append(&sql, > 49 "SELECT datetime(mtime,'localtime'), src, target, filename, comment, user" > 50 " FROM attachment", > 51 -1 > 52 ); > 53 if( zPage ){ > 54 if( g.okRdWiki==0 ) login_needed(); > 55 style_header("Attachments To %h", zPage); > 56 blob_appendf(&sql, " WHERE target=%Q", zPage); > 57 }else if( zTkt ){ > 58 if( g.okRdTkt==0 ) login_needed(); > 59 style_header("Attachments To Ticket %.10s", zTkt); > 60 blob_appendf(&sql, " WHERE target GLOB '%q*'", zTkt); > 61 }else{ > 62 if( g.okRdTkt==0 && g.okRdWiki==0 ) login_needed(); > 63 style_header("All Attachments"); > 64 } > 65 blob_appendf(&sql, " ORDER BY mtime DESC"); > 66 db_prepare(&q, "%s", blob_str(&sql)); > 67 while( db_step(&q)==SQLITE_ROW ){ > 68 const char *zDate = db_column_text(&q, 0); > 69 const char *zSrc = db_column_text(&q, 1); > 70 const char *zTarget = db_column_text(&q, 2); > 71 const char *zFilename = db_column_text(&q, 3); > 72 const char *zComment = db_column_text(&q, 4); > 73 const char *zUser = db_column_text(&q, 5); > 74 int i; > 75 char *zUrlTail; > 76 for(i=0; zFilename[i]; i++){ > 77 if( zFilename[i]=='/' && zFilename[i+1]!=0 ){ > 78 zFilename = &zFilename[i+1]; > 79 i = -1; > 80 } > 81 } > 82 if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){ > 83 zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename); > 84 }else{ > 85 zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); > 86 } > 87 @ > 88 @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a> > 89 @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br> > 90 if( zComment ) while( isspace(zComment[0]) ) zComment++; > 91 if( zComment && zComment[0] ){ > 92 @ %w(zComment)<br> > 93 } > 94 if( zPage==0 && zTkt==0 ){ > 95 if( zSrc==0 || zSrc[0]==0 ){ > 96 zSrc = "Deleted from"; > 97 }else { > 98 zSrc = "Added to"; > 99 } > 100 if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){ > 101 char zShort[20]; > 102 memcpy(zShort, zTarget, 10); > 103 zShort[10] = 0; > 104 @ %s(zSrc) ticket <a href="%s(g.zTop)/tktview?name=%s(zTarget)"> > 105 @ %s(zShort)</a> > 106 }else{ > 107 @ %s(zSrc) wiki page <a href="%s(g.zTop)/wiki?name=%t(zTarget)"> > 108 @ %h(zTarget)</a> > 109 } > 110 }else{ > 111 if( zSrc==0 || zSrc[0]==0 ){ > 112 @ Deleted > 113 }else { > 114 @ Added > 115 } > 116 } > 117 @ by %h(zUser) on > 118 hyperlink_to_date(zDate, "."); > 119 free(zUrlTail); > 120 } > 121 db_finalize(&q); > 122 style_footer(); > 123 return; > 124 } > 125 > 126 /* > 127 ** WEBPAGE: attachdownload > 128 ** WEBPAGE: attachimage > 129 ** WEBPAGE: attachview > 130 ** > 131 ** tkt=TICKETUUID > 132 ** page=WIKIPAGE > 133 ** file=FILENAME > 134 ** attachid=ID > 135 ** > 136 ** List attachments. > 137 */ > 138 void attachview_page(void){ > 139 const char *zPage = P("page"); > 140 const char *zTkt = P("tkt"); > 141 const char *zFile = P("file"); > 142 const char *zTarget = 0; > 143 int attachid = atoi(PD("attachid","0")); > 144 char *zUUID; > 145 > 146 if( zPage && zTkt ) zTkt = 0; > 147 if( zFile==0 ) fossil_redirect_home(); > 148 login_check_credentials(); > 149 if( zPage ){ > 150 if( g.okRdWiki==0 ) login_needed(); > 151 zTarget = zPage; > 152 }else if( zTkt ){ > 153 if( g.okRdTkt==0 ) login_needed(); > 154 zTarget = zTkt; > 155 }else{ > 156 fossil_redirect_home(); > 157 } > 158 if( attachid>0 ){ > 159 zUUID = db_text(0, > 160 "SELECT coalesce(src,'x') FROM attachment" > 161 " WHERE target=%Q AND attachid=%d", > 162 zTarget, attachid > 163 ); > 164 }else{ > 165 zUUID = db_text(0, > 166 "SELECT coalesce(src,'x') FROM attachment" > 167 " WHERE target=%Q AND filename=%Q" > 168 " ORDER BY mtime DESC LIMIT 1", > 169 zTarget, zFile > 170 ); > 171 } > 172 if( zUUID==0 || zUUID[0]==0 ){ > 173 style_header("No Such Attachment"); > 174 @ No such attachment.... > 175 style_footer(); > 176 return; > 177 }else if( zUUID[0]=='x' ){ > 178 style_header("Missing"); > 179 @ Attachment has been deleted > 180 style_footer(); > 181 return; > 182 } > 183 g.okRead = 1; > 184 cgi_replace_parameter("name",zUUID); > 185 if( strcmp(g.zPath,"attachview")==0 ){ > 186 artifact_page(); > 187 }else{ > 188 cgi_replace_parameter("m", mimetype_from_name(zFile)); > 189 rawartifact_page(); > 190 } > 191 } > 192 > 193 > 194 /* > 195 ** WEBPAGE: attachadd > 196 ** > 197 ** tkt=TICKETUUID > 198 ** page=WIKIPAGE > 199 ** from=URL > 200 ** > 201 ** Add a new attachment. > 202 */ > 203 void attachadd_page(void){ > 204 const char *zPage = P("page"); > 205 const char *zTkt = P("tkt"); > 206 const char *zFrom = P("from"); > 207 const char *aContent = P("f"); > 208 const char *zName = PD("f:filename","unknown"); > 209 const char *zTarget; > 210 const char *zTargetType; > 211 int szContent = atoi(PD("f:bytes","0")); > 212 > 213 if( P("cancel") ) cgi_redirect(zFrom); > 214 if( zPage && zTkt ) fossil_redirect_home(); > 215 if( zPage==0 && zTkt==0 ) fossil_redirect_home(); > 216 login_check_credentials(); > 217 if( zPage ){ > 218 if( g.okApndWiki==0 || g.okAttach==0 ) login_needed(); > 219 if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zPage) ){ > 220 fossil_redirect_home(); > 221 } > 222 zTarget = zPage; > 223 zTargetType = mprintf("Wiki Page <a href=\"%s/wiki?name=%h\">%h</a>", > 224 g.zTop, zPage, zPage); > 225 }else{ > 226 if( g.okApndTkt==0 || g.okAttach==0 ) login_needed(); > 227 if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTkt) ){ > 228 fossil_redirect_home(); > 229 } > 230 zTarget = zTkt; > 231 zTargetType = mprintf("Ticket <a href=\"%s/tktview?name=%.10s\">%.10s</a>", > 232 g.zTop, zTkt, zTkt); > 233 } > 234 if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop); > 235 if( P("cancel") ){ > 236 cgi_redirect(zFrom); > 237 } > 238 if( P("ok") && szContent>0 ){ > 239 Blob content; > 240 Blob manifest; > 241 Blob cksum; > 242 char *zUUID; > 243 const char *zComment; > 244 char *zDate; > 245 int rid; > 246 int i, n; > 247 > 248 db_begin_transaction(); > 249 blob_init(&content, aContent, szContent); > 250 rid = content_put(&content, 0, 0); > 251 zUUID = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); > 252 blob_zero(&manifest); > 253 for(i=n=0; zName[i]; i++){ > 254 if( zName[i]=='/' || zName[i]=='\\' ) n = i; > 255 } > 256 zName += n; > 257 if( zName[0]==0 ) zName = "unknown"; > 258 blob_appendf(&manifest, "A %F %F %s\n", zName, zTarget, zUUID); > 259 zComment = PD("comment", ""); > 260 while( isspace(zComment[0]) ) zComment++; > 261 n = strlen(zComment); > 262 while( n>0 && isspace(zComment[n-1]) ){ n--; } > 263 if( n>0 ){ > 264 blob_appendf(&manifest, "C %F\n", zComment); > 265 } > 266 zDate = db_text(0, "SELECT datetime('now')"); > 267 zDate[10] = 'T'; > 268 blob_appendf(&manifest, "D %s\n", zDate); > 269 blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody"); > 270 md5sum_blob(&manifest, &cksum); > 271 blob_appendf(&manifest, "Z %b\n", &cksum); > 272 rid = content_put(&manifest, 0, 0); > 273 manifest_crosslink(rid, &manifest); > 274 db_end_transaction(0); > 275 cgi_redirect(zFrom); > 276 } > 277 style_header("Add Attachment"); > 278 @ <h2>Add Attachment To %s(zTargetType)</h2> > 279 @ <form action="%s(g.zBaseURL)/attachadd" method="POST" > 280 @ enctype="multipart/form-data"> > 281 @ File to Attach: > 282 @ <input type="file" name="f" size="60"><br> > 283 @ Description:<br> > 284 @ <textarea name="comment" cols=80 rows=5 wrap="virtual"></textarea><br> > 285 if( zTkt ){ > 286 @ <input type="hidden" name="tkt" value="%h(zTkt)"> > 287 }else{ > 288 @ <input type="hidden" name="page" value="%h(zPage)"> > 289 } > 290 @ <input type="hidden" name="from" value="%h(zFrom)"> > 291 @ <input type="submit" name="ok" value="Add Attachment"> > 292 @ <input type="submit" name="cancel" value="Cancel"> > 293 @ </form> > 294 style_footer(); > 295 } > 296 > 297 > 298 /* > 299 ** WEBPAGE: attachdelete > 300 ** > 301 ** tkt=TICKETUUID > 302 ** page=WIKIPAGE > 303 ** file=FILENAME > 304 ** > 305 ** "Delete" an attachment. Because objects in Fossil are immutable > 306 ** the attachment isn't really deleted. Instead, we change the content > 307 ** of the attachment to NULL, which the system understands as being > 308 ** deleted. Historical values of the attachment are preserved. > 309 */ > 310 void attachdel_page(void){ > 311 const char *zPage = P("page"); > 312 const char *zTkt = P("tkt"); > 313 const char *zFile = P("file"); > 314 const char *zFrom = P("from"); > 315 const char *zTarget; > 316 > 317 if( zPage && zTkt ) fossil_redirect_home(); > 318 if( zPage==0 && zTkt==0 ) fossil_redirect_home(); > 319 if( zFile==0 ) fossil_redirect_home(); > 320 login_check_credentials(); > 321 if( zPage ){ > 322 if( g.okWrWiki==0 || g.okAttach==0 ) login_needed(); > 323 zTarget = zPage; > 324 }else{ > 325 if( g.okWrTkt==0 || g.okAttach==0 ) login_needed(); > 326 zTarget = zTkt; > 327 } > 328 if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop); > 329 if( P("cancel") ){ > 330 cgi_redirect(zFrom); > 331 } > 332 if( P("confirm") ){ > 333 int i, n, rid; > 334 char *zDate; > 335 Blob manifest; > 336 Blob cksum; > 337 > 338 db_begin_transaction(); > 339 blob_zero(&manifest); > 340 for(i=n=0; zFile[i]; i++){ > 341 if( zFile[i]=='/' || zFile[i]=='\\' ) n = i; > 342 } > 343 zFile += n; > 344 if( zFile[0]==0 ) zFile = "unknown"; > 345 blob_appendf(&manifest, "A %F %F\n", zFile, zTarget); > 346 zDate = db_text(0, "SELECT datetime('now')"); > 347 zDate[10] = 'T'; > 348 blob_appendf(&manifest, "D %s\n", zDate); > 349 blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody"); > 350 md5sum_blob(&manifest, &cksum); > 351 blob_appendf(&manifest, "Z %b\n", &cksum); > 352 rid = content_put(&manifest, 0, 0); > 353 manifest_crosslink(rid, &manifest); > 354 db_end_transaction(0); > 355 cgi_redirect(zFrom); > 356 } > 357 style_header("Delete Attachment"); > 358 @ <form action="%s(g.zBaseURL)/attachdelete" method="POST"> > 359 @ <p>Confirm that you want to delete the attachment named > 360 @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br> > 361 if( zTkt ){ > 362 @ <input type="hidden" name="tkt" value="%h(zTkt)"> > 363 }else{ > 364 @ <input type="hidden" name="page" value="%h(zPage)"> > 365 } > 366 @ <input type="hidden" name="file" value="%h(zFile)"> > 367 @ <input type="hidden" name="from" value="%h(zFrom)"> > 368 @ <input type="submit" name="confirm" value="Delete"> > 369 @ <input type="submit" name="cancel" value="Cancel"> > 370 @ </form> > 371 style_footer(); > 372 > 373 }

Changes to src/browse.c

111 int nCol, nRow; 111 int nCol, nRow; 112 int cnt, i; 112 int cnt, i; 113 char *zPrefix; 113 char *zPrefix; 114 Stmt q; 114 Stmt q; 115 const char *zCI = P("ci"); 115 const char *zCI = P("ci"); 116 int rid = 0; 116 int rid = 0; 117 Blob content; 117 Blob content; > 118 Blob dirname; 118 Manifest m; 119 Manifest m; 119 const char *zSubdirLink; 120 const char *zSubdirLink; 120 121 121 login_check_credentials(); 122 login_check_credentials(); 122 if( !g.okHistory ){ login_needed(); return; } 123 if( !g.okHistory ){ login_needed(); return; } 123 style_header("File List"); 124 style_header("File List"); 124 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, 125 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, ................................................................................................................................................................................ 131 if( zCI && (rid = name_to_rid(zCI))!=0 && content_get(rid, &content) ){ 132 if( zCI && (rid = name_to_rid(zCI))!=0 && content_get(rid, &content) ){ 132 if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){ 133 if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){ 133 zCI = 0; 134 zCI = 0; 134 } 135 } 135 } 136 } 136 137 137 /* Compute the title of the page */ 138 /* Compute the title of the page */ > 139 blob_zero(&dirname); 138 if( zD ){ 140 if( zD ){ 139 Blob title; | 141 blob_append(&dirname, "in directory ", -1); 140 < 141 blob_zero(&title); < 142 blob_appendf(&title, "Files in directory "); < 143 hyperlinked_path(zD, &title); | 142 hyperlinked_path(zD, &dirname); 144 @ <h2>%s(blob_str(&title)) < 145 blob_reset(&title); < 146 zPrefix = mprintf("%h/", zD); 143 zPrefix = mprintf("%h/", zD); 147 }else{ 144 }else{ 148 @ <h2>Files in the top-level directory | 145 blob_append(&dirname, "in the top-level directory", -1); 149 zPrefix = ""; 146 zPrefix = ""; 150 } 147 } 151 if( zCI ){ 148 if( zCI ){ 152 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); 149 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); 153 char zShort[20]; 150 char zShort[20]; 154 memcpy(zShort, zUuid, 10); 151 memcpy(zShort, zUuid, 10); 155 zShort[10] = 0; 152 zShort[10] = 0; 156 @ of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]</h2> | 153 @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>] > 154 @ %s(blob_str(&dirname))</h2> 157 zSubdirLink = mprintf("%s/dir?ci=%s&name=%T", g.zBaseURL, zUuid, zPrefix); 155 zSubdirLink = mprintf("%s/dir?ci=%s&name=%T", g.zBaseURL, zUuid, zPrefix); 158 if( zD ){ 156 if( zD ){ 159 style_submenu_element("Top", "Top", "%s/dir?ci=%s", g.zBaseURL, zUuid); 157 style_submenu_element("Top", "Top", "%s/dir?ci=%s", g.zBaseURL, zUuid); > 158 style_submenu_element("All", "All", "%s/dir?name=%t", g.zBaseURL, zD); > 159 }else{ > 160 style_submenu_element("All", "All", "%s/dir", g.zBaseURL); 160 } 161 } 161 }else{ 162 }else{ 162 @ </h2> | 163 @ <h2>The union of all files from all check-ins > 164 @ %s(blob_str(&dirname))</h2> 163 zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix); 165 zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix); > 166 if( zD ){ > 167 style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL); > 168 style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip", > 169 g.zBaseURL, zD); > 170 style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk", > 171 g.zBaseURL,zD); > 172 }else{ > 173 style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL); > 174 style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL); > 175 } 164 } 176 } 165 177 166 /* Compute the temporary table "localfiles" containing the names 178 /* Compute the temporary table "localfiles" containing the names 167 ** of all files and subdirectories in the zD[] directory. 179 ** of all files and subdirectories in the zD[] directory. 168 ** 180 ** 169 ** Subdirectory names begin with "/". This causes them to sort 181 ** Subdirectory names begin with "/". This causes them to sort 170 ** first and it also gives us an easy way to distinguish files 182 ** first and it also gives us an easy way to distinguish files

Changes to src/checkin.c

191 }else{ 191 }else{ 192 printf("UNCHANGED %s\n", zPathname); 192 printf("UNCHANGED %s\n", zPathname); 193 } 193 } 194 free(zFullName); 194 free(zFullName); 195 } 195 } 196 db_finalize(&q); 196 db_finalize(&q); 197 } 197 } > 198 > 199 /* > 200 ** Construct and return a string which is an SQL expression that will > 201 ** be TRUE if value zVal matches any of the GLOB expressions in the list > 202 ** zGlobList. For example: > 203 ** > 204 ** zVal: "x" > 205 ** zGlobList: "*.o,*.obj" > 206 ** > 207 ** Result: "(x GLOB '*.o' OR x GLOB '*.obj')" > 208 ** > 209 ** Each element of the GLOB list may optionally be enclosed in either '...' > 210 ** or "...". This allows commas in the expression. Whitespace at the > 211 ** beginning and end of each GLOB pattern is ignored, except when enclosed > 212 ** within '...' or "...". > 213 ** > 214 ** This routine makes no effort to free the memory space it uses. > 215 */ > 216 char *glob_expr(const char *zVal, const char *zGlobList){ > 217 Blob expr; > 218 char *zSep = "("; > 219 int nTerm = 0; > 220 int i; > 221 int cTerm; > 222 > 223 if( zGlobList==0 || zGlobList[0]==0 ) return "0"; > 224 blob_zero(&expr); > 225 while( zGlobList[0] ){ > 226 while( isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++; > 227 if( zGlobList[0]==0 ) break; > 228 if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){ > 229 cTerm = zGlobList[0]; > 230 zGlobList++; > 231 }else{ > 232 cTerm = ','; > 233 } > 234 for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){} > 235 if( cTerm==',' ){ > 236 while( i>0 && isspace(zGlobList[i-1]) ){ i--; } > 237 } > 238 blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList); > 239 zSep = " OR "; > 240 if( cTerm!=',' && zGlobList[i] ) i++; > 241 zGlobList += i; > 242 if( zGlobList[0] ) zGlobList++; > 243 nTerm++; > 244 } > 245 if( nTerm ){ > 246 blob_appendf(&expr, ")"); > 247 return blob_str(&expr); > 248 }else{ > 249 return "0"; > 250 } > 251 } 198 252 199 /* 253 /* 200 ** COMMAND: extras 254 ** COMMAND: extras 201 ** Usage: %fossil extras ?--dotfiles? | 255 ** Usage: %fossil extras ?--dotfiles? ?--ignore GLOBPATTERN? 202 ** 256 ** 203 ** Print a list of all files in the source tree that are not part of 257 ** Print a list of all files in the source tree that are not part of 204 ** the current checkout. See also the "clean" command. 258 ** the current checkout. See also the "clean" command. 205 ** 259 ** 206 ** Files and subdirectories whose names begin with "." are normally 260 ** Files and subdirectories whose names begin with "." are normally 207 ** ignored but can be included by adding the --dotfiles option. 261 ** ignored but can be included by adding the --dotfiles option. 208 */ 262 */ 209 void extra_cmd(void){ 263 void extra_cmd(void){ 210 Blob path; 264 Blob path; 211 Blob repo; 265 Blob repo; 212 Stmt q; 266 Stmt q; 213 int n; 267 int n; > 268 const char *zIgnoreFlag = find_option("ignore",0,1); 214 int allFlag = find_option("dotfiles",0,0)!=0; 269 int allFlag = find_option("dotfiles",0,0)!=0; > 270 215 db_must_be_within_tree(); 271 db_must_be_within_tree(); 216 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); 272 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); 217 n = strlen(g.zLocalRoot); 273 n = strlen(g.zLocalRoot); 218 blob_init(&path, g.zLocalRoot, n-1); 274 blob_init(&path, g.zLocalRoot, n-1); > 275 if( zIgnoreFlag==0 ){ > 276 zIgnoreFlag = db_get("ignore-glob", 0); > 277 } 219 vfile_scan(0, &path, blob_size(&path), allFlag); 278 vfile_scan(0, &path, blob_size(&path), allFlag); 220 db_prepare(&q, 279 db_prepare(&q, 221 "SELECT x FROM sfile" 280 "SELECT x FROM sfile" 222 " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_')" 281 " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_')" > 282 " AND NOT %s" 223 " ORDER BY 1"); | 283 " ORDER BY 1", > 284 glob_expr("x", zIgnoreFlag) > 285 ); 224 if( file_tree_name(g.zRepositoryName, &repo, 0) ){ 286 if( file_tree_name(g.zRepositoryName, &repo, 0) ){ 225 db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); 287 db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); 226 } 288 } 227 while( db_step(&q)==SQLITE_ROW ){ 289 while( db_step(&q)==SQLITE_ROW ){ 228 printf("%s\n", db_column_text(&q, 0)); 290 printf("%s\n", db_column_text(&q, 0)); 229 } 291 } 230 db_finalize(&q); 292 db_finalize(&q); ................................................................................................................................................................................ 681 } 743 } 682 blob_appendf(&manifest, "C %F\n", blob_str(&comment)); 744 blob_appendf(&manifest, "C %F\n", blob_str(&comment)); 683 zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now"); 745 zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now"); 684 zDate[10] = 'T'; 746 zDate[10] = 'T'; 685 blob_appendf(&manifest, "D %s\n", zDate); 747 blob_appendf(&manifest, "D %s\n", zDate); 686 zDate[10] = ' '; 748 zDate[10] = ' '; 687 db_prepare(&q, 749 db_prepare(&q, 688 "SELECT pathname, uuid, origname, blob.rid" | 750 "SELECT pathname, uuid, origname, blob.rid, isexe" 689 " FROM vfile JOIN blob ON vfile.mrid=blob.rid" 751 " FROM vfile JOIN blob ON vfile.mrid=blob.rid" 690 " WHERE NOT deleted AND vfile.vid=%d" 752 " WHERE NOT deleted AND vfile.vid=%d" 691 " ORDER BY 1", vid); 753 " ORDER BY 1", vid); 692 blob_zero(&filename); 754 blob_zero(&filename); 693 blob_appendf(&filename, "%s", g.zLocalRoot); 755 blob_appendf(&filename, "%s", g.zLocalRoot); 694 nBasename = blob_size(&filename); 756 nBasename = blob_size(&filename); 695 while( db_step(&q)==SQLITE_ROW ){ 757 while( db_step(&q)==SQLITE_ROW ){ 696 const char *zName = db_column_text(&q, 0); 758 const char *zName = db_column_text(&q, 0); 697 const char *zUuid = db_column_text(&q, 1); 759 const char *zUuid = db_column_text(&q, 1); 698 const char *zOrig = db_column_text(&q, 2); 760 const char *zOrig = db_column_text(&q, 2); 699 int frid = db_column_int(&q, 3); 761 int frid = db_column_int(&q, 3); > 762 int isexe = db_column_int(&q, 4); 700 const char *zPerm; 763 const char *zPerm; 701 blob_append(&filename, zName, -1); 764 blob_append(&filename, zName, -1); > 765 #ifndef __MINGW32__ > 766 /* For unix, extract the "executable" permission bit directly from > 767 ** the filesystem. On windows, the "executable" bit is retained > 768 ** unchanged from the original. */ 702 if( file_isexe(blob_str(&filename)) ){ | 769 isexe = file_isexe(blob_str(&filename)); > 770 #endif > 771 if( isexe ){ 703 zPerm = " x"; 772 zPerm = " x"; 704 }else{ 773 }else{ 705 zPerm = ""; 774 zPerm = ""; 706 } 775 } 707 blob_resize(&filename, nBasename); 776 blob_resize(&filename, nBasename); 708 if( zOrig==0 || strcmp(zOrig,zName)==0 ){ 777 if( zOrig==0 || strcmp(zOrig,zName)==0 ){ 709 blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm); 778 blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm);

Changes to src/checkout.c

91 if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){ 91 if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){ 92 return; 92 return; 93 } 93 } 94 content_get(vid, &manifest); 94 content_get(vid, &manifest); 95 vfile_build(vid, &manifest); 95 vfile_build(vid, &manifest); 96 blob_reset(&manifest); 96 blob_reset(&manifest); 97 } 97 } > 98 > 99 /* > 100 ** Set or clear the vfile.isexe flag for a file. > 101 */ > 102 static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){ > 103 db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q", > 104 onoff, vid, zFilename); > 105 } 98 106 99 /* 107 /* 100 ** Read the manifest file given by vid out of the repository 108 ** Read the manifest file given by vid out of the repository 101 ** and store it in the root of the local check-out. 109 ** and store it in the root of the local check-out. 102 */ 110 */ 103 void manifest_to_disk(int vid){ 111 void manifest_to_disk(int vid){ 104 char *zManFile; 112 char *zManFile; ................................................................................................................................................................................ 126 blob_appendf(&filename, "%s/", g.zLocalRoot); 134 blob_appendf(&filename, "%s/", g.zLocalRoot); 127 baseLen = blob_size(&filename); 135 baseLen = blob_size(&filename); 128 for(i=0; i<m.nFile; i++){ 136 for(i=0; i<m.nFile; i++){ 129 int isExe; 137 int isExe; 130 blob_append(&filename, m.aFile[i].zName, -1); 138 blob_append(&filename, m.aFile[i].zName, -1); 131 isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x"); 139 isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x"); 132 file_setexe(blob_str(&filename), isExe); 140 file_setexe(blob_str(&filename), isExe); > 141 set_or_clear_isexe(m.aFile[i].zName, vid, isExe); 133 blob_resize(&filename, baseLen); 142 blob_resize(&filename, baseLen); 134 } 143 } 135 blob_reset(&filename); 144 blob_reset(&filename); 136 manifest_clear(&m); 145 manifest_clear(&m); 137 } 146 } 138 147 139 /* 148 /*

Changes to src/db.c

494 ** of the result set as a string. Space to hold the string is 494 ** of the result set as a string. Space to hold the string is 495 ** obtained from malloc(). If the result set is empty, return 495 ** obtained from malloc(). If the result set is empty, return 496 ** zDefault instead. 496 ** zDefault instead. 497 */ 497 */ 498 char *db_text(char *zDefault, const char *zSql, ...){ 498 char *db_text(char *zDefault, const char *zSql, ...){ 499 va_list ap; 499 va_list ap; 500 Stmt s; 500 Stmt s; 501 char *z = zDefault; | 501 char *z; 502 va_start(ap, zSql); 502 va_start(ap, zSql); 503 db_vprepare(&s, zSql, ap); 503 db_vprepare(&s, zSql, ap); 504 va_end(ap); 504 va_end(ap); 505 if( db_step(&s)==SQLITE_ROW ){ 505 if( db_step(&s)==SQLITE_ROW ){ 506 z = mprintf("%s", sqlite3_column_text(s.pStmt, 0)); 506 z = mprintf("%s", sqlite3_column_text(s.pStmt, 0)); > 507 }else if( zDefault ){ > 508 z = mprintf("%s", zDefault); > 509 }else{ > 510 z = 0; 507 } 511 } 508 db_finalize(&s); 512 db_finalize(&s); 509 return z; 513 return z; 510 } 514 } > 515 > 516 #ifdef __MINGW32__ > 517 extern char *sqlite3_win32_mbcs_to_utf8(const char*); > 518 #endif 511 519 512 /* 520 /* 513 ** Initialize a new database file with the given schema. If anything 521 ** Initialize a new database file with the given schema. If anything 514 ** goes wrong, call db_err() to exit. 522 ** goes wrong, call db_err() to exit. 515 */ 523 */ 516 void db_init_database( 524 void db_init_database( 517 const char *zFileName, /* Name of database file to create */ 525 const char *zFileName, /* Name of database file to create */ ................................................................................................................................................................................ 659 lsize = file_size(zDbName); 667 lsize = file_size(zDbName); 660 if( lsize%1024!=0 || lsize<4096 ) return 0; 668 if( lsize%1024!=0 || lsize<4096 ) return 0; 661 db_open_or_attach(zDbName, "localdb"); 669 db_open_or_attach(zDbName, "localdb"); 662 g.localOpen = 1; 670 g.localOpen = 1; 663 db_open_config(0); 671 db_open_config(0); 664 db_open_repository(0); 672 db_open_repository(0); 665 673 > 674 /* If the "isexe" column is missing from the vfile table, then > 675 ** add it now. This code added on 2010-03-06. After all users have > 676 ** upgraded, this code can be safely deleted. > 677 */ > 678 rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0); > 679 sqlite3_finalize(pStmt); > 680 if( rc==SQLITE_ERROR ){ > 681 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0); > 682 } > 683 > 684 #if 0 666 /* If the "mtime" column is missing from the vfile table, then 685 /* If the "mtime" column is missing from the vfile table, then 667 ** add it now. This code added on 2008-12-06. After all users have 686 ** add it now. This code added on 2008-12-06. After all users have 668 ** upgraded, this code can be safely deleted. 687 ** upgraded, this code can be safely deleted. 669 */ 688 */ 670 rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0); 689 rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0); 671 sqlite3_finalize(pStmt); 690 sqlite3_finalize(pStmt); 672 if( rc==SQLITE_ERROR ){ 691 if( rc==SQLITE_ERROR ){ 673 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0); 692 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0); 674 } 693 } > 694 #endif 675 695 > 696 #if 0 676 /* If the "origname" column is missing from the vfile table, then 697 /* If the "origname" column is missing from the vfile table, then 677 ** add it now. This code added on 2008-11-09. After all users have 698 ** add it now. This code added on 2008-11-09. After all users have 678 ** upgraded, this code can be safely deleted. 699 ** upgraded, this code can be safely deleted. 679 */ 700 */ 680 rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0); 701 rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0); 681 sqlite3_finalize(pStmt); 702 sqlite3_finalize(pStmt); 682 if( rc==SQLITE_ERROR ){ 703 if( rc==SQLITE_ERROR ){ 683 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0); 704 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0); 684 } 705 } > 706 #endif 685 707 686 return 1; 708 return 1; 687 } 709 } 688 710 689 /* 711 /* 690 ** Locate the root directory of the local repository tree. The root 712 ** Locate the root directory of the local repository tree. The root 691 ** directory is found by searching for a file named "_FOSSIL_" or ".fos" 713 ** directory is found by searching for a file named "_FOSSIL_" or ".fos" ................................................................................................................................................................................ 883 ** new repositories. 905 ** new repositories. 884 ** 906 ** 885 ** The zInitialDate parameter determines the date of the initial check-in 907 ** The zInitialDate parameter determines the date of the initial check-in 886 ** that is automatically created. If zInitialDate is 0 then no initial 908 ** that is automatically created. If zInitialDate is 0 then no initial 887 ** check-in is created. The makeServerCodes flag determines whether or 909 ** check-in is created. The makeServerCodes flag determines whether or 888 ** not server and project codes are invented for this repository. 910 ** not server and project codes are invented for this repository. 889 */ 911 */ 890 void db_initial_setup (const char *zInitialDate, const char *zDefaultUser, int m | 912 void db_initial_setup( > 913 const char *zInitialDate, /* Initial date of repository. (ex: "now") */ > 914 const char *zDefaultUser, /* Default user for the repository */ > 915 int makeServerCodes /* True to make new server & project codes */ > 916 ){ 891 char *zDate; 917 char *zDate; 892 Blob hash; 918 Blob hash; 893 Blob manifest; 919 Blob manifest; 894 920 895 db_set("content-schema", CONTENT_SCHEMA, 0); 921 db_set("content-schema", CONTENT_SCHEMA, 0); 896 db_set("aux-schema", AUX_SCHEMA, 0); 922 db_set("aux-schema", AUX_SCHEMA, 0); 897 if( makeServerCodes ){ 923 if( makeServerCodes ){ ................................................................................................................................................................................ 940 ** By default, your current login name is used to create the default 966 ** By default, your current login name is used to create the default 941 ** admin user. This can be overridden using the -A|--admin-user 967 ** admin user. This can be overridden using the -A|--admin-user 942 ** parameter. 968 ** parameter. 943 ** 969 ** 944 ** Options: 970 ** Options: 945 ** 971 ** 946 ** --admin-user|-A USERNAME 972 ** --admin-user|-A USERNAME > 973 ** --date-override DATETIME 947 ** 974 ** 948 */ 975 */ 949 void create_repository_cmd(void){ 976 void create_repository_cmd(void){ 950 char *zPassword; 977 char *zPassword; 951 const char *zDate; /* Date of the initial check-in */ 978 const char *zDate; /* Date of the initial check-in */ 952 const char *zDefaultUser; /* Optional name of the default user */ 979 const char *zDefaultUser; /* Optional name of the default user */ 953 980 ................................................................................................................................................................................ 1403 ** The "unset" command clears a property setting. 1430 ** The "unset" command clears a property setting. 1404 ** 1431 ** 1405 ** 1432 ** 1406 ** autosync If enabled, automatically pull prior to commit 1433 ** autosync If enabled, automatically pull prior to commit 1407 ** or update and automatically push after commit or 1434 ** or update and automatically push after commit or 1408 ** tag or branch creation. If the the value is "pullonly" 1435 ** tag or branch creation. If the the value is "pullonly" 1409 ** then only pull operations occur automatically. 1436 ** then only pull operations occur automatically. > 1437 ** > 1438 ** binary-glob The VALUE is a comma-separated list of GLOB patterns > 1439 ** that should be treated as binary files for merging > 1440 ** purposes. Example: *.xml 1410 ** 1441 ** 1411 ** clearsign When enabled, fossil will attempt to sign all commits 1442 ** clearsign When enabled, fossil will attempt to sign all commits 1412 ** with gpg. When disabled (the default), commits will 1443 ** with gpg. When disabled (the default), commits will 1413 ** be unsigned. 1444 ** be unsigned. 1414 ** 1445 ** 1415 ** diff-command External command to run when performing a diff. 1446 ** diff-command External command to run when performing a diff. 1416 ** If undefined, the internal text diff will be used. 1447 ** If undefined, the internal text diff will be used. ................................................................................................................................................................................ 1421 ** editor Text editor command used for check-in comments. 1452 ** editor Text editor command used for check-in comments. 1422 ** 1453 ** 1423 ** gdiff-command External command to run when performing a graphical 1454 ** gdiff-command External command to run when performing a graphical 1424 ** diff. If undefined, text diff will be used. 1455 ** diff. If undefined, text diff will be used. 1425 ** 1456 ** 1426 ** http-port The TCP/IP port number to use by the "server" 1457 ** http-port The TCP/IP port number to use by the "server" 1427 ** and "ui" commands. Default: 8080 1458 ** and "ui" commands. Default: 8080 > 1459 ** > 1460 ** ignore-glob The VALUE is a comma-separated list of GLOB patterns > 1461 ** specifying files that the "extra" command will ignore. > 1462 ** Example: *.o,*.obj,*.exe 1428 ** 1463 ** 1429 ** localauth If enabled, require that HTTP connections from 1464 ** localauth If enabled, require that HTTP connections from 1430 ** 127.0.0.1 be authenticated by password. If 1465 ** 127.0.0.1 be authenticated by password. If 1431 ** false, all HTTP requests from localhost have 1466 ** false, all HTTP requests from localhost have 1432 ** unrestricted access to the repository. 1467 ** unrestricted access to the repository. 1433 ** 1468 ** 1434 ** mtime-changes Use file modification times (mtimes) to detect when 1469 ** mtime-changes Use file modification times (mtimes) to detect when ................................................................................................................................................................................ 1446 ** web browser when given a URL as an argument. 1481 ** web browser when given a URL as an argument. 1447 ** Defaults to "start" on windows, "open" on Mac, 1482 ** Defaults to "start" on windows, "open" on Mac, 1448 ** and "firefox" on Unix. 1483 ** and "firefox" on Unix. 1449 */ 1484 */ 1450 void setting_cmd(void){ 1485 void setting_cmd(void){ 1451 static const char *azName[] = { 1486 static const char *azName[] = { 1452 "autosync", 1487 "autosync", > 1488 "binary-glob", 1453 "clearsign", 1489 "clearsign", 1454 "diff-command", 1490 "diff-command", 1455 "dont-push", 1491 "dont-push", 1456 "editor", 1492 "editor", 1457 "gdiff-command", 1493 "gdiff-command", > 1494 "ignore-glob", 1458 "http-port", 1495 "http-port", 1459 "localauth", 1496 "localauth", 1460 "mtime-changes", 1497 "mtime-changes", 1461 "pgp-command", 1498 "pgp-command", 1462 "proxy", 1499 "proxy", 1463 "web-browser", 1500 "web-browser", 1464 }; 1501 };

Changes to src/diffcmd.c

47 } 47 } 48 blob_append(pBlob, zIn, -1); 48 blob_append(pBlob, zIn, -1); 49 } 49 } 50 50 51 /* 51 /* 52 ** This function implements a cross-platform "system()" interface. 52 ** This function implements a cross-platform "system()" interface. 53 */ 53 */ 54 int portable_system(char *zOrigCmd){ | 54 int portable_system(const char *zOrigCmd){ 55 int rc; 55 int rc; 56 #ifdef __MINGW32__ 56 #ifdef __MINGW32__ 57 /* On windows, we have to put double-quotes around the entire command. 57 /* On windows, we have to put double-quotes around the entire command. 58 ** Who knows why - this is just the way windows works. 58 ** Who knows why - this is just the way windows works. 59 */ 59 */ 60 char *zNewCmd = mprintf("\"%s\"", zOrigCmd); 60 char *zNewCmd = mprintf("\"%s\"", zOrigCmd); 61 rc = system(zNewCmd); 61 rc = system(zNewCmd);

Changes to src/file.c

161 chmod(zFilename, buf.st_mode | 0111); 161 chmod(zFilename, buf.st_mode | 0111); 162 } 162 } 163 }else{ 163 }else{ 164 if( (buf.st_mode & 0111)!=0 ){ 164 if( (buf.st_mode & 0111)!=0 ){ 165 chmod(zFilename, buf.st_mode & ~0111); 165 chmod(zFilename, buf.st_mode & ~0111); 166 } 166 } 167 } 167 } 168 #endif | 168 #endif /* __MINGW32__ */ 169 } 169 } 170 170 171 /* 171 /* 172 ** Create the directory named in the argument, if it does not already 172 ** Create the directory named in the argument, if it does not already 173 ** exist. If forceFlag is 1, delete any prior non-directory object 173 ** exist. If forceFlag is 1, delete any prior non-directory object 174 ** with the same name. 174 ** with the same name. 175 ** 175 **

Changes to src/finfo.c

98 ** Show the complete change history for a single file. 98 ** Show the complete change history for a single file. 99 */ 99 */ 100 void finfo_page(void){ 100 void finfo_page(void){ 101 Stmt q; 101 Stmt q; 102 const char *zFilename; 102 const char *zFilename; 103 char zPrevDate[20]; 103 char zPrevDate[20]; 104 Blob title; 104 Blob title; > 105 GraphContext *pGraph; 105 106 106 login_check_credentials(); 107 login_check_credentials(); 107 if( !g.okRead ){ login_needed(); return; } 108 if( !g.okRead ){ login_needed(); return; } 108 style_header("File History"); 109 style_header("File History"); 109 login_anonymous_available(); 110 login_anonymous_available(); 110 111 111 zPrevDate[0] = 0; 112 zPrevDate[0] = 0; 112 zFilename = PD("name",""); 113 zFilename = PD("name",""); 113 db_prepare(&q, 114 db_prepare(&q, > 115 "SELECT" > 116 " substr(b.uuid,1,10)," 114 "SELECT substr(b.uuid,1,10), datetime(event.mtime,'localtime')," | 117 " datetime(event.mtime,'localtime')," 115 " coalesce(event.ecomment, event.comment)," | 118 " coalesce(event.ecomment, event.comment)," 116 " coalesce(event.euser, event.user)," | 119 " coalesce(event.euser, event.user)," 117 " mlink.pid, mlink.fid, mlink.mid, mlink.fnid, ci.uuid" < > 120 " mlink.pid," > 121 " mlink.fid," > 122 " mlink.mid," > 123 " mlink.fnid," > 124 " ci.uuid," > 125 " event.bgcolor," > 126 " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0" > 127 " AND tagxref.rid=mlink.mid)" 118 " FROM mlink, blob b, event, blob ci" 128 " FROM mlink, blob b, event, blob ci" 119 " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)" 129 " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)" 120 " AND b.rid=mlink.fid" 130 " AND b.rid=mlink.fid" 121 " AND event.objid=mlink.mid" 131 " AND event.objid=mlink.mid" 122 " AND event.objid=ci.rid" 132 " AND event.objid=ci.rid" 123 " ORDER BY event.mtime DESC", 133 " ORDER BY event.mtime DESC", > 134 TAG_BRANCH, 124 zFilename 135 zFilename 125 ); 136 ); 126 blob_zero(&title); 137 blob_zero(&title); 127 blob_appendf(&title, "History of "); 138 blob_appendf(&title, "History of "); 128 hyperlinked_path(zFilename, &title); 139 hyperlinked_path(zFilename, &title); 129 @ <h2>%b(&title)</h2> 140 @ <h2>%b(&title)</h2> 130 blob_reset(&title); 141 blob_reset(&title); > 142 pGraph = graph_init(); > 143 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> 131 @ <table cellspacing=0 border=0 cellpadding=0> 144 @ <table cellspacing=0 border=0 cellpadding=0> 132 while( db_step(&q)==SQLITE_ROW ){ 145 while( db_step(&q)==SQLITE_ROW ){ 133 const char *zUuid = db_column_text(&q, 0); 146 const char *zUuid = db_column_text(&q, 0); 134 const char *zDate = db_column_text(&q, 1); 147 const char *zDate = db_column_text(&q, 1); 135 const char *zCom = db_column_text(&q, 2); 148 const char *zCom = db_column_text(&q, 2); 136 const char *zUser = db_column_text(&q, 3); 149 const char *zUser = db_column_text(&q, 3); 137 int fpid = db_column_int(&q, 4); 150 int fpid = db_column_int(&q, 4); 138 int frid = db_column_int(&q, 5); 151 int frid = db_column_int(&q, 5); 139 int mid = db_column_int(&q, 6); 152 int mid = db_column_int(&q, 6); 140 int fnid = db_column_int(&q, 7); 153 int fnid = db_column_int(&q, 7); 141 const char *zCkin = db_column_text(&q,8); 154 const char *zCkin = db_column_text(&q,8); > 155 const char *zBgClr = db_column_text(&q, 9); > 156 const char *zBr = db_column_text(&q, 10); > 157 int gidx; > 158 char zTime[10]; 142 char zShort[20]; 159 char zShort[20]; 143 char zShortCkin[20]; 160 char zShortCkin[20]; > 161 if( zBr==0 ) zBr = "trunk"; > 162 gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr); 144 if( memcmp(zDate, zPrevDate, 10) ){ 163 if( memcmp(zDate, zPrevDate, 10) ){ 145 sprintf(zPrevDate, "%.10s", zDate); 164 sprintf(zPrevDate, "%.10s", zDate); 146 @ <tr><td colspan=3> | 165 @ <tr><td> 147 @ <div class="divider">%s(zPrevDate)</div> | 166 @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div> 148 @ </td></tr> 167 @ </td></tr> 149 } 168 } > 169 memcpy(zTime, &zDate[11], 5); > 170 zTime[5] = 0; 150 @ <tr><td valign="top">%s(&zDate[11])</td> | 171 @ <tr><td valign="top" align="right"> 151 @ <td width="20"></td> < > 172 @ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td> > 173 @ <td width="20" align="left" valign="top"><div id="m%d(gidx)"></div></td> > 174 if( zBgClr && zBgClr[0] ){ > 175 @ <td valign="top" align="left" bgcolor="%h(zBgClr)"> > 176 }else{ 152 @ <td valign="top" align="left"> | 177 @ <td valign="top" align="left"> > 178 } 153 sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid); 179 sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid); 154 sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin); 180 sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin); 155 if( g.okHistory ){ 181 if( g.okHistory ){ 156 @ <a href="%s(g.zTop)/artifact/%s(zUuid)">[%s(zShort)]</a> 182 @ <a href="%s(g.zTop)/artifact/%s(zUuid)">[%s(zShort)]</a> 157 }else{ 183 }else{ 158 @ [%s(zShort)] 184 @ [%s(zShort)] 159 } 185 } 160 @ part of check-in 186 @ part of check-in 161 hyperlink_to_uuid(zShortCkin); 187 hyperlink_to_uuid(zShortCkin); 162 @ %h(zCom) (By: | 188 @ %h(zCom) (user: 163 hyperlink_to_user(zUser, zDate, " on"); | 189 hyperlink_to_user(zUser, zDate, ""); 164 hyperlink_to_date(zDate, ")"); | 190 @ branch: %h(zBr)) 165 if( g.okHistory ){ 191 if( g.okHistory ){ 166 if( fpid ){ 192 if( fpid ){ 167 @ <a href="%s(g.zBaseURL)/fdiff?v1=%d(fpid)&amp;v2=%d(frid)">[diff]</a> 193 @ <a href="%s(g.zBaseURL)/fdiff?v1=%d(fpid)&amp;v2=%d(frid)">[diff]</a> 168 } 194 } 169 @ <a href="%s(g.zBaseURL)/annotate?mid=%d(mid)&amp;fnid=%d(fnid)"> 195 @ <a href="%s(g.zBaseURL)/annotate?mid=%d(mid)&amp;fnid=%d(fnid)"> 170 @ [annotate]</a> 196 @ [annotate]</a> 171 @ </td> 197 @ </td> 172 } 198 } 173 } 199 } 174 db_finalize(&q); 200 db_finalize(&q); > 201 if( pGraph ){ > 202 graph_finish(pGraph, 1); > 203 if( pGraph->nErr ){ > 204 graph_free(pGraph); > 205 pGraph = 0; > 206 }else{ > 207 @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div> > 208 } > 209 } 175 @ </table> 210 @ </table> > 211 timeline_output_graph_javascript(pGraph); 176 style_footer(); 212 style_footer(); 177 } 213 }

Changes to src/graph.c

33 #define GR_MAX_RAIL 32 33 #define GR_MAX_RAIL 32 34 34 35 /* The graph appears vertically beside a timeline. Each row in the 35 /* The graph appears vertically beside a timeline. Each row in the 36 ** timeline corresponds to a row in the graph. 36 ** timeline corresponds to a row in the graph. 37 */ 37 */ 38 struct GraphRow { 38 struct GraphRow { 39 int rid; /* The rid for the check-in */ 39 int rid; /* The rid for the check-in */ 40 int isLeaf; /* True if the check-in is an open leaf */ < 41 int nParent; /* Number of parents */ 40 int nParent; /* Number of parents */ 42 int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/ 41 int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/ 43 char *zBranch; /* Branch name */ 42 char *zBranch; /* Branch name */ 44 43 45 GraphRow *pNext; /* Next row down in the list of all rows */ 44 GraphRow *pNext; /* Next row down in the list of all rows */ 46 GraphRow *pPrev; /* Previous row */ 45 GraphRow *pPrev; /* Previous row */ 47 46 48 int idx; /* Row index. First is 1. 0 used for "none" */ 47 int idx; /* Row index. First is 1. 0 used for "none" */ > 48 int isLeaf; /* True if no direct child nodes */ 49 int iRail; /* Which rail this check-in appears on. 0-based.*/ 49 int iRail; /* Which rail this check-in appears on. 0-based.*/ 50 int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */ 50 int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */ 51 int bDescender; /* Raiser from bottom of graph to here. */ 51 int bDescender; /* Raiser from bottom of graph to here. */ 52 u32 mergeIn; /* Merge in from other rails */ 52 u32 mergeIn; /* Merge in from other rails */ 53 int mergeOut; /* Merge out to this rail */ 53 int mergeOut; /* Merge out to this rail */ 54 int mergeUpto; /* Draw the merge rail up to this level */ 54 int mergeUpto; /* Draw the merge rail up to this level */ 55 55 ................................................................................................................................................................................ 61 struct GraphContext { 61 struct GraphContext { 62 int nErr; /* Number of errors encountered */ 62 int nErr; /* Number of errors encountered */ 63 int mxRail; /* Number of rails required to render the graph */ 63 int mxRail; /* Number of rails required to render the graph */ 64 GraphRow *pFirst; /* First row in the list */ 64 GraphRow *pFirst; /* First row in the list */ 65 GraphRow *pLast; /* Last row in the list */ 65 GraphRow *pLast; /* Last row in the list */ 66 int nBranch; /* Number of distinct branches */ 66 int nBranch; /* Number of distinct branches */ 67 char **azBranch; /* Names of the branches */ 67 char **azBranch; /* Names of the branches */ > 68 int nRow; /* Number of rows */ 68 int railMap[GR_MAX_RAIL]; /* Rail order mapping */ 69 int railMap[GR_MAX_RAIL]; /* Rail order mapping */ > 70 int nHash; /* Number of slots in apHash[] */ > 71 GraphRow **apHash; /* Hash table of rows */ 69 }; 72 }; 70 73 71 #endif 74 #endif 72 75 73 /* 76 /* 74 ** Malloc for zeroed space. Panic if unable to provide the 77 ** Malloc for zeroed space. Panic if unable to provide the 75 ** requested space. 78 ** requested space. ................................................................................................................................................................................ 97 while( p->pFirst ){ 100 while( p->pFirst ){ 98 pRow = p->pFirst; 101 pRow = p->pFirst; 99 p->pFirst = pRow->pNext; 102 p->pFirst = pRow->pNext; 100 free(pRow); 103 free(pRow); 101 } 104 } 102 for(i=0; i<p->nBranch; i++) free(p->azBranch[i]); 105 for(i=0; i<p->nBranch; i++) free(p->azBranch[i]); 103 free(p->azBranch); 106 free(p->azBranch); > 107 free(p->apHash); 104 free(p); 108 free(p); 105 } 109 } > 110 > 111 /* > 112 ** Insert a row into the hash table. If there is already another > 113 ** row with the same rid, the other row is replaced. > 114 */ > 115 static void hashInsert(GraphContext *p, GraphRow *pRow){ > 116 int h; > 117 h = pRow->rid % p->nHash; > 118 while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){ > 119 h++; > 120 if( h>=p->nHash ) h = 0; > 121 } > 122 p->apHash[h] = pRow; > 123 } > 124 > 125 /* > 126 ** Look up the row with rid. > 127 */ > 128 static GraphRow *hashFind(GraphContext *p, int rid){ > 129 int h = rid % p->nHash; > 130 while( p->apHash[h] && p->apHash[h]->rid!=rid ){ > 131 h++; > 132 if( h>=p->nHash ) h = 0; > 133 } > 134 return p->apHash[h]; > 135 } 106 136 107 /* 137 /* 108 ** Return the canonical pointer for a given branch name. 138 ** Return the canonical pointer for a given branch name. 109 ** Multiple calls to this routine with equivalent strings 139 ** Multiple calls to this routine with equivalent strings 110 ** will return the same pointer. 140 ** will return the same pointer. 111 */ 141 */ 112 static char *persistBranchName(GraphContext *p, const char *zBranch){ 142 static char *persistBranchName(GraphContext *p, const char *zBranch){ ................................................................................................................................................................................ 120 p->azBranch[p->nBranch-1] = mprintf("%s", zBranch); 150 p->azBranch[p->nBranch-1] = mprintf("%s", zBranch); 121 return p->azBranch[p->nBranch-1]; 151 return p->azBranch[p->nBranch-1]; 122 } 152 } 123 153 124 /* 154 /* 125 ** Add a new row t the graph context. Rows are added from top to bottom. 155 ** Add a new row t the graph context. Rows are added from top to bottom. 126 */ 156 */ 127 void graph_add_row( | 157 int graph_add_row( 128 GraphContext *p, /* The context to which the row is added */ 158 GraphContext *p, /* The context to which the row is added */ 129 int rid, /* RID for the check-in */ 159 int rid, /* RID for the check-in */ 130 int isLeaf, /* True if the check-in is an leaf */ < 131 int nParent, /* Number of parents */ 160 int nParent, /* Number of parents */ 132 int *aParent, /* Array of parents */ 161 int *aParent, /* Array of parents */ 133 const char *zBranch /* Branch for this check-in */ 162 const char *zBranch /* Branch for this check-in */ 134 ){ 163 ){ 135 GraphRow *pRow; 164 GraphRow *pRow; 136 165 137 if( p->nErr ) return; | 166 if( p->nErr ) return 0; 138 if( nParent>GR_MAX_PARENT ){ p->nErr++; return; } | 167 if( nParent>GR_MAX_PARENT ){ p->nErr++; return 0; } 139 pRow = (GraphRow*)safeMalloc( sizeof(GraphRow) ); 168 pRow = (GraphRow*)safeMalloc( sizeof(GraphRow) ); 140 pRow->rid = rid; 169 pRow->rid = rid; 141 pRow->isLeaf = isLeaf; < 142 pRow->nParent = nParent; 170 pRow->nParent = nParent; 143 pRow->zBranch = persistBranchName(p, zBranch); 171 pRow->zBranch = persistBranchName(p, zBranch); 144 memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent); 172 memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent); 145 if( p->pFirst==0 ){ 173 if( p->pFirst==0 ){ 146 p->pFirst = pRow; 174 p->pFirst = pRow; 147 }else{ 175 }else{ 148 p->pLast->pNext = pRow; 176 p->pLast->pNext = pRow; 149 } 177 } 150 p->pLast = pRow; 178 p->pLast = pRow; > 179 p->nRow++; > 180 pRow->idx = p->nRow; > 181 return pRow->idx; 151 } 182 } 152 183 153 /* 184 /* 154 ** Return the index of a rail currently not in use for any row between 185 ** Return the index of a rail currently not in use for any row between 155 ** top and bottom, inclusive. 186 ** top and bottom, inclusive. 156 */ 187 */ 157 static int findFreeRail( 188 static int findFreeRail( ................................................................................................................................................................................ 186 } 217 } 187 218 188 /* 219 /* 189 ** Compute the complete graph 220 ** Compute the complete graph 190 */ 221 */ 191 void graph_finish(GraphContext *p, int omitDescenders){ 222 void graph_finish(GraphContext *p, int omitDescenders){ 192 GraphRow *pRow, *pDesc; 223 GraphRow *pRow, *pDesc; 193 Bag allRids; < 194 Bag notLeaf; < 195 int i; 224 int i; 196 int nRow; < 197 u32 mask; 225 u32 mask; 198 u32 inUse; 226 u32 inUse; 199 227 200 if( p==0 || p->pFirst==0 || p->nErr ) return; 228 if( p==0 || p->pFirst==0 || p->nErr ) return; 201 229 202 /* Initialize all rows */ 230 /* Initialize all rows */ 203 bag_init(&allRids); | 231 p->nHash = p->nRow*2 + 1; 204 bag_init(&notLeaf); | 232 p->apHash = safeMalloc( sizeof(p->apHash[0])*p->nHash ); 205 nRow = 0; < 206 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ 233 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ 207 if( pRow->pNext ) pRow->pNext->pPrev = pRow; 234 if( pRow->pNext ) pRow->pNext->pPrev = pRow; 208 pRow->idx = ++nRow; < 209 pRow->iRail = -1; 235 pRow->iRail = -1; 210 pRow->mergeOut = -1; 236 pRow->mergeOut = -1; 211 bag_insert(&allRids, pRow->rid); | 237 hashInsert(p, pRow); 212 } 238 } 213 p->mxRail = -1; 239 p->mxRail = -1; 214 240 215 /* Purge merge-parents that are out-of-graph 241 /* Purge merge-parents that are out-of-graph 216 */ 242 */ 217 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ 243 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ 218 for(i=1; i<pRow->nParent; i++){ 244 for(i=1; i<pRow->nParent; i++){ 219 if( !bag_find(&allRids, pRow->aParent[i]) ){ | 245 if( hashFind(p, pRow->aParent[i])==0 ){ 220 pRow->aParent[i] = pRow->aParent[--pRow->nParent]; 246 pRow->aParent[i] = pRow->aParent[--pRow->nParent]; 221 i--; 247 i--; 222 } 248 } 223 } 249 } > 250 } > 251 > 252 /* Figure out which nodes have no direct children (children on > 253 ** the same rail). Mark such nodes is isLeaf. > 254 */ > 255 memset(p->apHash, 0, sizeof(p->apHash[0])*p->nHash); > 256 for(pRow=p->pLast; pRow; pRow=pRow->pPrev) pRow->isLeaf = 1; > 257 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){ > 258 GraphRow *pParent; > 259 hashInsert(p, pRow); 224 if( pRow->nParent>0 && bag_find(&allRids, pRow->aParent[0]) ){ | 260 if( pRow->nParent>0 225 bag_insert(&notLeaf, pRow->aParent[0]); | 261 && (pParent = hashFind(p, pRow->aParent[0]))!=0 > 262 && pRow->zBranch==pParent->zBranch > 263 ){ > 264 pParent->isLeaf = 0; 226 } 265 } 227 } 266 } 228 267 229 /* Identify rows where the primary parent is off screen. Assign 268 /* Identify rows where the primary parent is off screen. Assign 230 ** each to a rail and draw descenders to the bottom of the screen. 269 ** each to a rail and draw descenders to the bottom of the screen. 231 */ 270 */ 232 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ 271 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ 233 if( pRow->nParent==0 || !bag_find(&allRids,pRow->aParent[0]) ){ | 272 if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ 234 if( omitDescenders ){ 273 if( omitDescenders ){ 235 pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, 0, 0); 274 pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, 0, 0); 236 }else{ 275 }else{ 237 pRow->iRail = ++p->mxRail; 276 pRow->iRail = ++p->mxRail; 238 } 277 } 239 mask = 1<<(pRow->iRail); 278 mask = 1<<(pRow->iRail); 240 if( omitDescenders ){ 279 if( omitDescenders ){ ................................................................................................................................................................................ 255 */ 294 */ 256 inUse = (1<<(p->mxRail+1))-1; 295 inUse = (1<<(p->mxRail+1))-1; 257 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){ 296 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){ 258 int parentRid; 297 int parentRid; 259 if( pRow->iRail>=0 ) continue; 298 if( pRow->iRail>=0 ) continue; 260 assert( pRow->nParent>0 ); 299 assert( pRow->nParent>0 ); 261 parentRid = pRow->aParent[0]; 300 parentRid = pRow->aParent[0]; 262 assert( bag_find(&allRids, parentRid) ); < 263 for(pDesc=pRow->pNext; pDesc && pDesc->rid!=parentRid; pDesc=pDesc->pNext){} 301 for(pDesc=pRow->pNext; pDesc && pDesc->rid!=parentRid; pDesc=pDesc->pNext){} 264 if( pDesc==0 ){ 302 if( pDesc==0 ){ 265 /* Time skew */ 303 /* Time skew */ 266 pRow->iRail = ++p->mxRail; 304 pRow->iRail = ++p->mxRail; 267 pRow->railInUse = 1<<pRow->iRail; 305 pRow->railInUse = 1<<pRow->iRail; 268 continue; 306 continue; 269 } 307 } ................................................................................................................................................................................ 270 if( pDesc->aiRaiser[pDesc->iRail]==0 && pDesc->zBranch==pRow->zBranch ){ 308 if( pDesc->aiRaiser[pDesc->iRail]==0 && pDesc->zBranch==pRow->zBranch ){ 271 pRow->iRail = pDesc->iRail; 309 pRow->iRail = pDesc->iRail; 272 }else{ 310 }else{ 273 pRow->iRail = findFreeRail(p, 0, pDesc->idx, inUse, 0); 311 pRow->iRail = findFreeRail(p, 0, pDesc->idx, inUse, 0); 274 } 312 } 275 pDesc->aiRaiser[pRow->iRail] = pRow->idx; 313 pDesc->aiRaiser[pRow->iRail] = pRow->idx; 276 mask = 1<<pRow->iRail; 314 mask = 1<<pRow->iRail; 277 if( bag_find(&notLeaf, pRow->rid) ){ | 315 if( pRow->isLeaf ){ 278 inUse |= mask; | 316 inUse &= ~mask; 279 }else{ 317 }else{ 280 inUse &= ~mask; | 318 inUse |= mask; 281 } 319 } 282 for(pDesc = pRow; ; pDesc=pDesc->pNext){ 320 for(pDesc = pRow; ; pDesc=pDesc->pNext){ 283 assert( pDesc!=0 ); 321 assert( pDesc!=0 ); 284 pDesc->railInUse |= mask; 322 pDesc->railInUse |= mask; 285 if( pDesc->rid==parentRid ) break; 323 if( pDesc->rid==parentRid ) break; 286 } 324 } 287 } 325 } ................................................................................................................................................................................ 307 } 345 } 308 } 346 } 309 pRow->mergeIn |= 1<<pDesc->mergeOut; 347 pRow->mergeIn |= 1<<pDesc->mergeOut; 310 } 348 } 311 } 349 } 312 350 313 /* 351 /* 314 ** Sort the rail numbers | 352 ** Find the maximum rail number. 315 */ 353 */ 316 #if 0 < 317 p->mxRail = -1; < 318 mask = 0; < 319 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){ < 320 if( (mask & (1<<pRow->iRail))==0 ){ < 321 p->railMap[pRow->iRail] = ++p->mxRail; < 322 mask |= 1<<pRow->iRail; < 323 } < 324 if( pRow->mergeOut>=0 && (mask & (1<<pRow->mergeOut))==0 ){ < 325 p->railMap[pRow->mergeOut] = ++p->mxRail; < 326 mask |= 1<<pRow->mergeOut; < 327 } < 328 } < 329 #else < 330 for(i=0; i<GR_MAX_RAIL; i++) p->railMap[i] = i; 354 for(i=0; i<GR_MAX_RAIL; i++) p->railMap[i] = i; 331 p->mxRail = 0; 355 p->mxRail = 0; 332 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ 356 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ 333 if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; 357 if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; 334 if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut; 358 if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut; 335 } 359 } 336 #endif < 337 } 360 }

Changes to src/info.c

730 if( pDownloadName && blob_size(pDownloadName)==0 ){ 730 if( pDownloadName && blob_size(pDownloadName)==0 ){ 731 blob_append(pDownloadName, zUuid, -1); 731 blob_append(pDownloadName, zUuid, -1); 732 } 732 } 733 cnt++; 733 cnt++; 734 } 734 } 735 db_finalize(&q); 735 db_finalize(&q); 736 } 736 } > 737 db_prepare(&q, > 738 "SELECT target, filename, datetime(mtime), user, src" > 739 " FROM attachment" > 740 " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)" > 741 " ORDER BY mtime DESC", > 742 rid > 743 ); > 744 while( db_step(&q)==SQLITE_ROW ){ > 745 const char *zTarget = db_column_text(&q, 0); > 746 const char *zFilename = db_column_text(&q, 1); > 747 const char *zDate = db_column_text(&q, 2); > 748 const char *zUser = db_column_text(&q, 3); > 749 const char *zSrc = db_column_text(&q, 4); > 750 if( cnt>0 ){ > 751 @ Also attachment "%h(zFilename)" to > 752 }else{ > 753 @ Attachment "%h(zFilename)" to > 754 } > 755 if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){ > 756 char zShort[20]; > 757 memcpy(zShort, zTarget, 10); > 758 if( g.okHistory && g.okRdTkt ){ > 759 @ ticket [<a href="%s(g.zTop)/tktview?name=%s(zShort)">%s(zShort)</a>] > 760 }else{ > 761 @ ticket [%s(zShort)] > 762 } > 763 }else{ > 764 if( g.okHistory && g.okRdWiki ){ > 765 @ wiki page [<a href="%s(g.zTop)/wiki?name=%t(zTarget)">%h(zTarget)</a>] > 766 }else{ > 767 @ wiki page [%h(zTarget)] > 768 } > 769 } > 770 @ added by > 771 hyperlink_to_user(zUser,zDate," on"); > 772 hyperlink_to_date(zDate,"."); > 773 cnt++; > 774 if( pDownloadName && blob_size(pDownloadName)==0 ){ > 775 blob_append(pDownloadName, zSrc, -1); > 776 } > 777 } > 778 db_finalize(&q); 737 if( cnt==0 ){ 779 if( cnt==0 ){ 738 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); 780 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); 739 @ Control artifact. 781 @ Control artifact. 740 if( pDownloadName && blob_size(pDownloadName)==0 ){ 782 if( pDownloadName && blob_size(pDownloadName)==0 ){ 741 blob_append(pDownloadName, zUuid, -1); 783 blob_append(pDownloadName, zUuid, -1); 742 } 784 } 743 }else if( linkToView && g.okHistory ){ 785 }else if( linkToView && g.okHistory ){

Changes to src/login.c

474 for(i=0; zCap[i]; i++){ 474 for(i=0; zCap[i]; i++){ 475 switch( zCap[i] ){ 475 switch( zCap[i] ){ 476 case 's': g.okSetup = 1; /* Fall thru into Admin */ 476 case 's': g.okSetup = 1; /* Fall thru into Admin */ 477 case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip = 477 case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip = 478 g.okRdWiki = g.okWrWiki = g.okNewWiki = 478 g.okRdWiki = g.okWrWiki = g.okNewWiki = 479 g.okApndWiki = g.okHistory = g.okClone = 479 g.okApndWiki = g.okHistory = g.okClone = 480 g.okNewTkt = g.okPassword = g.okRdAddr = 480 g.okNewTkt = g.okPassword = g.okRdAddr = > 481 g.okTktFmt = g.okAttach = 1; 481 g.okTktFmt = 1; /* Fall thru into Read/Write */ | 482 /* Fall thru into Read/Write */ 482 case 'i': g.okRead = g.okWrite = 1; break; 483 case 'i': g.okRead = g.okWrite = 1; break; 483 case 'o': g.okRead = 1; break; 484 case 'o': g.okRead = 1; break; 484 case 'z': g.okZip = 1; break; 485 case 'z': g.okZip = 1; break; 485 486 486 case 'd': g.okDelete = 1; break; 487 case 'd': g.okDelete = 1; break; 487 case 'h': g.okHistory = 1; break; 488 case 'h': g.okHistory = 1; break; 488 case 'g': g.okClone = 1; break; 489 case 'g': g.okClone = 1; break; ................................................................................................................................................................................ 496 case 'e': g.okRdAddr = 1; break; 497 case 'e': g.okRdAddr = 1; break; 497 case 'r': g.okRdTkt = 1; break; 498 case 'r': g.okRdTkt = 1; break; 498 case 'n': g.okNewTkt = 1; break; 499 case 'n': g.okNewTkt = 1; break; 499 case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt = 500 case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt = 500 g.okApndTkt = 1; break; 501 g.okApndTkt = 1; break; 501 case 'c': g.okApndTkt = 1; break; 502 case 'c': g.okApndTkt = 1; break; 502 case 't': g.okTktFmt = 1; break; 503 case 't': g.okTktFmt = 1; break; > 504 case 'b': g.okAttach = 1; break; 503 505 504 /* The "u" privileges is a little different. It recursively 506 /* The "u" privileges is a little different. It recursively 505 ** inherits all privileges of the user named "reader" */ 507 ** inherits all privileges of the user named "reader" */ 506 case 'u': { 508 case 'u': { 507 if( zUser==0 ){ 509 if( zUser==0 ){ 508 zUser = db_text("", "SELECT cap FROM user WHERE login='reader'"); 510 zUser = db_text("", "SELECT cap FROM user WHERE login='reader'"); 509 login_set_capabilities(zUser); 511 login_set_capabilities(zUser); ................................................................................................................................................................................ 532 int login_has_capability(const char *zCap, int nCap){ 534 int login_has_capability(const char *zCap, int nCap){ 533 int i; 535 int i; 534 int rc = 1; 536 int rc = 1; 535 if( nCap<0 ) nCap = strlen(zCap); 537 if( nCap<0 ) nCap = strlen(zCap); 536 for(i=0; i<nCap && rc && zCap[i]; i++){ 538 for(i=0; i<nCap && rc && zCap[i]; i++){ 537 switch( zCap[i] ){ 539 switch( zCap[i] ){ 538 case 'a': rc = g.okAdmin; break; 540 case 'a': rc = g.okAdmin; break; 539 /* case 'b': */ | 541 case 'b': rc = g.okAttach; break; 540 case 'c': rc = g.okApndTkt; break; 542 case 'c': rc = g.okApndTkt; break; 541 case 'd': rc = g.okDelete; break; 543 case 'd': rc = g.okDelete; break; 542 case 'e': rc = g.okRdAddr; break; 544 case 'e': rc = g.okRdAddr; break; 543 case 'f': rc = g.okNewWiki; break; 545 case 'f': rc = g.okNewWiki; break; 544 case 'g': rc = g.okClone; break; 546 case 'g': rc = g.okClone; break; 545 case 'h': rc = g.okHistory; break; 547 case 'h': rc = g.okHistory; break; 546 case 'i': rc = g.okWrite; break; 548 case 'i': rc = g.okWrite; break;

Changes to src/main.c

128 int okNewWiki; /* f: create new wiki via web */ 128 int okNewWiki; /* f: create new wiki via web */ 129 int okApndWiki; /* m: append to wiki via web */ 129 int okApndWiki; /* m: append to wiki via web */ 130 int okWrWiki; /* k: edit wiki via web */ 130 int okWrWiki; /* k: edit wiki via web */ 131 int okRdTkt; /* r: view tickets via web */ 131 int okRdTkt; /* r: view tickets via web */ 132 int okNewTkt; /* n: create new tickets */ 132 int okNewTkt; /* n: create new tickets */ 133 int okApndTkt; /* c: append to tickets via the web */ 133 int okApndTkt; /* c: append to tickets via the web */ 134 int okWrTkt; /* w: make changes to tickets via web */ 134 int okWrTkt; /* w: make changes to tickets via web */ > 135 int okAttach; /* b: add attachments */ 135 int okTktFmt; /* t: create new ticket report formats */ 136 int okTktFmt; /* t: create new ticket report formats */ 136 int okRdAddr; /* e: read email addresses or other private data */ 137 int okRdAddr; /* e: read email addresses or other private data */ 137 int okZip; /* z: download zipped artifact via /zip URL */ 138 int okZip; /* z: download zipped artifact via /zip URL */ 138 139 139 /* For defense against Cross-site Request Forgery attacks */ 140 /* For defense against Cross-site Request Forgery attacks */ 140 char zCsrfToken[12]; /* Value of the anti-CSRF token */ 141 char zCsrfToken[12]; /* Value of the anti-CSRF token */ 141 int okCsrf; /* Anti-CSRF token is present and valid */ 142 int okCsrf; /* Anti-CSRF token is present and valid */

Changes to src/main.mk

11 11 12 XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) 12 XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) 13 13 14 14 15 SRC = \ 15 SRC = \ 16 $(SRCDIR)/add.c \ 16 $(SRCDIR)/add.c \ 17 $(SRCDIR)/allrepo.c \ 17 $(SRCDIR)/allrepo.c \ > 18 $(SRCDIR)/attach.c \ 18 $(SRCDIR)/bag.c \ 19 $(SRCDIR)/bag.c \ 19 $(SRCDIR)/blob.c \ 20 $(SRCDIR)/blob.c \ 20 $(SRCDIR)/branch.c \ 21 $(SRCDIR)/branch.c \ 21 $(SRCDIR)/browse.c \ 22 $(SRCDIR)/browse.c \ 22 $(SRCDIR)/captcha.c \ 23 $(SRCDIR)/captcha.c \ 23 $(SRCDIR)/cgi.c \ 24 $(SRCDIR)/cgi.c \ 24 $(SRCDIR)/checkin.c \ 25 $(SRCDIR)/checkin.c \ ................................................................................................................................................................................ 81 $(SRCDIR)/winhttp.c \ 82 $(SRCDIR)/winhttp.c \ 82 $(SRCDIR)/xfer.c \ 83 $(SRCDIR)/xfer.c \ 83 $(SRCDIR)/zip.c 84 $(SRCDIR)/zip.c 84 85 85 TRANS_SRC = \ 86 TRANS_SRC = \ 86 add_.c \ 87 add_.c \ 87 allrepo_.c \ 88 allrepo_.c \ > 89 attach_.c \ 88 bag_.c \ 90 bag_.c \ 89 blob_.c \ 91 blob_.c \ 90 branch_.c \ 92 branch_.c \ 91 browse_.c \ 93 browse_.c \ 92 captcha_.c \ 94 captcha_.c \ 93 cgi_.c \ 95 cgi_.c \ 94 checkin_.c \ 96 checkin_.c \ ................................................................................................................................................................................ 151 winhttp_.c \ 153 winhttp_.c \ 152 xfer_.c \ 154 xfer_.c \ 153 zip_.c 155 zip_.c 154 156 155 OBJ = \ 157 OBJ = \ 156 $(OBJDIR)/add.o \ 158 $(OBJDIR)/add.o \ 157 $(OBJDIR)/allrepo.o \ 159 $(OBJDIR)/allrepo.o \ > 160 $(OBJDIR)/attach.o \ 158 $(OBJDIR)/bag.o \ 161 $(OBJDIR)/bag.o \ 159 $(OBJDIR)/blob.o \ 162 $(OBJDIR)/blob.o \ 160 $(OBJDIR)/branch.o \ 163 $(OBJDIR)/branch.o \ 161 $(OBJDIR)/browse.o \ 164 $(OBJDIR)/browse.o \ 162 $(OBJDIR)/captcha.o \ 165 $(OBJDIR)/captcha.o \ 163 $(OBJDIR)/cgi.o \ 166 $(OBJDIR)/cgi.o \ 164 $(OBJDIR)/checkin.o \ 167 $(OBJDIR)/checkin.o \ ................................................................................................................................................................................ 262 # 265 # 263 $(SRCDIR)/../manifest: 266 $(SRCDIR)/../manifest: 264 # noop 267 # noop 265 268 266 clean: 269 clean: 267 rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h 270 rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h 268 rm -f translate makeheaders mkindex page_index.h headers 271 rm -f translate makeheaders mkindex page_index.h headers 269 rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h che | 272 rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h 270 273 271 page_index.h: $(TRANS_SRC) mkindex 274 page_index.h: $(TRANS_SRC) mkindex 272 ./mkindex $(TRANS_SRC) >$@ 275 ./mkindex $(TRANS_SRC) >$@ 273 headers: page_index.h makeheaders VERSION.h 276 headers: page_index.h makeheaders VERSION.h 274 ./makeheaders add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:bl | 277 ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_ 275 touch headers 278 touch headers 276 headers: Makefile 279 headers: Makefile 277 Makefile: 280 Makefile: 278 add_.c: $(SRCDIR)/add.c translate 281 add_.c: $(SRCDIR)/add.c translate 279 ./translate $(SRCDIR)/add.c >add_.c 282 ./translate $(SRCDIR)/add.c >add_.c 280 283 281 $(OBJDIR)/add.o: add_.c add.h $(SRCDIR)/config.h 284 $(OBJDIR)/add.o: add_.c add.h $(SRCDIR)/config.h ................................................................................................................................................................................ 285 allrepo_.c: $(SRCDIR)/allrepo.c translate 288 allrepo_.c: $(SRCDIR)/allrepo.c translate 286 ./translate $(SRCDIR)/allrepo.c >allrepo_.c 289 ./translate $(SRCDIR)/allrepo.c >allrepo_.c 287 290 288 $(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h 291 $(OBJDIR)/allrepo.o: allrepo_.c allrepo.h $(SRCDIR)/config.h 289 $(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c 292 $(XTCC) -o $(OBJDIR)/allrepo.o -c allrepo_.c 290 293 291 allrepo.h: headers 294 allrepo.h: headers > 295 attach_.c: $(SRCDIR)/attach.c translate > 296 ./translate $(SRCDIR)/attach.c >attach_.c > 297 > 298 $(OBJDIR)/attach.o: attach_.c attach.h $(SRCDIR)/config.h > 299 $(XTCC) -o $(OBJDIR)/attach.o -c attach_.c > 300 > 301 attach.h: headers 292 bag_.c: $(SRCDIR)/bag.c translate 302 bag_.c: $(SRCDIR)/bag.c translate 293 ./translate $(SRCDIR)/bag.c >bag_.c 303 ./translate $(SRCDIR)/bag.c >bag_.c 294 304 295 $(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h 305 $(OBJDIR)/bag.o: bag_.c bag.h $(SRCDIR)/config.h 296 $(XTCC) -o $(OBJDIR)/bag.o -c bag_.c 306 $(XTCC) -o $(OBJDIR)/bag.o -c bag_.c 297 307 298 bag.h: headers 308 bag.h: headers

Changes to src/makemake.tcl

5 5 6 # Basenames of all source files that get preprocessed using 6 # Basenames of all source files that get preprocessed using 7 # "translate" and "makeheaders" 7 # "translate" and "makeheaders" 8 # 8 # 9 set src { 9 set src { 10 add 10 add 11 allrepo 11 allrepo > 12 attach 12 bag 13 bag 13 blob 14 blob 14 branch 15 branch 15 browse 16 browse 16 captcha 17 captcha 17 cgi 18 cgi 18 checkin 19 checkin

Changes to src/manifest.c

35 ** Types of control files 35 ** Types of control files 36 */ 36 */ 37 #define CFTYPE_MANIFEST 1 37 #define CFTYPE_MANIFEST 1 38 #define CFTYPE_CLUSTER 2 38 #define CFTYPE_CLUSTER 2 39 #define CFTYPE_CONTROL 3 39 #define CFTYPE_CONTROL 3 40 #define CFTYPE_WIKI 4 40 #define CFTYPE_WIKI 4 41 #define CFTYPE_TICKET 5 41 #define CFTYPE_TICKET 5 42 < 43 /* < 44 ** Mode parameter values < 45 */ < 46 #define CFMODE_READ 1 | 42 #define CFTYPE_ATTACHMENT 6 47 #define CFMODE_APPEND 2 < 48 #define CFMODE_WRITE 3 < 49 43 50 /* 44 /* 51 ** A parsed manifest or cluster. 45 ** A parsed manifest or cluster. 52 */ 46 */ 53 struct Manifest { 47 struct Manifest { 54 Blob content; /* The original content blob */ 48 Blob content; /* The original content blob */ 55 int type; /* Type of file */ | 49 int type; /* Type of artifact. One of CFTYPE_xxxxx */ 56 int mode; /* Access mode */ < 57 char *zComment; /* Decoded comment */ | 50 char *zComment; /* Decoded comment. The C card. */ 58 double rDate; /* Time in the "D" line */ | 51 double rDate; /* Date and time from D card. 0.0 if no D card. */ 59 char *zUser; /* Name of the user */ | 52 char *zUser; /* Name of the user from the U card. */ 60 char *zRepoCksum; /* MD5 checksum of the baseline content */ | 53 char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ 61 char *zWiki; /* Text of the wiki page */ | 54 char *zWiki; /* Text of the wiki page. W card. */ 62 char *zWikiTitle; /* Name of the wiki page */ | 55 char *zWikiTitle; /* Name of the wiki page. L card. */ 63 char *zTicketUuid; /* UUID for a ticket */ | 56 char *zTicketUuid; /* UUID for a ticket. K card. */ > 57 char *zAttachName; /* Filename of an attachment. A card. */ > 58 char *zAttachSrc; /* UUID of document being attached. A card. */ > 59 char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ 64 int nFile; /* Number of F lines */ | 60 int nFile; /* Number of F cards */ 65 int nFileAlloc; /* Slots allocated in aFile[] */ 61 int nFileAlloc; /* Slots allocated in aFile[] */ 66 struct { 62 struct { 67 char *zName; /* Name of a file */ 63 char *zName; /* Name of a file */ 68 char *zUuid; /* UUID of the file */ 64 char *zUuid; /* UUID of the file */ 69 char *zPerm; /* File permissions */ 65 char *zPerm; /* File permissions */ 70 char *zPrior; /* Prior name if the name was changed */ 66 char *zPrior; /* Prior name if the name was changed */ 71 int iRename; /* index of renamed name in prior/next manifest */ 67 int iRename; /* index of renamed name in prior/next manifest */ 72 } *aFile; | 68 } *aFile; /* One entry for each F card */ 73 int nParent; /* Number of parents */ | 69 int nParent; /* Number of parents. */ 74 int nParentAlloc; /* Slots allocated in azParent[] */ 70 int nParentAlloc; /* Slots allocated in azParent[] */ 75 char **azParent; /* UUIDs of parents */ | 71 char **azParent; /* UUIDs of parents. One for each P card argument */ 76 int nCChild; /* Number of cluster children */ 72 int nCChild; /* Number of cluster children */ 77 int nCChildAlloc; /* Number of closts allocated in azCChild[] */ 73 int nCChildAlloc; /* Number of closts allocated in azCChild[] */ 78 char **azCChild; /* UUIDs of referenced objects in a cluster */ | 74 char **azCChild; /* UUIDs of referenced objects in a cluster. M cards */ 79 int nTag; /* Number of T lines */ | 75 int nTag; /* Number of T Cards */ 80 int nTagAlloc; /* Slots allocated in aTag[] */ 76 int nTagAlloc; /* Slots allocated in aTag[] */ 81 struct { 77 struct { 82 char *zName; /* Name of the tag */ 78 char *zName; /* Name of the tag */ 83 char *zUuid; /* UUID that the tag is applied to */ 79 char *zUuid; /* UUID that the tag is applied to */ 84 char *zValue; /* Value if the tag is really a property */ 80 char *zValue; /* Value if the tag is really a property */ 85 } *aTag; | 81 } *aTag; /* One for each T card */ 86 int nField; /* Number of J lines */ | 82 int nField; /* Number of J cards */ 87 int nFieldAlloc; /* Slots allocated in aField[] */ 83 int nFieldAlloc; /* Slots allocated in aField[] */ 88 struct { 84 struct { 89 char *zName; /* Key or field name */ 85 char *zName; /* Key or field name */ 90 char *zValue; /* Value of the field */ 86 char *zValue; /* Value of the field */ 91 } *aField; | 87 } *aField; /* One for each J card */ 92 int nAttach; /* Number of A lines */ < 93 int nAttachAlloc; /* Slots allocated in aAttach[] */ < 94 struct { < 95 char *zUuid; /* UUID of the attachment */ < 96 char *zName; /* Name of the attachment */ < 97 char *zDesc; /* Description of the attachment */ < 98 } *aAttach; < 99 }; 88 }; 100 #endif 89 #endif 101 90 102 91 103 /* 92 /* 104 ** Clear the memory allocated in a manifest object 93 ** Clear the memory allocated in a manifest object 105 */ 94 */ ................................................................................................................................................................................ 106 void manifest_clear(Manifest *p){ 95 void manifest_clear(Manifest *p){ 107 blob_reset(&p->content); 96 blob_reset(&p->content); 108 free(p->aFile); 97 free(p->aFile); 109 free(p->azParent); 98 free(p->azParent); 110 free(p->azCChild); 99 free(p->azCChild); 111 free(p->aTag); 100 free(p->aTag); 112 free(p->aField); 101 free(p->aField); 113 free(p->aAttach); < 114 memset(p, 0, sizeof(*p)); 102 memset(p, 0, sizeof(*p)); 115 } 103 } 116 104 117 /* 105 /* 118 ** Parse a blob into a Manifest object. The Manifest object 106 ** Parse a blob into a Manifest object. The Manifest object 119 ** takes over the input blob and will free it when the 107 ** takes over the input blob and will free it when the 120 ** Manifest object is freed. Zeros are inserted into the blob 108 ** Manifest object is freed. Zeros are inserted into the blob ................................................................................................................................................................................ 176 goto manifest_syntax_error; 164 goto manifest_syntax_error; 177 } 165 } 178 cPrevType = z[0]; 166 cPrevType = z[0]; 179 seenHeader = 1; 167 seenHeader = 1; 180 if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error; 168 if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error; 181 switch( z[0] ){ 169 switch( z[0] ){ 182 /* 170 /* 183 ** A <uuid> <filename> <description> | 171 ** A <filename> <target> ?<source>? 184 ** 172 ** 185 ** Identifies an attachment to either a wiki page or a ticket. 173 ** Identifies an attachment to either a wiki page or a ticket. 186 ** <uuid> is the artifact that is the attachment. | 174 ** <source> is the artifact that is the attachment. <source> > 175 ** is omitted to delete an attachment. <target> is the name of > 176 ** a wiki page or ticket to which that attachment is connected. 187 */ 177 */ 188 case 'A': { 178 case 'A': { 189 char *zName, *zUuid, *zDesc; | 179 char *zName, *zTarget, *zSrc; 190 md5sum_step_text(blob_buffer(&line), blob_size(&line)); 180 md5sum_step_text(blob_buffer(&line), blob_size(&line)); 191 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; 181 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; 192 if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error; 182 if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error; 193 if( blob_token(&line, &a3)==0 ) goto manifest_syntax_error; | 183 if( p->zAttachName!=0 ) goto manifest_syntax_error; 194 zUuid = blob_terminate(&a1); | 184 zName = blob_terminate(&a1); 195 zName = blob_terminate(&a2); | 185 zTarget = blob_terminate(&a2); > 186 blob_token(&line, &a3); 196 zDesc = blob_terminate(&a3); | 187 zSrc = blob_terminate(&a3); 197 if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error; < 198 if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; < 199 defossilize(zName); 188 defossilize(zName); 200 if( !file_is_simple_pathname(zName) ){ 189 if( !file_is_simple_pathname(zName) ){ 201 goto manifest_syntax_error; 190 goto manifest_syntax_error; 202 } 191 } 203 defossilize(zDesc); | 192 defossilize(zTarget); 204 if( p->nAttach>=p->nAttachAlloc ){ | 193 if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) 205 p->nAttachAlloc = p->nAttachAlloc*2 + 10; | 194 && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ 206 p->aAttach = realloc(p->aAttach, < 207 p->nAttachAlloc*sizeof(p->aAttach[0]) ); < 208 if( p->aAttach==0 ) fossil_panic("out of memory"); < 209 } < 210 i = p->nAttach++; < 211 p->aAttach[i].zUuid = zUuid; < 212 p->aAttach[i].zName = zName; < 213 p->aAttach[i].zDesc = zDesc; < 214 if( i>0 && strcmp(p->aAttach[i-1].zUuid, zUuid)>=0 ){ < 215 goto manifest_syntax_error; 195 goto manifest_syntax_error; 216 } 196 } > 197 if( blob_size(&a3)>0 > 198 && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){ > 199 goto manifest_syntax_error; > 200 } > 201 p->zAttachName = (char*)file_tail(zName); > 202 p->zAttachSrc = zSrc; > 203 p->zAttachTarget = zTarget; 217 break; 204 break; 218 } 205 } 219 206 220 /* 207 /* 221 ** C <comment> 208 ** C <comment> 222 ** 209 ** 223 ** Comment text is fossil-encoded. There may be no more than 210 ** Comment text is fossil-encoded. There may be no more than ................................................................................................................................................................................ 245 char *zDate; 232 char *zDate; 246 md5sum_step_text(blob_buffer(&line), blob_size(&line)); 233 md5sum_step_text(blob_buffer(&line), blob_size(&line)); 247 if( p->rDate!=0.0 ) goto manifest_syntax_error; 234 if( p->rDate!=0.0 ) goto manifest_syntax_error; 248 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; 235 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; 249 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; 236 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; 250 zDate = blob_terminate(&a1); 237 zDate = blob_terminate(&a1); 251 p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate); 238 p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate); 252 break; < 253 } < 254 < 255 /* < 256 ** E <mode> < 257 ** < 258 ** Access mode. <mode> can be one of "read", "append", < 259 ** or "write". < 260 */ < 261 case 'E': { < 262 md5sum_step_text(blob_buffer(&line), blob_size(&line)); < 263 if( p->mode!=0 ) goto manifest_syntax_error; < 264 if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; < 265 if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; < 266 if( blob_eq(&a1, "write") ){ < 267 p->mode = CFMODE_WRITE; < 268 }else if( blob_eq(&a1, "append") ){ < 269 p->mode = CFMODE_APPEND; < 270 }else if( blob_eq(&a1, "read") ){ < 271 p->mode = CFMODE_READ; < 272 }else{ < 273 goto manifest_syntax_error; < 274 } < 275 break; 239 break; 276 } 240 } 277 241 278 /* 242 /* 279 ** F <filename> <uuid> ?<permissions>? ?<old-name>? 243 ** F <filename> <uuid> ?<permissions>? ?<old-name>? 280 ** 244 ** 281 ** Identifies a file in a manifest. Multiple F lines are 245 ** Identifies a file in a manifest. Multiple F lines are ................................................................................................................................................................................ 606 if( !seenHeader ) goto manifest_syntax_error; 570 if( !seenHeader ) goto manifest_syntax_error; 607 571 608 if( p->nFile>0 || p->zRepoCksum!=0 ){ 572 if( p->nFile>0 || p->zRepoCksum!=0 ){ 609 if( p->nCChild>0 ) goto manifest_syntax_error; 573 if( p->nCChild>0 ) goto manifest_syntax_error; 610 if( p->rDate==0.0 ) goto manifest_syntax_error; 574 if( p->rDate==0.0 ) goto manifest_syntax_error; 611 if( p->nField>0 ) goto manifest_syntax_error; 575 if( p->nField>0 ) goto manifest_syntax_error; 612 if( p->zTicketUuid ) goto manifest_syntax_error; 576 if( p->zTicketUuid ) goto manifest_syntax_error; 613 if( p->nAttach>0 ) goto manifest_syntax_error; < 614 if( p->zWiki ) goto manifest_syntax_error; 577 if( p->zWiki ) goto manifest_syntax_error; 615 if( p->zWikiTitle ) goto manifest_syntax_error; 578 if( p->zWikiTitle ) goto manifest_syntax_error; 616 if( p->zTicketUuid ) goto manifest_syntax_error; 579 if( p->zTicketUuid ) goto manifest_syntax_error; > 580 if( p->zAttachName ) goto manifest_syntax_error; 617 p->type = CFTYPE_MANIFEST; 581 p->type = CFTYPE_MANIFEST; 618 }else if( p->nCChild>0 ){ 582 }else if( p->nCChild>0 ){ 619 if( p->rDate>0.0 ) goto manifest_syntax_error; 583 if( p->rDate>0.0 ) goto manifest_syntax_error; 620 if( p->zComment!=0 ) goto manifest_syntax_error; 584 if( p->zComment!=0 ) goto manifest_syntax_error; 621 if( p->zUser!=0 ) goto manifest_syntax_error; 585 if( p->zUser!=0 ) goto manifest_syntax_error; 622 if( p->nTag>0 ) goto manifest_syntax_error; 586 if( p->nTag>0 ) goto manifest_syntax_error; 623 if( p->nParent>0 ) goto manifest_syntax_error; 587 if( p->nParent>0 ) goto manifest_syntax_error; 624 if( p->zRepoCksum!=0 ) goto manifest_syntax_error; < 625 if( p->nField>0 ) goto manifest_syntax_error; 588 if( p->nField>0 ) goto manifest_syntax_error; 626 if( p->zTicketUuid ) goto manifest_syntax_error; 589 if( p->zTicketUuid ) goto manifest_syntax_error; 627 if( p->nAttach>0 ) goto manifest_syntax_error; < 628 if( p->zWiki ) goto manifest_syntax_error; 590 if( p->zWiki ) goto manifest_syntax_error; 629 if( p->zWikiTitle ) goto manifest_syntax_error; 591 if( p->zWikiTitle ) goto manifest_syntax_error; > 592 if( p->zAttachName ) goto manifest_syntax_error; 630 if( !seenZ ) goto manifest_syntax_error; 593 if( !seenZ ) goto manifest_syntax_error; 631 p->type = CFTYPE_CLUSTER; 594 p->type = CFTYPE_CLUSTER; 632 }else if( p->nField>0 ){ 595 }else if( p->nField>0 ){ 633 if( p->rDate==0.0 ) goto manifest_syntax_error; 596 if( p->rDate==0.0 ) goto manifest_syntax_error; 634 if( p->zRepoCksum!=0 ) goto manifest_syntax_error; < 635 if( p->zWiki ) goto manifest_syntax_error; 597 if( p->zWiki ) goto manifest_syntax_error; 636 if( p->zWikiTitle ) goto manifest_syntax_error; 598 if( p->zWikiTitle ) goto manifest_syntax_error; 637 if( p->nCChild>0 ) goto manifest_syntax_error; 599 if( p->nCChild>0 ) goto manifest_syntax_error; 638 if( p->nTag>0 ) goto manifest_syntax_error; 600 if( p->nTag>0 ) goto manifest_syntax_error; 639 if( p->zTicketUuid==0 ) goto manifest_syntax_error; 601 if( p->zTicketUuid==0 ) goto manifest_syntax_error; 640 if( p->zUser==0 ) goto manifest_syntax_error; 602 if( p->zUser==0 ) goto manifest_syntax_error; > 603 if( p->zAttachName ) goto manifest_syntax_error; 641 if( !seenZ ) goto manifest_syntax_error; 604 if( !seenZ ) goto manifest_syntax_error; 642 p->type = CFTYPE_TICKET; 605 p->type = CFTYPE_TICKET; 643 }else if( p->zWiki!=0 ){ 606 }else if( p->zWiki!=0 ){ 644 if( p->rDate==0.0 ) goto manifest_syntax_error; 607 if( p->rDate==0.0 ) goto manifest_syntax_error; 645 if( p->zRepoCksum!=0 ) goto manifest_syntax_error; < 646 if( p->nCChild>0 ) goto manifest_syntax_error; 608 if( p->nCChild>0 ) goto manifest_syntax_error; 647 if( p->nTag>0 ) goto manifest_syntax_error; 609 if( p->nTag>0 ) goto manifest_syntax_error; 648 if( p->zTicketUuid!=0 ) goto manifest_syntax_error; 610 if( p->zTicketUuid!=0 ) goto manifest_syntax_error; 649 if( p->zWikiTitle==0 ) goto manifest_syntax_error; 611 if( p->zWikiTitle==0 ) goto manifest_syntax_error; > 612 if( p->zAttachName ) goto manifest_syntax_error; 650 if( !seenZ ) goto manifest_syntax_error; 613 if( !seenZ ) goto manifest_syntax_error; 651 p->type = CFTYPE_WIKI; 614 p->type = CFTYPE_WIKI; 652 }else if( p->nTag>0 ){ 615 }else if( p->nTag>0 ){ 653 if( p->rDate<=0.0 ) goto manifest_syntax_error; 616 if( p->rDate<=0.0 ) goto manifest_syntax_error; 654 if( p->zRepoCksum!=0 ) goto manifest_syntax_error; < 655 if( p->nParent>0 ) goto manifest_syntax_error; 617 if( p->nParent>0 ) goto manifest_syntax_error; 656 if( p->nAttach>0 ) goto manifest_syntax_error; < 657 if( p->nField>0 ) goto manifest_syntax_error; < 658 if( p->zWiki ) goto manifest_syntax_error; < 659 if( p->zWikiTitle ) goto manifest_syntax_error; 618 if( p->zWikiTitle ) goto manifest_syntax_error; 660 if( p->zTicketUuid ) goto manifest_syntax_error; 619 if( p->zTicketUuid ) goto manifest_syntax_error; > 620 if( p->zAttachName ) goto manifest_syntax_error; 661 if( !seenZ ) goto manifest_syntax_error; 621 if( !seenZ ) goto manifest_syntax_error; 662 p->type = CFTYPE_CONTROL; 622 p->type = CFTYPE_CONTROL; 663 }else{ | 623 }else if( p->zAttachName ){ 664 if( p->nCChild>0 ) goto manifest_syntax_error; 624 if( p->nCChild>0 ) goto manifest_syntax_error; 665 if( p->rDate==0.0 ) goto manifest_syntax_error; 625 if( p->rDate==0.0 ) goto manifest_syntax_error; > 626 if( p->zTicketUuid ) goto manifest_syntax_error; > 627 if( p->zWikiTitle ) goto manifest_syntax_error; > 628 if( !seenZ ) goto manifest_syntax_error; > 629 p->type = CFTYPE_ATTACHMENT; > 630 }else{ > 631 if( p->nCChild>0 ) goto manifest_syntax_error; > 632 if( p->rDate<=0.0 ) goto manifest_syntax_error; > 633 if( p->nParent>0 ) goto manifest_syntax_error; 666 if( p->nField>0 ) goto manifest_syntax_error; 634 if( p->nField>0 ) goto manifest_syntax_error; 667 if( p->zTicketUuid ) goto manifest_syntax_error; 635 if( p->zTicketUuid ) goto manifest_syntax_error; 668 if( p->nAttach>0 ) goto manifest_syntax_error; < 669 if( p->zWiki ) goto manifest_syntax_error; 636 if( p->zWiki ) goto manifest_syntax_error; 670 if( p->zWikiTitle ) goto manifest_syntax_error; 637 if( p->zWikiTitle ) goto manifest_syntax_error; 671 if( p->zTicketUuid ) goto manifest_syntax_error; 638 if( p->zTicketUuid ) goto manifest_syntax_error; > 639 if( p->zAttachName ) goto manifest_syntax_error; 672 p->type = CFTYPE_MANIFEST; 640 p->type = CFTYPE_MANIFEST; 673 } 641 } 674 < 675 md5sum_init(); 642 md5sum_init(); 676 return 1; 643 return 1; 677 644 678 manifest_syntax_error: 645 manifest_syntax_error: 679 /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/ 646 /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/ 680 md5sum_init(); 647 md5sum_init(); 681 manifest_clear(p); 648 manifest_clear(p); ................................................................................................................................................................................ 1030 if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){ 997 if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){ 1031 manifest_clear(&m); 998 manifest_clear(&m); 1032 return 0; 999 return 0; 1033 } 1000 } 1034 db_begin_transaction(); 1001 db_begin_transaction(); 1035 if( m.type==CFTYPE_MANIFEST ){ 1002 if( m.type==CFTYPE_MANIFEST ){ 1036 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ 1003 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ > 1004 char *zCom; 1037 for(i=0; i<m.nParent; i++){ 1005 for(i=0; i<m.nParent; i++){ 1038 int pid = uuid_to_rid(m.azParent[i], 1); 1006 int pid = uuid_to_rid(m.azParent[i], 1); 1039 db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" 1007 db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" 1040 "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate); 1008 "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate); 1041 if( i==0 ){ 1009 if( i==0 ){ 1042 add_mlink(pid, 0, rid, &m); 1010 add_mlink(pid, 0, rid, &m); 1043 parentid = pid; 1011 parentid = pid; ................................................................................................................................................................................ 1063 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", 1031 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", 1064 TAG_DATE, rid, m.rDate, 1032 TAG_DATE, rid, m.rDate, 1065 rid, m.zUser, m.zComment, 1033 rid, m.zUser, m.zComment, 1066 TAG_BGCOLOR, rid, 1034 TAG_BGCOLOR, rid, 1067 TAG_USER, rid, 1035 TAG_USER, rid, 1068 TAG_COMMENT, rid 1036 TAG_COMMENT, rid 1069 ); 1037 ); > 1038 zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event" > 1039 " WHERE rowid=last_insert_rowid()"); > 1040 wiki_extract_links(zCom, rid, 0, m.rDate, 1, WIKI_INLINE); > 1041 free(zCom); 1070 } 1042 } 1071 } 1043 } 1072 if( m.type==CFTYPE_CLUSTER ){ 1044 if( m.type==CFTYPE_CLUSTER ){ 1073 tag_insert("cluster", 1, 0, rid, m.rDate, rid); 1045 tag_insert("cluster", 1, 0, rid, m.rDate, rid); 1074 for(i=0; i<m.nCChild; i++){ 1046 for(i=0; i<m.nCChild; i++){ 1075 int mid; 1047 int mid; 1076 mid = uuid_to_rid(m.azCChild[i], 1); 1048 mid = uuid_to_rid(m.azCChild[i], 1); ................................................................................................................................................................................ 1106 } 1078 } 1107 } 1079 } 1108 if( m.type==CFTYPE_WIKI ){ 1080 if( m.type==CFTYPE_WIKI ){ 1109 char *zTag = mprintf("wiki-%s", m.zWikiTitle); 1081 char *zTag = mprintf("wiki-%s", m.zWikiTitle); 1110 int tagid = tag_findid(zTag, 1); 1082 int tagid = tag_findid(zTag, 1); 1111 int prior; 1083 int prior; 1112 char *zComment; 1084 char *zComment; > 1085 int nWiki; > 1086 char zLength[40]; > 1087 while( isspace(m.zWiki[0]) ) m.zWiki++; > 1088 nWiki = strlen(m.zWiki); > 1089 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); 1113 tag_insert(zTag, 1, 0, rid, m.rDate, rid); | 1090 tag_insert(zTag, 1, zLength, rid, m.rDate, rid); 1114 free(zTag); 1091 free(zTag); 1115 prior = db_int(0, 1092 prior = db_int(0, 1116 "SELECT rid FROM tagxref" 1093 "SELECT rid FROM tagxref" 1117 " WHERE tagid=%d AND mtime<%.17g" 1094 " WHERE tagid=%d AND mtime<%.17g" 1118 " ORDER BY mtime DESC", 1095 " ORDER BY mtime DESC", 1119 tagid, m.rDate 1096 tagid, m.rDate 1120 ); 1097 ); 1121 if( prior ){ 1098 if( prior ){ 1122 content_deltify(prior, rid, 0); 1099 content_deltify(prior, rid, 0); 1123 } 1100 } > 1101 if( nWiki>0 ){ 1124 zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle); | 1102 zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle); > 1103 }else{ > 1104 zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle); > 1105 } 1125 db_multi_exec( 1106 db_multi_exec( 1126 "REPLACE INTO event(type,mtime,objid,user,comment," 1107 "REPLACE INTO event(type,mtime,objid,user,comment," 1127 " bgcolor,euser,ecomment)" 1108 " bgcolor,euser,ecomment)" 1128 "VALUES('w',%.17g,%d,%Q,%Q," 1109 "VALUES('w',%.17g,%d,%Q,%Q," 1129 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," 1110 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," 1130 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," 1111 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," 1131 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", 1112 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", ................................................................................................................................................................................ 1142 1123 1143 assert( manifest_crosslink_busy==1 ); 1124 assert( manifest_crosslink_busy==1 ); 1144 zTag = mprintf("tkt-%s", m.zTicketUuid); 1125 zTag = mprintf("tkt-%s", m.zTicketUuid); 1145 tag_insert(zTag, 1, 0, rid, m.rDate, rid); 1126 tag_insert(zTag, 1, 0, rid, m.rDate, rid); 1146 free(zTag); 1127 free(zTag); 1147 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", 1128 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", 1148 m.zTicketUuid); 1129 m.zTicketUuid); > 1130 } > 1131 if( m.type==CFTYPE_ATTACHMENT ){ > 1132 db_multi_exec( > 1133 "INSERT INTO attachment(attachid, mtime, src, target," > 1134 "filename, comment, user)" > 1135 "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);", > 1136 rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName, > 1137 (m.zComment ? m.zComment : ""), m.zUser > 1138 ); > 1139 db_multi_exec( > 1140 "UPDATE attachment SET isLatest = (mtime==" > 1141 "(SELECT max(mtime) FROM attachment" > 1142 " WHERE target=%Q AND filename=%Q))" > 1143 " WHERE target=%Q AND filename=%Q", > 1144 m.zAttachTarget, m.zAttachName, > 1145 m.zAttachTarget, m.zAttachName > 1146 ); > 1147 if( strlen(m.zAttachTarget)!=UUID_SIZE > 1148 || !validate16(m.zAttachTarget, UUID_SIZE) > 1149 ){ > 1150 char *zComment; > 1151 if( m.zAttachSrc && m.zAttachSrc[0] ){ > 1152 zComment = mprintf("Add attachment \"%h\" to wiki page [%h]", > 1153 m.zAttachName, m.zAttachTarget); > 1154 }else{ > 1155 zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]", > 1156 m.zAttachName, m.zAttachTarget); > 1157 } > 1158 db_multi_exec( > 1159 "REPLACE INTO event(type,mtime,objid,user,comment)" > 1160 "VALUES('w',%.17g,%d,%Q,%Q)", > 1161 m.rDate, rid, m.zUser, zComment > 1162 ); > 1163 free(zComment); > 1164 }else{ > 1165 char *zComment; > 1166 if( m.zAttachSrc && m.zAttachSrc[0] ){ > 1167 zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]", > 1168 m.zAttachName, m.zAttachTarget); > 1169 }else{ > 1170 zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]", > 1171 m.zAttachName, m.zAttachTarget); > 1172 } > 1173 db_multi_exec( > 1174 "REPLACE INTO event(type,mtime,objid,user,comment)" > 1175 "VALUES('t',%.17g,%d,%Q,%Q)", > 1176 m.rDate, rid, m.zUser, zComment > 1177 ); > 1178 free(zComment); > 1179 } 1149 } 1180 } 1150 db_end_transaction(0); 1181 db_end_transaction(0); 1151 manifest_clear(&m); 1182 manifest_clear(&m); 1152 return 1; 1183 return 1; 1153 } 1184 }

Changes to src/merge.c

28 #include "merge.h" 28 #include "merge.h" 29 #include <assert.h> 29 #include <assert.h> 30 30 31 31 32 /* 32 /* 33 ** COMMAND: merge 33 ** COMMAND: merge 34 ** 34 ** 35 ** Usage: %fossil merge [--cherrypick] VERSION | 35 ** Usage: %fossil merge [--cherrypick] [--backout] VERSION 36 ** 36 ** 37 ** The argument is a version that should be merged into the current 37 ** The argument is a version that should be merged into the current 38 ** checkout. All changes from VERSION back to the nearest common 38 ** checkout. All changes from VERSION back to the nearest common 39 ** ancestor are merged. Except, if the --cherrypick option is used | 39 ** ancestor are merged. Except, if either of the --cherrypick or 40 ** only the changes associated with the single check-in VERSION are | 40 ** --backout options are used only the changes associated with the 41 ** merged. | 41 ** single check-in VERSION are merged. The --backout option causes > 42 ** the changes associated with VERSION to be removed from the current > 43 ** checkout rather than added. 42 ** 44 ** 43 ** Only file content is merged. The result continues to use the 45 ** Only file content is merged. The result continues to use the 44 ** file and directory names from the current check-out even if those | 46 ** file and directory names from the current checkout even if those 45 ** names might have been changed in the branch being merged in. 47 ** names might have been changed in the branch being merged in. > 48 ** > 49 ** Other options: > 50 ** > 51 ** --detail Show additional details of the merge > 52 ** > 53 ** --binary GLOBPATTERN Treat files that match GLOBPATTERN as binary > 54 ** and do not try to merge parallel changes. This > 55 ** option overrides the "binary-glob" setting. 46 */ 56 */ 47 void merge_cmd(void){ 57 void merge_cmd(void){ 48 int vid; /* Current version */ 58 int vid; /* Current version */ 49 int mid; /* Version we are merging against */ 59 int mid; /* Version we are merging against */ 50 int pid; /* The pivot version - most recent common ancestor */ 60 int pid; /* The pivot version - most recent common ancestor */ 51 int detailFlag; /* True if the --detail option is present */ 61 int detailFlag; /* True if the --detail option is present */ 52 int pickFlag; /* True if the --cherrypick option is present */ 62 int pickFlag; /* True if the --cherrypick option is present */ > 63 int backoutFlag; /* True if the --backout optioni is present */ > 64 const char *zBinGlob; /* The value of --binary */ 53 Stmt q; 65 Stmt q; 54 66 55 detailFlag = find_option("detail",0,0)!=0; 67 detailFlag = find_option("detail",0,0)!=0; 56 pickFlag = find_option("cherrypick",0,0)!=0; 68 pickFlag = find_option("cherrypick",0,0)!=0; > 69 backoutFlag = find_option("backout",0,0)!=0; > 70 zBinGlob = find_option("binary",0,1); 57 if( g.argc!=3 ){ 71 if( g.argc!=3 ){ 58 usage("VERSION"); 72 usage("VERSION"); 59 } 73 } 60 db_must_be_within_tree(); 74 db_must_be_within_tree(); > 75 if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); 61 vid = db_lget_int("checkout", 0); 76 vid = db_lget_int("checkout", 0); 62 if( vid==0 ){ 77 if( vid==0 ){ 63 fossil_fatal("nothing is checked out"); 78 fossil_fatal("nothing is checked out"); 64 } 79 } 65 mid = name_to_rid(g.argv[2]); 80 mid = name_to_rid(g.argv[2]); 66 if( mid==0 ){ 81 if( mid==0 ){ 67 fossil_fatal("not a version: %s", g.argv[2]); 82 fossil_fatal("not a version: %s", g.argv[2]); 68 } 83 } 69 if( mid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", mid) ){ 84 if( mid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", mid) ){ 70 fossil_fatal("not a version: %s", g.argv[2]); 85 fossil_fatal("not a version: %s", g.argv[2]); 71 } 86 } 72 if( pickFlag ){ | 87 if( pickFlag || backoutFlag ){ 73 pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid); 88 pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid); 74 if( pid<=0 ){ 89 if( pid<=0 ){ 75 fossil_fatal("cannot find an ancestor for %s", g.argv[2]); 90 fossil_fatal("cannot find an ancestor for %s", g.argv[2]); > 91 } > 92 if( backoutFlag ){ > 93 int t = pid; > 94 pid = mid; > 95 mid = t; 76 } 96 } 77 }else{ 97 }else{ 78 pivot_set_primary(mid); 98 pivot_set_primary(mid); 79 pivot_set_secondary(vid); 99 pivot_set_secondary(vid); 80 db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0"); 100 db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0"); 81 while( db_step(&q)==SQLITE_ROW ){ 101 while( db_step(&q)==SQLITE_ROW ){ 82 pivot_set_secondary(db_column_int(&q,0)); 102 pivot_set_secondary(db_column_int(&q,0)); ................................................................................................................................................................................ 227 } 247 } 228 db_finalize(&q); 248 db_finalize(&q); 229 249 230 /* 250 /* 231 ** Do a three-way merge on files that have changes pid->mid and pid->vid 251 ** Do a three-way merge on files that have changes pid->mid and pid->vid 232 */ 252 */ 233 db_prepare(&q, 253 db_prepare(&q, 234 "SELECT ridm, idv, ridp, ridv FROM fv" | 254 "SELECT ridm, idv, ridp, ridv, %s FROM fv" 235 " WHERE idp>0 AND idv>0 AND idm>0" 255 " WHERE idp>0 AND idv>0 AND idm>0" 236 " AND ridm!=ridp AND (ridv!=ridp OR chnged)" | 256 " AND ridm!=ridp AND (ridv!=ridp OR chnged)", > 257 glob_expr("fv.fn", zBinGlob) 237 ); 258 ); 238 while( db_step(&q)==SQLITE_ROW ){ 259 while( db_step(&q)==SQLITE_ROW ){ 239 int ridm = db_column_int(&q, 0); 260 int ridm = db_column_int(&q, 0); 240 int idv = db_column_int(&q, 1); 261 int idv = db_column_int(&q, 1); 241 int ridp = db_column_int(&q, 2); 262 int ridp = db_column_int(&q, 2); 242 int ridv = db_column_int(&q, 3); 263 int ridv = db_column_int(&q, 3); > 264 int isBinary = db_column_int(&q, 4); 243 int rc; 265 int rc; 244 char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv); 266 char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv); 245 char *zFullPath; 267 char *zFullPath; 246 Blob m, p, v, r; 268 Blob m, p, v, r; 247 /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ 269 /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ 248 if( detailFlag ){ 270 if( detailFlag ){ 249 printf("MERGE %s (pivot=%d v1=%d v2=%d)\n", zName, ridp, ridm, ridv); 271 printf("MERGE %s (pivot=%d v1=%d v2=%d)\n", zName, ridp, ridm, ridv); ................................................................................................................................................................................ 252 } 274 } 253 undo_save(zName); 275 undo_save(zName); 254 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); 276 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); 255 content_get(ridp, &p); 277 content_get(ridp, &p); 256 content_get(ridm, &m); 278 content_get(ridm, &m); 257 blob_zero(&v); 279 blob_zero(&v); 258 blob_read_from_file(&v, zFullPath); 280 blob_read_from_file(&v, zFullPath); > 281 if( isBinary ){ > 282 rc = -1; > 283 blob_zero(&r); > 284 }else{ 259 rc = blob_merge(&p, &m, &v, &r); | 285 rc = blob_merge(&p, &m, &v, &r); > 286 } 260 if( rc>=0 ){ 287 if( rc>=0 ){ 261 blob_write_to_file(&r, zFullPath); 288 blob_write_to_file(&r, zFullPath); 262 if( rc>0 ){ 289 if( rc>0 ){ 263 printf("***** %d merge conflicts in %s\n", rc, zName); 290 printf("***** %d merge conflicts in %s\n", rc, zName); 264 } 291 } 265 }else{ 292 }else{ 266 printf("***** Cannot merge binary file %s\n", zName); 293 printf("***** Cannot merge binary file %s\n", zName);

Changes to src/name.c

109 db_finalize(&q); 109 db_finalize(&q); 110 rc = 0; 110 rc = 0; 111 }else{ 111 }else{ 112 rc = 0; 112 rc = 0; 113 } 113 } 114 return rc; 114 return rc; 115 } 115 } > 116 > 117 /* > 118 ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. > 119 */ > 120 static int is_date(const char *z){ > 121 if( !isdigit(z[0]) ) return 0; > 122 if( !isdigit(z[1]) ) return 0; > 123 if( !isdigit(z[2]) ) return 0; > 124 if( !isdigit(z[3]) ) return 0; > 125 if( z[4]!='-') return 0; > 126 if( !isdigit(z[5]) ) return 0; > 127 if( !isdigit(z[6]) ) return 0; > 128 if( z[7]!='-') return 0; > 129 if( !isdigit(z[8]) ) return 0; > 130 if( !isdigit(z[9]) ) return 0; > 131 return 1; > 132 } 116 133 117 /* 134 /* 118 ** Convert a symbolic tag name into the UUID of a check-in that contains 135 ** Convert a symbolic tag name into the UUID of a check-in that contains 119 ** that tag. If the tag appears on multiple check-ins, return the UUID 136 ** that tag. If the tag appears on multiple check-ins, return the UUID 120 ** of the most recent check-in with the tag. 137 ** of the most recent check-in with the tag. 121 ** 138 ** > 139 ** If the input string is of the form: > 140 ** > 141 ** tag:date > 142 ** > 143 ** Then return the UUID of the oldest check-in with that tag that is > 144 ** not older than 'date'. > 145 ** > 146 ** An input of "tip" returns the most recent check-in. > 147 ** 122 ** Memory to hold the returned string comes from malloc() and needs to 148 ** Memory to hold the returned string comes from malloc() and needs to 123 ** be freed by the caller. 149 ** be freed by the caller. 124 */ 150 */ 125 char *tag_to_uuid(const char *zTag){ 151 char *tag_to_uuid(const char *zTag){ 126 char *zUuid = 152 char *zUuid = 127 db_text(0, 153 db_text(0, 128 "SELECT blob.uuid" 154 "SELECT blob.uuid" ................................................................................................................................................................................ 130 " WHERE tag.tagname='sym-'||%Q " 156 " WHERE tag.tagname='sym-'||%Q " 131 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " 157 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " 132 " AND event.objid=tagxref.rid " 158 " AND event.objid=tagxref.rid " 133 " AND blob.rid=event.objid " 159 " AND blob.rid=event.objid " 134 " ORDER BY event.mtime DESC ", 160 " ORDER BY event.mtime DESC ", 135 zTag 161 zTag 136 ); 162 ); > 163 if( zUuid==0 ){ > 164 int nTag = strlen(zTag); > 165 int i; > 166 for(i=0; i<nTag-10; i++){ > 167 if( zTag[i]==':' && is_date(&zTag[i+1]) ){ > 168 char *zDate = mprintf("%s", &zTag[i+1]); > 169 char *zTagBase = mprintf("%.*s", i, zTag); > 170 int nDate = strlen(zDate); > 171 int useUtc = 0; > 172 if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ > 173 nDate -= 3; > 174 zDate[nDate] = 0; > 175 useUtc = 1; > 176 } > 177 zUuid = db_text(0, > 178 "SELECT blob.uuid" > 179 " FROM tag, tagxref, event, blob" > 180 " WHERE tag.tagname='sym-'||%Q " > 181 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " > 182 " AND event.objid=tagxref.rid " > 183 " AND blob.rid=event.objid " > 184 " AND event.mtime<=julianday(%Q %s)" > 185 " ORDER BY event.mtime DESC ", > 186 zTagBase, zDate, (useUtc ? "" : ",'utc'") > 187 ); > 188 break; > 189 } > 190 } > 191 if( zUuid==0 && strcmp(zTag, "tip")==0 ){ > 192 zUuid = db_text(0, > 193 "SELECT blob.uuid" > 194 " FROM event, blob" > 195 " WHERE event.type='ci'" > 196 " AND blob.rid=event.objid" > 197 " ORDER BY event.mtime DESC" > 198 ); > 199 } > 200 } 137 return zUuid; 201 return zUuid; 138 } 202 } 139 203 140 /* 204 /* 141 ** Convert a date/time string into a UUID. 205 ** Convert a date/time string into a UUID. 142 ** 206 ** 143 ** Input forms accepted: 207 ** Input forms accepted: ................................................................................................................................................................................ 160 }else if( memcmp(zDate, "local:", 6)==0 ){ 224 }else if( memcmp(zDate, "local:", 6)==0 ){ 161 zDate += 6; 225 zDate += 6; 162 }else if( memcmp(zDate, "utc:", 4)==0 ){ 226 }else if( memcmp(zDate, "utc:", 4)==0 ){ 163 zDate += 4; 227 zDate += 4; 164 useUtc = 1; 228 useUtc = 1; 165 } 229 } 166 n = strlen(zDate); 230 n = strlen(zDate); 167 if( n<10 || zDate[4]!='-' || zDate[7]!='-' ) return 0; | 231 if( n<10 || !is_date(zDate) ) return 0; 168 if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ 232 if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ 169 zCopy = mprintf("%s", zDate); 233 zCopy = mprintf("%s", zDate); 170 zCopy[n-3] = 0; 234 zCopy[n-3] = 0; 171 zDate = zCopy; 235 zDate = zCopy; 172 n -= 3; 236 n -= 3; 173 useUtc = 1; 237 useUtc = 1; 174 } 238 }

Changes to src/schema.c

307 @ value TEXT, -- Value of the tag. Might be NULL. 307 @ value TEXT, -- Value of the tag. Might be NULL. 308 @ mtime TIMESTAMP, -- Time of addition or removal 308 @ mtime TIMESTAMP, -- Time of addition or removal 309 @ rid INTEGER REFERENCE blob, -- Artifact tag is applied to 309 @ rid INTEGER REFERENCE blob, -- Artifact tag is applied to 310 @ UNIQUE(rid, tagid) 310 @ UNIQUE(rid, tagid) 311 @ ); 311 @ ); 312 @ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime); 312 @ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime); 313 @ 313 @ > 314 @ -- When a hyperlink occurs from one artifact to another (for example > 315 @ -- when a check-in comment refers to a ticket) an entry is made in > 316 @ -- the following table for that hyperlink. This table is used to > 317 @ -- facilitate the display of "back links". > 318 @ -- > 319 @ CREATE TABLE backlink( > 320 @ target TEXT, -- Where the hyperlink points to > 321 @ srctype INT, -- 0: check-in 1: ticket 2: wiki > 322 @ srcid INT, -- rid for checkin or wiki. tkt_id for ticket. > 323 @ mtime TIMESTAMP, -- time that the hyperlink was added > 324 @ UNIQUE(target, srctype, srcid) > 325 @ ); > 326 @ CREATE INDEX backlink_src ON backlink(srcid, srctype); > 327 @ > 328 @ -- Each attachment is an entry in the following table. Only > 329 @ -- the most recent attachment (identified by the D card) is saved. > 330 @ -- > 331 @ CREATE TABLE attachment( > 332 @ attachid INTEGER PRIMARY KEY, -- Local id for this attachment > 333 @ isLatest BOOLEAN DEFAULT 0, -- True if this is the one to use > 334 @ mtime TIMESTAMP, -- Time when attachment last changed > 335 @ src TEXT, -- UUID of the attachment. NULL to delete > 336 @ target TEXT, -- Object attached to. Wikiname or Tkt UUID > 337 @ filename TEXT, -- Filename for the attachment > 338 @ comment TEXT, -- Comment associated with this attachment > 339 @ user TEXT -- Name of user adding attachment > 340 @ ); > 341 @ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime); > 342 @ CREATE INDEX attachment_idx2 ON attachment(src); > 343 @ 314 @ -- Template for the TICKET table 344 @ -- Template for the TICKET table 315 @ -- 345 @ -- 316 @ -- NB: when changing the schema of the TICKET table here, also make the 346 @ -- NB: when changing the schema of the TICKET table here, also make the 317 @ -- same change in tktsetup.c. 347 @ -- same change in tktsetup.c. 318 @ -- 348 @ -- 319 @ CREATE TABLE ticket( 349 @ CREATE TABLE ticket( 320 @ -- Do not change any column that begins with tkt_ 350 @ -- Do not change any column that begins with tkt_ ................................................................................................................................................................................ 391 @ -- 421 @ -- 392 @ -- 422 @ -- 393 @ CREATE TABLE vfile( 423 @ CREATE TABLE vfile( 394 @ id INTEGER PRIMARY KEY, -- ID of the checked out file 424 @ id INTEGER PRIMARY KEY, -- ID of the checked out file 395 @ vid INTEGER REFERENCES blob, -- The baseline this file is part of. 425 @ vid INTEGER REFERENCES blob, -- The baseline this file is part of. 396 @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add 426 @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add 397 @ deleted BOOLEAN DEFAULT 0, -- True if deleted 427 @ deleted BOOLEAN DEFAULT 0, -- True if deleted > 428 @ isexe BOOLEAN, -- True if file should be executable 398 @ rid INTEGER, -- Originally from this repository record 429 @ rid INTEGER, -- Originally from this repository record 399 @ mrid INTEGER, -- Based on this record due to a merge 430 @ mrid INTEGER, -- Based on this record due to a merge 400 @ mtime INTEGER, -- Modification time of file on disk 431 @ mtime INTEGER, -- Modification time of file on disk 401 @ pathname TEXT, -- Full pathname relative to root 432 @ pathname TEXT, -- Full pathname relative to root 402 @ origname TEXT, -- Original pathname. NULL if unchanged 433 @ origname TEXT, -- Original pathname. NULL if unchanged 403 @ UNIQUE(pathname,vid) 434 @ UNIQUE(pathname,vid) 404 @ ); 435 @ );

Changes to src/setup.c

143 @ <td valign="top"> 143 @ <td valign="top"> 144 @ <b>Notes:</b> 144 @ <b>Notes:</b> 145 @ <ol> 145 @ <ol> 146 @ <li><p>The permission flags are as follows:</p> 146 @ <li><p>The permission flags are as follows:</p> 147 @ <table> 147 @ <table> 148 @ <tr><td valign="top"><b>a</b></td> 148 @ <tr><td valign="top"><b>a</b></td> 149 @ <td><i>Admin:</i> Create and delete users</td></tr> 149 @ <td><i>Admin:</i> Create and delete users</td></tr> > 150 @ <tr><td valign="top"><b>b</b></td> > 151 @ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr> 150 @ <tr><td valign="top"><b>c</b></td> 152 @ <tr><td valign="top"><b>c</b></td> 151 @ <td><i>Append-Tkt:</i> Append to tickets</td></tr> 153 @ <td><i>Append-Tkt:</i> Append to tickets</td></tr> 152 @ <tr><td valign="top"><b>d</b></td> 154 @ <tr><td valign="top"><b>d</b></td> 153 @ <td><i>Delete:</i> Delete wiki and tickets</td></tr> 155 @ <td><i>Delete:</i> Delete wiki and tickets</td></tr> 154 @ <tr><td valign="top"><b>e</b></td> 156 @ <tr><td valign="top"><b>e</b></td> 155 @ <td><i>Email:</i> View sensitive data such as EMail addresses</td></tr> 157 @ <td><i>Email:</i> View sensitive data such as EMail addresses</td></tr> 156 @ <tr><td valign="top"><b>f</b></td> 158 @ <tr><td valign="top"><b>f</b></td> ................................................................................................................................................................................ 235 /* 237 /* 236 ** WEBPAGE: /setup_uedit 238 ** WEBPAGE: /setup_uedit 237 */ 239 */ 238 void user_edit(void){ 240 void user_edit(void){ 239 const char *zId, *zLogin, *zInfo, *zCap, *zPw; 241 const char *zId, *zLogin, *zInfo, *zCap, *zPw; 240 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap; 242 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap; 241 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae; 243 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae; 242 char *oat, *oau, *oav, *oaz; | 244 char *oat, *oau, *oav, *oab, *oaz; 243 const char *inherit[128]; 245 const char *inherit[128]; 244 int doWrite; 246 int doWrite; 245 int uid; 247 int uid; 246 int higherUser = 0; /* True if user being edited is SETUP and the */ 248 int higherUser = 0; /* True if user being edited is SETUP and the */ 247 /* user doing the editing is ADMIN. Disallow editing */ 249 /* user doing the editing is ADMIN. Disallow editing */ 248 250 249 /* Must have ADMIN privleges to access this page 251 /* Must have ADMIN privleges to access this page ................................................................................................................................................................................ 272 ** to the page that displays a list of users. 274 ** to the page that displays a list of users. 273 */ 275 */ 274 doWrite = cgi_all("login","info","pw") && !higherUser; 276 doWrite = cgi_all("login","info","pw") && !higherUser; 275 if( doWrite ){ 277 if( doWrite ){ 276 char zCap[50]; 278 char zCap[50]; 277 int i = 0; 279 int i = 0; 278 int aa = P("aa")!=0; 280 int aa = P("aa")!=0; > 281 int ab = P("ab")!=0; 279 int ad = P("ad")!=0; 282 int ad = P("ad")!=0; 280 int ae = P("ae")!=0; 283 int ae = P("ae")!=0; 281 int ai = P("ai")!=0; 284 int ai = P("ai")!=0; 282 int aj = P("aj")!=0; 285 int aj = P("aj")!=0; 283 int ak = P("ak")!=0; 286 int ak = P("ak")!=0; 284 int an = P("an")!=0; 287 int an = P("an")!=0; 285 int ao = P("ao")!=0; 288 int ao = P("ao")!=0; ................................................................................................................................................................................ 293 int ah = P("ah")!=0; 296 int ah = P("ah")!=0; 294 int ag = P("ag")!=0; 297 int ag = P("ag")!=0; 295 int at = P("at")!=0; 298 int at = P("at")!=0; 296 int au = P("au")!=0; 299 int au = P("au")!=0; 297 int av = P("av")!=0; 300 int av = P("av")!=0; 298 int az = P("az")!=0; 301 int az = P("az")!=0; 299 if( aa ){ zCap[i++] = 'a'; } 302 if( aa ){ zCap[i++] = 'a'; } > 303 if( ab ){ zCap[i++] = 'b'; } 300 if( ac ){ zCap[i++] = 'c'; } 304 if( ac ){ zCap[i++] = 'c'; } 301 if( ad ){ zCap[i++] = 'd'; } 305 if( ad ){ zCap[i++] = 'd'; } 302 if( ae ){ zCap[i++] = 'e'; } 306 if( ae ){ zCap[i++] = 'e'; } 303 if( af ){ zCap[i++] = 'f'; } 307 if( af ){ zCap[i++] = 'f'; } 304 if( ah ){ zCap[i++] = 'h'; } 308 if( ah ){ zCap[i++] = 'h'; } 305 if( ag ){ zCap[i++] = 'g'; } 309 if( ag ){ zCap[i++] = 'g'; } 306 if( ai ){ zCap[i++] = 'i'; } 310 if( ai ){ zCap[i++] = 'i'; } ................................................................................................................................................................................ 349 353 350 /* Load the existing information about the user, if any 354 /* Load the existing information about the user, if any 351 */ 355 */ 352 zLogin = ""; 356 zLogin = ""; 353 zInfo = ""; 357 zInfo = ""; 354 zCap = ""; 358 zCap = ""; 355 zPw = ""; 359 zPw = ""; 356 oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam = | 360 oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam = 357 oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = ""; 361 oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = ""; 358 if( uid ){ 362 if( uid ){ 359 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); 363 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); 360 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); 364 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); 361 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); 365 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); 362 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); 366 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); 363 if( strchr(zCap, 'a') ) oaa = " checked"; 367 if( strchr(zCap, 'a') ) oaa = " checked"; > 368 if( strchr(zCap, 'b') ) oab = " checked"; 364 if( strchr(zCap, 'c') ) oac = " checked"; 369 if( strchr(zCap, 'c') ) oac = " checked"; 365 if( strchr(zCap, 'd') ) oad = " checked"; 370 if( strchr(zCap, 'd') ) oad = " checked"; 366 if( strchr(zCap, 'e') ) oae = " checked"; 371 if( strchr(zCap, 'e') ) oae = " checked"; 367 if( strchr(zCap, 'f') ) oaf = " checked"; 372 if( strchr(zCap, 'f') ) oaf = " checked"; 368 if( strchr(zCap, 'g') ) oag = " checked"; 373 if( strchr(zCap, 'g') ) oag = " checked"; 369 if( strchr(zCap, 'h') ) oah = " checked"; 374 if( strchr(zCap, 'h') ) oah = " checked"; 370 if( strchr(zCap, 'i') ) oai = " checked"; 375 if( strchr(zCap, 'i') ) oai = " checked"; ................................................................................................................................................................................ 463 @ <input type="checkbox" name="au"%s(oau)/>%s(B('u'))Reader<br> 468 @ <input type="checkbox" name="au"%s(oau)/>%s(B('u'))Reader<br> 464 @ <input type="checkbox" name="av"%s(oav)/>%s(B('v'))Developer<br> 469 @ <input type="checkbox" name="av"%s(oav)/>%s(B('v'))Developer<br> 465 @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br> 470 @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br> 466 @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br> 471 @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br> 467 @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br> 472 @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br> 468 @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br> 473 @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br> 469 @ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br> 474 @ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br> > 475 @ <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br> 470 @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Tkt<br> | 476 @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br> 471 @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Tkt<br> | 477 @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br> 472 @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Tkt<br> | 478 @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br> 473 @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Tkt<br> | 479 @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br> 474 @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Tkt Report<br> | 480 @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br> 475 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip 481 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip 476 @ </td> 482 @ </td> 477 @ </tr> 483 @ </tr> 478 @ <tr> 484 @ <tr> 479 @ <td align="right">Password:</td> 485 @ <td align="right">Password:</td> 480 if( zPw[0] ){ 486 if( zPw[0] ){ 481 /* Obscure the password for all users */ 487 /* Obscure the password for all users */ ................................................................................................................................................................................ 560 @ The <b>Check-out</b> privilege allows remote users to "pull". 566 @ The <b>Check-out</b> privilege allows remote users to "pull". 561 @ The <b>Clone</b> privilege allows remote users to "clone". 567 @ The <b>Clone</b> privilege allows remote users to "clone". 562 @ </li><p> 568 @ </li><p> 563 @ 569 @ 564 @ <li><p> 570 @ <li><p> 565 @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and 571 @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and 566 @ <b>Write Wiki</b> privileges control access to wiki pages. The 572 @ <b>Write Wiki</b> privileges control access to wiki pages. The 567 @ <b>Read Tkt</b>, <b>New Tkt</b>, <b>Append Tkt</b>, and | 573 @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and 568 @ <b>Write Tkt</b> privileges control access to trouble tickets. | 574 @ <b>Write Ticket</b> privileges control access to trouble tickets. 569 @ The <b>Tkt Report</b> privilege allows the user to create or edit | 575 @ The <b>Ticket Report</b> privilege allows the user to create or edit 570 @ ticket report formats. 576 @ ticket report formats. 571 @ </p></li> 577 @ </p></li> 572 @ 578 @ 573 @ <li><p> 579 @ <li><p> 574 @ Users with the <b>Password</b> privilege are allowed to change their 580 @ Users with the <b>Password</b> privilege are allowed to change their 575 @ own password. Recommended ON for most users but OFF for special 581 @ own password. Recommended ON for most users but OFF for special 576 @ users "developer", "anonymous", and "nobody". 582 @ users "developer", "anonymous", and "nobody". ................................................................................................................................................................................ 579 @ <li><p> 585 @ <li><p> 580 @ The <b>EMail</b> privilege allows the display of sensitive information 586 @ The <b>EMail</b> privilege allows the display of sensitive information 581 @ such as the email address of users and contact information on tickets. 587 @ such as the email address of users and contact information on tickets. 582 @ Recommended OFF for "anonymous" and for "nobody" but ON for 588 @ Recommended OFF for "anonymous" and for "nobody" but ON for 583 @ "developer". 589 @ "developer". 584 @ </p></li> 590 @ </p></li> 585 @ 591 @ > 592 @ <li><p> > 593 @ The <b>Attachment</b> privilege is needed in order to add attachments > 594 @ to tickets or wiki. Write privilege on the ticket or wiki is also > 595 @ required.</p></li> > 596 @ 586 @ <li><p> 597 @ <li><p> 587 @ Login is prohibited if the password is an empty string. 598 @ Login is prohibited if the password is an empty string. 588 @ </p></li> 599 @ </p></li> 589 @ </ul> 600 @ </ul> 590 @ 601 @ 591 @ <h2>Special Logins</h2> 602 @ <h2>Special Logins</h2> 592 @ 603 @

Changes to src/skins.c

160 @ padding: 0.2ex 2ex; 160 @ padding: 0.2ex 2ex; 161 @ }'); 161 @ }'); 162 @ REPLACE INTO config VALUES('header','<html> 162 @ REPLACE INTO config VALUES('header','<html> 163 @ <head> 163 @ <head> 164 @ <title>$<project_name>: $<title></title> 164 @ <title>$<project_name>: $<title></title> 165 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" 165 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" 166 @ href="$baseurl/timeline.rss"> 166 @ href="$baseurl/timeline.rss"> 167 @ <link rel="stylesheet" href="$baseurl/style.css" type="text/css" | 167 @ <link rel="stylesheet" href="$baseurl/style.css?blackwhite" type="text/css" 168 @ media="screen"> 168 @ media="screen"> 169 @ </head> 169 @ </head> 170 @ <body> 170 @ <body> 171 @ <div class="header"> 171 @ <div class="header"> 172 @ <div class="logo"> 172 @ <div class="logo"> 173 @ <nobr>$<project_name></nobr> 173 @ <nobr>$<project_name></nobr> 174 @ </div> 174 @ </div> ................................................................................................................................................................................ 180 @ puts "Logged in as $login" 180 @ puts "Logged in as $login" 181 @ } else { 181 @ } else { 182 @ puts "Not logged in" 182 @ puts "Not logged in" 183 @ } 183 @ } 184 @ </th1></nobr></div> 184 @ </th1></nobr></div> 185 @ </div> 185 @ </div> 186 @ <div class="mainmenu"><th1> 186 @ <div class="mainmenu"><th1> 187 @ html "<a href="$baseurl$index_page">Home</a> " | 187 @ html "<a href=''$baseurl$index_page''>Home</a> " 188 @ if {[anycap jor]} { 188 @ if {[anycap jor]} { 189 @ html "<a href="$baseurl/timeline">Timeline</a> " | 189 @ html "<a href=''$baseurl/timeline''>Timeline</a> " 190 @ } 190 @ } 191 @ if {[hascap oh]} { 191 @ if {[hascap oh]} { 192 @ html "<a href="$baseurl/dir">Files</a> " | 192 @ html "<a href=''$baseurl/dir?ci=tip''>Files</a> " 193 @ } 193 @ } 194 @ if {[hascap o]} { 194 @ if {[hascap o]} { 195 @ html "<a href="$baseurl/leaves">Leaves</a> " | 195 @ html "<a href=''$baseurl/leaves''>Leaves</a> " 196 @ html "<a href="$baseurl/brlist">Branches</a> " | 196 @ html "<a href=''$baseurl/brlist''>Branches</a> " 197 @ html "<a href="$baseurl/taglist">Tags</a> " | 197 @ html "<a href=''$baseurl/taglist''>Tags</a> " 198 @ } 198 @ } 199 @ if {[hascap r]} { 199 @ if {[hascap r]} { 200 @ html "<a href="$baseurl/reportlist">Tickets</a> " | 200 @ html "<a href=''$baseurl/reportlist''>Tickets</a> " 201 @ } 201 @ } 202 @ if {[hascap j]} { 202 @ if {[hascap j]} { 203 @ html "<a href="$baseurl/wiki">Wiki</a> " | 203 @ html "<a href=''$baseurl/wiki''>Wiki</a> " 204 @ } 204 @ } 205 @ if {[hascap s]} { 205 @ if {[hascap s]} { 206 @ html "<a href="$baseurl/setup">Admin</a> " | 206 @ html "<a href=''$baseurl/setup''>Admin</a> " 207 @ } elseif {[hascap a]} { 207 @ } elseif {[hascap a]} { 208 @ html "<a href="$baseurl/setup_ulist">Users</a> " | 208 @ html "<a href=''$baseurl/setup_ulist''>Users</a> " 209 @ } 209 @ } 210 @ if {[info exists login]} { 210 @ if {[info exists login]} { 211 @ html "<a href="$baseurl/login">Logout</a> " | 211 @ html "<a href=''$baseurl/login''>Logout</a> " 212 @ } else { 212 @ } else { 213 @ html "<a href="$baseurl/login">Login</a> " | 213 @ html "<a href=''$baseurl/login''>Login</a> " 214 @ } 214 @ } 215 @ </th1></div> 215 @ </th1></div> 216 @ '); 216 @ '); 217 @ REPLACE INTO config VALUES('footer','<div class="footer"> 217 @ REPLACE INTO config VALUES('footer','<div class="footer"> 218 @ Fossil version $manifest_version $manifest_date 218 @ Fossil version $manifest_version $manifest_date 219 @ </div> 219 @ </div> 220 @ </body></html> 220 @ </body></html> ................................................................................................................................................................................ 365 @ } 365 @ } 366 @ '); 366 @ '); 367 @ REPLACE INTO config VALUES('header','<html> 367 @ REPLACE INTO config VALUES('header','<html> 368 @ <head> 368 @ <head> 369 @ <title>$<project_name>: $<title></title> 369 @ <title>$<project_name>: $<title></title> 370 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" 370 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" 371 @ href="$baseurl/timeline.rss"> 371 @ href="$baseurl/timeline.rss"> 372 @ <link rel="stylesheet" href="$baseurl/style.css" type="text/css" | 372 @ <link rel="stylesheet" href="$baseurl/style.css?tan" type="text/css" 373 @ media="screen"> 373 @ media="screen"> 374 @ </head> 374 @ </head> 375 @ <body> 375 @ <body> 376 @ <div class="header"> 376 @ <div class="header"> 377 @ <div class="title">$<title></div> 377 @ <div class="title">$<title></div> 378 @ <div class="status"> 378 @ <div class="status"> 379 @ <div class="logo"><nobr>$<project_name></nobr></div><br/> 379 @ <div class="logo"><nobr>$<project_name></nobr></div><br/> ................................................................................................................................................................................ 382 @ puts "Logged in as $login" 382 @ puts "Logged in as $login" 383 @ } else { 383 @ } else { 384 @ puts "Not logged in" 384 @ puts "Not logged in" 385 @ } 385 @ } 386 @ </th1></nobr></div> 386 @ </th1></nobr></div> 387 @ </div> 387 @ </div> 388 @ <div class="mainmenu"><th1> 388 @ <div class="mainmenu"><th1> 389 @ html "<a href="$baseurl$index_page">Home</a> " | 389 @ html "<a href=''$baseurl$index_page''>Home</a> " 390 @ if {[anycap jor]} { 390 @ if {[anycap jor]} { 391 @ html "<a href="$baseurl/timeline">Timeline</a> " | 391 @ html "<a href=''$baseurl/timeline''>Timeline</a> " 392 @ } 392 @ } 393 @ if {[hascap oh]} { 393 @ if {[hascap oh]} { 394 @ html "<a href="$baseurl/dir">Files</a> " | 394 @ html "<a href=''$baseurl/dir?ci=tip''>Files</a> " 395 @ } 395 @ } 396 @ if {[hascap o]} { 396 @ if {[hascap o]} { 397 @ html "<a href="$baseurl/leaves">Leaves</a> " | 397 @ html "<a href=''$baseurl/leaves''>Leaves</a> " 398 @ html "<a href="$baseurl/brlist">Branches</a> " | 398 @ html "<a href=''$baseurl/brlist''>Branches</a> " 399 @ html "<a href="$baseurl/taglist">Tags</a> " | 399 @ html "<a href=''$baseurl/taglist''>Tags</a> " 400 @ } 400 @ } 401 @ if {[hascap r]} { 401 @ if {[hascap r]} { 402 @ html "<a href="$baseurl/reportlist">Tickets</a> " | 402 @ html "<a href=''$baseurl/reportlist''>Tickets</a> " 403 @ } 403 @ } 404 @ if {[hascap j]} { 404 @ if {[hascap j]} { 405 @ html "<a href="$baseurl/wiki">Wiki</a> " | 405 @ html "<a href=''$baseurl/wiki''>Wiki</a> " 406 @ } 406 @ } 407 @ if {[hascap s]} { 407 @ if {[hascap s]} { 408 @ html "<a href="$baseurl/setup">Admin</a> " | 408 @ html "<a href=''$baseurl/setup''>Admin</a> " 409 @ } elseif {[hascap a]} { 409 @ } elseif {[hascap a]} { 410 @ html "<a href="$baseurl/setup_ulist">Users</a> " | 410 @ html "<a href=''$baseurl/setup_ulist''>Users</a> " 411 @ } 411 @ } 412 @ if {[info exists login]} { 412 @ if {[info exists login]} { 413 @ html "<a href="$baseurl/login">Logout</a> " | 413 @ html "<a href=''$baseurl/login''>Logout</a> " 414 @ } else { 414 @ } else { 415 @ html "<a href="$baseurl/login">Login</a> " | 415 @ html "<a href=''$baseurl/login''>Login</a> " 416 @ } 416 @ } 417 @ </th1></div> 417 @ </th1></div> 418 @ '); 418 @ '); 419 @ REPLACE INTO config VALUES('footer','<div class="footer"> 419 @ REPLACE INTO config VALUES('footer','<div class="footer"> 420 @ Fossil version $manifest_version $manifest_date 420 @ Fossil version $manifest_version $manifest_date 421 @ </div> 421 @ </div> 422 @ </body></html> 422 @ </body></html> ................................................................................................................................................................................ 512 @ text-decoration: none; 512 @ text-decoration: none; 513 @ } 513 @ } 514 @ div.mainmenu a:hover { 514 @ div.mainmenu a:hover { 515 @ color: #eee; 515 @ color: #eee; 516 @ background-color: #333; 516 @ background-color: #333; 517 @ } 517 @ } 518 @ 518 @ 519 @ /* Container for the sub-menu and content so they don"t spread | 519 @ /* Container for the sub-menu and content so they don''t spread 520 @ ** out underneath the main menu */ 520 @ ** out underneath the main menu */ 521 @ #container { 521 @ #container { 522 @ padding-left: 9em; 522 @ padding-left: 9em; 523 @ } 523 @ } 524 @ 524 @ 525 @ /* The submenu bar that *sometimes* appears below the main menu */ 525 @ /* The submenu bar that *sometimes* appears below the main menu */ 526 @ div.submenu { 526 @ div.submenu { ................................................................................................................................................................................ 598 @ padding: 0.2ex 2ex; 598 @ padding: 0.2ex 2ex; 599 @ }'); 599 @ }'); 600 @ REPLACE INTO config VALUES('header','<html> 600 @ REPLACE INTO config VALUES('header','<html> 601 @ <head> 601 @ <head> 602 @ <title>$<project_name>: $<title></title> 602 @ <title>$<project_name>: $<title></title> 603 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" 603 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" 604 @ href="$baseurl/timeline.rss"> 604 @ href="$baseurl/timeline.rss"> 605 @ <link rel="stylesheet" href="$baseurl/style.css" type="text/css" | 605 @ <link rel="stylesheet" href="$baseurl/style.css?black2" type="text/css" 606 @ media="screen"> 606 @ media="screen"> 607 @ </head> 607 @ </head> 608 @ <body> 608 @ <body> 609 @ <div class="header"> 609 @ <div class="header"> 610 @ <div class="logo"> 610 @ <div class="logo"> 611 @ <!-- <img src="$baseurl/logo" alt="logo"> --> 611 @ <!-- <img src="$baseurl/logo" alt="logo"> --> 612 @ <br><nobr>$<project_name></nobr> 612 @ <br><nobr>$<project_name></nobr> ................................................................................................................................................................................ 617 @ puts "Logged in as $login" 617 @ puts "Logged in as $login" 618 @ } else { 618 @ } else { 619 @ puts "Not logged in" 619 @ puts "Not logged in" 620 @ } 620 @ } 621 @ </th1></nobr></div> 621 @ </th1></nobr></div> 622 @ </div> 622 @ </div> 623 @ <div class="mainmenu"><ul><th1> 623 @ <div class="mainmenu"><ul><th1> 624 @ html "<li><a href="$baseurl$index_page">Home</a></li>" | 624 @ html "<li><a href=''$baseurl$index_page''>Home</a></li>" 625 @ if {[anycap jor]} { 625 @ if {[anycap jor]} { 626 @ html "<li><a href="$baseurl/timeline">Timeline</a></li>" | 626 @ html "<li><a href=''$baseurl/timeline''>Timeline</a></li>" 627 @ } 627 @ } 628 @ if {[hascap oh]} { 628 @ if {[hascap oh]} { 629 @ html "<li><a href="$baseurl/dir">Files</a></li>" | 629 @ html "<li><a href=''$baseurl/dir?ci=tip''>Files</a></li>" 630 @ } 630 @ } 631 @ if {[hascap o]} { 631 @ if {[hascap o]} { 632 @ html "<li><a href="$baseurl/leaves">Leaves</a></li>" | 632 @ html "<li><a href=''$baseurl/leaves''>Leaves</a></li>" 633 @ html "<li><a href="$baseurl/brlist">Branches</a></li>" | 633 @ html "<li><a href=''$baseurl/brlist''>Branches</a></li>" 634 @ html "<li><a href="$baseurl/taglist">Tags</a></li>" | 634 @ html "<li><a href=''$baseurl/taglist''>Tags</a></li>" 635 @ } 635 @ } 636 @ if {[hascap r]} { 636 @ if {[hascap r]} { 637 @ html "<li><a href="$baseurl/reportlist">Tickets</a></li>" | 637 @ html "<li><a href=''$baseurl/reportlist''>Tickets</a></li>" 638 @ } 638 @ } 639 @ if {[hascap j]} { 639 @ if {[hascap j]} { 640 @ html "<li><a href="$baseurl/wiki">Wiki</a></li>" | 640 @ html "<li><a href=''$baseurl/wiki''>Wiki</a></li>" 641 @ } 641 @ } 642 @ if {[hascap s]} { 642 @ if {[hascap s]} { 643 @ html "<li><a href="$baseurl/setup">Admin</a></li>" | 643 @ html "<li><a href=''$baseurl/setup''>Admin</a></li>" 644 @ } elseif {[hascap a]} { 644 @ } elseif {[hascap a]} { 645 @ html "<li><a href="$baseurl/setup_ulist">Users</a></li>" | 645 @ html "<li><a href=''$baseurl/setup_ulist''>Users</a></li>" 646 @ } 646 @ } 647 @ if {[info exists login]} { 647 @ if {[info exists login]} { 648 @ html "<li><a href="$baseurl/login">Logout</a></li>" | 648 @ html "<li><a href=''$baseurl/login''>Logout</a></li>" 649 @ } else { 649 @ } else { 650 @ html "<li><a href="$baseurl/login">Login</a></li>" | 650 @ html "<li><a href=''$baseurl/login''>Login</a></li>" 651 @ } 651 @ } 652 @ </th1></ul></div> 652 @ </th1></ul></div> 653 @ <div id="container"> 653 @ <div id="container"> 654 @ '); 654 @ '); 655 @ REPLACE INTO config VALUES('footer','</div> 655 @ REPLACE INTO config VALUES('footer','</div> 656 @ <div class="footer"> 656 @ <div class="footer"> 657 @ Fossil version $manifest_version $manifest_date 657 @ Fossil version $manifest_version $manifest_date

Changes to src/style.c

186 */ 186 */ 187 const char zDefaultHeader[] = 187 const char zDefaultHeader[] = 188 @ <html> 188 @ <html> 189 @ <head> 189 @ <head> 190 @ <title>$<project_name>: $<title></title> 190 @ <title>$<project_name>: $<title></title> 191 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" 191 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" 192 @ href="$baseurl/timeline.rss"> 192 @ href="$baseurl/timeline.rss"> 193 @ <link rel="stylesheet" href="$baseurl/style.css" type="text/css" | 193 @ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css" 194 @ media="screen"> 194 @ media="screen"> 195 @ </head> 195 @ </head> 196 @ <body> 196 @ <body> 197 @ <div class="header"> 197 @ <div class="header"> 198 @ <div class="logo"> 198 @ <div class="logo"> 199 @ <img src="$baseurl/logo" alt="logo"> 199 @ <img src="$baseurl/logo" alt="logo"> 200 @ <br><nobr>$<project_name></nobr> 200 @ <br><nobr>$<project_name></nobr> ................................................................................................................................................................................ 210 @ </div> 210 @ </div> 211 @ <div class="mainmenu"><th1> 211 @ <div class="mainmenu"><th1> 212 @ html "<a href='$baseurl$index_page'>Home</a> " 212 @ html "<a href='$baseurl$index_page'>Home</a> " 213 @ if {[anycap jor]} { 213 @ if {[anycap jor]} { 214 @ html "<a href='$baseurl/timeline'>Timeline</a> " 214 @ html "<a href='$baseurl/timeline'>Timeline</a> " 215 @ } 215 @ } 216 @ if {[hascap oh]} { 216 @ if {[hascap oh]} { 217 @ html "<a href='$baseurl/dir'>Files</a> " | 217 @ html "<a href='$baseurl/dir?ci=tip'>Files</a> " 218 @ } 218 @ } 219 @ if {[hascap o]} { 219 @ if {[hascap o]} { 220 @ html "<a href='$baseurl/leaves'>Leaves</a> " 220 @ html "<a href='$baseurl/leaves'>Leaves</a> " 221 @ html "<a href='$baseurl/brlist'>Branches</a> " 221 @ html "<a href='$baseurl/brlist'>Branches</a> " 222 @ html "<a href='$baseurl/taglist'>Tags</a> " 222 @ html "<a href='$baseurl/taglist'>Tags</a> " 223 @ } 223 @ } 224 @ if {[hascap r]} { 224 @ if {[hascap r]} {

Changes to src/tag.c

198 case TAG_USER: { 198 case TAG_USER: { 199 zCol = "euser"; 199 zCol = "euser"; 200 break; 200 break; 201 } 201 } 202 } 202 } 203 if( zCol ){ 203 if( zCol ){ 204 db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid); 204 db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid); > 205 if( tagid==TAG_COMMENT ){ > 206 char *zCopy = mprintf("%s", zValue); > 207 wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE); > 208 free(zCopy); > 209 } 205 } 210 } 206 if( tagid==TAG_DATE ){ 211 if( tagid==TAG_DATE ){ 207 db_multi_exec("UPDATE event SET mtime=julianday(%Q) WHERE objid=%d", 212 db_multi_exec("UPDATE event SET mtime=julianday(%Q) WHERE objid=%d", 208 zValue, rid); 213 zValue, rid); 209 } 214 } 210 if( tagtype==0 || tagtype==2 ){ 215 if( tagtype==0 || tagtype==2 ){ 211 tag_propagate(rid, tagid, tagtype, rid, zValue, mtime); 216 tag_propagate(rid, tagid, tagtype, rid, zValue, mtime); ................................................................................................................................................................................ 561 566 562 login_check_credentials(); 567 login_check_credentials(); 563 if( !g.okRead ){ login_needed(); return; } 568 if( !g.okRead ){ login_needed(); return; } 564 569 565 style_header("Tagged Check-ins"); 570 style_header("Tagged Check-ins"); 566 style_submenu_element("List", "List", "taglist"); 571 style_submenu_element("List", "List", "taglist"); 567 login_anonymous_available(); 572 login_anonymous_available(); 568 @ <h2>Check-ins with non-propagating tags:</t2> | 573 @ <h2>Check-ins with non-propagating tags:</h2> 569 db_prepare(&q, 574 db_prepare(&q, 570 "%s AND blob.rid IN (SELECT rid FROM tagxref" 575 "%s AND blob.rid IN (SELECT rid FROM tagxref" 571 " WHERE tagtype=1 AND srcid>0" 576 " WHERE tagtype=1 AND srcid>0" 572 " AND tagid IN (SELECT tagid FROM tag " 577 " AND tagid IN (SELECT tagid FROM tag " 573 " WHERE tagname GLOB 'sym-*'))" 578 " WHERE tagname GLOB 'sym-*'))" 574 " ORDER BY event.mtime DESC", 579 " ORDER BY event.mtime DESC", 575 timeline_query_for_www() 580 timeline_query_for_www()

Changes to src/timeline.c

198 wikiFlags = WIKI_INLINE | WIKI_NOBLOCK; 198 wikiFlags = WIKI_INLINE | WIKI_NOBLOCK; 199 } 199 } 200 if( tmFlags & TIMELINE_GRAPH ){ 200 if( tmFlags & TIMELINE_GRAPH ){ 201 pGraph = graph_init(); 201 pGraph = graph_init(); 202 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> 202 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> 203 } 203 } 204 204 205 db_multi_exec( < 206 "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);" < 207 "DELETE FROM seen;" < 208 ); < 209 @ <table cellspacing=0 border=0 cellpadding=0> 205 @ <table cellspacing=0 border=0 cellpadding=0> 210 blob_zero(&comment); 206 blob_zero(&comment); 211 while( db_step(pQuery)==SQLITE_ROW ){ 207 while( db_step(pQuery)==SQLITE_ROW ){ 212 int rid = db_column_int(pQuery, 0); 208 int rid = db_column_int(pQuery, 0); 213 const char *zUuid = db_column_text(pQuery, 1); 209 const char *zUuid = db_column_text(pQuery, 1); 214 int nPChild = db_column_int(pQuery, 5); 210 int nPChild = db_column_int(pQuery, 5); 215 int nParent = db_column_int(pQuery, 6); 211 int nParent = db_column_int(pQuery, 6); ................................................................................................................................................................................ 239 @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr> 235 @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr> 240 suppressCnt = 0; 236 suppressCnt = 0; 241 } 237 } 242 if( strcmp(zType,"div")==0 ){ 238 if( strcmp(zType,"div")==0 ){ 243 @ <tr><td colspan=3><hr></td></tr> 239 @ <tr><td colspan=3><hr></td></tr> 244 continue; 240 continue; 245 } 241 } 246 db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid); < 247 if( memcmp(zDate, zPrevDate, 10) ){ 242 if( memcmp(zDate, zPrevDate, 10) ){ 248 sprintf(zPrevDate, "%.10s", zDate); 243 sprintf(zPrevDate, "%.10s", zDate); 249 @ <tr><td> 244 @ <tr><td> 250 @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div> 245 @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div> 251 @ </td></tr> 246 @ </td></tr> 252 } 247 } 253 memcpy(zTime, &zDate[11], 5); 248 memcpy(zTime, &zDate[11], 5); 254 zTime[5] = 0; 249 zTime[5] = 0; 255 @ <tr> 250 @ <tr> 256 @ <td valign="top" align="right">%s(zTime)</td> 251 @ <td valign="top" align="right">%s(zTime)</td> 257 @ <td width="20" align="left" valign="top"> 252 @ <td width="20" align="left" valign="top"> > 253 if( pGraph && zType[0]=='c' ){ > 254 int nParent = 0; > 255 int aParent[32]; > 256 const char *zBr; > 257 int gidx; > 258 static Stmt qparent; > 259 static Stmt qbranch; > 260 db_static_prepare(&qparent, > 261 "SELECT pid FROM plink WHERE cid=:rid ORDER BY isprim DESC" > 262 ); > 263 db_static_prepare(&qbranch, > 264 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", > 265 TAG_BRANCH > 266 ); > 267 db_bind_int(&qparent, ":rid", rid); > 268 while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){ > 269 aParent[nParent++] = db_column_int(&qparent, 0); > 270 } > 271 db_reset(&qparent); > 272 db_bind_int(&qbranch, ":rid", rid); > 273 if( db_step(&qbranch)==SQLITE_ROW ){ > 274 zBr = db_column_text(&qbranch, 0); > 275 }else{ > 276 zBr = "trunk"; > 277 } > 278 gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr); > 279 db_reset(&qbranch); 258 @ <div id="m%d(rid)"></div> | 280 @ <div id="m%d(gidx)"></div> > 281 } 259 if( zBgClr && zBgClr[0] ){ 282 if( zBgClr && zBgClr[0] ){ 260 @ <td valign="top" align="left" bgcolor="%h(zBgClr)"> 283 @ <td valign="top" align="left" bgcolor="%h(zBgClr)"> 261 }else{ 284 }else{ 262 @ <td valign="top" align="left"> 285 @ <td valign="top" align="left"> 263 } 286 } 264 if( zType[0]=='c' ){ 287 if( zType[0]=='c' ){ 265 const char *azTag[5]; 288 const char *azTag[5]; ................................................................................................................................................................................ 288 } 311 } 289 if( nTag>0 ){ 312 if( nTag>0 ){ 290 int i; 313 int i; 291 for(i=0; i<nTag; i++){ 314 for(i=0; i<nTag; i++){ 292 @ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b> 315 @ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b> 293 } 316 } 294 } 317 } 295 if( pGraph ){ < 296 int nParent = 0; < 297 int aParent[32]; < 298 const char *zBr; < 299 static Stmt qparent; < 300 static Stmt qbranch; < 301 db_static_prepare(&qparent, < 302 "SELECT pid FROM plink WHERE cid=:rid ORDER BY isprim DESC" < 303 ); < 304 db_static_prepare(&qbranch, < 305 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", < 306 TAG_BRANCH < 307 ); < 308 db_bind_int(&qparent, ":rid", rid); < 309 while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){ < 310 aParent[nParent++] = db_column_int(&qparent, 0); < 311 } < 312 db_reset(&qparent); < 313 db_bind_int(&qbranch, ":rid", rid); < 314 if( db_step(&qbranch)==SQLITE_ROW ){ < 315 zBr = db_column_text(&qbranch, 0); < 316 }else{ < 317 zBr = "trunk"; < 318 } < 319 graph_add_row(pGraph, rid, isLeaf, nParent, aParent, zBr); < 320 db_reset(&qbranch); < 321 } < 322 }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ 318 }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ 323 hyperlink_to_uuid(zUuid); 319 hyperlink_to_uuid(zUuid); 324 } 320 } 325 db_column_blob(pQuery, commentColumn, &comment); 321 db_column_blob(pQuery, commentColumn, &comment); 326 if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ 322 if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ 327 Blob truncated; 323 Blob truncated; 328 blob_zero(&truncated); 324 blob_zero(&truncated); ................................................................................................................................................................................ 356 graph_free(pGraph); 352 graph_free(pGraph); 357 pGraph = 0; 353 pGraph = 0; 358 }else{ 354 }else{ 359 @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div> 355 @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div> 360 } 356 } 361 } 357 } 362 @ </table> 358 @ </table> > 359 timeline_output_graph_javascript(pGraph); > 360 } > 361 > 362 /* > 363 ** Generate all of the necessary javascript to generate a timeline > 364 ** graph. > 365 */ > 366 void timeline_output_graph_javascript(GraphContext *pGraph){ 363 if( pGraph && pGraph->nErr==0 ){ 367 if( pGraph && pGraph->nErr==0 ){ 364 GraphRow *pRow; 368 GraphRow *pRow; 365 int i; 369 int i; 366 char cSep; 370 char cSep; 367 @ <script type="text/JavaScript"> 371 @ <script type="text/JavaScript"> 368 cgi_printf("var rowinfo = [\n"); 372 cgi_printf("var rowinfo = [\n"); 369 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ 373 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ 370 cgi_printf("{id:\"m%d\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:", 374 cgi_printf("{id:\"m%d\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:", 371 pRow->rid, | 375 pRow->idx, 372 pRow->iRail, 376 pRow->iRail, 373 pRow->bDescender, 377 pRow->bDescender, 374 pRow->mergeOut, 378 pRow->mergeOut, 375 pRow->mergeUpto, 379 pRow->mergeUpto, 376 pRow->aiRaiser[pRow->iRail] 380 pRow->aiRaiser[pRow->iRail] 377 ); 381 ); 378 cSep = '['; 382 cSep = '['; ................................................................................................................................................................................ 696 tagid = 0; 700 tagid = 0; 697 } 701 } 698 if( zType[0]=='a' ){ 702 if( zType[0]=='a' ){ 699 tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH; 703 tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH; 700 }else{ 704 }else{ 701 tmFlags = TIMELINE_GRAPH; 705 tmFlags = TIMELINE_GRAPH; 702 } 706 } 703 if( P("ng")