Fossil SCM

Add the mtime query parameter to the /tree page.

drh 2014-12-16 23:27 UTC age-in-file-tree
Commit fd4e1b55d702de4d078473c58139baa0455b1534
1 file changed +139 -25
+139 -25
--- src/browse.c
+++ src/browse.c
@@ -304,87 +304,101 @@
304304
305305
/*
306306
** A single line of the file hierarchy
307307
*/
308308
struct FileTreeNode {
309
- FileTreeNode *pNext; /* Next line in sequence */
310
- FileTreeNode *pPrev; /* Previous line */
311
- FileTreeNode *pParent; /* Directory containing this line */
309
+ FileTreeNode *pNext; /* Next entry in an ordered list of them all */
310
+ FileTreeNode *pParent; /* Directory containing this entry */
311
+ FileTreeNode *pSibling; /* Next element in the same subdirectory */
312
+ FileTreeNode *pChild; /* List of child nodes */
313
+ FileTreeNode *pLastChild; /* Last child on the pChild list */
312314
char *zName; /* Name of this entry. The "tail" */
313315
char *zFullName; /* Full pathname of this entry */
314316
char *zUuid; /* SHA1 hash of this file. May be NULL. */
315317
double mtime; /* Modification time for this entry */
316318
unsigned nFullName; /* Length of zFullName */
317319
unsigned iLevel; /* Levels of parent directories */
318
- u8 isDir; /* True if there are children */
319
- u8 isLast; /* True if this is the last child of its parent */
320320
};
321321
322322
/*
323323
** A complete file hierarchy
324324
*/
325325
struct FileTree {
326326
FileTreeNode *pFirst; /* First line of the list */
327327
FileTreeNode *pLast; /* Last line of the list */
328
+ FileTreeNode *pLastTop; /* Last top-level node */
328329
};
329330
330331
/*
331332
** Add one or more new FileTreeNodes to the FileTree object so that the
332
-** leaf object zPathname is at the end of the node list
333
+** leaf object zPathname is at the end of the node list.
334
+**
335
+** The caller invokes this routine once for each leaf node (each file
336
+** as opposed to each directory). This routine fills in any missing
337
+** intermediate nodes automatically.
338
+**
339
+** When constructing a list of FileTreeNodes, all entries that have
340
+** a common directory prefix must be added consecutively in order for
341
+** the tree to be constructed properly.
333342
*/
334343
static void tree_add_node(
335344
FileTree *pTree, /* Tree into which nodes are added */
336345
const char *zPath, /* The full pathname of file to add */
337346
const char *zUuid, /* UUID of the file. Might be NULL. */
338347
double mtime /* Modification time for this entry */
339348
){
340349
int i;
341
- FileTreeNode *pParent;
342
- FileTreeNode *pChild;
350
+ FileTreeNode *pParent; /* Parent (directory) of the next node to insert */
343351
344
- pChild = pTree->pLast;
345
- pParent = pChild ? pChild->pParent : 0;
352
+ /* Make pParent point to the most recent ancestor of zPath, or
353
+ ** NULL if there are no prior entires that are a container for zPath.
354
+ */
355
+ pParent = pTree->pLast;
346356
while( pParent!=0 &&
347357
( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
348358
|| zPath[pParent->nFullName]!='/' )
349359
){
350
- pChild = pParent;
351
- pParent = pChild->pParent;
360
+ pParent = pParent->pParent;
352361
}
353362
i = pParent ? pParent->nFullName+1 : 0;
354
- if( pChild ) pChild->isLast = 0;
355363
while( zPath[i] ){
356364
FileTreeNode *pNew;
357365
int iStart = i;
358366
int nByte;
359367
while( zPath[i] && zPath[i]!='/' ){ i++; }
360368
nByte = sizeof(*pNew) + i + 1;
361369
if( zUuid!=0 && zPath[i]==0 ) nByte += UUID_SIZE+1;
362370
pNew = fossil_malloc( nByte );
371
+ memset(pNew, 0, sizeof(*pNew));
363372
pNew->zFullName = (char*)&pNew[1];
364373
memcpy(pNew->zFullName, zPath, i);
365374
pNew->zFullName[i] = 0;
366375
pNew->nFullName = i;
367376
if( zUuid!=0 && zPath[i]==0 ){
368377
pNew->zUuid = pNew->zFullName + i + 1;
369378
memcpy(pNew->zUuid, zUuid, UUID_SIZE+1);
370
- }else{
371
- pNew->zUuid = 0;
372379
}
373380
pNew->zName = pNew->zFullName + iStart;
374381
if( pTree->pLast ){
375382
pTree->pLast->pNext = pNew;
376383
}else{
377384
pTree->pFirst = pNew;
378385
}
379
- pNew->pPrev = pTree->pLast;
380
- pNew->pNext = 0;
386
+ pTree->pLast = pNew;
381387
pNew->pParent = pParent;
382
- pTree->pLast = pNew;
383
- pNew->iLevel = pParent ? pParent->iLevel+1 : 0;
384
- pNew->isDir = zPath[i]=='/';
385
- pNew->isLast = 1;
388
+ if( pParent ){
389
+ if( pParent->pChild ){
390
+ pParent->pLastChild->pSibling = pNew;
391
+ }else{
392
+ pParent->pChild = pNew;
393
+ }
394
+ pNew->iLevel = pParent->iLevel + 1;
395
+ pParent->pLastChild = pNew;
396
+ }else{
397
+ if( pTree->pLastTop ) pTree->pLastTop->pSibling = pNew;
398
+ pTree->pLastTop = pNew;
399
+ }
386400
pNew->mtime = mtime;
387401
while( zPath[i]=='/' ){ i++; }
388402
pParent = pNew;
389403
}
390404
while( pParent && pParent->pParent ){
@@ -392,10 +406,104 @@
392406
pParent->pParent->mtime = pParent->mtime;
393407
}
394408
pParent = pParent->pParent;
395409
}
396410
}
411
+
412
+/* Comparison function for two FileTreeNode objects. Sort first by
413
+** mtime (larger numbers first) and then by zName (smaller names first).
414
+**
415
+** Return negative if pLeft<pRight.
416
+** Return positive if pLeft>pRight.
417
+** Return zero if pLeft==pRight.
418
+*/
419
+static int compareNodes(FileTreeNode *pLeft, FileTreeNode *pRight){
420
+ if( pLeft->mtime>pRight->mtime ) return -1;
421
+ if( pLeft->mtime<pRight->mtime ) return +1;
422
+ return fossil_stricmp(pLeft->zName, pRight->zName);
423
+}
424
+
425
+/* Merge together two sorted lists of FileTreeNode objects */
426
+static FileTreeNode *mergeNodes(FileTreeNode *pLeft, FileTreeNode *pRight){
427
+ FileTreeNode *pEnd;
428
+ FileTreeNode base;
429
+ pEnd = &base;
430
+ while( pLeft && pRight ){
431
+ if( compareNodes(pLeft,pRight)<=0 ){
432
+ pEnd = pEnd->pSibling = pLeft;
433
+ pLeft = pLeft->pSibling;
434
+ }else{
435
+ pEnd = pEnd->pSibling = pRight;
436
+ pRight = pRight->pSibling;
437
+ }
438
+ }
439
+ if( pLeft ){
440
+ pEnd->pSibling = pLeft;
441
+ }else{
442
+ pEnd->pSibling = pRight;
443
+ }
444
+ return base.pSibling;
445
+}
446
+
447
+/* Sort a list of FileTreeNode objects in mtime order. */
448
+static FileTreeNode *sortNodesByMtime(FileTreeNode *p){
449
+ FileTreeNode *a[30];
450
+ FileTreeNode *pX;
451
+ int i;
452
+
453
+ memset(a, 0, sizeof(a));
454
+ while( p ){
455
+ pX = p;
456
+ p = pX->pSibling;
457
+ pX->pSibling = 0;
458
+ for(i=0; i<count(a)-1 && a[i]!=0; i++){
459
+ pX = mergeNodes(a[i], pX);
460
+ a[i] = 0;
461
+ }
462
+ a[i] = mergeNodes(a[i], pX);
463
+ }
464
+ pX = 0;
465
+ for(i=0; i<count(a); i++){
466
+ pX = mergeNodes(a[i], pX);
467
+ }
468
+ return pX;
469
+}
470
+
471
+/* Sort an entire FileTreeNode tree by mtime
472
+**
473
+** This routine invalidates the following fields:
474
+**
475
+** FileTreeNode.pLastChild
476
+** FileTreeNode.pNext
477
+**
478
+** Use relinkTree to reconnect the pNext pointers.
479
+*/
480
+static FileTreeNode *sortTreeByMtime(FileTreeNode *p){
481
+ FileTreeNode *pX;
482
+ for(pX=p; pX; pX=pX->pSibling){
483
+ if( pX->pChild ) pX->pChild = sortTreeByMtime(pX->pChild);
484
+ }
485
+ return sortNodesByMtime(p);
486
+}
487
+
488
+/* Reconstruct the FileTree by reconnecting the FileTreeNode.pNext
489
+** fields in sequential order.
490
+*/
491
+static void relinkTree(FileTree *pTree, FileTreeNode *pRoot){
492
+ while( pRoot ){
493
+ if( pTree->pLast ){
494
+ pTree->pLast->pNext = pRoot;
495
+ }else{
496
+ pTree->pFirst = pRoot;
497
+ }
498
+ pTree->pLast = pRoot;
499
+ if( pRoot->pChild ) relinkTree(pTree, pRoot->pChild);
500
+ pRoot = pRoot->pSibling;
501
+ }
502
+ if( pTree->pLast ) pTree->pLast->pNext = 0;
503
+}
504
+
397505
398506
/*
399507
** WEBPAGE: tree
400508
**
401509
** Query parameters:
@@ -403,10 +511,11 @@
403511
** name=PATH Directory to display. Optional
404512
** ci=LABEL Show only files in this check-in. Optional.
405513
** re=REGEXP Show only files matching REGEXP. Optional.
406514
** expand Begin with the tree fully expanded.
407515
** nofiles Show directories (folders) only. Omit files.
516
+** mtime Order directory elements by decreasing mtime
408517
*/
409518
void page_tree(void){
410519
char *zD = fossil_strdup(P("name"));
411520
int nD = zD ? strlen(zD)+1 : 0;
412521
const char *zCI = P("ci");
@@ -556,11 +665,11 @@
556665
db_finalize(&q);
557666
}
558667
559668
if( showDirOnly ){
560669
for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
561
- if( p->isDir && p->nFullName>nD ) nFile++;
670
+ if( p->pChild!=0 && p->nFullName>nD ) nFile++;
562671
}
563672
zObjType = "folders";
564673
style_submenu_element("Files","Files","%s",
565674
url_render(&sURI,"nofiles",0,0,0));
566675
}else{
@@ -604,13 +713,18 @@
604713
if( zNow ){
605714
@ <div class="filetreeage">%s(zNow)</div>
606715
}
607716
@ </div>
608717
@ <ul>
718
+ if( zCI && P("mtime")!=0 ){
719
+ p = sortTreeByMtime(sTree.pFirst);
720
+ memset(&sTree, 0, sizeof(sTree));
721
+ relinkTree(&sTree, p);
722
+ }
609723
for(p=sTree.pFirst, nDir=0; p; p=p->pNext){
610
- const char *zLastClass = p->isLast ? " last" : "";
611
- if( p->isDir ){
724
+ const char *zLastClass = p->pSibling==0 ? " last" : "";
725
+ if( p->pChild ){
612726
const char *zSubdirClass = p->nFullName==nD-1 ? " subdir" : "";
613727
@ <li class="dir%s(zSubdirClass)%s(zLastClass)"><div class="filetreeline">
614728
@ %z(href("%s",url_render(&sURI,"name",p->zFullName,0,0)))%h(p->zName)</a>
615729
if( p->mtime>0.0 ){
616730
char *zAge = human_readable_age(rNow - p->mtime);
@@ -637,11 +751,11 @@
637751
char *zAge = human_readable_age(rNow - p->mtime);
638752
@ <div class="filetreeage">%s(zAge)</div>
639753
}
640754
@ </div>
641755
}
642
- if( p->isLast ){
756
+ if( p->pSibling==0 ){
643757
int nClose = p->iLevel - (p->pNext ? p->pNext->iLevel : 0);
644758
while( nClose-- > 0 ){
645759
@ </ul>
646760
}
647761
}
648762
--- src/browse.c
+++ src/browse.c
@@ -304,87 +304,101 @@
304
305 /*
306 ** A single line of the file hierarchy
307 */
308 struct FileTreeNode {
309 FileTreeNode *pNext; /* Next line in sequence */
310 FileTreeNode *pPrev; /* Previous line */
311 FileTreeNode *pParent; /* Directory containing this line */
 
 
312 char *zName; /* Name of this entry. The "tail" */
313 char *zFullName; /* Full pathname of this entry */
314 char *zUuid; /* SHA1 hash of this file. May be NULL. */
315 double mtime; /* Modification time for this entry */
316 unsigned nFullName; /* Length of zFullName */
317 unsigned iLevel; /* Levels of parent directories */
318 u8 isDir; /* True if there are children */
319 u8 isLast; /* True if this is the last child of its parent */
320 };
321
322 /*
323 ** A complete file hierarchy
324 */
325 struct FileTree {
326 FileTreeNode *pFirst; /* First line of the list */
327 FileTreeNode *pLast; /* Last line of the list */
 
328 };
329
330 /*
331 ** Add one or more new FileTreeNodes to the FileTree object so that the
332 ** leaf object zPathname is at the end of the node list
 
 
 
 
 
 
 
 
333 */
334 static void tree_add_node(
335 FileTree *pTree, /* Tree into which nodes are added */
336 const char *zPath, /* The full pathname of file to add */
337 const char *zUuid, /* UUID of the file. Might be NULL. */
338 double mtime /* Modification time for this entry */
339 ){
340 int i;
341 FileTreeNode *pParent;
342 FileTreeNode *pChild;
343
344 pChild = pTree->pLast;
345 pParent = pChild ? pChild->pParent : 0;
 
 
346 while( pParent!=0 &&
347 ( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
348 || zPath[pParent->nFullName]!='/' )
349 ){
350 pChild = pParent;
351 pParent = pChild->pParent;
352 }
353 i = pParent ? pParent->nFullName+1 : 0;
354 if( pChild ) pChild->isLast = 0;
355 while( zPath[i] ){
356 FileTreeNode *pNew;
357 int iStart = i;
358 int nByte;
359 while( zPath[i] && zPath[i]!='/' ){ i++; }
360 nByte = sizeof(*pNew) + i + 1;
361 if( zUuid!=0 && zPath[i]==0 ) nByte += UUID_SIZE+1;
362 pNew = fossil_malloc( nByte );
 
363 pNew->zFullName = (char*)&pNew[1];
364 memcpy(pNew->zFullName, zPath, i);
365 pNew->zFullName[i] = 0;
366 pNew->nFullName = i;
367 if( zUuid!=0 && zPath[i]==0 ){
368 pNew->zUuid = pNew->zFullName + i + 1;
369 memcpy(pNew->zUuid, zUuid, UUID_SIZE+1);
370 }else{
371 pNew->zUuid = 0;
372 }
373 pNew->zName = pNew->zFullName + iStart;
374 if( pTree->pLast ){
375 pTree->pLast->pNext = pNew;
376 }else{
377 pTree->pFirst = pNew;
378 }
379 pNew->pPrev = pTree->pLast;
380 pNew->pNext = 0;
381 pNew->pParent = pParent;
382 pTree->pLast = pNew;
383 pNew->iLevel = pParent ? pParent->iLevel+1 : 0;
384 pNew->isDir = zPath[i]=='/';
385 pNew->isLast = 1;
 
 
 
 
 
 
 
 
386 pNew->mtime = mtime;
387 while( zPath[i]=='/' ){ i++; }
388 pParent = pNew;
389 }
390 while( pParent && pParent->pParent ){
@@ -392,10 +406,104 @@
392 pParent->pParent->mtime = pParent->mtime;
393 }
394 pParent = pParent->pParent;
395 }
396 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
398 /*
399 ** WEBPAGE: tree
400 **
401 ** Query parameters:
@@ -403,10 +511,11 @@
403 ** name=PATH Directory to display. Optional
404 ** ci=LABEL Show only files in this check-in. Optional.
405 ** re=REGEXP Show only files matching REGEXP. Optional.
406 ** expand Begin with the tree fully expanded.
407 ** nofiles Show directories (folders) only. Omit files.
 
408 */
409 void page_tree(void){
410 char *zD = fossil_strdup(P("name"));
411 int nD = zD ? strlen(zD)+1 : 0;
412 const char *zCI = P("ci");
@@ -556,11 +665,11 @@
556 db_finalize(&q);
557 }
558
559 if( showDirOnly ){
560 for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
561 if( p->isDir && p->nFullName>nD ) nFile++;
562 }
563 zObjType = "folders";
564 style_submenu_element("Files","Files","%s",
565 url_render(&sURI,"nofiles",0,0,0));
566 }else{
@@ -604,13 +713,18 @@
604 if( zNow ){
605 @ <div class="filetreeage">%s(zNow)</div>
606 }
607 @ </div>
608 @ <ul>
 
 
 
 
 
609 for(p=sTree.pFirst, nDir=0; p; p=p->pNext){
610 const char *zLastClass = p->isLast ? " last" : "";
611 if( p->isDir ){
612 const char *zSubdirClass = p->nFullName==nD-1 ? " subdir" : "";
613 @ <li class="dir%s(zSubdirClass)%s(zLastClass)"><div class="filetreeline">
614 @ %z(href("%s",url_render(&sURI,"name",p->zFullName,0,0)))%h(p->zName)</a>
615 if( p->mtime>0.0 ){
616 char *zAge = human_readable_age(rNow - p->mtime);
@@ -637,11 +751,11 @@
637 char *zAge = human_readable_age(rNow - p->mtime);
638 @ <div class="filetreeage">%s(zAge)</div>
639 }
640 @ </div>
641 }
642 if( p->isLast ){
643 int nClose = p->iLevel - (p->pNext ? p->pNext->iLevel : 0);
644 while( nClose-- > 0 ){
645 @ </ul>
646 }
647 }
648
--- src/browse.c
+++ src/browse.c
@@ -304,87 +304,101 @@
304
305 /*
306 ** A single line of the file hierarchy
307 */
308 struct FileTreeNode {
309 FileTreeNode *pNext; /* Next entry in an ordered list of them all */
310 FileTreeNode *pParent; /* Directory containing this entry */
311 FileTreeNode *pSibling; /* Next element in the same subdirectory */
312 FileTreeNode *pChild; /* List of child nodes */
313 FileTreeNode *pLastChild; /* Last child on the pChild list */
314 char *zName; /* Name of this entry. The "tail" */
315 char *zFullName; /* Full pathname of this entry */
316 char *zUuid; /* SHA1 hash of this file. May be NULL. */
317 double mtime; /* Modification time for this entry */
318 unsigned nFullName; /* Length of zFullName */
319 unsigned iLevel; /* Levels of parent directories */
 
 
320 };
321
322 /*
323 ** A complete file hierarchy
324 */
325 struct FileTree {
326 FileTreeNode *pFirst; /* First line of the list */
327 FileTreeNode *pLast; /* Last line of the list */
328 FileTreeNode *pLastTop; /* Last top-level node */
329 };
330
331 /*
332 ** Add one or more new FileTreeNodes to the FileTree object so that the
333 ** leaf object zPathname is at the end of the node list.
334 **
335 ** The caller invokes this routine once for each leaf node (each file
336 ** as opposed to each directory). This routine fills in any missing
337 ** intermediate nodes automatically.
338 **
339 ** When constructing a list of FileTreeNodes, all entries that have
340 ** a common directory prefix must be added consecutively in order for
341 ** the tree to be constructed properly.
342 */
343 static void tree_add_node(
344 FileTree *pTree, /* Tree into which nodes are added */
345 const char *zPath, /* The full pathname of file to add */
346 const char *zUuid, /* UUID of the file. Might be NULL. */
347 double mtime /* Modification time for this entry */
348 ){
349 int i;
350 FileTreeNode *pParent; /* Parent (directory) of the next node to insert */
 
351
352 /* Make pParent point to the most recent ancestor of zPath, or
353 ** NULL if there are no prior entires that are a container for zPath.
354 */
355 pParent = pTree->pLast;
356 while( pParent!=0 &&
357 ( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
358 || zPath[pParent->nFullName]!='/' )
359 ){
360 pParent = pParent->pParent;
 
361 }
362 i = pParent ? pParent->nFullName+1 : 0;
 
363 while( zPath[i] ){
364 FileTreeNode *pNew;
365 int iStart = i;
366 int nByte;
367 while( zPath[i] && zPath[i]!='/' ){ i++; }
368 nByte = sizeof(*pNew) + i + 1;
369 if( zUuid!=0 && zPath[i]==0 ) nByte += UUID_SIZE+1;
370 pNew = fossil_malloc( nByte );
371 memset(pNew, 0, sizeof(*pNew));
372 pNew->zFullName = (char*)&pNew[1];
373 memcpy(pNew->zFullName, zPath, i);
374 pNew->zFullName[i] = 0;
375 pNew->nFullName = i;
376 if( zUuid!=0 && zPath[i]==0 ){
377 pNew->zUuid = pNew->zFullName + i + 1;
378 memcpy(pNew->zUuid, zUuid, UUID_SIZE+1);
 
 
379 }
380 pNew->zName = pNew->zFullName + iStart;
381 if( pTree->pLast ){
382 pTree->pLast->pNext = pNew;
383 }else{
384 pTree->pFirst = pNew;
385 }
386 pTree->pLast = pNew;
 
387 pNew->pParent = pParent;
388 if( pParent ){
389 if( pParent->pChild ){
390 pParent->pLastChild->pSibling = pNew;
391 }else{
392 pParent->pChild = pNew;
393 }
394 pNew->iLevel = pParent->iLevel + 1;
395 pParent->pLastChild = pNew;
396 }else{
397 if( pTree->pLastTop ) pTree->pLastTop->pSibling = pNew;
398 pTree->pLastTop = pNew;
399 }
400 pNew->mtime = mtime;
401 while( zPath[i]=='/' ){ i++; }
402 pParent = pNew;
403 }
404 while( pParent && pParent->pParent ){
@@ -392,10 +406,104 @@
406 pParent->pParent->mtime = pParent->mtime;
407 }
408 pParent = pParent->pParent;
409 }
410 }
411
412 /* Comparison function for two FileTreeNode objects. Sort first by
413 ** mtime (larger numbers first) and then by zName (smaller names first).
414 **
415 ** Return negative if pLeft<pRight.
416 ** Return positive if pLeft>pRight.
417 ** Return zero if pLeft==pRight.
418 */
419 static int compareNodes(FileTreeNode *pLeft, FileTreeNode *pRight){
420 if( pLeft->mtime>pRight->mtime ) return -1;
421 if( pLeft->mtime<pRight->mtime ) return +1;
422 return fossil_stricmp(pLeft->zName, pRight->zName);
423 }
424
425 /* Merge together two sorted lists of FileTreeNode objects */
426 static FileTreeNode *mergeNodes(FileTreeNode *pLeft, FileTreeNode *pRight){
427 FileTreeNode *pEnd;
428 FileTreeNode base;
429 pEnd = &base;
430 while( pLeft && pRight ){
431 if( compareNodes(pLeft,pRight)<=0 ){
432 pEnd = pEnd->pSibling = pLeft;
433 pLeft = pLeft->pSibling;
434 }else{
435 pEnd = pEnd->pSibling = pRight;
436 pRight = pRight->pSibling;
437 }
438 }
439 if( pLeft ){
440 pEnd->pSibling = pLeft;
441 }else{
442 pEnd->pSibling = pRight;
443 }
444 return base.pSibling;
445 }
446
447 /* Sort a list of FileTreeNode objects in mtime order. */
448 static FileTreeNode *sortNodesByMtime(FileTreeNode *p){
449 FileTreeNode *a[30];
450 FileTreeNode *pX;
451 int i;
452
453 memset(a, 0, sizeof(a));
454 while( p ){
455 pX = p;
456 p = pX->pSibling;
457 pX->pSibling = 0;
458 for(i=0; i<count(a)-1 && a[i]!=0; i++){
459 pX = mergeNodes(a[i], pX);
460 a[i] = 0;
461 }
462 a[i] = mergeNodes(a[i], pX);
463 }
464 pX = 0;
465 for(i=0; i<count(a); i++){
466 pX = mergeNodes(a[i], pX);
467 }
468 return pX;
469 }
470
471 /* Sort an entire FileTreeNode tree by mtime
472 **
473 ** This routine invalidates the following fields:
474 **
475 ** FileTreeNode.pLastChild
476 ** FileTreeNode.pNext
477 **
478 ** Use relinkTree to reconnect the pNext pointers.
479 */
480 static FileTreeNode *sortTreeByMtime(FileTreeNode *p){
481 FileTreeNode *pX;
482 for(pX=p; pX; pX=pX->pSibling){
483 if( pX->pChild ) pX->pChild = sortTreeByMtime(pX->pChild);
484 }
485 return sortNodesByMtime(p);
486 }
487
488 /* Reconstruct the FileTree by reconnecting the FileTreeNode.pNext
489 ** fields in sequential order.
490 */
491 static void relinkTree(FileTree *pTree, FileTreeNode *pRoot){
492 while( pRoot ){
493 if( pTree->pLast ){
494 pTree->pLast->pNext = pRoot;
495 }else{
496 pTree->pFirst = pRoot;
497 }
498 pTree->pLast = pRoot;
499 if( pRoot->pChild ) relinkTree(pTree, pRoot->pChild);
500 pRoot = pRoot->pSibling;
501 }
502 if( pTree->pLast ) pTree->pLast->pNext = 0;
503 }
504
505
506 /*
507 ** WEBPAGE: tree
508 **
509 ** Query parameters:
@@ -403,10 +511,11 @@
511 ** name=PATH Directory to display. Optional
512 ** ci=LABEL Show only files in this check-in. Optional.
513 ** re=REGEXP Show only files matching REGEXP. Optional.
514 ** expand Begin with the tree fully expanded.
515 ** nofiles Show directories (folders) only. Omit files.
516 ** mtime Order directory elements by decreasing mtime
517 */
518 void page_tree(void){
519 char *zD = fossil_strdup(P("name"));
520 int nD = zD ? strlen(zD)+1 : 0;
521 const char *zCI = P("ci");
@@ -556,11 +665,11 @@
665 db_finalize(&q);
666 }
667
668 if( showDirOnly ){
669 for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
670 if( p->pChild!=0 && p->nFullName>nD ) nFile++;
671 }
672 zObjType = "folders";
673 style_submenu_element("Files","Files","%s",
674 url_render(&sURI,"nofiles",0,0,0));
675 }else{
@@ -604,13 +713,18 @@
713 if( zNow ){
714 @ <div class="filetreeage">%s(zNow)</div>
715 }
716 @ </div>
717 @ <ul>
718 if( zCI && P("mtime")!=0 ){
719 p = sortTreeByMtime(sTree.pFirst);
720 memset(&sTree, 0, sizeof(sTree));
721 relinkTree(&sTree, p);
722 }
723 for(p=sTree.pFirst, nDir=0; p; p=p->pNext){
724 const char *zLastClass = p->pSibling==0 ? " last" : "";
725 if( p->pChild ){
726 const char *zSubdirClass = p->nFullName==nD-1 ? " subdir" : "";
727 @ <li class="dir%s(zSubdirClass)%s(zLastClass)"><div class="filetreeline">
728 @ %z(href("%s",url_render(&sURI,"name",p->zFullName,0,0)))%h(p->zName)</a>
729 if( p->mtime>0.0 ){
730 char *zAge = human_readable_age(rNow - p->mtime);
@@ -637,11 +751,11 @@
751 char *zAge = human_readable_age(rNow - p->mtime);
752 @ <div class="filetreeage">%s(zAge)</div>
753 }
754 @ </div>
755 }
756 if( p->pSibling==0 ){
757 int nClose = p->iLevel - (p->pNext ? p->pNext->iLevel : 0);
758 while( nClose-- > 0 ){
759 @ </ul>
760 }
761 }
762

Keyboard Shortcuts

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