Fossil SCM

Add the /tree URI for showing a hierarchical file listing. The URI works, but there are no hyperlinks to it yet.

drh 2013-12-31 10:43 trunk
Commit 7791b704109adb8951d89ef8a70a21ebcfa30ace
1 file changed +286 -3
+286 -3
--- src/browse.c
+++ src/browse.c
@@ -101,11 +101,11 @@
101101
/*
102102
** WEBPAGE: dir
103103
**
104104
** Query parameters:
105105
**
106
-** name=PATH Directory to display. Required.
106
+** name=PATH Directory to display. Optional. Top-level if missing
107107
** ci=LABEL Show only files in this check-in. Optional.
108108
*/
109109
void page_dir(void){
110110
char *zD = fossil_strdup(P("name"));
111111
int nD = zD ? strlen(zD)+1 : 0;
@@ -156,20 +156,20 @@
156156
zPrefix = mprintf("%s/", zD);
157157
if( linkTrunk ){
158158
style_submenu_element("Trunk", "Trunk", "%R/dir?name=%t&ci=trunk",
159159
zD);
160160
}
161
- if ( linkTip ){
161
+ if( linkTip ){
162162
style_submenu_element("Tip", "Tip", "%R/dir?name=%t&ci=tip", zD);
163163
}
164164
}else{
165165
blob_append(&dirname, "in the top-level directory", -1);
166166
zPrefix = "";
167167
if( linkTrunk ){
168168
style_submenu_element("Trunk", "Trunk", "%R/dir?ci=trunk");
169169
}
170
- if ( linkTip ){
170
+ if( linkTip ){
171171
style_submenu_element("Tip", "Tip", "%R/dir?ci=tip");
172172
}
173173
}
174174
if( zCI ){
175175
char zShort[20];
@@ -287,10 +287,293 @@
287287
db_finalize(&q);
288288
manifest_destroy(pM);
289289
@ </ul></td></tr></table>
290290
style_footer();
291291
}
292
+
293
+/*
294
+** Objects used by the "tree" webpage.
295
+*/
296
+typedef struct FileTreeNode FileTreeNode;
297
+typedef struct FileTree FileTree;
298
+
299
+/*
300
+** A single line of the file hierarchy
301
+*/
302
+struct FileTreeNode {
303
+ FileTreeNode *pNext; /* Next line in sequence */
304
+ FileTreeNode *pPrev; /* Previous line */
305
+ FileTreeNode *pParent; /* Directory containing this line */
306
+ char *zName; /* Name of this entry. The "tail" */
307
+ char *zFullName; /* Full pathname of this entry */
308
+ char *zUuid; /* SHA1 hash of this file. May be NULL. */
309
+ unsigned nFullName; /* Length of zFullName */
310
+ unsigned iLevel; /* Levels of parent directories */
311
+ u8 isDir; /* True if there are children */
312
+ u8 isLast; /* True if this is the last child of its parent */
313
+};
314
+
315
+/*
316
+** A complete file hierarchy
317
+*/
318
+struct FileTree {
319
+ FileTreeNode *pFirst; /* First line of the list */
320
+ FileTreeNode *pLast; /* Last line of the list */
321
+};
322
+
323
+/*
324
+** Add one or more new FileTreeNodes to the FileTree object so that the
325
+** leaf object zPathname is at the end of the node list
326
+*/
327
+static void tree_add_node(
328
+ FileTree *pTree, /* Tree into which nodes are added */
329
+ const char *zPath, /* The full pathname of file to add */
330
+ const char *zUuid /* UUID of the file. Might be NULL. */
331
+){
332
+ int i;
333
+ FileTreeNode *pParent;
334
+ FileTreeNode *pChild;
335
+
336
+ pChild = pTree->pLast;
337
+ pParent = pChild ? pChild->pParent : 0;
338
+ while( pParent!=0 &&
339
+ ( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
340
+ || zPath[pParent->nFullName]!='/' )
341
+ ){
342
+ pChild = pParent;
343
+ pParent = pChild->pParent;
344
+ }
345
+ i = pParent ? pParent->nFullName+1 : 0;
346
+ if( pChild ) pChild->isLast = 0;
347
+ while( zPath[i] ){
348
+ FileTreeNode *pNew;
349
+ int iStart = i;
350
+ int nByte;
351
+ while( zPath[i] && zPath[i]!='/' ){ i++; }
352
+ nByte = sizeof(*pNew) + i + 1;
353
+ if( zUuid!=0 && zPath[i]==0 ) nByte += UUID_SIZE+1;
354
+ pNew = fossil_malloc( nByte );
355
+ pNew->zFullName = (char*)&pNew[1];
356
+ memcpy(pNew->zFullName, zPath, i);
357
+ pNew->zFullName[i] = 0;
358
+ pNew->nFullName = i;
359
+ if( zUuid!=0 && zPath[i]==0 ){
360
+ pNew->zUuid = pNew->zFullName + i + 1;
361
+ memcpy(pNew->zUuid, zUuid, UUID_SIZE+1);
362
+ }else{
363
+ pNew->zUuid = 0;
364
+ }
365
+ pNew->zName = pNew->zFullName + iStart;
366
+ if( pTree->pLast ){
367
+ pTree->pLast->pNext = pNew;
368
+ }else{
369
+ pTree->pFirst = pNew;
370
+ }
371
+ pNew->pPrev = pTree->pLast;
372
+ pNew->pNext = 0;
373
+ pNew->pParent = pParent;
374
+ pTree->pLast = pNew;
375
+ pNew->iLevel = pParent ? pParent->iLevel+1 : 0;
376
+ pNew->isDir = zPath[i]=='/';
377
+ pNew->isLast = 1;
378
+ while( zPath[i]=='/' ){ i++; }
379
+ pParent = pNew;
380
+ }
381
+}
382
+
383
+/*
384
+** Render parent lines for pNode
385
+*/
386
+static void tree_indentation(FileTreeNode *p){
387
+ if( p==0 ) return;
388
+ tree_indentation(p->pParent);
389
+ if( p->isLast ){
390
+ cgi_append_content(" ", 4);
391
+ }else{
392
+ cgi_append_content("&#x2502; ", 11);
393
+ }
394
+}
395
+
396
+
397
+/*
398
+** WEBPAGE: tree
399
+**
400
+** Query parameters:
401
+**
402
+** name=PATH Directory to display. Optional
403
+** ci=LABEL Show only files in this check-in. Optional.
404
+** re=REGEXP Show only files matching REGEXP. Optional.
405
+*/
406
+void page_tree(void){
407
+ char *zD = fossil_strdup(P("name"));
408
+ int nD = zD ? strlen(zD)+1 : 0;
409
+ const char *zCI = P("ci");
410
+ int rid = 0;
411
+ char *zUuid = 0;
412
+ Blob dirname;
413
+ Manifest *pM = 0;
414
+ int linkTrunk = 1, linkTip = 1;
415
+ const char *zRE;
416
+ ReCompiled *pRE = 0;
417
+ FileTreeNode *p;
418
+ FileTree sTree;
419
+
420
+ memset(&sTree, 0, sizeof(sTree));
421
+ login_check_credentials();
422
+ if( !g.perm.Read ){ login_needed(); return; }
423
+ while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
424
+ style_header("File List");
425
+ sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
426
+ pathelementFunc, 0, 0);
427
+
428
+ /* If a regular expression is specified, compile it */
429
+ zRE = P("re");
430
+ if( zRE ) re_compile(&pRE, zRE, 0);
431
+
432
+ /* If the name= parameter is an empty string, make it a NULL pointer */
433
+ if( zD && strlen(zD)==0 ){ zD = 0; }
434
+
435
+ /* If a specific check-in is requested, fetch and parse it. If the
436
+ ** specific check-in does not exist, clear zCI. zCI==0 will cause all
437
+ ** files from all check-ins to be displayed.
438
+ */
439
+ if( zCI ){
440
+ pM = manifest_get_by_name(zCI, &rid);
441
+ if( pM ){
442
+ int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
443
+ linkTrunk = trunkRid && rid != trunkRid;
444
+ linkTip = rid != symbolic_name_to_rid("tip", "ci");
445
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
446
+ }else{
447
+ zCI = 0;
448
+ }
449
+ }
450
+
451
+ /* Compute the title of the page */
452
+ blob_zero(&dirname);
453
+ if( zD ){
454
+ blob_appendf(&dirname, "in directory %h", zD);
455
+ if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
456
+ if( linkTrunk ){
457
+ style_submenu_element("Trunk", "Trunk", "%R/tree?name=%t&ci=trunk",
458
+ zD);
459
+ }
460
+ if ( linkTip ){
461
+ style_submenu_element("Tip", "Tip", "%R/tree?name=%t&ci=tip", zD);
462
+ }
463
+ }else{
464
+ if( zRE ){
465
+ blob_appendf(&dirname, "matching \"%s\"", zRE);
466
+ }else{
467
+ blob_append(&dirname, "in the top-level directory", -1);
468
+ }
469
+ if( linkTrunk ){
470
+ style_submenu_element("Trunk", "Trunk", "%R/tree?ci=trunk");
471
+ }
472
+ if ( linkTip ){
473
+ style_submenu_element("Tip", "Tip", "%R/tree?ci=tip");
474
+ }
475
+ }
476
+ if( zCI ){
477
+ char zShort[20];
478
+ memcpy(zShort, zUuid, 10);
479
+ zShort[10] = 0;
480
+ @ <h2>Files of check-in [%z(href("vinfo?name=%T",zUuid))%s(zShort)</a>]
481
+ @ %s(blob_str(&dirname))</h2>
482
+ if( zD ){
483
+ style_submenu_element("Top", "Top", "%R/tree?ci=%S", zUuid);
484
+ style_submenu_element("All", "All", "%R/tree?name=%t", zD);
485
+ }else{
486
+ style_submenu_element("All", "All", "%R/tree");
487
+ }
488
+ }else{
489
+ @ <h2>The union of all files from all check-ins
490
+ @ %s(blob_str(&dirname))</h2>
491
+ }
492
+
493
+ /* Compute the file hierarchy.
494
+ */
495
+ if( zCI ){
496
+ Stmt ins, q;
497
+ ManifestFile *pFile;
498
+
499
+ db_multi_exec(
500
+ "CREATE TEMP TABLE filelist("
501
+ " x TEXT PRIMARY KEY COLLATE nocase,"
502
+ " uuid TEXT"
503
+ ") WITHOUT ROWID;"
504
+ );
505
+ db_prepare(&ins, "INSERT OR IGNORE INTO filelist VALUES(:f,:u)");
506
+ manifest_file_rewind(pM);
507
+ while( (pFile = manifest_file_next(pM,0))!=0 ){
508
+ if( nD>0
509
+ && (fossil_strncmp(pFile->zName, zD, nD-1)!=0
510
+ || pFile->zName[nD-1]!='/')
511
+ ){
512
+ continue;
513
+ }
514
+ if( pRE && re_match(pRE, (const u8*)pFile->zName, -1)==0 ) continue;
515
+ db_bind_text(&ins, ":f", &pFile->zName[nD]);
516
+ db_bind_text(&ins, ":u", pFile->zUuid);
517
+ db_step(&ins);
518
+ db_reset(&ins);
519
+ }
520
+ db_finalize(&ins);
521
+ db_prepare(&q, "SELECT x, uuid FROM filelist ORDER BY x");
522
+ while( db_step(&q)==SQLITE_ROW ){
523
+ tree_add_node(&sTree, db_column_text(&q,0), db_column_text(&q,1));
524
+ }
525
+ db_finalize(&q);
526
+ }else{
527
+ Stmt q;
528
+ db_prepare(&q, "SELECT name FROM filename ORDER BY name COLLATE nocase");
529
+ while( db_step(&q)==SQLITE_ROW ){
530
+ const char *z = db_column_text(&q, 0);
531
+ if( nD>0 && (fossil_strncmp(z, zD, nD-1)!=0 || z[nD-1]!='/') ){
532
+ continue;
533
+ }
534
+ if( pRE && re_match(pRE, (const u8*)z, -1)==0 ) continue;
535
+ tree_add_node(&sTree, z+nD, 0);
536
+ }
537
+ db_finalize(&q);
538
+ }
539
+
540
+ /* Generate a multi-column table listing the contents of zD[]
541
+ ** directory.
542
+ */
543
+ @ <pre>
544
+ if( nD ){
545
+ cgi_printf("%.*h\n", nD, zD);
546
+ }else{
547
+ @ .
548
+ }
549
+ for(p=sTree.pFirst; p; p=p->pNext){
550
+ tree_indentation(p->pParent);
551
+ if( p->isLast ){
552
+ cgi_append_content("&#x2514;&#x2500;&#x2500; ", 25);
553
+ }else{
554
+ cgi_append_content("&#x251c;&#x2500;&#x2500; ", 25);
555
+ }
556
+ if( p->isDir ){
557
+ @ %h(p->zName)
558
+ }else{
559
+ char *zLink;
560
+ if( zCI ){
561
+ zLink = href("%R/artifact/%s",p->zUuid);
562
+ }else{
563
+ zLink = href("%R/finfo?name=%T",p->zFullName);
564
+ }
565
+ @ %z(zLink)%h(p->zName)</a>
566
+ }
567
+ }
568
+ @ </pre>
569
+ style_footer();
570
+
571
+ /* We could free memory used by sTree here if we needed to. But
572
+ ** the process is about to exit, so doing so would not really accomplish
573
+ ** anything useful. */
574
+}
292575
293576
/*
294577
** Return a CSS class name based on the given filename's extension.
295578
** Result must be freed by the caller.
296579
**/
297580
--- src/browse.c
+++ src/browse.c
@@ -101,11 +101,11 @@
101 /*
102 ** WEBPAGE: dir
103 **
104 ** Query parameters:
105 **
106 ** name=PATH Directory to display. Required.
107 ** ci=LABEL Show only files in this check-in. Optional.
108 */
109 void page_dir(void){
110 char *zD = fossil_strdup(P("name"));
111 int nD = zD ? strlen(zD)+1 : 0;
@@ -156,20 +156,20 @@
156 zPrefix = mprintf("%s/", zD);
157 if( linkTrunk ){
158 style_submenu_element("Trunk", "Trunk", "%R/dir?name=%t&ci=trunk",
159 zD);
160 }
161 if ( linkTip ){
162 style_submenu_element("Tip", "Tip", "%R/dir?name=%t&ci=tip", zD);
163 }
164 }else{
165 blob_append(&dirname, "in the top-level directory", -1);
166 zPrefix = "";
167 if( linkTrunk ){
168 style_submenu_element("Trunk", "Trunk", "%R/dir?ci=trunk");
169 }
170 if ( linkTip ){
171 style_submenu_element("Tip", "Tip", "%R/dir?ci=tip");
172 }
173 }
174 if( zCI ){
175 char zShort[20];
@@ -287,10 +287,293 @@
287 db_finalize(&q);
288 manifest_destroy(pM);
289 @ </ul></td></tr></table>
290 style_footer();
291 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
293 /*
294 ** Return a CSS class name based on the given filename's extension.
295 ** Result must be freed by the caller.
296 **/
297
--- src/browse.c
+++ src/browse.c
@@ -101,11 +101,11 @@
101 /*
102 ** WEBPAGE: dir
103 **
104 ** Query parameters:
105 **
106 ** name=PATH Directory to display. Optional. Top-level if missing
107 ** ci=LABEL Show only files in this check-in. Optional.
108 */
109 void page_dir(void){
110 char *zD = fossil_strdup(P("name"));
111 int nD = zD ? strlen(zD)+1 : 0;
@@ -156,20 +156,20 @@
156 zPrefix = mprintf("%s/", zD);
157 if( linkTrunk ){
158 style_submenu_element("Trunk", "Trunk", "%R/dir?name=%t&ci=trunk",
159 zD);
160 }
161 if( linkTip ){
162 style_submenu_element("Tip", "Tip", "%R/dir?name=%t&ci=tip", zD);
163 }
164 }else{
165 blob_append(&dirname, "in the top-level directory", -1);
166 zPrefix = "";
167 if( linkTrunk ){
168 style_submenu_element("Trunk", "Trunk", "%R/dir?ci=trunk");
169 }
170 if( linkTip ){
171 style_submenu_element("Tip", "Tip", "%R/dir?ci=tip");
172 }
173 }
174 if( zCI ){
175 char zShort[20];
@@ -287,10 +287,293 @@
287 db_finalize(&q);
288 manifest_destroy(pM);
289 @ </ul></td></tr></table>
290 style_footer();
291 }
292
293 /*
294 ** Objects used by the "tree" webpage.
295 */
296 typedef struct FileTreeNode FileTreeNode;
297 typedef struct FileTree FileTree;
298
299 /*
300 ** A single line of the file hierarchy
301 */
302 struct FileTreeNode {
303 FileTreeNode *pNext; /* Next line in sequence */
304 FileTreeNode *pPrev; /* Previous line */
305 FileTreeNode *pParent; /* Directory containing this line */
306 char *zName; /* Name of this entry. The "tail" */
307 char *zFullName; /* Full pathname of this entry */
308 char *zUuid; /* SHA1 hash of this file. May be NULL. */
309 unsigned nFullName; /* Length of zFullName */
310 unsigned iLevel; /* Levels of parent directories */
311 u8 isDir; /* True if there are children */
312 u8 isLast; /* True if this is the last child of its parent */
313 };
314
315 /*
316 ** A complete file hierarchy
317 */
318 struct FileTree {
319 FileTreeNode *pFirst; /* First line of the list */
320 FileTreeNode *pLast; /* Last line of the list */
321 };
322
323 /*
324 ** Add one or more new FileTreeNodes to the FileTree object so that the
325 ** leaf object zPathname is at the end of the node list
326 */
327 static void tree_add_node(
328 FileTree *pTree, /* Tree into which nodes are added */
329 const char *zPath, /* The full pathname of file to add */
330 const char *zUuid /* UUID of the file. Might be NULL. */
331 ){
332 int i;
333 FileTreeNode *pParent;
334 FileTreeNode *pChild;
335
336 pChild = pTree->pLast;
337 pParent = pChild ? pChild->pParent : 0;
338 while( pParent!=0 &&
339 ( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
340 || zPath[pParent->nFullName]!='/' )
341 ){
342 pChild = pParent;
343 pParent = pChild->pParent;
344 }
345 i = pParent ? pParent->nFullName+1 : 0;
346 if( pChild ) pChild->isLast = 0;
347 while( zPath[i] ){
348 FileTreeNode *pNew;
349 int iStart = i;
350 int nByte;
351 while( zPath[i] && zPath[i]!='/' ){ i++; }
352 nByte = sizeof(*pNew) + i + 1;
353 if( zUuid!=0 && zPath[i]==0 ) nByte += UUID_SIZE+1;
354 pNew = fossil_malloc( nByte );
355 pNew->zFullName = (char*)&pNew[1];
356 memcpy(pNew->zFullName, zPath, i);
357 pNew->zFullName[i] = 0;
358 pNew->nFullName = i;
359 if( zUuid!=0 && zPath[i]==0 ){
360 pNew->zUuid = pNew->zFullName + i + 1;
361 memcpy(pNew->zUuid, zUuid, UUID_SIZE+1);
362 }else{
363 pNew->zUuid = 0;
364 }
365 pNew->zName = pNew->zFullName + iStart;
366 if( pTree->pLast ){
367 pTree->pLast->pNext = pNew;
368 }else{
369 pTree->pFirst = pNew;
370 }
371 pNew->pPrev = pTree->pLast;
372 pNew->pNext = 0;
373 pNew->pParent = pParent;
374 pTree->pLast = pNew;
375 pNew->iLevel = pParent ? pParent->iLevel+1 : 0;
376 pNew->isDir = zPath[i]=='/';
377 pNew->isLast = 1;
378 while( zPath[i]=='/' ){ i++; }
379 pParent = pNew;
380 }
381 }
382
383 /*
384 ** Render parent lines for pNode
385 */
386 static void tree_indentation(FileTreeNode *p){
387 if( p==0 ) return;
388 tree_indentation(p->pParent);
389 if( p->isLast ){
390 cgi_append_content(" ", 4);
391 }else{
392 cgi_append_content("&#x2502; ", 11);
393 }
394 }
395
396
397 /*
398 ** WEBPAGE: tree
399 **
400 ** Query parameters:
401 **
402 ** name=PATH Directory to display. Optional
403 ** ci=LABEL Show only files in this check-in. Optional.
404 ** re=REGEXP Show only files matching REGEXP. Optional.
405 */
406 void page_tree(void){
407 char *zD = fossil_strdup(P("name"));
408 int nD = zD ? strlen(zD)+1 : 0;
409 const char *zCI = P("ci");
410 int rid = 0;
411 char *zUuid = 0;
412 Blob dirname;
413 Manifest *pM = 0;
414 int linkTrunk = 1, linkTip = 1;
415 const char *zRE;
416 ReCompiled *pRE = 0;
417 FileTreeNode *p;
418 FileTree sTree;
419
420 memset(&sTree, 0, sizeof(sTree));
421 login_check_credentials();
422 if( !g.perm.Read ){ login_needed(); return; }
423 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
424 style_header("File List");
425 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
426 pathelementFunc, 0, 0);
427
428 /* If a regular expression is specified, compile it */
429 zRE = P("re");
430 if( zRE ) re_compile(&pRE, zRE, 0);
431
432 /* If the name= parameter is an empty string, make it a NULL pointer */
433 if( zD && strlen(zD)==0 ){ zD = 0; }
434
435 /* If a specific check-in is requested, fetch and parse it. If the
436 ** specific check-in does not exist, clear zCI. zCI==0 will cause all
437 ** files from all check-ins to be displayed.
438 */
439 if( zCI ){
440 pM = manifest_get_by_name(zCI, &rid);
441 if( pM ){
442 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
443 linkTrunk = trunkRid && rid != trunkRid;
444 linkTip = rid != symbolic_name_to_rid("tip", "ci");
445 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
446 }else{
447 zCI = 0;
448 }
449 }
450
451 /* Compute the title of the page */
452 blob_zero(&dirname);
453 if( zD ){
454 blob_appendf(&dirname, "in directory %h", zD);
455 if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
456 if( linkTrunk ){
457 style_submenu_element("Trunk", "Trunk", "%R/tree?name=%t&ci=trunk",
458 zD);
459 }
460 if ( linkTip ){
461 style_submenu_element("Tip", "Tip", "%R/tree?name=%t&ci=tip", zD);
462 }
463 }else{
464 if( zRE ){
465 blob_appendf(&dirname, "matching \"%s\"", zRE);
466 }else{
467 blob_append(&dirname, "in the top-level directory", -1);
468 }
469 if( linkTrunk ){
470 style_submenu_element("Trunk", "Trunk", "%R/tree?ci=trunk");
471 }
472 if ( linkTip ){
473 style_submenu_element("Tip", "Tip", "%R/tree?ci=tip");
474 }
475 }
476 if( zCI ){
477 char zShort[20];
478 memcpy(zShort, zUuid, 10);
479 zShort[10] = 0;
480 @ <h2>Files of check-in [%z(href("vinfo?name=%T",zUuid))%s(zShort)</a>]
481 @ %s(blob_str(&dirname))</h2>
482 if( zD ){
483 style_submenu_element("Top", "Top", "%R/tree?ci=%S", zUuid);
484 style_submenu_element("All", "All", "%R/tree?name=%t", zD);
485 }else{
486 style_submenu_element("All", "All", "%R/tree");
487 }
488 }else{
489 @ <h2>The union of all files from all check-ins
490 @ %s(blob_str(&dirname))</h2>
491 }
492
493 /* Compute the file hierarchy.
494 */
495 if( zCI ){
496 Stmt ins, q;
497 ManifestFile *pFile;
498
499 db_multi_exec(
500 "CREATE TEMP TABLE filelist("
501 " x TEXT PRIMARY KEY COLLATE nocase,"
502 " uuid TEXT"
503 ") WITHOUT ROWID;"
504 );
505 db_prepare(&ins, "INSERT OR IGNORE INTO filelist VALUES(:f,:u)");
506 manifest_file_rewind(pM);
507 while( (pFile = manifest_file_next(pM,0))!=0 ){
508 if( nD>0
509 && (fossil_strncmp(pFile->zName, zD, nD-1)!=0
510 || pFile->zName[nD-1]!='/')
511 ){
512 continue;
513 }
514 if( pRE && re_match(pRE, (const u8*)pFile->zName, -1)==0 ) continue;
515 db_bind_text(&ins, ":f", &pFile->zName[nD]);
516 db_bind_text(&ins, ":u", pFile->zUuid);
517 db_step(&ins);
518 db_reset(&ins);
519 }
520 db_finalize(&ins);
521 db_prepare(&q, "SELECT x, uuid FROM filelist ORDER BY x");
522 while( db_step(&q)==SQLITE_ROW ){
523 tree_add_node(&sTree, db_column_text(&q,0), db_column_text(&q,1));
524 }
525 db_finalize(&q);
526 }else{
527 Stmt q;
528 db_prepare(&q, "SELECT name FROM filename ORDER BY name COLLATE nocase");
529 while( db_step(&q)==SQLITE_ROW ){
530 const char *z = db_column_text(&q, 0);
531 if( nD>0 && (fossil_strncmp(z, zD, nD-1)!=0 || z[nD-1]!='/') ){
532 continue;
533 }
534 if( pRE && re_match(pRE, (const u8*)z, -1)==0 ) continue;
535 tree_add_node(&sTree, z+nD, 0);
536 }
537 db_finalize(&q);
538 }
539
540 /* Generate a multi-column table listing the contents of zD[]
541 ** directory.
542 */
543 @ <pre>
544 if( nD ){
545 cgi_printf("%.*h\n", nD, zD);
546 }else{
547 @ .
548 }
549 for(p=sTree.pFirst; p; p=p->pNext){
550 tree_indentation(p->pParent);
551 if( p->isLast ){
552 cgi_append_content("&#x2514;&#x2500;&#x2500; ", 25);
553 }else{
554 cgi_append_content("&#x251c;&#x2500;&#x2500; ", 25);
555 }
556 if( p->isDir ){
557 @ %h(p->zName)
558 }else{
559 char *zLink;
560 if( zCI ){
561 zLink = href("%R/artifact/%s",p->zUuid);
562 }else{
563 zLink = href("%R/finfo?name=%T",p->zFullName);
564 }
565 @ %z(zLink)%h(p->zName)</a>
566 }
567 }
568 @ </pre>
569 style_footer();
570
571 /* We could free memory used by sTree here if we needed to. But
572 ** the process is about to exit, so doing so would not really accomplish
573 ** anything useful. */
574 }
575
576 /*
577 ** Return a CSS class name based on the given filename's extension.
578 ** Result must be freed by the caller.
579 **/
580

Keyboard Shortcuts

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