Fossil SCM

Merged in trunk for various menu item fixes before deployment to my server.

stephan 2020-05-12 15:10 fileedit-ajaxify merge
Commit 8cc7953b9a709167f019fc7fe8242aef19e9f0dc5140547050509884af8a6021
+14
--- src/branch.c
+++ src/branch.c
@@ -18,10 +18,24 @@
1818
** This file contains code used to create new branches within a repository.
1919
*/
2020
#include "config.h"
2121
#include "branch.h"
2222
#include <assert.h>
23
+
24
+/*
25
+** Return true if zBr is the branch name associated with check-in with
26
+** blob.uuid value of zUuid
27
+*/
28
+int branch_includes_uuid(const char *zBr, const char *zUuid){
29
+ return db_exists(
30
+ "SELECT 1 FROM tagxref, blob"
31
+ " WHERE blob.uuid=%Q AND tagxref.rid=blob.rid"
32
+ " AND tagxref.value=%Q AND tagxref.tagtype>0"
33
+ " AND tagxref.tagid=%d",
34
+ zUuid, zBr, TAG_BRANCH
35
+ );
36
+}
2337
2438
/*
2539
** If RID refers to a check-in, return the name of the branch for that
2640
** check-in.
2741
**
2842
--- src/branch.c
+++ src/branch.c
@@ -18,10 +18,24 @@
18 ** This file contains code used to create new branches within a repository.
19 */
20 #include "config.h"
21 #include "branch.h"
22 #include <assert.h>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
24 /*
25 ** If RID refers to a check-in, return the name of the branch for that
26 ** check-in.
27 **
28
--- src/branch.c
+++ src/branch.c
@@ -18,10 +18,24 @@
18 ** This file contains code used to create new branches within a repository.
19 */
20 #include "config.h"
21 #include "branch.h"
22 #include <assert.h>
23
24 /*
25 ** Return true if zBr is the branch name associated with check-in with
26 ** blob.uuid value of zUuid
27 */
28 int branch_includes_uuid(const char *zBr, const char *zUuid){
29 return db_exists(
30 "SELECT 1 FROM tagxref, blob"
31 " WHERE blob.uuid=%Q AND tagxref.rid=blob.rid"
32 " AND tagxref.value=%Q AND tagxref.tagtype>0"
33 " AND tagxref.tagid=%d",
34 zUuid, zBr, TAG_BRANCH
35 );
36 }
37
38 /*
39 ** If RID refers to a check-in, return the name of the branch for that
40 ** check-in.
41 **
42
+119 -79
--- src/browse.c
+++ src/browse.c
@@ -59,10 +59,18 @@
5959
zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]);
6060
sqlite3_result_text(context, zOut, i-n+1, sqlite3_free);
6161
}
6262
}
6363
64
+/*
65
+** Flag arguments for hyperlinked_path()
66
+*/
67
+#if INTERFACE
68
+# define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */
69
+# define LINKPATH_FILE 0x0002 /* Link final term to /file */
70
+#endif
71
+
6472
/*
6573
** Given a pathname which is a relative path from the root of
6674
** the repository to a file or directory, compute a string which
6775
** is an HTML rendering of that path with hyperlinks on each
6876
** directory component of the path where the hyperlink redirects
@@ -76,29 +84,36 @@
7684
void hyperlinked_path(
7785
const char *zPath, /* Path to render */
7886
Blob *pOut, /* Write into this blob */
7987
const char *zCI, /* check-in name, or NULL */
8088
const char *zURI, /* "dir" or "tree" */
81
- const char *zREx /* Extra query parameters */
89
+ const char *zREx, /* Extra query parameters */
90
+ unsigned int mFlags /* Extra flags */
8291
){
8392
int i, j;
8493
char *zSep = "";
8594
8695
for(i=0; zPath[i]; i=j){
8796
for(j=i; zPath[j] && zPath[j]!='/'; j++){}
88
- if( zPath[j] && g.perm.Hyperlink ){
89
- if( zCI ){
90
- char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI);
91
- blob_appendf(pOut, "%s%z%#h</a>",
92
- zSep, zLink, j-i, &zPath[i]);
93
- }else{
94
- char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
95
- blob_appendf(pOut, "%s%z%#h</a>",
96
- zSep, zLink, j-i, &zPath[i]);
97
- }
98
- }else{
99
- blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
97
+ if( zPath[j]==0 ){
98
+ if( mFlags & LINKPATH_FILE ){
99
+ zURI = "file";
100
+ }else if( mFlags & LINKPATH_FINFO ){
101
+ zURI = "finfo";
102
+ }else{
103
+ blob_appendf(pOut, "/%h", zPath+i);
104
+ break;
105
+ }
106
+ }
107
+ if( zCI ){
108
+ char *zLink = href("%R/%s?name=%#T%s&ci=%T", zURI, j, zPath, zREx,zCI);
109
+ blob_appendf(pOut, "%s%z%#h</a>",
110
+ zSep, zLink, j-i, &zPath[i]);
111
+ }else{
112
+ char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
113
+ blob_appendf(pOut, "%s%z%#h</a>",
114
+ zSep, zLink, j-i, &zPath[i]);
100115
}
101116
zSep = "/";
102117
while( zPath[j]=='/' ){ j++; }
103118
}
104119
}
@@ -127,17 +142,17 @@
127142
char *zPrefix;
128143
Stmt q;
129144
const char *zCI = P("ci");
130145
int rid = 0;
131146
char *zUuid = 0;
132
- Blob dirname;
133147
Manifest *pM = 0;
134148
const char *zSubdirLink;
135149
int linkTrunk = 1;
136150
int linkTip = 1;
137151
HQuery sURI;
138152
int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
153
+ int isBranchCI = 0; /* True if ci= refers to a branch name */
139154
char *zHeader = 0;
140155
141156
if( zCI && strlen(zCI)==0 ){ zCI = 0; }
142157
if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
143158
login_check_credentials();
@@ -157,22 +172,29 @@
157172
int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
158173
linkTrunk = trunkRid && rid != trunkRid;
159174
linkTip = rid != symbolic_name_to_rid("tip", "ci");
160175
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
161176
isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
177
+ isBranchCI = branch_includes_uuid(zCI, zUuid);
162178
}else{
163179
zCI = 0;
164180
}
165181
}
166182
167183
assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
168
- if( isSymbolicCI ) {
169
- zHeader = mprintf("%s at %s", (zD ? zD : "Files"), zCI);
170
- }else if( zUuid && strlen(zUuid) ){
171
- zHeader = mprintf("%s at [%S]", (zD ? zD : "Files"), zUuid);
184
+ if( zD==0 ){
185
+ if( zCI ){
186
+ zHeader = mprintf("Top-level Files of %s", zCI);
187
+ }else{
188
+ zHeader = mprintf("All Top-level Files");
189
+ }
172190
}else{
173
- zHeader = mprintf("%s", (zD ? zD : "All Files"));
191
+ if( zCI ){
192
+ zHeader = mprintf("Files in %s/ of %s", zD, zCI);
193
+ }else{
194
+ zHeader = mprintf("All File in %s/", zD);
195
+ }
174196
}
175197
style_header("%s", zHeader);
176198
fossil_free(zHeader);
177199
style_adunit_config(ADUNIT_RIGHT_OK);
178200
sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
@@ -179,47 +201,49 @@
179201
pathelementFunc, 0, 0);
180202
url_initialize(&sURI, "dir");
181203
cgi_query_parameters_to_url(&sURI);
182204
183205
/* Compute the title of the page */
184
- blob_zero(&dirname);
185206
if( zD ){
186
- blob_append(&dirname, "in directory ", -1);
187
- hyperlinked_path(zD, &dirname, zCI, "dir", "");
207
+ Blob dirname;
208
+ blob_init(&dirname, 0, 0);
209
+ hyperlinked_path(zD, &dirname, zCI, "dir", "", 0);
210
+ @ <h2>Files in directory %s(blob_str(&dirname)) \
211
+ blob_reset(&dirname);
188212
zPrefix = mprintf("%s/", zD);
189213
style_submenu_element("Top-Level", "%s",
190214
url_render(&sURI, "name", 0, 0, 0));
191215
}else{
192
- blob_append(&dirname, "in the top-level directory", -1);
216
+ @ <h2>Files in the top-level directory \
193217
zPrefix = "";
194218
}
219
+ if( zCI ){
220
+ if( fossil_strcmp(zCI,"tip")==0 ){
221
+ @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a></h2>
222
+ }else if( isBranchCI ){
223
+ @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \
224
+ @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
225
+ }else {
226
+ @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2>
227
+ }
228
+ zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix);
229
+ if( nD==0 ){
230
+ style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
231
+ }
232
+ }else{
233
+ @ in any check-in</h2>
234
+ zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
235
+ }
195236
if( linkTrunk ){
196237
style_submenu_element("Trunk", "%s",
197238
url_render(&sURI, "ci", "trunk", 0, 0));
198239
}
199240
if( linkTip ){
200241
style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
201242
}
202
- if( zCI ){
203
- @ <h2>Files at check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>]
204
- @ %s(blob_str(&dirname))
205
- if( zD ){
206
- @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
207
- }
208
- @ </h2>
209
- zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix);
210
- if( nD==0 ){
211
- style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid);
212
- }
213
- }else{
214
- @ <h2>All files known in the repository
215
- @ %s(blob_str(&dirname))
216
- if( zD ){
217
- @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
218
- }
219
- @ </h2>
220
- zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
243
+ if( zD ){
244
+ style_submenu_element("History","%R/timeline?chng=%T/*", zD);
221245
}
222246
style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
223247
style_submenu_element("Tree-View", "%s",
224248
url_render(&sURI, "type", "tree", 0, 0));
225249
@@ -296,11 +320,11 @@
296320
zFN++;
297321
@ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
298322
}else{
299323
const char *zLink;
300324
if( zCI ){
301
- zLink = href("%R/file?name=%T%T&ci=%!S",zPrefix,zFN,zCI);
325
+ zLink = href("%R/file?name=%T%T&ci=%T",zPrefix,zFN,zCI);
302326
}else{
303327
zLink = href("%R/finfo?name=%T%T",zPrefix,zFN);
304328
}
305329
@ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li>
306330
}
@@ -626,10 +650,11 @@
626650
int startExpanded; /* True to start out with the tree expanded */
627651
int showDirOnly; /* Show directories only. Omit files */
628652
int nDir = 0; /* Number of directories. Used for ID attributes */
629653
char *zProjectName = db_get("project-name", 0);
630654
int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */
655
+ int isBranchCI = 0; /* ci= refers to a branch name */
631656
char *zHeader = 0;
632657
633658
if( zCI && strlen(zCI)==0 ){ zCI = 0; }
634659
if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; }
635660
memset(&sTree, 0, sizeof(sTree));
@@ -675,10 +700,11 @@
675700
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
676701
rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
677702
zNow = db_text("", "SELECT datetime(mtime,toLocal())"
678703
" FROM event WHERE objid=%d", rid);
679704
isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
705
+ isBranchCI = branch_includes_uuid(zCI, zUuid);
680706
}else{
681707
zCI = 0;
682708
}
683709
}
684710
if( zCI==0 ){
@@ -685,41 +711,42 @@
685711
rNow = db_double(0.0, "SELECT max(mtime) FROM event");
686712
zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
687713
}
688714
689715
assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
690
- if( isSymbolicCI ) {
691
- zHeader = mprintf("%s at %s",
692
- (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zCI);
693
- }else if( zUuid && strlen(zUuid) ){
694
- zHeader = mprintf("%s at [%S]",
695
- (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zUuid);
716
+ if( zD==0 ){
717
+ if( zCI ){
718
+ zHeader = mprintf("Top-level Files of %s", zCI);
719
+ }else{
720
+ zHeader = mprintf("All Top-level Files");
721
+ }
696722
}else{
697
- zHeader = mprintf("%s",
698
- (zD ? zD : (showDirOnly ?"All Folders Hierarchy":"All Files Tree-View")));
723
+ if( zCI ){
724
+ zHeader = mprintf("Files in %s/ of %s", zD, zCI);
725
+ }else{
726
+ zHeader = mprintf("All File in %s/", zD);
727
+ }
699728
}
700729
style_header("%s", zHeader);
701730
fossil_free(zHeader);
702731
703732
/* Compute the title of the page */
704733
blob_zero(&dirname);
705734
if( zD ){
706735
blob_append(&dirname, "within directory ", -1);
707
- hyperlinked_path(zD, &dirname, zCI, "tree", zREx);
736
+ hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0);
708737
if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
709738
style_submenu_element("Top-Level", "%s",
710739
url_render(&sURI, "name", 0, 0, 0));
711
- }else{
712
- if( zRE ){
713
- blob_appendf(&dirname, "matching \"%s\"", zRE);
714
- }
740
+ }else if( zRE ){
741
+ blob_appendf(&dirname, "matching \"%s\"", zRE);
715742
}
716743
style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0);
717744
if( zCI ){
718745
style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
719746
if( nD==0 && !showDirOnly ){
720
- style_submenu_element("File Ages", "%R/fileage?name=%s", zUuid);
747
+ style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
721748
}
722749
}
723750
if( linkTrunk ){
724751
style_submenu_element("Trunk", "%s",
725752
url_render(&sURI, "ci", "trunk", 0, 0));
@@ -774,10 +801,11 @@
774801
tree_add_node(&sTree, zName, zUuid, mtime);
775802
nFile++;
776803
}
777804
db_finalize(&q);
778805
}
806
+ style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
779807
780808
if( showDirOnly ){
781809
for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
782810
if( p->pChild!=0 && p->nFullName>nD ) nFile++;
783811
}
@@ -784,18 +812,24 @@
784812
zObjType = "Folders";
785813
}else{
786814
zObjType = "Files";
787815
}
788816
789
- style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
790
-
791
- if( zCI ){
792
- @ <h2>%s(zObjType) at check-in
793
- if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
794
- @ "%h(zCI)"
795
- }
796
- @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))
817
+ if( zCI && strcmp(zCI,"tip")==0 ){
818
+ @ <h2>%s(zObjType) in the %z(href("%R/info?name=tip"))latest check-in</a>
819
+ }else if( isBranchCI ){
820
+ @ <h2>%s(zObjType) in the %z(href("%R/info?name=%T",zCI))latest check-in\
821
+ @ </a> for branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a>
822
+ if( blob_size(&dirname) ){
823
+ @ and %s(blob_str(&dirname))</h2>
824
+ }
825
+ }else if( zCI ){
826
+ @ <h2>%s(zObjType) for check-in \
827
+ @ %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2>
828
+ if( blob_size(&dirname) ){
829
+ @ and %s(blob_str(&dirname))</h2>
830
+ }
797831
}else{
798832
int n = db_int(0, "SELECT count(*) FROM plink");
799833
@ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname))
800834
}
801835
if( useMtime ){
@@ -853,11 +887,11 @@
853887
nDir++;
854888
}else if( !showDirOnly ){
855889
const char *zFileClass = fileext_class(p->zName);
856890
char *zLink;
857891
if( zCI ){
858
- zLink = href("%R/file?name=%T&ci=%!S",p->zFullName,zCI);
892
+ zLink = href("%R/file?name=%T&ci=%T",p->zFullName,zCI);
859893
}else{
860894
zLink = href("%R/finfo?name=%T",p->zFullName);
861895
}
862896
@ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline">
863897
@ %z(zLink)%h(p->zName)</a>
@@ -1030,10 +1064,11 @@
10301064
int rid;
10311065
const char *zName;
10321066
const char *zGlob;
10331067
const char *zUuid;
10341068
const char *zNow; /* Time of check-in */
1069
+ int isBranchCI; /* name= is a branch name */
10351070
int showId = PB("showid");
10361071
Stmt q1, q2;
10371072
double baseTime;
10381073
login_check_credentials();
10391074
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -1043,28 +1078,34 @@
10431078
rid = symbolic_name_to_rid(zName, "ci");
10441079
if( rid==0 ){
10451080
fossil_fatal("not a valid check-in: %s", zName);
10461081
}
10471082
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
1083
+ isBranchCI = branch_includes_uuid(zName,zUuid);
10481084
baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid);
10491085
zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event"
10501086
" WHERE objid=%d", rid);
10511087
style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName);
10521088
style_header("File Ages");
10531089
zGlob = P("glob");
10541090
compute_fileage(rid,zGlob);
10551091
db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);");
10561092
1057
- @ <h1>Files in
1058
- @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a>
1093
+ if( fossil_strcmp(zName,"tip")==0 ){
1094
+ @ <h1>Files in the %z(href("%R/info?name=tip"))latest check-in</a>
1095
+ }else if( isBranchCI ){
1096
+ @ <h1>Files in the %z(href("%R/info?name=%T",zName))latest check-in</a>
1097
+ @ of branch %z(href("%R/timeline?r=%T",zName))%h(zName)</a>
1098
+ }else{
1099
+ @ <h1>Files in check-in %z(href("%R/info?name=%T",zName))%h(zName)</a>
1100
+ }
10591101
if( zGlob && zGlob[0] ){
10601102
@ that match "%h(zGlob)"
10611103
}
10621104
@ ordered by age</h1>
10631105
@
1064
- @ <p>File ages are expressed relative to the
1065
- @ %z(href("%R/ci/%!S",zUuid))[%S(zUuid)]</a> check-in time of
1106
+ @ <p>File ages are expressed relative to the check-in time of
10661107
@ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p>
10671108
@
10681109
@ <div class='fileage'><table>
10691110
@ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr>
10701111
db_prepare(&q1,
@@ -1079,14 +1120,13 @@
10791120
" AND blob.rid=event.objid\n"
10801121
" ORDER BY event.mtime DESC;",
10811122
TAG_BRANCH
10821123
);
10831124
db_prepare(&q2,
1084
- "SELECT blob.uuid, filename.name, fileage.fid\n"
1085
- " FROM fileage, blob, filename\n"
1125
+ "SELECT filename.name, fileage.fid\n"
1126
+ " FROM fileage, filename\n"
10861127
" WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid"
1087
- " AND blob.rid=fileage.fid;"
10881128
);
10891129
while( db_step(&q1)==SQLITE_ROW ){
10901130
double age = baseTime - db_column_double(&q1, 0);
10911131
int mid = db_column_int(&q1, 1);
10921132
const char *zUuid = db_column_text(&q1, 2);
@@ -1096,24 +1136,24 @@
10961136
char *zAge = human_readable_age(age);
10971137
@ <tr><td>%s(zAge)</td>
10981138
@ <td>
10991139
db_bind_int(&q2, ":mid", mid);
11001140
while( db_step(&q2)==SQLITE_ROW ){
1101
- const char *zFUuid = db_column_text(&q2,0);
1102
- const char *zFile = db_column_text(&q2,1);
1103
- int fid = db_column_int(&q2,2);
1141
+ const char *zFile = db_column_text(&q2,0);
1142
+ @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \
11041143
if( showId ){
1105
- @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br />
1144
+ int fid = db_column_int(&q2,1);
1145
+ @ (%d(fid))<br />
11061146
}else{
1107
- @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br />
1147
+ @ </a><br />
11081148
}
11091149
}
11101150
db_reset(&q2);
11111151
@ </td>
11121152
@ <td>
11131153
@ %W(zComment)
1114
- @ (check-in:&nbsp;%z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>,
1154
+ @ (check-in:&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>,
11151155
if( showId ){
11161156
@ id: %d(mid)
11171157
}
11181158
@ user:&nbsp;%z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>,
11191159
@ branch:&nbsp;\
11201160
--- src/browse.c
+++ src/browse.c
@@ -59,10 +59,18 @@
59 zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]);
60 sqlite3_result_text(context, zOut, i-n+1, sqlite3_free);
61 }
62 }
63
 
 
 
 
 
 
 
 
64 /*
65 ** Given a pathname which is a relative path from the root of
66 ** the repository to a file or directory, compute a string which
67 ** is an HTML rendering of that path with hyperlinks on each
68 ** directory component of the path where the hyperlink redirects
@@ -76,29 +84,36 @@
76 void hyperlinked_path(
77 const char *zPath, /* Path to render */
78 Blob *pOut, /* Write into this blob */
79 const char *zCI, /* check-in name, or NULL */
80 const char *zURI, /* "dir" or "tree" */
81 const char *zREx /* Extra query parameters */
 
82 ){
83 int i, j;
84 char *zSep = "";
85
86 for(i=0; zPath[i]; i=j){
87 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
88 if( zPath[j] && g.perm.Hyperlink ){
89 if( zCI ){
90 char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI);
91 blob_appendf(pOut, "%s%z%#h</a>",
92 zSep, zLink, j-i, &zPath[i]);
93 }else{
94 char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
95 blob_appendf(pOut, "%s%z%#h</a>",
96 zSep, zLink, j-i, &zPath[i]);
97 }
98 }else{
99 blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
 
 
 
 
 
 
100 }
101 zSep = "/";
102 while( zPath[j]=='/' ){ j++; }
103 }
104 }
@@ -127,17 +142,17 @@
127 char *zPrefix;
128 Stmt q;
129 const char *zCI = P("ci");
130 int rid = 0;
131 char *zUuid = 0;
132 Blob dirname;
133 Manifest *pM = 0;
134 const char *zSubdirLink;
135 int linkTrunk = 1;
136 int linkTip = 1;
137 HQuery sURI;
138 int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
 
139 char *zHeader = 0;
140
141 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
142 if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
143 login_check_credentials();
@@ -157,22 +172,29 @@
157 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
158 linkTrunk = trunkRid && rid != trunkRid;
159 linkTip = rid != symbolic_name_to_rid("tip", "ci");
160 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
161 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
 
162 }else{
163 zCI = 0;
164 }
165 }
166
167 assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
168 if( isSymbolicCI ) {
169 zHeader = mprintf("%s at %s", (zD ? zD : "Files"), zCI);
170 }else if( zUuid && strlen(zUuid) ){
171 zHeader = mprintf("%s at [%S]", (zD ? zD : "Files"), zUuid);
 
 
172 }else{
173 zHeader = mprintf("%s", (zD ? zD : "All Files"));
 
 
 
 
174 }
175 style_header("%s", zHeader);
176 fossil_free(zHeader);
177 style_adunit_config(ADUNIT_RIGHT_OK);
178 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
@@ -179,47 +201,49 @@
179 pathelementFunc, 0, 0);
180 url_initialize(&sURI, "dir");
181 cgi_query_parameters_to_url(&sURI);
182
183 /* Compute the title of the page */
184 blob_zero(&dirname);
185 if( zD ){
186 blob_append(&dirname, "in directory ", -1);
187 hyperlinked_path(zD, &dirname, zCI, "dir", "");
 
 
 
188 zPrefix = mprintf("%s/", zD);
189 style_submenu_element("Top-Level", "%s",
190 url_render(&sURI, "name", 0, 0, 0));
191 }else{
192 blob_append(&dirname, "in the top-level directory", -1);
193 zPrefix = "";
194 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195 if( linkTrunk ){
196 style_submenu_element("Trunk", "%s",
197 url_render(&sURI, "ci", "trunk", 0, 0));
198 }
199 if( linkTip ){
200 style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
201 }
202 if( zCI ){
203 @ <h2>Files at check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>]
204 @ %s(blob_str(&dirname))
205 if( zD ){
206 @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
207 }
208 @ </h2>
209 zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix);
210 if( nD==0 ){
211 style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid);
212 }
213 }else{
214 @ <h2>All files known in the repository
215 @ %s(blob_str(&dirname))
216 if( zD ){
217 @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
218 }
219 @ </h2>
220 zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
221 }
222 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
223 style_submenu_element("Tree-View", "%s",
224 url_render(&sURI, "type", "tree", 0, 0));
225
@@ -296,11 +320,11 @@
296 zFN++;
297 @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
298 }else{
299 const char *zLink;
300 if( zCI ){
301 zLink = href("%R/file?name=%T%T&ci=%!S",zPrefix,zFN,zCI);
302 }else{
303 zLink = href("%R/finfo?name=%T%T",zPrefix,zFN);
304 }
305 @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li>
306 }
@@ -626,10 +650,11 @@
626 int startExpanded; /* True to start out with the tree expanded */
627 int showDirOnly; /* Show directories only. Omit files */
628 int nDir = 0; /* Number of directories. Used for ID attributes */
629 char *zProjectName = db_get("project-name", 0);
630 int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */
 
631 char *zHeader = 0;
632
633 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
634 if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; }
635 memset(&sTree, 0, sizeof(sTree));
@@ -675,10 +700,11 @@
675 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
676 rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
677 zNow = db_text("", "SELECT datetime(mtime,toLocal())"
678 " FROM event WHERE objid=%d", rid);
679 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
 
680 }else{
681 zCI = 0;
682 }
683 }
684 if( zCI==0 ){
@@ -685,41 +711,42 @@
685 rNow = db_double(0.0, "SELECT max(mtime) FROM event");
686 zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
687 }
688
689 assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
690 if( isSymbolicCI ) {
691 zHeader = mprintf("%s at %s",
692 (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zCI);
693 }else if( zUuid && strlen(zUuid) ){
694 zHeader = mprintf("%s at [%S]",
695 (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zUuid);
696 }else{
697 zHeader = mprintf("%s",
698 (zD ? zD : (showDirOnly ?"All Folders Hierarchy":"All Files Tree-View")));
 
 
 
699 }
700 style_header("%s", zHeader);
701 fossil_free(zHeader);
702
703 /* Compute the title of the page */
704 blob_zero(&dirname);
705 if( zD ){
706 blob_append(&dirname, "within directory ", -1);
707 hyperlinked_path(zD, &dirname, zCI, "tree", zREx);
708 if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
709 style_submenu_element("Top-Level", "%s",
710 url_render(&sURI, "name", 0, 0, 0));
711 }else{
712 if( zRE ){
713 blob_appendf(&dirname, "matching \"%s\"", zRE);
714 }
715 }
716 style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0);
717 if( zCI ){
718 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
719 if( nD==0 && !showDirOnly ){
720 style_submenu_element("File Ages", "%R/fileage?name=%s", zUuid);
721 }
722 }
723 if( linkTrunk ){
724 style_submenu_element("Trunk", "%s",
725 url_render(&sURI, "ci", "trunk", 0, 0));
@@ -774,10 +801,11 @@
774 tree_add_node(&sTree, zName, zUuid, mtime);
775 nFile++;
776 }
777 db_finalize(&q);
778 }
 
779
780 if( showDirOnly ){
781 for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
782 if( p->pChild!=0 && p->nFullName>nD ) nFile++;
783 }
@@ -784,18 +812,24 @@
784 zObjType = "Folders";
785 }else{
786 zObjType = "Files";
787 }
788
789 style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
790
791 if( zCI ){
792 @ <h2>%s(zObjType) at check-in
793 if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
794 @ "%h(zCI)"
795 }
796 @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))
 
 
 
 
 
 
797 }else{
798 int n = db_int(0, "SELECT count(*) FROM plink");
799 @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname))
800 }
801 if( useMtime ){
@@ -853,11 +887,11 @@
853 nDir++;
854 }else if( !showDirOnly ){
855 const char *zFileClass = fileext_class(p->zName);
856 char *zLink;
857 if( zCI ){
858 zLink = href("%R/file?name=%T&ci=%!S",p->zFullName,zCI);
859 }else{
860 zLink = href("%R/finfo?name=%T",p->zFullName);
861 }
862 @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline">
863 @ %z(zLink)%h(p->zName)</a>
@@ -1030,10 +1064,11 @@
1030 int rid;
1031 const char *zName;
1032 const char *zGlob;
1033 const char *zUuid;
1034 const char *zNow; /* Time of check-in */
 
1035 int showId = PB("showid");
1036 Stmt q1, q2;
1037 double baseTime;
1038 login_check_credentials();
1039 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -1043,28 +1078,34 @@
1043 rid = symbolic_name_to_rid(zName, "ci");
1044 if( rid==0 ){
1045 fossil_fatal("not a valid check-in: %s", zName);
1046 }
1047 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
 
1048 baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid);
1049 zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event"
1050 " WHERE objid=%d", rid);
1051 style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName);
1052 style_header("File Ages");
1053 zGlob = P("glob");
1054 compute_fileage(rid,zGlob);
1055 db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);");
1056
1057 @ <h1>Files in
1058 @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a>
 
 
 
 
 
 
1059 if( zGlob && zGlob[0] ){
1060 @ that match "%h(zGlob)"
1061 }
1062 @ ordered by age</h1>
1063 @
1064 @ <p>File ages are expressed relative to the
1065 @ %z(href("%R/ci/%!S",zUuid))[%S(zUuid)]</a> check-in time of
1066 @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p>
1067 @
1068 @ <div class='fileage'><table>
1069 @ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr>
1070 db_prepare(&q1,
@@ -1079,14 +1120,13 @@
1079 " AND blob.rid=event.objid\n"
1080 " ORDER BY event.mtime DESC;",
1081 TAG_BRANCH
1082 );
1083 db_prepare(&q2,
1084 "SELECT blob.uuid, filename.name, fileage.fid\n"
1085 " FROM fileage, blob, filename\n"
1086 " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid"
1087 " AND blob.rid=fileage.fid;"
1088 );
1089 while( db_step(&q1)==SQLITE_ROW ){
1090 double age = baseTime - db_column_double(&q1, 0);
1091 int mid = db_column_int(&q1, 1);
1092 const char *zUuid = db_column_text(&q1, 2);
@@ -1096,24 +1136,24 @@
1096 char *zAge = human_readable_age(age);
1097 @ <tr><td>%s(zAge)</td>
1098 @ <td>
1099 db_bind_int(&q2, ":mid", mid);
1100 while( db_step(&q2)==SQLITE_ROW ){
1101 const char *zFUuid = db_column_text(&q2,0);
1102 const char *zFile = db_column_text(&q2,1);
1103 int fid = db_column_int(&q2,2);
1104 if( showId ){
1105 @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br />
 
1106 }else{
1107 @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br />
1108 }
1109 }
1110 db_reset(&q2);
1111 @ </td>
1112 @ <td>
1113 @ %W(zComment)
1114 @ (check-in:&nbsp;%z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>,
1115 if( showId ){
1116 @ id: %d(mid)
1117 }
1118 @ user:&nbsp;%z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>,
1119 @ branch:&nbsp;\
1120
--- src/browse.c
+++ src/browse.c
@@ -59,10 +59,18 @@
59 zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]);
60 sqlite3_result_text(context, zOut, i-n+1, sqlite3_free);
61 }
62 }
63
64 /*
65 ** Flag arguments for hyperlinked_path()
66 */
67 #if INTERFACE
68 # define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */
69 # define LINKPATH_FILE 0x0002 /* Link final term to /file */
70 #endif
71
72 /*
73 ** Given a pathname which is a relative path from the root of
74 ** the repository to a file or directory, compute a string which
75 ** is an HTML rendering of that path with hyperlinks on each
76 ** directory component of the path where the hyperlink redirects
@@ -76,29 +84,36 @@
84 void hyperlinked_path(
85 const char *zPath, /* Path to render */
86 Blob *pOut, /* Write into this blob */
87 const char *zCI, /* check-in name, or NULL */
88 const char *zURI, /* "dir" or "tree" */
89 const char *zREx, /* Extra query parameters */
90 unsigned int mFlags /* Extra flags */
91 ){
92 int i, j;
93 char *zSep = "";
94
95 for(i=0; zPath[i]; i=j){
96 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
97 if( zPath[j]==0 ){
98 if( mFlags & LINKPATH_FILE ){
99 zURI = "file";
100 }else if( mFlags & LINKPATH_FINFO ){
101 zURI = "finfo";
102 }else{
103 blob_appendf(pOut, "/%h", zPath+i);
104 break;
105 }
106 }
107 if( zCI ){
108 char *zLink = href("%R/%s?name=%#T%s&ci=%T", zURI, j, zPath, zREx,zCI);
109 blob_appendf(pOut, "%s%z%#h</a>",
110 zSep, zLink, j-i, &zPath[i]);
111 }else{
112 char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
113 blob_appendf(pOut, "%s%z%#h</a>",
114 zSep, zLink, j-i, &zPath[i]);
115 }
116 zSep = "/";
117 while( zPath[j]=='/' ){ j++; }
118 }
119 }
@@ -127,17 +142,17 @@
142 char *zPrefix;
143 Stmt q;
144 const char *zCI = P("ci");
145 int rid = 0;
146 char *zUuid = 0;
 
147 Manifest *pM = 0;
148 const char *zSubdirLink;
149 int linkTrunk = 1;
150 int linkTip = 1;
151 HQuery sURI;
152 int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
153 int isBranchCI = 0; /* True if ci= refers to a branch name */
154 char *zHeader = 0;
155
156 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
157 if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
158 login_check_credentials();
@@ -157,22 +172,29 @@
172 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
173 linkTrunk = trunkRid && rid != trunkRid;
174 linkTip = rid != symbolic_name_to_rid("tip", "ci");
175 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
176 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
177 isBranchCI = branch_includes_uuid(zCI, zUuid);
178 }else{
179 zCI = 0;
180 }
181 }
182
183 assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
184 if( zD==0 ){
185 if( zCI ){
186 zHeader = mprintf("Top-level Files of %s", zCI);
187 }else{
188 zHeader = mprintf("All Top-level Files");
189 }
190 }else{
191 if( zCI ){
192 zHeader = mprintf("Files in %s/ of %s", zD, zCI);
193 }else{
194 zHeader = mprintf("All File in %s/", zD);
195 }
196 }
197 style_header("%s", zHeader);
198 fossil_free(zHeader);
199 style_adunit_config(ADUNIT_RIGHT_OK);
200 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
@@ -179,47 +201,49 @@
201 pathelementFunc, 0, 0);
202 url_initialize(&sURI, "dir");
203 cgi_query_parameters_to_url(&sURI);
204
205 /* Compute the title of the page */
 
206 if( zD ){
207 Blob dirname;
208 blob_init(&dirname, 0, 0);
209 hyperlinked_path(zD, &dirname, zCI, "dir", "", 0);
210 @ <h2>Files in directory %s(blob_str(&dirname)) \
211 blob_reset(&dirname);
212 zPrefix = mprintf("%s/", zD);
213 style_submenu_element("Top-Level", "%s",
214 url_render(&sURI, "name", 0, 0, 0));
215 }else{
216 @ <h2>Files in the top-level directory \
217 zPrefix = "";
218 }
219 if( zCI ){
220 if( fossil_strcmp(zCI,"tip")==0 ){
221 @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a></h2>
222 }else if( isBranchCI ){
223 @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \
224 @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
225 }else {
226 @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2>
227 }
228 zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix);
229 if( nD==0 ){
230 style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
231 }
232 }else{
233 @ in any check-in</h2>
234 zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
235 }
236 if( linkTrunk ){
237 style_submenu_element("Trunk", "%s",
238 url_render(&sURI, "ci", "trunk", 0, 0));
239 }
240 if( linkTip ){
241 style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
242 }
243 if( zD ){
244 style_submenu_element("History","%R/timeline?chng=%T/*", zD);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245 }
246 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
247 style_submenu_element("Tree-View", "%s",
248 url_render(&sURI, "type", "tree", 0, 0));
249
@@ -296,11 +320,11 @@
320 zFN++;
321 @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
322 }else{
323 const char *zLink;
324 if( zCI ){
325 zLink = href("%R/file?name=%T%T&ci=%T",zPrefix,zFN,zCI);
326 }else{
327 zLink = href("%R/finfo?name=%T%T",zPrefix,zFN);
328 }
329 @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li>
330 }
@@ -626,10 +650,11 @@
650 int startExpanded; /* True to start out with the tree expanded */
651 int showDirOnly; /* Show directories only. Omit files */
652 int nDir = 0; /* Number of directories. Used for ID attributes */
653 char *zProjectName = db_get("project-name", 0);
654 int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */
655 int isBranchCI = 0; /* ci= refers to a branch name */
656 char *zHeader = 0;
657
658 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
659 if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; }
660 memset(&sTree, 0, sizeof(sTree));
@@ -675,10 +700,11 @@
700 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
701 rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
702 zNow = db_text("", "SELECT datetime(mtime,toLocal())"
703 " FROM event WHERE objid=%d", rid);
704 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
705 isBranchCI = branch_includes_uuid(zCI, zUuid);
706 }else{
707 zCI = 0;
708 }
709 }
710 if( zCI==0 ){
@@ -685,41 +711,42 @@
711 rNow = db_double(0.0, "SELECT max(mtime) FROM event");
712 zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
713 }
714
715 assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
716 if( zD==0 ){
717 if( zCI ){
718 zHeader = mprintf("Top-level Files of %s", zCI);
719 }else{
720 zHeader = mprintf("All Top-level Files");
721 }
722 }else{
723 if( zCI ){
724 zHeader = mprintf("Files in %s/ of %s", zD, zCI);
725 }else{
726 zHeader = mprintf("All File in %s/", zD);
727 }
728 }
729 style_header("%s", zHeader);
730 fossil_free(zHeader);
731
732 /* Compute the title of the page */
733 blob_zero(&dirname);
734 if( zD ){
735 blob_append(&dirname, "within directory ", -1);
736 hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0);
737 if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
738 style_submenu_element("Top-Level", "%s",
739 url_render(&sURI, "name", 0, 0, 0));
740 }else if( zRE ){
741 blob_appendf(&dirname, "matching \"%s\"", zRE);
 
 
742 }
743 style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0);
744 if( zCI ){
745 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
746 if( nD==0 && !showDirOnly ){
747 style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
748 }
749 }
750 if( linkTrunk ){
751 style_submenu_element("Trunk", "%s",
752 url_render(&sURI, "ci", "trunk", 0, 0));
@@ -774,10 +801,11 @@
801 tree_add_node(&sTree, zName, zUuid, mtime);
802 nFile++;
803 }
804 db_finalize(&q);
805 }
806 style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
807
808 if( showDirOnly ){
809 for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
810 if( p->pChild!=0 && p->nFullName>nD ) nFile++;
811 }
@@ -784,18 +812,24 @@
812 zObjType = "Folders";
813 }else{
814 zObjType = "Files";
815 }
816
817 if( zCI && strcmp(zCI,"tip")==0 ){
818 @ <h2>%s(zObjType) in the %z(href("%R/info?name=tip"))latest check-in</a>
819 }else if( isBranchCI ){
820 @ <h2>%s(zObjType) in the %z(href("%R/info?name=%T",zCI))latest check-in\
821 @ </a> for branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a>
822 if( blob_size(&dirname) ){
823 @ and %s(blob_str(&dirname))</h2>
824 }
825 }else if( zCI ){
826 @ <h2>%s(zObjType) for check-in \
827 @ %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2>
828 if( blob_size(&dirname) ){
829 @ and %s(blob_str(&dirname))</h2>
830 }
831 }else{
832 int n = db_int(0, "SELECT count(*) FROM plink");
833 @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname))
834 }
835 if( useMtime ){
@@ -853,11 +887,11 @@
887 nDir++;
888 }else if( !showDirOnly ){
889 const char *zFileClass = fileext_class(p->zName);
890 char *zLink;
891 if( zCI ){
892 zLink = href("%R/file?name=%T&ci=%T",p->zFullName,zCI);
893 }else{
894 zLink = href("%R/finfo?name=%T",p->zFullName);
895 }
896 @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline">
897 @ %z(zLink)%h(p->zName)</a>
@@ -1030,10 +1064,11 @@
1064 int rid;
1065 const char *zName;
1066 const char *zGlob;
1067 const char *zUuid;
1068 const char *zNow; /* Time of check-in */
1069 int isBranchCI; /* name= is a branch name */
1070 int showId = PB("showid");
1071 Stmt q1, q2;
1072 double baseTime;
1073 login_check_credentials();
1074 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -1043,28 +1078,34 @@
1078 rid = symbolic_name_to_rid(zName, "ci");
1079 if( rid==0 ){
1080 fossil_fatal("not a valid check-in: %s", zName);
1081 }
1082 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
1083 isBranchCI = branch_includes_uuid(zName,zUuid);
1084 baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid);
1085 zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event"
1086 " WHERE objid=%d", rid);
1087 style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName);
1088 style_header("File Ages");
1089 zGlob = P("glob");
1090 compute_fileage(rid,zGlob);
1091 db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);");
1092
1093 if( fossil_strcmp(zName,"tip")==0 ){
1094 @ <h1>Files in the %z(href("%R/info?name=tip"))latest check-in</a>
1095 }else if( isBranchCI ){
1096 @ <h1>Files in the %z(href("%R/info?name=%T",zName))latest check-in</a>
1097 @ of branch %z(href("%R/timeline?r=%T",zName))%h(zName)</a>
1098 }else{
1099 @ <h1>Files in check-in %z(href("%R/info?name=%T",zName))%h(zName)</a>
1100 }
1101 if( zGlob && zGlob[0] ){
1102 @ that match "%h(zGlob)"
1103 }
1104 @ ordered by age</h1>
1105 @
1106 @ <p>File ages are expressed relative to the check-in time of
 
1107 @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p>
1108 @
1109 @ <div class='fileage'><table>
1110 @ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr>
1111 db_prepare(&q1,
@@ -1079,14 +1120,13 @@
1120 " AND blob.rid=event.objid\n"
1121 " ORDER BY event.mtime DESC;",
1122 TAG_BRANCH
1123 );
1124 db_prepare(&q2,
1125 "SELECT filename.name, fileage.fid\n"
1126 " FROM fileage, filename\n"
1127 " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid"
 
1128 );
1129 while( db_step(&q1)==SQLITE_ROW ){
1130 double age = baseTime - db_column_double(&q1, 0);
1131 int mid = db_column_int(&q1, 1);
1132 const char *zUuid = db_column_text(&q1, 2);
@@ -1096,24 +1136,24 @@
1136 char *zAge = human_readable_age(age);
1137 @ <tr><td>%s(zAge)</td>
1138 @ <td>
1139 db_bind_int(&q2, ":mid", mid);
1140 while( db_step(&q2)==SQLITE_ROW ){
1141 const char *zFile = db_column_text(&q2,0);
1142 @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \
 
1143 if( showId ){
1144 int fid = db_column_int(&q2,1);
1145 @ (%d(fid))<br />
1146 }else{
1147 @ </a><br />
1148 }
1149 }
1150 db_reset(&q2);
1151 @ </td>
1152 @ <td>
1153 @ %W(zComment)
1154 @ (check-in:&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>,
1155 if( showId ){
1156 @ id: %d(mid)
1157 }
1158 @ user:&nbsp;%z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>,
1159 @ branch:&nbsp;\
1160
+17 -11
--- src/etag.c
+++ src/etag.c
@@ -24,10 +24,12 @@
2424
** (1) The mtime on the Fossil executable
2525
** (2) The last change to the CONFIG table
2626
** (3) The last change to the EVENT table
2727
** (4) The value of the display cookie
2828
** (5) A hash value supplied by the page generator
29
+** (6) The details of the request URI
30
+** (7) The name user as determined by the login cookie
2931
**
3032
** Item (1) is always included in the ETag. The other elements are
3133
** optional. Because (1) is always included as part of the ETag, all
3234
** outstanding ETags can be invalidated by touching the fossil executable.
3335
**
@@ -61,10 +63,11 @@
6163
#define ETAG_CONFIG 0x01 /* Output depends on the CONFIG table */
6264
#define ETAG_DATA 0x02 /* Output depends on the EVENT table */
6365
#define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */
6466
#define ETAG_HASH 0x08 /* Output depends on a hash */
6567
#define ETAG_QUERY 0x10 /* Output depends on PATH_INFO and QUERY_STRING */
68
+ /* and the g.zLogin value */
6669
#endif
6770
6871
static char zETag[33]; /* The generated ETag */
6972
static int iMaxAge = 0; /* The max-age parameter in the reply */
7073
static sqlite3_int64 iEtagMtime = 0; /* Last-Modified time */
@@ -71,23 +74,21 @@
7174
7275
/*
7376
** Return a hash that changes every time the Fossil source code is
7477
** rebuilt.
7578
**
76
-** The current implementation is a hash of MANIFEST_UUID, __DATE__, and
77
-** __TIME__. But this might change in the future if we think of a better
78
-** way to compute an identifier that changes with each build.
79
+** The FOSSIL_BUILD_HASH string that is returned here gets computed by
80
+** the mkversion utility program. The result is a hash of MANIFEST_UUID
81
+** and the unix timestamp for when the mkversion utility program is run.
82
+**
83
+** During development rebuilds, if you need the source code id to change
84
+** in order to invalidate caches, simply "touch" the "manifest" file in
85
+** the top of the source directory prior to running "make" and a new
86
+** FOSSIL_BUILD_HASH will be generated automatically.
7987
*/
8088
const char *fossil_exe_id(void){
81
- static char zExecId[33];
82
- if( zExecId[0]==0 ){
83
- Blob x;
84
- blob_init(&x, MANIFEST_UUID "," __DATE__ "," __TIME__, -1);
85
- md5sum_blob(&x, &x);
86
- memcpy(zExecId, x.aData, 32);
87
- }
88
- return zExecId;
89
+ return FOSSIL_BUILD_HASH;
8990
}
9091
9192
/*
9293
** Generate an ETag
9394
*/
@@ -141,10 +142,15 @@
141142
if( zQS ){
142143
md5sum_step_text("?", 1);
143144
md5sum_step_text(zQS, -1);
144145
}
145146
md5sum_step_text("\n",1);
147
+ if( g.zLogin ){
148
+ md5sum_step_text("login: ", -1);
149
+ md5sum_step_text(g.zLogin, -1);
150
+ md5sum_step_text("\n", 1);
151
+ }
146152
}
147153
148154
/* Generate the ETag */
149155
memcpy(zETag, md5sum_finish(0), 33);
150156
151157
--- src/etag.c
+++ src/etag.c
@@ -24,10 +24,12 @@
24 ** (1) The mtime on the Fossil executable
25 ** (2) The last change to the CONFIG table
26 ** (3) The last change to the EVENT table
27 ** (4) The value of the display cookie
28 ** (5) A hash value supplied by the page generator
 
 
29 **
30 ** Item (1) is always included in the ETag. The other elements are
31 ** optional. Because (1) is always included as part of the ETag, all
32 ** outstanding ETags can be invalidated by touching the fossil executable.
33 **
@@ -61,10 +63,11 @@
61 #define ETAG_CONFIG 0x01 /* Output depends on the CONFIG table */
62 #define ETAG_DATA 0x02 /* Output depends on the EVENT table */
63 #define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */
64 #define ETAG_HASH 0x08 /* Output depends on a hash */
65 #define ETAG_QUERY 0x10 /* Output depends on PATH_INFO and QUERY_STRING */
 
66 #endif
67
68 static char zETag[33]; /* The generated ETag */
69 static int iMaxAge = 0; /* The max-age parameter in the reply */
70 static sqlite3_int64 iEtagMtime = 0; /* Last-Modified time */
@@ -71,23 +74,21 @@
71
72 /*
73 ** Return a hash that changes every time the Fossil source code is
74 ** rebuilt.
75 **
76 ** The current implementation is a hash of MANIFEST_UUID, __DATE__, and
77 ** __TIME__. But this might change in the future if we think of a better
78 ** way to compute an identifier that changes with each build.
 
 
 
 
 
79 */
80 const char *fossil_exe_id(void){
81 static char zExecId[33];
82 if( zExecId[0]==0 ){
83 Blob x;
84 blob_init(&x, MANIFEST_UUID "," __DATE__ "," __TIME__, -1);
85 md5sum_blob(&x, &x);
86 memcpy(zExecId, x.aData, 32);
87 }
88 return zExecId;
89 }
90
91 /*
92 ** Generate an ETag
93 */
@@ -141,10 +142,15 @@
141 if( zQS ){
142 md5sum_step_text("?", 1);
143 md5sum_step_text(zQS, -1);
144 }
145 md5sum_step_text("\n",1);
 
 
 
 
 
146 }
147
148 /* Generate the ETag */
149 memcpy(zETag, md5sum_finish(0), 33);
150
151
--- src/etag.c
+++ src/etag.c
@@ -24,10 +24,12 @@
24 ** (1) The mtime on the Fossil executable
25 ** (2) The last change to the CONFIG table
26 ** (3) The last change to the EVENT table
27 ** (4) The value of the display cookie
28 ** (5) A hash value supplied by the page generator
29 ** (6) The details of the request URI
30 ** (7) The name user as determined by the login cookie
31 **
32 ** Item (1) is always included in the ETag. The other elements are
33 ** optional. Because (1) is always included as part of the ETag, all
34 ** outstanding ETags can be invalidated by touching the fossil executable.
35 **
@@ -61,10 +63,11 @@
63 #define ETAG_CONFIG 0x01 /* Output depends on the CONFIG table */
64 #define ETAG_DATA 0x02 /* Output depends on the EVENT table */
65 #define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */
66 #define ETAG_HASH 0x08 /* Output depends on a hash */
67 #define ETAG_QUERY 0x10 /* Output depends on PATH_INFO and QUERY_STRING */
68 /* and the g.zLogin value */
69 #endif
70
71 static char zETag[33]; /* The generated ETag */
72 static int iMaxAge = 0; /* The max-age parameter in the reply */
73 static sqlite3_int64 iEtagMtime = 0; /* Last-Modified time */
@@ -71,23 +74,21 @@
74
75 /*
76 ** Return a hash that changes every time the Fossil source code is
77 ** rebuilt.
78 **
79 ** The FOSSIL_BUILD_HASH string that is returned here gets computed by
80 ** the mkversion utility program. The result is a hash of MANIFEST_UUID
81 ** and the unix timestamp for when the mkversion utility program is run.
82 **
83 ** During development rebuilds, if you need the source code id to change
84 ** in order to invalidate caches, simply "touch" the "manifest" file in
85 ** the top of the source directory prior to running "make" and a new
86 ** FOSSIL_BUILD_HASH will be generated automatically.
87 */
88 const char *fossil_exe_id(void){
89 return FOSSIL_BUILD_HASH;
 
 
 
 
 
 
 
90 }
91
92 /*
93 ** Generate an ETag
94 */
@@ -141,10 +142,15 @@
142 if( zQS ){
143 md5sum_step_text("?", 1);
144 md5sum_step_text(zQS, -1);
145 }
146 md5sum_step_text("\n",1);
147 if( g.zLogin ){
148 md5sum_step_text("login: ", -1);
149 md5sum_step_text(g.zLogin, -1);
150 md5sum_step_text("\n", 1);
151 }
152 }
153
154 /* Generate the ETag */
155 memcpy(zETag, md5sum_finish(0), 33);
156
157
+4 -3
--- src/finfo.c
+++ src/finfo.c
@@ -438,12 +438,13 @@
438438
}else if( n>0 ){
439439
blob_appendf(&title, "First %d ancestors of file ", n);
440440
}else{
441441
blob_appendf(&title, "Ancestors of file ");
442442
}
443
- blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>",
444
- zFilename, zFilename);
443
+ blob_appendf(&title,"%z%h</a>",
444
+ href("%R/file?name=%T&ci=%!S", zFilename, zUuid),
445
+ zFilename);
445446
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
446447
blob_append(&title, origCheckin ? " between " : " from ", -1);
447448
blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
448449
if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
449450
fossil_free(zUuid);
@@ -453,11 +454,11 @@
453454
blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
454455
fossil_free(zUuid);
455456
}
456457
}else{
457458
blob_appendf(&title, "History for ");
458
- hyperlinked_path(zFilename, &title, 0, "tree", "");
459
+ hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
459460
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
460461
}
461462
if( uBg ){
462463
blob_append(&title, " (color-coded by user)", -1);
463464
}
464465
--- src/finfo.c
+++ src/finfo.c
@@ -438,12 +438,13 @@
438 }else if( n>0 ){
439 blob_appendf(&title, "First %d ancestors of file ", n);
440 }else{
441 blob_appendf(&title, "Ancestors of file ");
442 }
443 blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>",
444 zFilename, zFilename);
 
445 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
446 blob_append(&title, origCheckin ? " between " : " from ", -1);
447 blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
448 if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
449 fossil_free(zUuid);
@@ -453,11 +454,11 @@
453 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
454 fossil_free(zUuid);
455 }
456 }else{
457 blob_appendf(&title, "History for ");
458 hyperlinked_path(zFilename, &title, 0, "tree", "");
459 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
460 }
461 if( uBg ){
462 blob_append(&title, " (color-coded by user)", -1);
463 }
464
--- src/finfo.c
+++ src/finfo.c
@@ -438,12 +438,13 @@
438 }else if( n>0 ){
439 blob_appendf(&title, "First %d ancestors of file ", n);
440 }else{
441 blob_appendf(&title, "Ancestors of file ");
442 }
443 blob_appendf(&title,"%z%h</a>",
444 href("%R/file?name=%T&ci=%!S", zFilename, zUuid),
445 zFilename);
446 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
447 blob_append(&title, origCheckin ? " between " : " from ", -1);
448 blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
449 if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
450 fossil_free(zUuid);
@@ -453,11 +454,11 @@
454 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
455 fossil_free(zUuid);
456 }
457 }else{
458 blob_appendf(&title, "History for ");
459 hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
460 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
461 }
462 if( uBg ){
463 blob_append(&title, " (color-coded by user)", -1);
464 }
465
+4 -3
--- src/finfo.c
+++ src/finfo.c
@@ -438,12 +438,13 @@
438438
}else if( n>0 ){
439439
blob_appendf(&title, "First %d ancestors of file ", n);
440440
}else{
441441
blob_appendf(&title, "Ancestors of file ");
442442
}
443
- blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>",
444
- zFilename, zFilename);
443
+ blob_appendf(&title,"%z%h</a>",
444
+ href("%R/file?name=%T&ci=%!S", zFilename, zUuid),
445
+ zFilename);
445446
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
446447
blob_append(&title, origCheckin ? " between " : " from ", -1);
447448
blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
448449
if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
449450
fossil_free(zUuid);
@@ -453,11 +454,11 @@
453454
blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
454455
fossil_free(zUuid);
455456
}
456457
}else{
457458
blob_appendf(&title, "History for ");
458
- hyperlinked_path(zFilename, &title, 0, "tree", "");
459
+ hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
459460
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
460461
}
461462
if( uBg ){
462463
blob_append(&title, " (color-coded by user)", -1);
463464
}
464465
--- src/finfo.c
+++ src/finfo.c
@@ -438,12 +438,13 @@
438 }else if( n>0 ){
439 blob_appendf(&title, "First %d ancestors of file ", n);
440 }else{
441 blob_appendf(&title, "Ancestors of file ");
442 }
443 blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>",
444 zFilename, zFilename);
 
445 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
446 blob_append(&title, origCheckin ? " between " : " from ", -1);
447 blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
448 if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
449 fossil_free(zUuid);
@@ -453,11 +454,11 @@
453 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
454 fossil_free(zUuid);
455 }
456 }else{
457 blob_appendf(&title, "History for ");
458 hyperlinked_path(zFilename, &title, 0, "tree", "");
459 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
460 }
461 if( uBg ){
462 blob_append(&title, " (color-coded by user)", -1);
463 }
464
--- src/finfo.c
+++ src/finfo.c
@@ -438,12 +438,13 @@
438 }else if( n>0 ){
439 blob_appendf(&title, "First %d ancestors of file ", n);
440 }else{
441 blob_appendf(&title, "Ancestors of file ");
442 }
443 blob_appendf(&title,"%z%h</a>",
444 href("%R/file?name=%T&ci=%!S", zFilename, zUuid),
445 zFilename);
446 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
447 blob_append(&title, origCheckin ? " between " : " from ", -1);
448 blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
449 if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
450 fossil_free(zUuid);
@@ -453,11 +454,11 @@
454 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
455 fossil_free(zUuid);
456 }
457 }else{
458 blob_appendf(&title, "History for ");
459 hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
460 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
461 }
462 if( uBg ){
463 blob_append(&title, " (color-coded by user)", -1);
464 }
465
+140 -137
--- src/info.c
+++ src/info.c
@@ -1664,12 +1664,12 @@
16641664
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
16651665
cookie_link_parameter("diff","diff","2");
16661666
diffType = atoi(PD("diff","2"));
16671667
cookie_render();
16681668
if( P("from") && P("to") ){
1669
- v1 = artifact_from_ci_and_filename(0, "from");
1670
- v2 = artifact_from_ci_and_filename(0, "to");
1669
+ v1 = artifact_from_ci_and_filename("from");
1670
+ v2 = artifact_from_ci_and_filename("to");
16711671
}else{
16721672
Stmt q;
16731673
v1 = name_to_rid_www("v1");
16741674
v2 = name_to_rid_www("v2");
16751675
@@ -1766,12 +1766,12 @@
17661766
*/
17671767
void rawartifact_page(void){
17681768
int rid = 0;
17691769
char *zUuid;
17701770
1771
- if( P("ci") && P("filename") ){
1772
- rid = artifact_from_ci_and_filename(0, 0);
1771
+ if( P("ci") ){
1772
+ rid = artifact_from_ci_and_filename(0);
17731773
}
17741774
if( rid==0 ){
17751775
rid = name_to_rid_www("name");
17761776
}
17771777
login_check_credentials();
@@ -1945,59 +1945,53 @@
19451945
19461946
/*
19471947
** Look for "ci" and "filename" query parameters. If found, try to
19481948
** use them to extract the record ID of an artifact for the file.
19491949
**
1950
-** Also look for "fn" as an alias for "filename". If either "filename"
1951
-** or "fn" is present but "ci" is missing, use "tip" as a default value
1952
-** for "ci".
1953
-**
1954
-** If zNameParam is not NULL, this use that parameter as the filename
1955
-** rather than "fn" or "filename".
1956
-**
1957
-** If pUrl is not NULL, then record the "ci" and "filename" values in
1958
-** pUrl.
1959
-**
1960
-** At least one of pUrl or zNameParam must be NULL.
1950
+** Also look for "fn" and "name" as an aliases for "filename". If any
1951
+** "filename" or "fn" or "name" are present but "ci" is missing, then
1952
+** use "tip" as the default value for "ci".
1953
+**
1954
+** If zNameParam is not NULL, then use that parameter as the filename
1955
+** rather than "fn" or "filename" or "name". the zNameParam is used
1956
+** for the from= and to= query parameters of /fdiff.
19611957
*/
1962
-int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){
1958
+int artifact_from_ci_and_filename(const char *zNameParam){
19631959
const char *zFilename;
19641960
const char *zCI;
19651961
int cirid;
19661962
Manifest *pManifest;
19671963
ManifestFile *pFile;
1964
+ int rid = 0;
19681965
19691966
if( zNameParam ){
19701967
zFilename = P(zNameParam);
19711968
}else{
19721969
zFilename = P("filename");
19731970
if( zFilename==0 ){
19741971
zFilename = P("fn");
19751972
}
1973
+ if( zFilename==0 ){
1974
+ zFilename = P("name");
1975
+ }
19761976
}
19771977
if( zFilename==0 ) return 0;
19781978
1979
- zCI = P("ci");
1980
- cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci");
1979
+ zCI = PD("ci", "tip");
1980
+ cirid = name_to_typed_rid(zCI, "ci");
19811981
if( cirid<=0 ) return 0;
19821982
pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0);
19831983
if( pManifest==0 ) return 0;
19841984
manifest_file_rewind(pManifest);
19851985
while( (pFile = manifest_file_next(pManifest,0))!=0 ){
19861986
if( fossil_strcmp(zFilename, pFile->zName)==0 ){
1987
- int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1988
- manifest_destroy(pManifest);
1989
- if( pUrl ){
1990
- assert( zNameParam==0 );
1991
- url_add_parameter(pUrl, "fn", zFilename);
1992
- if( zCI ) url_add_parameter(pUrl, "ci", zCI);
1993
- }
1994
- return rid;
1987
+ rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1988
+ break;
19951989
}
19961990
}
19971991
manifest_destroy(pManifest);
1998
- return 0;
1992
+ return rid;
19991993
}
20001994
20011995
/*
20021996
** The "z" argument is a string that contains the text of a source code
20031997
** file. This routine appends that text to the HTTP reply with line numbering.
@@ -2099,21 +2093,31 @@
20992093
** ln=N - highlight line number N
21002094
** ln=M-N - highlight lines M through N inclusive
21012095
** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
21022096
** verbose - show more detail in the description
21032097
** download - redirect to the download (artifact page only)
2104
-** name=SHA1HASH - Provide the SHA1HASH as a query parameter
2105
-** filename=NAME - Show information for content file NAME
2106
-** fn=NAME - "fn" is shorthand for "filename"
2107
-** ci=VERSION - The specific check-in to use for "filename=".
2098
+** name=NAME - filename or hash as a query parameter
2099
+** filename=NAME - alternative spelling for "name="
2100
+** fn=NAME - alternative spelling for "name="
2101
+** ci=VERSION - The specific check-in to use with "name=" to
2102
+** identify the file.
21082103
**
21092104
** The /artifact page show the complete content of a file
2110
-** identified by HASH as preformatted text. The
2111
-** /whatis page shows only a description of the file. The /file
2112
-** page shows the most recent version of the file or directory
2113
-** called NAME, or a list of the top-level directory if NAME is
2114
-** omitted.
2105
+** identified by HASH. The /whatis page shows only a description
2106
+** of how the artifact is used. The /file page shows the most recent
2107
+** version of the file or directory called NAME, or a list of the
2108
+** top-level directory if NAME is omitted.
2109
+**
2110
+** For /artifact and /whatis, the name= query parameter can refer to
2111
+** either the name of a file, or an artifact hash. If the ci= query
2112
+** parameter is also present, then name= must refer to a file name.
2113
+** If ci= is omitted, then the hash interpretation is preferred but
2114
+** if name= cannot be understood as a hash, a default "tip" value is
2115
+** used for ci=.
2116
+**
2117
+** For /file, name= can only be interpreted as a filename. As before,
2118
+** a default value of "tip" is used for ci= if ci= is omitted.
21152119
*/
21162120
void artifact_page(void){
21172121
int rid = 0;
21182122
Blob content;
21192123
const char *zMime;
@@ -2128,139 +2132,138 @@
21282132
int isFile = fossil_strcmp(g.zPath,"file")==0;
21292133
const char *zLn = P("ln");
21302134
const char *zName = P("name");
21312135
const char *zCI = P("ci");
21322136
HQuery url;
2133
- Blob dirname;
21342137
char *zCIUuid = 0;
21352138
int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
2139
+ int isBranchCI = 0; /* ci= refers to a branch name */
21362140
char *zHeader = 0;
21372141
21382142
login_check_credentials();
21392143
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2140
- url_initialize(&url, g.zPath);
2141
- if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2142
- if( zCI ){
2143
- blob_zero(&dirname);
2144
- hyperlinked_path(zName, &dirname, zCI, "dir", "");
2145
- blob_reset(&dirname);
2146
-
2147
- if( name_to_uuid2(zCI, "ci", &zCIUuid) ){
2148
- isSymbolicCI = (sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI)) != 0);
2149
- }
2150
- }
2151
- if( isFile && zName ) {
2152
- rid = artifact_from_ci_and_filename(0, "name");
2153
- }else{
2154
- rid = artifact_from_ci_and_filename(&url, 0);
2155
- }
2156
- if( rid==0 ){
2157
- url_add_parameter(&url, "name", zName);
2158
- if( isFile ){
2159
- int isUnknownAtCI = 0;
2160
-
2161
- /* Do a top-level directory listing in /file mode if no argument
2162
- ** specified */
2163
- if( zName==0 || zName[0]==0 ){
2164
- if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2165
- page_tree();
2166
- return;
2167
- }
2168
- /* Look for a single file with the given name */
2169
- rid = db_int(0,
2170
- "SELECT fid FROM filename, mlink, event"
2171
- " WHERE name=%Q"
2172
- " AND mlink.fnid=filename.fnid"
2173
- " AND event.objid=mlink.mid"
2174
- " ORDER BY event.mtime DESC LIMIT 1",
2175
- zName
2176
- );
2177
- /* If found only by the name, then the file is unknown in the check-in.
2178
- ** Possibly, the file was renamed/deleted.
2179
- */
2180
- if( rid && zCIUuid ){
2181
- rid = 0;
2182
- isUnknownAtCI = 1;
2183
- }
2184
- /* If no file called NAME exists, instead look for a directory
2185
- ** with that name, and do a directory listing */
2186
- if( rid==0 ){
2187
- int nName = (int)strlen(zName);
2188
- if( nName && zName[nName-1]=='/' ) nName--;
2189
- if( db_exists(
2190
- "SELECT 1 FROM filename"
2191
- " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2192
- nName, zName, nName+1, nName, zName
2193
- ) ){
2194
- if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2195
- page_tree();
2196
- return;
2197
- }
2198
- }
2199
- /* If no file or directory called NAME: issue an error */
2200
- if( rid==0 ){
2201
- if( isUnknownAtCI ){
2202
- if( isSymbolicCI ){
2203
- zHeader = mprintf("No such file at %s", zCI);
2204
- }else{
2205
- zHeader = mprintf("No such file at [%S]", zCIUuid);
2206
- }
2207
- style_header("%s", zHeader);
2208
- fossil_free(zHeader);
2209
- @ File %z(href("%R/finfo?name=%T",zName))%h(zName)</a> is not known
2210
- @ at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>].
2211
- }else{
2212
- style_header("No such file");
2213
- @ File '%h(zName)' is not known in this repository.
2214
- }
2215
- style_footer();
2216
- return;
2217
- }
2218
- }else{
2219
- rid = name_to_rid_www("name");
2220
- }
2221
- }
2222
-
2223
- if( rid==0 ){
2224
- style_header("No such artifact");
2225
- @ Artifact '%h(zName)' does not exist in this repository.
2226
- style_footer();
2227
- return;
2228
- }
2144
+
2145
+ /* Capture and normalize the name= and ci= query parameters */
2146
+ if( zName==0 ){
2147
+ zName = P("filename");
2148
+ if( zName==0 ){
2149
+ zName = P("fn");
2150
+ }
2151
+ }
2152
+ if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2153
+ if( zCI
2154
+ && name_to_uuid2(zCI, "ci", &zCIUuid)
2155
+ && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0
2156
+ ){
2157
+ isSymbolicCI = 1;
2158
+ isBranchCI = branch_includes_uuid(zCI, zCIUuid);
2159
+ }
2160
+
2161
+ /* The name= query parameter (or at least one of its alternative
2162
+ ** spellings) is required. Except for /file, show a top-level
2163
+ ** directory listing if name= is omitted.
2164
+ */
2165
+ if( zName==0 ){
2166
+ if( isFile ){
2167
+ if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2168
+ page_tree();
2169
+ return;
2170
+ }
2171
+ style_header("Missing name= query parameter");
2172
+ @ The name= query parameter is missing
2173
+ style_footer();
2174
+ return;
2175
+ }
2176
+
2177
+ url_initialize(&url, g.zPath);
2178
+ url_add_parameter(&url, "name", zName);
2179
+ url_add_parameter(&url, "ci", zCI);
2180
+
2181
+ if( zCI==0 && !isFile ){
2182
+ /* If there is no ci= query parameter, then prefer to interpret
2183
+ ** name= as a hash for /artifact and /whatis. But for not for /file.
2184
+ ** For /file, a name= without a ci= while prefer to use the default
2185
+ ** "tip" value for ci=. */
2186
+ rid = name_to_rid(zName);
2187
+ }
2188
+ if( rid==0 ){
2189
+ rid = artifact_from_ci_and_filename(0);
2190
+ }
2191
+
2192
+ if( rid==0 ){ /* Artifact not found */
2193
+ if( isFile ){
2194
+ /* For /file, also check to see if name= refers to a directory,
2195
+ ** and if so, do a listing for that directory */
2196
+ int nName = (int)strlen(zName);
2197
+ if( nName && zName[nName-1]=='/' ) nName--;
2198
+ if( db_exists(
2199
+ "SELECT 1 FROM filename"
2200
+ " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2201
+ nName, zName, nName+1, nName, zName
2202
+ ) ){
2203
+ if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2204
+ page_tree();
2205
+ return;
2206
+ }
2207
+ style_header("No such file");
2208
+ @ File '%h(zName)' does not exist in this repository.
2209
+ }else{
2210
+ style_header("No such artifact");
2211
+ @ Artifact '%h(zName)' does not exist in this repository.
2212
+ }
2213
+ style_footer();
2214
+ return;
2215
+ }
2216
+
22292217
if( descOnly || P("verbose")!=0 ){
22302218
url_add_parameter(&url, "verbose", "1");
22312219
objdescFlags |= OBJDESC_DETAIL;
22322220
}
22332221
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
22342222
2223
+ asText = P("txt")!=0;
22352224
if( isFile ){
2236
- if( zCI ){
2225
+ if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2226
+ zCI = "tip";
2227
+ @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a>
2228
+ @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
2229
+ }else{
22372230
const char *zPath;
22382231
Blob path;
22392232
blob_zero(&path);
2240
- hyperlinked_path(zName, &path, zCI, "dir", "");
2233
+ hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO);
22412234
zPath = blob_str(&path);
2242
- @ <h2>File %s(zPath) at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]
2243
- @ </h2>
2235
+ @ <h2>File %s(zPath) \
2236
+ if( isBranchCI ){
2237
+ @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
2238
+ }else if( isSymbolicCI ){
2239
+ @ as of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
2240
+ }else{
2241
+ @ as of check-in [%z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2>
2242
+ }
22442243
blob_reset(&path);
2245
- }else{
2246
- @ <h2>Latest version of file '%h(zName)':</h2>
22472244
}
22482245
style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2246
+ style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
2247
+ zName, zCI);
2248
+ style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
2249
+ zName, zCI);
2250
+ blob_init(&downloadName, zName, -1);
2251
+ objType = OBJTYPE_CONTENT;
22492252
}else{
22502253
@ <h2>Artifact
22512254
style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
22522255
if( g.perm.Setup ){
22532256
@ (%d(rid)):</h2>
22542257
}else{
22552258
@ :</h2>
22562259
}
2260
+ blob_zero(&downloadName);
2261
+ if( asText ) objdescFlags &= ~OBJDESC_BASE;
2262
+ objType = object_description(rid, objdescFlags,
2263
+ (isFile?zName:0), &downloadName);
22572264
}
2258
- blob_zero(&downloadName);
2259
- asText = P("txt")!=0;
2260
- if( asText ) objdescFlags &= ~OBJDESC_BASE;
2261
- objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName);
22622265
if( !descOnly && P("download")!=0 ){
22632266
cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
22642267
db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
22652268
/*NOTREACHED*/
22662269
}
@@ -2288,11 +2291,11 @@
22882291
zHeader = mprintf("Artifact [%S]", zUuid);
22892292
}
22902293
style_header("%s", zHeader);
22912294
fossil_free(zCIUuid);
22922295
fossil_free(zHeader);
2293
- if( g.perm.Admin ){
2296
+ if( !isFile && g.perm.Admin ){
22942297
Stmt q;
22952298
db_prepare(&q,
22962299
"SELECT coalesce(user.login,rcvfrom.uid),"
22972300
" datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
22982301
" FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
22992302
--- src/info.c
+++ src/info.c
@@ -1664,12 +1664,12 @@
1664 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1665 cookie_link_parameter("diff","diff","2");
1666 diffType = atoi(PD("diff","2"));
1667 cookie_render();
1668 if( P("from") && P("to") ){
1669 v1 = artifact_from_ci_and_filename(0, "from");
1670 v2 = artifact_from_ci_and_filename(0, "to");
1671 }else{
1672 Stmt q;
1673 v1 = name_to_rid_www("v1");
1674 v2 = name_to_rid_www("v2");
1675
@@ -1766,12 +1766,12 @@
1766 */
1767 void rawartifact_page(void){
1768 int rid = 0;
1769 char *zUuid;
1770
1771 if( P("ci") && P("filename") ){
1772 rid = artifact_from_ci_and_filename(0, 0);
1773 }
1774 if( rid==0 ){
1775 rid = name_to_rid_www("name");
1776 }
1777 login_check_credentials();
@@ -1945,59 +1945,53 @@
1945
1946 /*
1947 ** Look for "ci" and "filename" query parameters. If found, try to
1948 ** use them to extract the record ID of an artifact for the file.
1949 **
1950 ** Also look for "fn" as an alias for "filename". If either "filename"
1951 ** or "fn" is present but "ci" is missing, use "tip" as a default value
1952 ** for "ci".
1953 **
1954 ** If zNameParam is not NULL, this use that parameter as the filename
1955 ** rather than "fn" or "filename".
1956 **
1957 ** If pUrl is not NULL, then record the "ci" and "filename" values in
1958 ** pUrl.
1959 **
1960 ** At least one of pUrl or zNameParam must be NULL.
1961 */
1962 int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){
1963 const char *zFilename;
1964 const char *zCI;
1965 int cirid;
1966 Manifest *pManifest;
1967 ManifestFile *pFile;
 
1968
1969 if( zNameParam ){
1970 zFilename = P(zNameParam);
1971 }else{
1972 zFilename = P("filename");
1973 if( zFilename==0 ){
1974 zFilename = P("fn");
1975 }
 
 
 
1976 }
1977 if( zFilename==0 ) return 0;
1978
1979 zCI = P("ci");
1980 cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci");
1981 if( cirid<=0 ) return 0;
1982 pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0);
1983 if( pManifest==0 ) return 0;
1984 manifest_file_rewind(pManifest);
1985 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
1986 if( fossil_strcmp(zFilename, pFile->zName)==0 ){
1987 int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1988 manifest_destroy(pManifest);
1989 if( pUrl ){
1990 assert( zNameParam==0 );
1991 url_add_parameter(pUrl, "fn", zFilename);
1992 if( zCI ) url_add_parameter(pUrl, "ci", zCI);
1993 }
1994 return rid;
1995 }
1996 }
1997 manifest_destroy(pManifest);
1998 return 0;
1999 }
2000
2001 /*
2002 ** The "z" argument is a string that contains the text of a source code
2003 ** file. This routine appends that text to the HTTP reply with line numbering.
@@ -2099,21 +2093,31 @@
2099 ** ln=N - highlight line number N
2100 ** ln=M-N - highlight lines M through N inclusive
2101 ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
2102 ** verbose - show more detail in the description
2103 ** download - redirect to the download (artifact page only)
2104 ** name=SHA1HASH - Provide the SHA1HASH as a query parameter
2105 ** filename=NAME - Show information for content file NAME
2106 ** fn=NAME - "fn" is shorthand for "filename"
2107 ** ci=VERSION - The specific check-in to use for "filename=".
 
2108 **
2109 ** The /artifact page show the complete content of a file
2110 ** identified by HASH as preformatted text. The
2111 ** /whatis page shows only a description of the file. The /file
2112 ** page shows the most recent version of the file or directory
2113 ** called NAME, or a list of the top-level directory if NAME is
2114 ** omitted.
 
 
 
 
 
 
 
 
 
2115 */
2116 void artifact_page(void){
2117 int rid = 0;
2118 Blob content;
2119 const char *zMime;
@@ -2128,139 +2132,138 @@
2128 int isFile = fossil_strcmp(g.zPath,"file")==0;
2129 const char *zLn = P("ln");
2130 const char *zName = P("name");
2131 const char *zCI = P("ci");
2132 HQuery url;
2133 Blob dirname;
2134 char *zCIUuid = 0;
2135 int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
 
2136 char *zHeader = 0;
2137
2138 login_check_credentials();
2139 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2140 url_initialize(&url, g.zPath);
2141 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2142 if( zCI ){
2143 blob_zero(&dirname);
2144 hyperlinked_path(zName, &dirname, zCI, "dir", "");
2145 blob_reset(&dirname);
2146
2147 if( name_to_uuid2(zCI, "ci", &zCIUuid) ){
2148 isSymbolicCI = (sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI)) != 0);
2149 }
2150 }
2151 if( isFile && zName ) {
2152 rid = artifact_from_ci_and_filename(0, "name");
2153 }else{
2154 rid = artifact_from_ci_and_filename(&url, 0);
2155 }
2156 if( rid==0 ){
2157 url_add_parameter(&url, "name", zName);
2158 if( isFile ){
2159 int isUnknownAtCI = 0;
2160
2161 /* Do a top-level directory listing in /file mode if no argument
2162 ** specified */
2163 if( zName==0 || zName[0]==0 ){
2164 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2165 page_tree();
2166 return;
2167 }
2168 /* Look for a single file with the given name */
2169 rid = db_int(0,
2170 "SELECT fid FROM filename, mlink, event"
2171 " WHERE name=%Q"
2172 " AND mlink.fnid=filename.fnid"
2173 " AND event.objid=mlink.mid"
2174 " ORDER BY event.mtime DESC LIMIT 1",
2175 zName
2176 );
2177 /* If found only by the name, then the file is unknown in the check-in.
2178 ** Possibly, the file was renamed/deleted.
2179 */
2180 if( rid && zCIUuid ){
2181 rid = 0;
2182 isUnknownAtCI = 1;
2183 }
2184 /* If no file called NAME exists, instead look for a directory
2185 ** with that name, and do a directory listing */
2186 if( rid==0 ){
2187 int nName = (int)strlen(zName);
2188 if( nName && zName[nName-1]=='/' ) nName--;
2189 if( db_exists(
2190 "SELECT 1 FROM filename"
2191 " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2192 nName, zName, nName+1, nName, zName
2193 ) ){
2194 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2195 page_tree();
2196 return;
2197 }
2198 }
2199 /* If no file or directory called NAME: issue an error */
2200 if( rid==0 ){
2201 if( isUnknownAtCI ){
2202 if( isSymbolicCI ){
2203 zHeader = mprintf("No such file at %s", zCI);
2204 }else{
2205 zHeader = mprintf("No such file at [%S]", zCIUuid);
2206 }
2207 style_header("%s", zHeader);
2208 fossil_free(zHeader);
2209 @ File %z(href("%R/finfo?name=%T",zName))%h(zName)</a> is not known
2210 @ at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>].
2211 }else{
2212 style_header("No such file");
2213 @ File '%h(zName)' is not known in this repository.
2214 }
2215 style_footer();
2216 return;
2217 }
2218 }else{
2219 rid = name_to_rid_www("name");
2220 }
2221 }
2222
2223 if( rid==0 ){
2224 style_header("No such artifact");
2225 @ Artifact '%h(zName)' does not exist in this repository.
2226 style_footer();
2227 return;
2228 }
2229 if( descOnly || P("verbose")!=0 ){
2230 url_add_parameter(&url, "verbose", "1");
2231 objdescFlags |= OBJDESC_DETAIL;
2232 }
2233 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2234
 
2235 if( isFile ){
2236 if( zCI ){
 
 
 
 
2237 const char *zPath;
2238 Blob path;
2239 blob_zero(&path);
2240 hyperlinked_path(zName, &path, zCI, "dir", "");
2241 zPath = blob_str(&path);
2242 @ <h2>File %s(zPath) at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]
2243 @ </h2>
 
 
 
 
 
 
2244 blob_reset(&path);
2245 }else{
2246 @ <h2>Latest version of file '%h(zName)':</h2>
2247 }
2248 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
 
 
 
 
 
 
2249 }else{
2250 @ <h2>Artifact
2251 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
2252 if( g.perm.Setup ){
2253 @ (%d(rid)):</h2>
2254 }else{
2255 @ :</h2>
2256 }
 
 
 
 
2257 }
2258 blob_zero(&downloadName);
2259 asText = P("txt")!=0;
2260 if( asText ) objdescFlags &= ~OBJDESC_BASE;
2261 objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName);
2262 if( !descOnly && P("download")!=0 ){
2263 cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
2264 db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
2265 /*NOTREACHED*/
2266 }
@@ -2288,11 +2291,11 @@
2288 zHeader = mprintf("Artifact [%S]", zUuid);
2289 }
2290 style_header("%s", zHeader);
2291 fossil_free(zCIUuid);
2292 fossil_free(zHeader);
2293 if( g.perm.Admin ){
2294 Stmt q;
2295 db_prepare(&q,
2296 "SELECT coalesce(user.login,rcvfrom.uid),"
2297 " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
2298 " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
2299
--- src/info.c
+++ src/info.c
@@ -1664,12 +1664,12 @@
1664 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1665 cookie_link_parameter("diff","diff","2");
1666 diffType = atoi(PD("diff","2"));
1667 cookie_render();
1668 if( P("from") && P("to") ){
1669 v1 = artifact_from_ci_and_filename("from");
1670 v2 = artifact_from_ci_and_filename("to");
1671 }else{
1672 Stmt q;
1673 v1 = name_to_rid_www("v1");
1674 v2 = name_to_rid_www("v2");
1675
@@ -1766,12 +1766,12 @@
1766 */
1767 void rawartifact_page(void){
1768 int rid = 0;
1769 char *zUuid;
1770
1771 if( P("ci") ){
1772 rid = artifact_from_ci_and_filename(0);
1773 }
1774 if( rid==0 ){
1775 rid = name_to_rid_www("name");
1776 }
1777 login_check_credentials();
@@ -1945,59 +1945,53 @@
1945
1946 /*
1947 ** Look for "ci" and "filename" query parameters. If found, try to
1948 ** use them to extract the record ID of an artifact for the file.
1949 **
1950 ** Also look for "fn" and "name" as an aliases for "filename". If any
1951 ** "filename" or "fn" or "name" are present but "ci" is missing, then
1952 ** use "tip" as the default value for "ci".
1953 **
1954 ** If zNameParam is not NULL, then use that parameter as the filename
1955 ** rather than "fn" or "filename" or "name". the zNameParam is used
1956 ** for the from= and to= query parameters of /fdiff.
 
 
 
 
1957 */
1958 int artifact_from_ci_and_filename(const char *zNameParam){
1959 const char *zFilename;
1960 const char *zCI;
1961 int cirid;
1962 Manifest *pManifest;
1963 ManifestFile *pFile;
1964 int rid = 0;
1965
1966 if( zNameParam ){
1967 zFilename = P(zNameParam);
1968 }else{
1969 zFilename = P("filename");
1970 if( zFilename==0 ){
1971 zFilename = P("fn");
1972 }
1973 if( zFilename==0 ){
1974 zFilename = P("name");
1975 }
1976 }
1977 if( zFilename==0 ) return 0;
1978
1979 zCI = PD("ci", "tip");
1980 cirid = name_to_typed_rid(zCI, "ci");
1981 if( cirid<=0 ) return 0;
1982 pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0);
1983 if( pManifest==0 ) return 0;
1984 manifest_file_rewind(pManifest);
1985 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
1986 if( fossil_strcmp(zFilename, pFile->zName)==0 ){
1987 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1988 break;
 
 
 
 
 
 
1989 }
1990 }
1991 manifest_destroy(pManifest);
1992 return rid;
1993 }
1994
1995 /*
1996 ** The "z" argument is a string that contains the text of a source code
1997 ** file. This routine appends that text to the HTTP reply with line numbering.
@@ -2099,21 +2093,31 @@
2093 ** ln=N - highlight line number N
2094 ** ln=M-N - highlight lines M through N inclusive
2095 ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
2096 ** verbose - show more detail in the description
2097 ** download - redirect to the download (artifact page only)
2098 ** name=NAME - filename or hash as a query parameter
2099 ** filename=NAME - alternative spelling for "name="
2100 ** fn=NAME - alternative spelling for "name="
2101 ** ci=VERSION - The specific check-in to use with "name=" to
2102 ** identify the file.
2103 **
2104 ** The /artifact page show the complete content of a file
2105 ** identified by HASH. The /whatis page shows only a description
2106 ** of how the artifact is used. The /file page shows the most recent
2107 ** version of the file or directory called NAME, or a list of the
2108 ** top-level directory if NAME is omitted.
2109 **
2110 ** For /artifact and /whatis, the name= query parameter can refer to
2111 ** either the name of a file, or an artifact hash. If the ci= query
2112 ** parameter is also present, then name= must refer to a file name.
2113 ** If ci= is omitted, then the hash interpretation is preferred but
2114 ** if name= cannot be understood as a hash, a default "tip" value is
2115 ** used for ci=.
2116 **
2117 ** For /file, name= can only be interpreted as a filename. As before,
2118 ** a default value of "tip" is used for ci= if ci= is omitted.
2119 */
2120 void artifact_page(void){
2121 int rid = 0;
2122 Blob content;
2123 const char *zMime;
@@ -2128,139 +2132,138 @@
2132 int isFile = fossil_strcmp(g.zPath,"file")==0;
2133 const char *zLn = P("ln");
2134 const char *zName = P("name");
2135 const char *zCI = P("ci");
2136 HQuery url;
 
2137 char *zCIUuid = 0;
2138 int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
2139 int isBranchCI = 0; /* ci= refers to a branch name */
2140 char *zHeader = 0;
2141
2142 login_check_credentials();
2143 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2144
2145 /* Capture and normalize the name= and ci= query parameters */
2146 if( zName==0 ){
2147 zName = P("filename");
2148 if( zName==0 ){
2149 zName = P("fn");
2150 }
2151 }
2152 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2153 if( zCI
2154 && name_to_uuid2(zCI, "ci", &zCIUuid)
2155 && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0
2156 ){
2157 isSymbolicCI = 1;
2158 isBranchCI = branch_includes_uuid(zCI, zCIUuid);
2159 }
2160
2161 /* The name= query parameter (or at least one of its alternative
2162 ** spellings) is required. Except for /file, show a top-level
2163 ** directory listing if name= is omitted.
2164 */
2165 if( zName==0 ){
2166 if( isFile ){
2167 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2168 page_tree();
2169 return;
2170 }
2171 style_header("Missing name= query parameter");
2172 @ The name= query parameter is missing
2173 style_footer();
2174 return;
2175 }
2176
2177 url_initialize(&url, g.zPath);
2178 url_add_parameter(&url, "name", zName);
2179 url_add_parameter(&url, "ci", zCI);
2180
2181 if( zCI==0 && !isFile ){
2182 /* If there is no ci= query parameter, then prefer to interpret
2183 ** name= as a hash for /artifact and /whatis. But for not for /file.
2184 ** For /file, a name= without a ci= while prefer to use the default
2185 ** "tip" value for ci=. */
2186 rid = name_to_rid(zName);
2187 }
2188 if( rid==0 ){
2189 rid = artifact_from_ci_and_filename(0);
2190 }
2191
2192 if( rid==0 ){ /* Artifact not found */
2193 if( isFile ){
2194 /* For /file, also check to see if name= refers to a directory,
2195 ** and if so, do a listing for that directory */
2196 int nName = (int)strlen(zName);
2197 if( nName && zName[nName-1]=='/' ) nName--;
2198 if( db_exists(
2199 "SELECT 1 FROM filename"
2200 " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2201 nName, zName, nName+1, nName, zName
2202 ) ){
2203 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2204 page_tree();
2205 return;
2206 }
2207 style_header("No such file");
2208 @ File '%h(zName)' does not exist in this repository.
2209 }else{
2210 style_header("No such artifact");
2211 @ Artifact '%h(zName)' does not exist in this repository.
2212 }
2213 style_footer();
2214 return;
2215 }
2216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2217 if( descOnly || P("verbose")!=0 ){
2218 url_add_parameter(&url, "verbose", "1");
2219 objdescFlags |= OBJDESC_DETAIL;
2220 }
2221 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2222
2223 asText = P("txt")!=0;
2224 if( isFile ){
2225 if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2226 zCI = "tip";
2227 @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a>
2228 @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
2229 }else{
2230 const char *zPath;
2231 Blob path;
2232 blob_zero(&path);
2233 hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO);
2234 zPath = blob_str(&path);
2235 @ <h2>File %s(zPath) \
2236 if( isBranchCI ){
2237 @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
2238 }else if( isSymbolicCI ){
2239 @ as of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
2240 }else{
2241 @ as of check-in [%z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2>
2242 }
2243 blob_reset(&path);
 
 
2244 }
2245 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2246 style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
2247 zName, zCI);
2248 style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
2249 zName, zCI);
2250 blob_init(&downloadName, zName, -1);
2251 objType = OBJTYPE_CONTENT;
2252 }else{
2253 @ <h2>Artifact
2254 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
2255 if( g.perm.Setup ){
2256 @ (%d(rid)):</h2>
2257 }else{
2258 @ :</h2>
2259 }
2260 blob_zero(&downloadName);
2261 if( asText ) objdescFlags &= ~OBJDESC_BASE;
2262 objType = object_description(rid, objdescFlags,
2263 (isFile?zName:0), &downloadName);
2264 }
 
 
 
 
2265 if( !descOnly && P("download")!=0 ){
2266 cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
2267 db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
2268 /*NOTREACHED*/
2269 }
@@ -2288,11 +2291,11 @@
2291 zHeader = mprintf("Artifact [%S]", zUuid);
2292 }
2293 style_header("%s", zHeader);
2294 fossil_free(zCIUuid);
2295 fossil_free(zHeader);
2296 if( !isFile && g.perm.Admin ){
2297 Stmt q;
2298 db_prepare(&q,
2299 "SELECT coalesce(user.login,rcvfrom.uid),"
2300 " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
2301 " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
2302
+140 -137
--- src/info.c
+++ src/info.c
@@ -1664,12 +1664,12 @@
16641664
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
16651665
cookie_link_parameter("diff","diff","2");
16661666
diffType = atoi(PD("diff","2"));
16671667
cookie_render();
16681668
if( P("from") && P("to") ){
1669
- v1 = artifact_from_ci_and_filename(0, "from");
1670
- v2 = artifact_from_ci_and_filename(0, "to");
1669
+ v1 = artifact_from_ci_and_filename("from");
1670
+ v2 = artifact_from_ci_and_filename("to");
16711671
}else{
16721672
Stmt q;
16731673
v1 = name_to_rid_www("v1");
16741674
v2 = name_to_rid_www("v2");
16751675
@@ -1766,12 +1766,12 @@
17661766
*/
17671767
void rawartifact_page(void){
17681768
int rid = 0;
17691769
char *zUuid;
17701770
1771
- if( P("ci") && P("filename") ){
1772
- rid = artifact_from_ci_and_filename(0, 0);
1771
+ if( P("ci") ){
1772
+ rid = artifact_from_ci_and_filename(0);
17731773
}
17741774
if( rid==0 ){
17751775
rid = name_to_rid_www("name");
17761776
}
17771777
login_check_credentials();
@@ -1945,59 +1945,53 @@
19451945
19461946
/*
19471947
** Look for "ci" and "filename" query parameters. If found, try to
19481948
** use them to extract the record ID of an artifact for the file.
19491949
**
1950
-** Also look for "fn" as an alias for "filename". If either "filename"
1951
-** or "fn" is present but "ci" is missing, use "tip" as a default value
1952
-** for "ci".
1953
-**
1954
-** If zNameParam is not NULL, this use that parameter as the filename
1955
-** rather than "fn" or "filename".
1956
-**
1957
-** If pUrl is not NULL, then record the "ci" and "filename" values in
1958
-** pUrl.
1959
-**
1960
-** At least one of pUrl or zNameParam must be NULL.
1950
+** Also look for "fn" and "name" as an aliases for "filename". If any
1951
+** "filename" or "fn" or "name" are present but "ci" is missing, then
1952
+** use "tip" as the default value for "ci".
1953
+**
1954
+** If zNameParam is not NULL, then use that parameter as the filename
1955
+** rather than "fn" or "filename" or "name". the zNameParam is used
1956
+** for the from= and to= query parameters of /fdiff.
19611957
*/
1962
-int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){
1958
+int artifact_from_ci_and_filename(const char *zNameParam){
19631959
const char *zFilename;
19641960
const char *zCI;
19651961
int cirid;
19661962
Manifest *pManifest;
19671963
ManifestFile *pFile;
1964
+ int rid = 0;
19681965
19691966
if( zNameParam ){
19701967
zFilename = P(zNameParam);
19711968
}else{
19721969
zFilename = P("filename");
19731970
if( zFilename==0 ){
19741971
zFilename = P("fn");
19751972
}
1973
+ if( zFilename==0 ){
1974
+ zFilename = P("name");
1975
+ }
19761976
}
19771977
if( zFilename==0 ) return 0;
19781978
1979
- zCI = P("ci");
1980
- cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci");
1979
+ zCI = PD("ci", "tip");
1980
+ cirid = name_to_typed_rid(zCI, "ci");
19811981
if( cirid<=0 ) return 0;
19821982
pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0);
19831983
if( pManifest==0 ) return 0;
19841984
manifest_file_rewind(pManifest);
19851985
while( (pFile = manifest_file_next(pManifest,0))!=0 ){
19861986
if( fossil_strcmp(zFilename, pFile->zName)==0 ){
1987
- int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1988
- manifest_destroy(pManifest);
1989
- if( pUrl ){
1990
- assert( zNameParam==0 );
1991
- url_add_parameter(pUrl, "fn", zFilename);
1992
- if( zCI ) url_add_parameter(pUrl, "ci", zCI);
1993
- }
1994
- return rid;
1987
+ rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1988
+ break;
19951989
}
19961990
}
19971991
manifest_destroy(pManifest);
1998
- return 0;
1992
+ return rid;
19991993
}
20001994
20011995
/*
20021996
** The "z" argument is a string that contains the text of a source code
20031997
** file. This routine appends that text to the HTTP reply with line numbering.
@@ -2099,21 +2093,31 @@
20992093
** ln=N - highlight line number N
21002094
** ln=M-N - highlight lines M through N inclusive
21012095
** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
21022096
** verbose - show more detail in the description
21032097
** download - redirect to the download (artifact page only)
2104
-** name=SHA1HASH - Provide the SHA1HASH as a query parameter
2105
-** filename=NAME - Show information for content file NAME
2106
-** fn=NAME - "fn" is shorthand for "filename"
2107
-** ci=VERSION - The specific check-in to use for "filename=".
2098
+** name=NAME - filename or hash as a query parameter
2099
+** filename=NAME - alternative spelling for "name="
2100
+** fn=NAME - alternative spelling for "name="
2101
+** ci=VERSION - The specific check-in to use with "name=" to
2102
+** identify the file.
21082103
**
21092104
** The /artifact page show the complete content of a file
2110
-** identified by HASH as preformatted text. The
2111
-** /whatis page shows only a description of the file. The /file
2112
-** page shows the most recent version of the file or directory
2113
-** called NAME, or a list of the top-level directory if NAME is
2114
-** omitted.
2105
+** identified by HASH. The /whatis page shows only a description
2106
+** of how the artifact is used. The /file page shows the most recent
2107
+** version of the file or directory called NAME, or a list of the
2108
+** top-level directory if NAME is omitted.
2109
+**
2110
+** For /artifact and /whatis, the name= query parameter can refer to
2111
+** either the name of a file, or an artifact hash. If the ci= query
2112
+** parameter is also present, then name= must refer to a file name.
2113
+** If ci= is omitted, then the hash interpretation is preferred but
2114
+** if name= cannot be understood as a hash, a default "tip" value is
2115
+** used for ci=.
2116
+**
2117
+** For /file, name= can only be interpreted as a filename. As before,
2118
+** a default value of "tip" is used for ci= if ci= is omitted.
21152119
*/
21162120
void artifact_page(void){
21172121
int rid = 0;
21182122
Blob content;
21192123
const char *zMime;
@@ -2128,139 +2132,138 @@
21282132
int isFile = fossil_strcmp(g.zPath,"file")==0;
21292133
const char *zLn = P("ln");
21302134
const char *zName = P("name");
21312135
const char *zCI = P("ci");
21322136
HQuery url;
2133
- Blob dirname;
21342137
char *zCIUuid = 0;
21352138
int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
2139
+ int isBranchCI = 0; /* ci= refers to a branch name */
21362140
char *zHeader = 0;
21372141
21382142
login_check_credentials();
21392143
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2140
- url_initialize(&url, g.zPath);
2141
- if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2142
- if( zCI ){
2143
- blob_zero(&dirname);
2144
- hyperlinked_path(zName, &dirname, zCI, "dir", "");
2145
- blob_reset(&dirname);
2146
-
2147
- if( name_to_uuid2(zCI, "ci", &zCIUuid) ){
2148
- isSymbolicCI = (sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI)) != 0);
2149
- }
2150
- }
2151
- if( isFile && zName ) {
2152
- rid = artifact_from_ci_and_filename(0, "name");
2153
- }else{
2154
- rid = artifact_from_ci_and_filename(&url, 0);
2155
- }
2156
- if( rid==0 ){
2157
- url_add_parameter(&url, "name", zName);
2158
- if( isFile ){
2159
- int isUnknownAtCI = 0;
2160
-
2161
- /* Do a top-level directory listing in /file mode if no argument
2162
- ** specified */
2163
- if( zName==0 || zName[0]==0 ){
2164
- if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2165
- page_tree();
2166
- return;
2167
- }
2168
- /* Look for a single file with the given name */
2169
- rid = db_int(0,
2170
- "SELECT fid FROM filename, mlink, event"
2171
- " WHERE name=%Q"
2172
- " AND mlink.fnid=filename.fnid"
2173
- " AND event.objid=mlink.mid"
2174
- " ORDER BY event.mtime DESC LIMIT 1",
2175
- zName
2176
- );
2177
- /* If found only by the name, then the file is unknown in the check-in.
2178
- ** Possibly, the file was renamed/deleted.
2179
- */
2180
- if( rid && zCIUuid ){
2181
- rid = 0;
2182
- isUnknownAtCI = 1;
2183
- }
2184
- /* If no file called NAME exists, instead look for a directory
2185
- ** with that name, and do a directory listing */
2186
- if( rid==0 ){
2187
- int nName = (int)strlen(zName);
2188
- if( nName && zName[nName-1]=='/' ) nName--;
2189
- if( db_exists(
2190
- "SELECT 1 FROM filename"
2191
- " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2192
- nName, zName, nName+1, nName, zName
2193
- ) ){
2194
- if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2195
- page_tree();
2196
- return;
2197
- }
2198
- }
2199
- /* If no file or directory called NAME: issue an error */
2200
- if( rid==0 ){
2201
- if( isUnknownAtCI ){
2202
- if( isSymbolicCI ){
2203
- zHeader = mprintf("No such file at %s", zCI);
2204
- }else{
2205
- zHeader = mprintf("No such file at [%S]", zCIUuid);
2206
- }
2207
- style_header("%s", zHeader);
2208
- fossil_free(zHeader);
2209
- @ File %z(href("%R/finfo?name=%T",zName))%h(zName)</a> is not known
2210
- @ at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>].
2211
- }else{
2212
- style_header("No such file");
2213
- @ File '%h(zName)' is not known in this repository.
2214
- }
2215
- style_footer();
2216
- return;
2217
- }
2218
- }else{
2219
- rid = name_to_rid_www("name");
2220
- }
2221
- }
2222
-
2223
- if( rid==0 ){
2224
- style_header("No such artifact");
2225
- @ Artifact '%h(zName)' does not exist in this repository.
2226
- style_footer();
2227
- return;
2228
- }
2144
+
2145
+ /* Capture and normalize the name= and ci= query parameters */
2146
+ if( zName==0 ){
2147
+ zName = P("filename");
2148
+ if( zName==0 ){
2149
+ zName = P("fn");
2150
+ }
2151
+ }
2152
+ if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2153
+ if( zCI
2154
+ && name_to_uuid2(zCI, "ci", &zCIUuid)
2155
+ && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0
2156
+ ){
2157
+ isSymbolicCI = 1;
2158
+ isBranchCI = branch_includes_uuid(zCI, zCIUuid);
2159
+ }
2160
+
2161
+ /* The name= query parameter (or at least one of its alternative
2162
+ ** spellings) is required. Except for /file, show a top-level
2163
+ ** directory listing if name= is omitted.
2164
+ */
2165
+ if( zName==0 ){
2166
+ if( isFile ){
2167
+ if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2168
+ page_tree();
2169
+ return;
2170
+ }
2171
+ style_header("Missing name= query parameter");
2172
+ @ The name= query parameter is missing
2173
+ style_footer();
2174
+ return;
2175
+ }
2176
+
2177
+ url_initialize(&url, g.zPath);
2178
+ url_add_parameter(&url, "name", zName);
2179
+ url_add_parameter(&url, "ci", zCI);
2180
+
2181
+ if( zCI==0 && !isFile ){
2182
+ /* If there is no ci= query parameter, then prefer to interpret
2183
+ ** name= as a hash for /artifact and /whatis. But for not for /file.
2184
+ ** For /file, a name= without a ci= while prefer to use the default
2185
+ ** "tip" value for ci=. */
2186
+ rid = name_to_rid(zName);
2187
+ }
2188
+ if( rid==0 ){
2189
+ rid = artifact_from_ci_and_filename(0);
2190
+ }
2191
+
2192
+ if( rid==0 ){ /* Artifact not found */
2193
+ if( isFile ){
2194
+ /* For /file, also check to see if name= refers to a directory,
2195
+ ** and if so, do a listing for that directory */
2196
+ int nName = (int)strlen(zName);
2197
+ if( nName && zName[nName-1]=='/' ) nName--;
2198
+ if( db_exists(
2199
+ "SELECT 1 FROM filename"
2200
+ " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2201
+ nName, zName, nName+1, nName, zName
2202
+ ) ){
2203
+ if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2204
+ page_tree();
2205
+ return;
2206
+ }
2207
+ style_header("No such file");
2208
+ @ File '%h(zName)' does not exist in this repository.
2209
+ }else{
2210
+ style_header("No such artifact");
2211
+ @ Artifact '%h(zName)' does not exist in this repository.
2212
+ }
2213
+ style_footer();
2214
+ return;
2215
+ }
2216
+
22292217
if( descOnly || P("verbose")!=0 ){
22302218
url_add_parameter(&url, "verbose", "1");
22312219
objdescFlags |= OBJDESC_DETAIL;
22322220
}
22332221
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
22342222
2223
+ asText = P("txt")!=0;
22352224
if( isFile ){
2236
- if( zCI ){
2225
+ if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2226
+ zCI = "tip";
2227
+ @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a>
2228
+ @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
2229
+ }else{
22372230
const char *zPath;
22382231
Blob path;
22392232
blob_zero(&path);
2240
- hyperlinked_path(zName, &path, zCI, "dir", "");
2233
+ hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO);
22412234
zPath = blob_str(&path);
2242
- @ <h2>File %s(zPath) at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]
2243
- @ </h2>
2235
+ @ <h2>File %s(zPath) \
2236
+ if( isBranchCI ){
2237
+ @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
2238
+ }else if( isSymbolicCI ){
2239
+ @ as of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
2240
+ }else{
2241
+ @ as of check-in [%z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2>
2242
+ }
22442243
blob_reset(&path);
2245
- }else{
2246
- @ <h2>Latest version of file '%h(zName)':</h2>
22472244
}
22482245
style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2246
+ style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
2247
+ zName, zCI);
2248
+ style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
2249
+ zName, zCI);
2250
+ blob_init(&downloadName, zName, -1);
2251
+ objType = OBJTYPE_CONTENT;
22492252
}else{
22502253
@ <h2>Artifact
22512254
style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
22522255
if( g.perm.Setup ){
22532256
@ (%d(rid)):</h2>
22542257
}else{
22552258
@ :</h2>
22562259
}
2260
+ blob_zero(&downloadName);
2261
+ if( asText ) objdescFlags &= ~OBJDESC_BASE;
2262
+ objType = object_description(rid, objdescFlags,
2263
+ (isFile?zName:0), &downloadName);
22572264
}
2258
- blob_zero(&downloadName);
2259
- asText = P("txt")!=0;
2260
- if( asText ) objdescFlags &= ~OBJDESC_BASE;
2261
- objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName);
22622265
if( !descOnly && P("download")!=0 ){
22632266
cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
22642267
db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
22652268
/*NOTREACHED*/
22662269
}
@@ -2288,11 +2291,11 @@
22882291
zHeader = mprintf("Artifact [%S]", zUuid);
22892292
}
22902293
style_header("%s", zHeader);
22912294
fossil_free(zCIUuid);
22922295
fossil_free(zHeader);
2293
- if( g.perm.Admin ){
2296
+ if( !isFile && g.perm.Admin ){
22942297
Stmt q;
22952298
db_prepare(&q,
22962299
"SELECT coalesce(user.login,rcvfrom.uid),"
22972300
" datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
22982301
" FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
22992302
--- src/info.c
+++ src/info.c
@@ -1664,12 +1664,12 @@
1664 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1665 cookie_link_parameter("diff","diff","2");
1666 diffType = atoi(PD("diff","2"));
1667 cookie_render();
1668 if( P("from") && P("to") ){
1669 v1 = artifact_from_ci_and_filename(0, "from");
1670 v2 = artifact_from_ci_and_filename(0, "to");
1671 }else{
1672 Stmt q;
1673 v1 = name_to_rid_www("v1");
1674 v2 = name_to_rid_www("v2");
1675
@@ -1766,12 +1766,12 @@
1766 */
1767 void rawartifact_page(void){
1768 int rid = 0;
1769 char *zUuid;
1770
1771 if( P("ci") && P("filename") ){
1772 rid = artifact_from_ci_and_filename(0, 0);
1773 }
1774 if( rid==0 ){
1775 rid = name_to_rid_www("name");
1776 }
1777 login_check_credentials();
@@ -1945,59 +1945,53 @@
1945
1946 /*
1947 ** Look for "ci" and "filename" query parameters. If found, try to
1948 ** use them to extract the record ID of an artifact for the file.
1949 **
1950 ** Also look for "fn" as an alias for "filename". If either "filename"
1951 ** or "fn" is present but "ci" is missing, use "tip" as a default value
1952 ** for "ci".
1953 **
1954 ** If zNameParam is not NULL, this use that parameter as the filename
1955 ** rather than "fn" or "filename".
1956 **
1957 ** If pUrl is not NULL, then record the "ci" and "filename" values in
1958 ** pUrl.
1959 **
1960 ** At least one of pUrl or zNameParam must be NULL.
1961 */
1962 int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){
1963 const char *zFilename;
1964 const char *zCI;
1965 int cirid;
1966 Manifest *pManifest;
1967 ManifestFile *pFile;
 
1968
1969 if( zNameParam ){
1970 zFilename = P(zNameParam);
1971 }else{
1972 zFilename = P("filename");
1973 if( zFilename==0 ){
1974 zFilename = P("fn");
1975 }
 
 
 
1976 }
1977 if( zFilename==0 ) return 0;
1978
1979 zCI = P("ci");
1980 cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci");
1981 if( cirid<=0 ) return 0;
1982 pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0);
1983 if( pManifest==0 ) return 0;
1984 manifest_file_rewind(pManifest);
1985 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
1986 if( fossil_strcmp(zFilename, pFile->zName)==0 ){
1987 int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1988 manifest_destroy(pManifest);
1989 if( pUrl ){
1990 assert( zNameParam==0 );
1991 url_add_parameter(pUrl, "fn", zFilename);
1992 if( zCI ) url_add_parameter(pUrl, "ci", zCI);
1993 }
1994 return rid;
1995 }
1996 }
1997 manifest_destroy(pManifest);
1998 return 0;
1999 }
2000
2001 /*
2002 ** The "z" argument is a string that contains the text of a source code
2003 ** file. This routine appends that text to the HTTP reply with line numbering.
@@ -2099,21 +2093,31 @@
2099 ** ln=N - highlight line number N
2100 ** ln=M-N - highlight lines M through N inclusive
2101 ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
2102 ** verbose - show more detail in the description
2103 ** download - redirect to the download (artifact page only)
2104 ** name=SHA1HASH - Provide the SHA1HASH as a query parameter
2105 ** filename=NAME - Show information for content file NAME
2106 ** fn=NAME - "fn" is shorthand for "filename"
2107 ** ci=VERSION - The specific check-in to use for "filename=".
 
2108 **
2109 ** The /artifact page show the complete content of a file
2110 ** identified by HASH as preformatted text. The
2111 ** /whatis page shows only a description of the file. The /file
2112 ** page shows the most recent version of the file or directory
2113 ** called NAME, or a list of the top-level directory if NAME is
2114 ** omitted.
 
 
 
 
 
 
 
 
 
2115 */
2116 void artifact_page(void){
2117 int rid = 0;
2118 Blob content;
2119 const char *zMime;
@@ -2128,139 +2132,138 @@
2128 int isFile = fossil_strcmp(g.zPath,"file")==0;
2129 const char *zLn = P("ln");
2130 const char *zName = P("name");
2131 const char *zCI = P("ci");
2132 HQuery url;
2133 Blob dirname;
2134 char *zCIUuid = 0;
2135 int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
 
2136 char *zHeader = 0;
2137
2138 login_check_credentials();
2139 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2140 url_initialize(&url, g.zPath);
2141 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2142 if( zCI ){
2143 blob_zero(&dirname);
2144 hyperlinked_path(zName, &dirname, zCI, "dir", "");
2145 blob_reset(&dirname);
2146
2147 if( name_to_uuid2(zCI, "ci", &zCIUuid) ){
2148 isSymbolicCI = (sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI)) != 0);
2149 }
2150 }
2151 if( isFile && zName ) {
2152 rid = artifact_from_ci_and_filename(0, "name");
2153 }else{
2154 rid = artifact_from_ci_and_filename(&url, 0);
2155 }
2156 if( rid==0 ){
2157 url_add_parameter(&url, "name", zName);
2158 if( isFile ){
2159 int isUnknownAtCI = 0;
2160
2161 /* Do a top-level directory listing in /file mode if no argument
2162 ** specified */
2163 if( zName==0 || zName[0]==0 ){
2164 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2165 page_tree();
2166 return;
2167 }
2168 /* Look for a single file with the given name */
2169 rid = db_int(0,
2170 "SELECT fid FROM filename, mlink, event"
2171 " WHERE name=%Q"
2172 " AND mlink.fnid=filename.fnid"
2173 " AND event.objid=mlink.mid"
2174 " ORDER BY event.mtime DESC LIMIT 1",
2175 zName
2176 );
2177 /* If found only by the name, then the file is unknown in the check-in.
2178 ** Possibly, the file was renamed/deleted.
2179 */
2180 if( rid && zCIUuid ){
2181 rid = 0;
2182 isUnknownAtCI = 1;
2183 }
2184 /* If no file called NAME exists, instead look for a directory
2185 ** with that name, and do a directory listing */
2186 if( rid==0 ){
2187 int nName = (int)strlen(zName);
2188 if( nName && zName[nName-1]=='/' ) nName--;
2189 if( db_exists(
2190 "SELECT 1 FROM filename"
2191 " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2192 nName, zName, nName+1, nName, zName
2193 ) ){
2194 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2195 page_tree();
2196 return;
2197 }
2198 }
2199 /* If no file or directory called NAME: issue an error */
2200 if( rid==0 ){
2201 if( isUnknownAtCI ){
2202 if( isSymbolicCI ){
2203 zHeader = mprintf("No such file at %s", zCI);
2204 }else{
2205 zHeader = mprintf("No such file at [%S]", zCIUuid);
2206 }
2207 style_header("%s", zHeader);
2208 fossil_free(zHeader);
2209 @ File %z(href("%R/finfo?name=%T",zName))%h(zName)</a> is not known
2210 @ at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>].
2211 }else{
2212 style_header("No such file");
2213 @ File '%h(zName)' is not known in this repository.
2214 }
2215 style_footer();
2216 return;
2217 }
2218 }else{
2219 rid = name_to_rid_www("name");
2220 }
2221 }
2222
2223 if( rid==0 ){
2224 style_header("No such artifact");
2225 @ Artifact '%h(zName)' does not exist in this repository.
2226 style_footer();
2227 return;
2228 }
2229 if( descOnly || P("verbose")!=0 ){
2230 url_add_parameter(&url, "verbose", "1");
2231 objdescFlags |= OBJDESC_DETAIL;
2232 }
2233 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2234
 
2235 if( isFile ){
2236 if( zCI ){
 
 
 
 
2237 const char *zPath;
2238 Blob path;
2239 blob_zero(&path);
2240 hyperlinked_path(zName, &path, zCI, "dir", "");
2241 zPath = blob_str(&path);
2242 @ <h2>File %s(zPath) at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]
2243 @ </h2>
 
 
 
 
 
 
2244 blob_reset(&path);
2245 }else{
2246 @ <h2>Latest version of file '%h(zName)':</h2>
2247 }
2248 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
 
 
 
 
 
 
2249 }else{
2250 @ <h2>Artifact
2251 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
2252 if( g.perm.Setup ){
2253 @ (%d(rid)):</h2>
2254 }else{
2255 @ :</h2>
2256 }
 
 
 
 
2257 }
2258 blob_zero(&downloadName);
2259 asText = P("txt")!=0;
2260 if( asText ) objdescFlags &= ~OBJDESC_BASE;
2261 objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName);
2262 if( !descOnly && P("download")!=0 ){
2263 cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
2264 db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
2265 /*NOTREACHED*/
2266 }
@@ -2288,11 +2291,11 @@
2288 zHeader = mprintf("Artifact [%S]", zUuid);
2289 }
2290 style_header("%s", zHeader);
2291 fossil_free(zCIUuid);
2292 fossil_free(zHeader);
2293 if( g.perm.Admin ){
2294 Stmt q;
2295 db_prepare(&q,
2296 "SELECT coalesce(user.login,rcvfrom.uid),"
2297 " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
2298 " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
2299
--- src/info.c
+++ src/info.c
@@ -1664,12 +1664,12 @@
1664 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1665 cookie_link_parameter("diff","diff","2");
1666 diffType = atoi(PD("diff","2"));
1667 cookie_render();
1668 if( P("from") && P("to") ){
1669 v1 = artifact_from_ci_and_filename("from");
1670 v2 = artifact_from_ci_and_filename("to");
1671 }else{
1672 Stmt q;
1673 v1 = name_to_rid_www("v1");
1674 v2 = name_to_rid_www("v2");
1675
@@ -1766,12 +1766,12 @@
1766 */
1767 void rawartifact_page(void){
1768 int rid = 0;
1769 char *zUuid;
1770
1771 if( P("ci") ){
1772 rid = artifact_from_ci_and_filename(0);
1773 }
1774 if( rid==0 ){
1775 rid = name_to_rid_www("name");
1776 }
1777 login_check_credentials();
@@ -1945,59 +1945,53 @@
1945
1946 /*
1947 ** Look for "ci" and "filename" query parameters. If found, try to
1948 ** use them to extract the record ID of an artifact for the file.
1949 **
1950 ** Also look for "fn" and "name" as an aliases for "filename". If any
1951 ** "filename" or "fn" or "name" are present but "ci" is missing, then
1952 ** use "tip" as the default value for "ci".
1953 **
1954 ** If zNameParam is not NULL, then use that parameter as the filename
1955 ** rather than "fn" or "filename" or "name". the zNameParam is used
1956 ** for the from= and to= query parameters of /fdiff.
 
 
 
 
1957 */
1958 int artifact_from_ci_and_filename(const char *zNameParam){
1959 const char *zFilename;
1960 const char *zCI;
1961 int cirid;
1962 Manifest *pManifest;
1963 ManifestFile *pFile;
1964 int rid = 0;
1965
1966 if( zNameParam ){
1967 zFilename = P(zNameParam);
1968 }else{
1969 zFilename = P("filename");
1970 if( zFilename==0 ){
1971 zFilename = P("fn");
1972 }
1973 if( zFilename==0 ){
1974 zFilename = P("name");
1975 }
1976 }
1977 if( zFilename==0 ) return 0;
1978
1979 zCI = PD("ci", "tip");
1980 cirid = name_to_typed_rid(zCI, "ci");
1981 if( cirid<=0 ) return 0;
1982 pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0);
1983 if( pManifest==0 ) return 0;
1984 manifest_file_rewind(pManifest);
1985 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
1986 if( fossil_strcmp(zFilename, pFile->zName)==0 ){
1987 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1988 break;
 
 
 
 
 
 
1989 }
1990 }
1991 manifest_destroy(pManifest);
1992 return rid;
1993 }
1994
1995 /*
1996 ** The "z" argument is a string that contains the text of a source code
1997 ** file. This routine appends that text to the HTTP reply with line numbering.
@@ -2099,21 +2093,31 @@
2093 ** ln=N - highlight line number N
2094 ** ln=M-N - highlight lines M through N inclusive
2095 ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
2096 ** verbose - show more detail in the description
2097 ** download - redirect to the download (artifact page only)
2098 ** name=NAME - filename or hash as a query parameter
2099 ** filename=NAME - alternative spelling for "name="
2100 ** fn=NAME - alternative spelling for "name="
2101 ** ci=VERSION - The specific check-in to use with "name=" to
2102 ** identify the file.
2103 **
2104 ** The /artifact page show the complete content of a file
2105 ** identified by HASH. The /whatis page shows only a description
2106 ** of how the artifact is used. The /file page shows the most recent
2107 ** version of the file or directory called NAME, or a list of the
2108 ** top-level directory if NAME is omitted.
2109 **
2110 ** For /artifact and /whatis, the name= query parameter can refer to
2111 ** either the name of a file, or an artifact hash. If the ci= query
2112 ** parameter is also present, then name= must refer to a file name.
2113 ** If ci= is omitted, then the hash interpretation is preferred but
2114 ** if name= cannot be understood as a hash, a default "tip" value is
2115 ** used for ci=.
2116 **
2117 ** For /file, name= can only be interpreted as a filename. As before,
2118 ** a default value of "tip" is used for ci= if ci= is omitted.
2119 */
2120 void artifact_page(void){
2121 int rid = 0;
2122 Blob content;
2123 const char *zMime;
@@ -2128,139 +2132,138 @@
2132 int isFile = fossil_strcmp(g.zPath,"file")==0;
2133 const char *zLn = P("ln");
2134 const char *zName = P("name");
2135 const char *zCI = P("ci");
2136 HQuery url;
 
2137 char *zCIUuid = 0;
2138 int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
2139 int isBranchCI = 0; /* ci= refers to a branch name */
2140 char *zHeader = 0;
2141
2142 login_check_credentials();
2143 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2144
2145 /* Capture and normalize the name= and ci= query parameters */
2146 if( zName==0 ){
2147 zName = P("filename");
2148 if( zName==0 ){
2149 zName = P("fn");
2150 }
2151 }
2152 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2153 if( zCI
2154 && name_to_uuid2(zCI, "ci", &zCIUuid)
2155 && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0
2156 ){
2157 isSymbolicCI = 1;
2158 isBranchCI = branch_includes_uuid(zCI, zCIUuid);
2159 }
2160
2161 /* The name= query parameter (or at least one of its alternative
2162 ** spellings) is required. Except for /file, show a top-level
2163 ** directory listing if name= is omitted.
2164 */
2165 if( zName==0 ){
2166 if( isFile ){
2167 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2168 page_tree();
2169 return;
2170 }
2171 style_header("Missing name= query parameter");
2172 @ The name= query parameter is missing
2173 style_footer();
2174 return;
2175 }
2176
2177 url_initialize(&url, g.zPath);
2178 url_add_parameter(&url, "name", zName);
2179 url_add_parameter(&url, "ci", zCI);
2180
2181 if( zCI==0 && !isFile ){
2182 /* If there is no ci= query parameter, then prefer to interpret
2183 ** name= as a hash for /artifact and /whatis. But for not for /file.
2184 ** For /file, a name= without a ci= while prefer to use the default
2185 ** "tip" value for ci=. */
2186 rid = name_to_rid(zName);
2187 }
2188 if( rid==0 ){
2189 rid = artifact_from_ci_and_filename(0);
2190 }
2191
2192 if( rid==0 ){ /* Artifact not found */
2193 if( isFile ){
2194 /* For /file, also check to see if name= refers to a directory,
2195 ** and if so, do a listing for that directory */
2196 int nName = (int)strlen(zName);
2197 if( nName && zName[nName-1]=='/' ) nName--;
2198 if( db_exists(
2199 "SELECT 1 FROM filename"
2200 " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2201 nName, zName, nName+1, nName, zName
2202 ) ){
2203 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2204 page_tree();
2205 return;
2206 }
2207 style_header("No such file");
2208 @ File '%h(zName)' does not exist in this repository.
2209 }else{
2210 style_header("No such artifact");
2211 @ Artifact '%h(zName)' does not exist in this repository.
2212 }
2213 style_footer();
2214 return;
2215 }
2216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2217 if( descOnly || P("verbose")!=0 ){
2218 url_add_parameter(&url, "verbose", "1");
2219 objdescFlags |= OBJDESC_DETAIL;
2220 }
2221 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2222
2223 asText = P("txt")!=0;
2224 if( isFile ){
2225 if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2226 zCI = "tip";
2227 @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a>
2228 @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
2229 }else{
2230 const char *zPath;
2231 Blob path;
2232 blob_zero(&path);
2233 hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO);
2234 zPath = blob_str(&path);
2235 @ <h2>File %s(zPath) \
2236 if( isBranchCI ){
2237 @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
2238 }else if( isSymbolicCI ){
2239 @ as of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
2240 }else{
2241 @ as of check-in [%z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2>
2242 }
2243 blob_reset(&path);
 
 
2244 }
2245 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2246 style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
2247 zName, zCI);
2248 style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
2249 zName, zCI);
2250 blob_init(&downloadName, zName, -1);
2251 objType = OBJTYPE_CONTENT;
2252 }else{
2253 @ <h2>Artifact
2254 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
2255 if( g.perm.Setup ){
2256 @ (%d(rid)):</h2>
2257 }else{
2258 @ :</h2>
2259 }
2260 blob_zero(&downloadName);
2261 if( asText ) objdescFlags &= ~OBJDESC_BASE;
2262 objType = object_description(rid, objdescFlags,
2263 (isFile?zName:0), &downloadName);
2264 }
 
 
 
 
2265 if( !descOnly && P("download")!=0 ){
2266 cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
2267 db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
2268 /*NOTREACHED*/
2269 }
@@ -2288,11 +2291,11 @@
2291 zHeader = mprintf("Artifact [%S]", zUuid);
2292 }
2293 style_header("%s", zHeader);
2294 fossil_free(zCIUuid);
2295 fossil_free(zHeader);
2296 if( !isFile && g.perm.Admin ){
2297 Stmt q;
2298 db_prepare(&q,
2299 "SELECT coalesce(user.login,rcvfrom.uid),"
2300 " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
2301 " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
2302
--- src/mkversion.c
+++ src/mkversion.c
@@ -9,27 +9,59 @@
99
*/
1010
#include <stdio.h>
1111
#include <string.h>
1212
#include <stdlib.h>
1313
#include <ctype.h>
14
+#include <time.h>
1415
1516
static FILE *open_for_reading(const char *zFilename){
1617
FILE *f = fopen(zFilename, "r");
1718
if( f==0 ){
1819
fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
1920
exit(1);
2021
}
2122
return f;
2223
}
24
+
25
+/*
26
+** Given an arbitrary-length input string key zIn, generate
27
+** an N-byte hexadecimal hash of that string into zOut.
28
+*/
29
+static void hash(const char *zIn, int N, char *zOut){
30
+ unsigned char i, j, t;
31
+ int m, n;
32
+ unsigned char s[256];
33
+ for(m=0; m<256; m++){ s[m] = m; }
34
+ for(j=0, m=n=0; m<256; m++, n++){
35
+ j += s[m] + zIn[n];
36
+ if( zIn[n]==0 ){ n = -1; }
37
+ t = s[j];
38
+ s[j] = s[m];
39
+ s[m] = t;
40
+ }
41
+ i = j = 0;
42
+ for(n=0; n<N-2; n+=2){
43
+ i++;
44
+ t = s[i];
45
+ j += t;
46
+ s[i] = s[j];
47
+ s[j] = t;
48
+ t += s[i];
49
+ zOut[n] = "0123456789abcdef"[(t>>4)&0xf];
50
+ zOut[n+1] = "0123456789abcdef"[t&0xf];
51
+ }
52
+ zOut[n] = 0;
53
+}
2354
2455
int main(int argc, char *argv[]){
2556
FILE *m,*u,*v;
2657
char *z;
2758
#if defined(__DMC__) /* e.g. 0x857 */
2859
int i = 0;
2960
#endif
3061
int j = 0, x = 0, d = 0;
62
+ size_t n;
3163
int vn[3];
3264
char b[1000];
3365
char vx[1000];
3466
if( argc!=4 ){
3567
fprintf(stderr, "Usage: %s manifest.uuid manifest VERSION\n", argv[0]);
@@ -45,10 +77,16 @@
4577
fclose(u);
4678
for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){}
4779
*z = 0;
4880
printf("#define MANIFEST_UUID \"%s\"\n",b);
4981
printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
82
+ n = strlen(b);
83
+ if( n + 50 < sizeof(b) ){
84
+ sprintf(b+n, "%d", (int)time(0));
85
+ hash(b,33,vx);
86
+ printf("#define FOSSIL_BUILD_HASH \"%s\"\n", vx);
87
+ }
5088
m = open_for_reading(argv[2]);
5189
while(b == fgets(b, sizeof(b)-1,m)){
5290
if(0 == strncmp("D ",b,2)){
5391
int k, n;
5492
char zDateNum[30];
5593
--- src/mkversion.c
+++ src/mkversion.c
@@ -9,27 +9,59 @@
9 */
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <ctype.h>
 
14
15 static FILE *open_for_reading(const char *zFilename){
16 FILE *f = fopen(zFilename, "r");
17 if( f==0 ){
18 fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
19 exit(1);
20 }
21 return f;
22 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
24 int main(int argc, char *argv[]){
25 FILE *m,*u,*v;
26 char *z;
27 #if defined(__DMC__) /* e.g. 0x857 */
28 int i = 0;
29 #endif
30 int j = 0, x = 0, d = 0;
 
31 int vn[3];
32 char b[1000];
33 char vx[1000];
34 if( argc!=4 ){
35 fprintf(stderr, "Usage: %s manifest.uuid manifest VERSION\n", argv[0]);
@@ -45,10 +77,16 @@
45 fclose(u);
46 for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){}
47 *z = 0;
48 printf("#define MANIFEST_UUID \"%s\"\n",b);
49 printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
 
 
 
 
 
 
50 m = open_for_reading(argv[2]);
51 while(b == fgets(b, sizeof(b)-1,m)){
52 if(0 == strncmp("D ",b,2)){
53 int k, n;
54 char zDateNum[30];
55
--- src/mkversion.c
+++ src/mkversion.c
@@ -9,27 +9,59 @@
9 */
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include <time.h>
15
16 static FILE *open_for_reading(const char *zFilename){
17 FILE *f = fopen(zFilename, "r");
18 if( f==0 ){
19 fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
20 exit(1);
21 }
22 return f;
23 }
24
25 /*
26 ** Given an arbitrary-length input string key zIn, generate
27 ** an N-byte hexadecimal hash of that string into zOut.
28 */
29 static void hash(const char *zIn, int N, char *zOut){
30 unsigned char i, j, t;
31 int m, n;
32 unsigned char s[256];
33 for(m=0; m<256; m++){ s[m] = m; }
34 for(j=0, m=n=0; m<256; m++, n++){
35 j += s[m] + zIn[n];
36 if( zIn[n]==0 ){ n = -1; }
37 t = s[j];
38 s[j] = s[m];
39 s[m] = t;
40 }
41 i = j = 0;
42 for(n=0; n<N-2; n+=2){
43 i++;
44 t = s[i];
45 j += t;
46 s[i] = s[j];
47 s[j] = t;
48 t += s[i];
49 zOut[n] = "0123456789abcdef"[(t>>4)&0xf];
50 zOut[n+1] = "0123456789abcdef"[t&0xf];
51 }
52 zOut[n] = 0;
53 }
54
55 int main(int argc, char *argv[]){
56 FILE *m,*u,*v;
57 char *z;
58 #if defined(__DMC__) /* e.g. 0x857 */
59 int i = 0;
60 #endif
61 int j = 0, x = 0, d = 0;
62 size_t n;
63 int vn[3];
64 char b[1000];
65 char vx[1000];
66 if( argc!=4 ){
67 fprintf(stderr, "Usage: %s manifest.uuid manifest VERSION\n", argv[0]);
@@ -45,10 +77,16 @@
77 fclose(u);
78 for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){}
79 *z = 0;
80 printf("#define MANIFEST_UUID \"%s\"\n",b);
81 printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
82 n = strlen(b);
83 if( n + 50 < sizeof(b) ){
84 sprintf(b+n, "%d", (int)time(0));
85 hash(b,33,vx);
86 printf("#define FOSSIL_BUILD_HASH \"%s\"\n", vx);
87 }
88 m = open_for_reading(argv[2]);
89 while(b == fgets(b, sizeof(b)-1,m)){
90 if(0 == strncmp("D ",b,2)){
91 int k, n;
92 char zDateNum[30];
93
+2 -2
--- src/shun.c
+++ src/shun.c
@@ -187,11 +187,11 @@
187187
@ sight - set the "hidden" tag on such artifacts instead.</p>
188188
@
189189
@ <blockquote>
190190
@ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
191191
login_insert_csrf_secret();
192
- @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid">
192
+ @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid">
193193
if( zShun ){
194194
if( strlen(zShun) ){
195195
@ %h(zShun)
196196
}else if( nRcvid ){
197197
db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
@@ -214,11 +214,11 @@
214214
@ operations.</p>
215215
@
216216
@ <blockquote>
217217
@ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
218218
login_insert_csrf_secret();
219
- @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid">
219
+ @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid">
220220
if( zAccept ){
221221
if( strlen(zAccept) ){
222222
@ %h(zAccept)
223223
}else if( nRcvid ){
224224
db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
225225
--- src/shun.c
+++ src/shun.c
@@ -187,11 +187,11 @@
187 @ sight - set the "hidden" tag on such artifacts instead.</p>
188 @
189 @ <blockquote>
190 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
191 login_insert_csrf_secret();
192 @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid">
193 if( zShun ){
194 if( strlen(zShun) ){
195 @ %h(zShun)
196 }else if( nRcvid ){
197 db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
@@ -214,11 +214,11 @@
214 @ operations.</p>
215 @
216 @ <blockquote>
217 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
218 login_insert_csrf_secret();
219 @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid">
220 if( zAccept ){
221 if( strlen(zAccept) ){
222 @ %h(zAccept)
223 }else if( nRcvid ){
224 db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
225
--- src/shun.c
+++ src/shun.c
@@ -187,11 +187,11 @@
187 @ sight - set the "hidden" tag on such artifacts instead.</p>
188 @
189 @ <blockquote>
190 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
191 login_insert_csrf_secret();
192 @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid">
193 if( zShun ){
194 if( strlen(zShun) ){
195 @ %h(zShun)
196 }else if( nRcvid ){
197 db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
@@ -214,11 +214,11 @@
214 @ operations.</p>
215 @
216 @ <blockquote>
217 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
218 login_insert_csrf_secret();
219 @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid">
220 if( zAccept ){
221 if( strlen(zAccept) ){
222 @ %h(zAccept)
223 }else if( nRcvid ){
224 db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
225
--- src/timeline.c
+++ src/timeline.c
@@ -2201,10 +2201,17 @@
22012201
"CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
22022202
"INSERT OR IGNORE INTO selected_nodes"
22032203
" SELECT tagxref.rid FROM tagxref NATURAL JOIN tag"
22042204
" WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/
22052205
);
2206
+ if( zMark ){
2207
+ /* If the t=release option is used with m=UUID, then also
2208
+ ** include the UUID check-in in the display list */
2209
+ int ridMark = name_to_rid(zMark);
2210
+ db_multi_exec(
2211
+ "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
2212
+ }
22062213
if( !related ){
22072214
blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
22082215
}else{
22092216
db_multi_exec(
22102217
"CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
@@ -2427,10 +2434,13 @@
24272434
blob_appendf(&desc, " related to tags matching %h", zMatchDesc);
24282435
}else{
24292436
blob_appendf(&desc, " with tags matching %h", zMatchDesc);
24302437
}
24312438
}
2439
+ if( zMark ){
2440
+ blob_appendf(&desc," plus check-in \"%h\"", zMark);
2441
+ }
24322442
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
24332443
}
24342444
addFileGlobDescription(zChng, &desc);
24352445
if( rAfter>0.0 ){
24362446
if( rBefore>0.0 ){
24372447
--- src/timeline.c
+++ src/timeline.c
@@ -2201,10 +2201,17 @@
2201 "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
2202 "INSERT OR IGNORE INTO selected_nodes"
2203 " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag"
2204 " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/
2205 );
 
 
 
 
 
 
 
2206 if( !related ){
2207 blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
2208 }else{
2209 db_multi_exec(
2210 "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
@@ -2427,10 +2434,13 @@
2427 blob_appendf(&desc, " related to tags matching %h", zMatchDesc);
2428 }else{
2429 blob_appendf(&desc, " with tags matching %h", zMatchDesc);
2430 }
2431 }
 
 
 
2432 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2433 }
2434 addFileGlobDescription(zChng, &desc);
2435 if( rAfter>0.0 ){
2436 if( rBefore>0.0 ){
2437
--- src/timeline.c
+++ src/timeline.c
@@ -2201,10 +2201,17 @@
2201 "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
2202 "INSERT OR IGNORE INTO selected_nodes"
2203 " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag"
2204 " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/
2205 );
2206 if( zMark ){
2207 /* If the t=release option is used with m=UUID, then also
2208 ** include the UUID check-in in the display list */
2209 int ridMark = name_to_rid(zMark);
2210 db_multi_exec(
2211 "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
2212 }
2213 if( !related ){
2214 blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
2215 }else{
2216 db_multi_exec(
2217 "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
@@ -2427,10 +2434,13 @@
2434 blob_appendf(&desc, " related to tags matching %h", zMatchDesc);
2435 }else{
2436 blob_appendf(&desc, " with tags matching %h", zMatchDesc);
2437 }
2438 }
2439 if( zMark ){
2440 blob_appendf(&desc," plus check-in \"%h\"", zMark);
2441 }
2442 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2443 }
2444 addFileGlobDescription(zChng, &desc);
2445 if( rAfter>0.0 ){
2446 if( rBefore>0.0 ){
2447

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button