ADDED src/backlink.c Index: src/backlink.c ================================================================== --- /dev/null +++ src/backlink.c @@ -0,0 +1,387 @@ +/* +** Copyright (c) 2020 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the Simplified BSD License (also +** known as the "2-Clause License" or "FreeBSD License".) + +** This program is distributed in the hope that it will be useful, +** but without any warranty; without even the implied warranty of +** merchantability or fitness for a particular purpose. +** +** Author contact information: +** drh@sqlite.org +** http://www.hwaci.com/drh/ +** +******************************************************************************* +** +** This file contains code to implement for managing backlinks and +** the "backlink" table of the repository database. +** +** A backlink is a reference in Fossil-Wiki or Markdown to some other +** object in the repository. +*/ +#include "config.h" +#include "backlink.h" +#include + + +/* +** Show a graph all wiki, tickets, and check-ins that refer to object zUuid. +** +** If zLabel is not NULL and the graph is not empty, then output zLabel as +** a prefix to the graph. +*/ +void render_backlink_graph(const char *zUuid, const char *zLabel){ + Blob sql; + Stmt q; + char *zGlob; + zGlob = mprintf("%.5s*", zUuid); + db_multi_exec( + "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);\n" + "DELETE FROM ok;\n" + "INSERT OR IGNORE INTO ok(rid)\n" + " SELECT CASE srctype\n" + " WHEN 2 THEN (SELECT rid FROM tagxref WHERE tagid=backlink.srcid\n" + " ORDER BY mtime DESC LIMIT 1)\n" + " ELSE srcid END\n" + " FROM backlink\n" + " WHERE target GLOB %Q" + " AND %Q GLOB (target || '*');", + zGlob, zUuid + ); + if( !db_exists("SELECT 1 FROM ok") ) return; + if( zLabel ) cgi_printf("%s", zLabel); + blob_zero(&sql); + blob_append(&sql, timeline_query_for_www(), -1); + blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC"); + db_prepare(&q, "%s", blob_sql_text(&sql)); + www_print_timeline(&q, + TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL|TIMELINE_REFS, + 0, 0, 0, 0, 0, 0); + db_finalize(&q); +} + +/* +** WEBPAGE: test-backlink-timeline +** +** Show a timeline of all check-ins and other events that have entries +** in the backlink table. This is used for testing the rendering +** of the "References" section of the /info page. +*/ +void backlink_timeline_page(void){ + Blob sql; + Stmt q; + + login_check_credentials(); + if( !g.perm.Read || !g.perm.RdTkt || !g.perm.RdWiki ){ + login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); + return; + } + style_header("Backlink Timeline (Internal Testing Use)"); + db_multi_exec( + "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" + "DELETE FROM ok;" + "INSERT OR IGNORE INTO ok" + " SELECT blob.rid FROM backlink, blob" + " WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')" + ); + blob_zero(&sql); + blob_append(&sql, timeline_query_for_www(), -1); + blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC"); + db_prepare(&q, "%s", blob_sql_text(&sql)); + www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, + 0, 0, 0, 0, 0, 0); + db_finalize(&q); + style_footer(); +} + +/* +** WEBPAGE: test-backlinks +** +** Show a table of all backlinks. Admin access only. +*/ +void backlink_table_page(void){ + Stmt q; + int n; + login_check_credentials(); + if( !g.perm.Admin ){ + login_needed(g.anon.Admin); + return; + } + style_header("Backlink Table (Internal Testing Use)"); + n = db_int(0, "SELECT count(*) FROM backlink"); + @

%d(n) backlink table entries:

+ db_prepare(&q, + "SELECT target, srctype, srcid, datetime(mtime)," + " CASE srctype" + " WHEN 2 THEN (SELECT substr(tagname,6) FROM tag" + " WHERE tagid=srcid AND tagname GLOB 'wiki-*')" + " ELSE null END FROM backlink" + ); + style_table_sorter(); + @ + @ + @ + while( db_step(&q)==SQLITE_ROW ){ + const char *zTarget = db_column_text(&q, 0); + int srctype = db_column_int(&q, 1); + int srcid = db_column_int(&q, 2); + const char *zMtime = db_column_text(&q, 3); + @ + } + @ + @
Source Target mtime
%h(zTarget) + switch( srctype ){ + case BKLNK_COMMENT: { + @ comment-%d(srcid) + break; + } + case BKLNK_TICKET: { + @ ticket-%d(srcid) + break; + } + case BKLNK_WIKI: { + const char *zName = db_column_text(&q, 4); + @ wiki-%d(srcid) + break; + } + default: { + @ unknown(%d(srctype)) - %d(srcid) + break; + } + } + @ %h(zMtime)
+ db_finalize(&q); + style_footer(); +} + +/* +** Remove all prior backlinks for the wiki page given. Then +** add new backlinks for the latest version of the wiki page. +*/ +void backlink_wiki_refresh(const char *zWikiTitle){ + int tagid = wiki_tagid(zWikiTitle); + int rid; + Manifest *pWiki; + if( tagid==0 ) return; + rid = db_int(0, "SELECT rid FROM tagxref WHERE tagid=%d" + " ORDER BY mtime DESC LIMIT 1", tagid); + if( rid==0 ) return; + pWiki = manifest_get(rid, CFTYPE_WIKI, 0); + if( pWiki ){ + backlink_extract(pWiki->zWiki, pWiki->zMimetype, tagid, 2, pWiki->rDate,1); + manifest_destroy(pWiki); + } +} + +/* +** Structure used to pass down state information through the +** markup formatters into the BACKLINK generator. +*/ +#if INTERFACE +struct Backlink { + int srcid; /* srcid for the source document */ + int srctype; /* One of BKLNK_*. 0=comment 1=ticket 2=wiki */ + double mtime; /* mtime field for new BACKLINK table entries */ +}; +#endif + + +/* +** zTarget is a hyperlink target in some markup format. If this +** target is a self-reference to some other object in the repository, +** then create an appropriate backlink. +*/ +void backlink_create(Backlink *p, const char *zTarget, int nTarget){ + char zLink[HNAME_MAX+4]; + if( zTarget==0 ) return; + if( nTarget<4 ) return; + if( nTarget>=10 && strncmp(zTarget,"/info/",6)==0 ){ + zTarget += 6; + nTarget -= 6; + } + if( nTarget>HNAME_MAX ) return; + if( !validate16(zTarget, nTarget) ) return; + memcpy(zLink, zTarget, nTarget); + zLink[nTarget] = 0; + canonical16(zLink, nTarget); + db_multi_exec( + "REPLACE INTO backlink(target,srctype,srcid,mtime)" + "VALUES(%Q,%d,%d,%.17g)", zLink, p->srctype, p->srcid, p->mtime + ); +} + +/* +** This routine is called by the markdown formatter for each hyperlink. +** If the hyperlink is a backlink, add it to the BACKLINK table. +*/ +static int backlink_md_link( + Blob *ob, /* Write output text here (not used in this case) */ + Blob *target, /* The hyperlink target */ + Blob *title, /* Hyperlink title */ + Blob *content, /* Content of the link */ + void *opaque +){ + Backlink *p = (Backlink*)opaque; + char *zTarget = blob_buffer(target); + int nTarget = blob_size(target); + + backlink_create(p, zTarget, nTarget); + return 1; +} + +/* No-op routine for the rendering callbacks that we do not need */ +static void mkdn_noop0(Blob *x){ return; } +static int mkdn_noop1(Blob *x){ return 1; } + +/* +** Scan markdown text and add self-hyperlinks to the BACKLINK table. +*/ +void markdown_extract_links( + char *zInputText, + Backlink *p +){ + struct mkd_renderer html_renderer = { + /* prolog */ (void(*)(Blob*,void*))mkdn_noop0, + /* epilog */ (void(*)(Blob*,void*))mkdn_noop0, + /* blockcode */ (void(*)(Blob*,Blob*,void*))mkdn_noop0, + /* blockquote */ (void(*)(Blob*,Blob*,void*))mkdn_noop0, + /* blockhtml */ (void(*)(Blob*,Blob*,void*))mkdn_noop0, + /* header */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0, + /* hrule */ (void(*)(Blob*,void*))mkdn_noop0, + /* list */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0, + /* listitem */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0, + /* paragraph */ (void(*)(Blob*,Blob*,void*))mkdn_noop0, + /* table */ (void(*)(Blob*,Blob*,Blob*,void*))mkdn_noop0, + /* table_cell */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0, + /* table_row */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0, + /* autolink */ (int(*)(Blob*,Blob*,enum mkd_autolink,void*))mkdn_noop1, + /* codespan */ (int(*)(Blob*,Blob*,int,void*))mkdn_noop1, + /* dbl_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1, + /* emphasis */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1, + /* image */ (int(*)(Blob*,Blob*,Blob*,Blob*,void*))mkdn_noop1, + /* linebreak */ (int(*)(Blob*,void*))mkdn_noop1, + /* link */ backlink_md_link, + /* r_html_tag */ (int(*)(Blob*,Blob*,void*))mkdn_noop1, + /* tri_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1, + 0, /* entity */ + 0, /* normal_text */ + "*_", /* emphasis characters */ + 0 /* client data */ + }; + Blob out, in; + html_renderer.opaque = (void*)p; + blob_init(&out, 0, 0); + blob_init(&in, zInputText, -1); + markdown(&out, &in, &html_renderer); + blob_reset(&out); + blob_reset(&in); +} + +/* +** Parse text looking for hyperlinks. Insert references into the +** BACKLINK table. +*/ +void backlink_extract( + char *zSrc, /* Input text from which links are extracted */ + const char *zMimetype, /* Mimetype of input. NULL means fossil-wiki */ + int srcid, /* srcid for the source document */ + int srctype, /* One of BKLNK_*. 0=comment 1=ticket 2=wiki */ + double mtime, /* mtime field for new BACKLINK table entries */ + int replaceFlag /* True to overwrite prior BACKLINK entries */ +){ + Backlink bklnk; + if( replaceFlag ){ + db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d", + srctype, srcid); + } + bklnk.srcid = srcid; + assert( ValidBklnk(srctype) ); + bklnk.srctype = srctype; + bklnk.mtime = mtime; + if( zMimetype==0 || strstr(zMimetype,"wiki")!=0 ){ + wiki_extract_links(zSrc, &bklnk, srctype==BKLNK_COMMENT ? WIKI_INLINE : 0); + }else if( strstr(zMimetype,"markdown")!=0 ){ + markdown_extract_links(zSrc, &bklnk); + } +} + +/* +** COMMAND: test-backlinks +** +** Usage: %fossil test-backlinks SRCTYPE SRCID ?OPTIONS? INPUT-FILE +** +** Read the content of INPUT-FILE and pass it into the backlink_extract() +** routine. But instead of adding backlinks to the backlink table, +** just print them on stdout. SRCID and SRCTYPE are integers. +** +** Options: +** --mtime DATETIME Use an alternative date/time. Defaults to the +** current date/time. +** --mimetype TYPE Use an alternative mimetype. +*/ +void test_backlinks_cmd(void){ + const char *zMTime = find_option("mtime",0,1); + const char *zMimetype = find_option("mimetype",0,1); + Blob in; + int srcid; + int srctype; + double mtime; + + verify_all_options(); + if( g.argc!=5 ){ + usage("SRCTYPE SRCID INPUTFILE"); + } + srctype = atoi(g.argv[2]); + if( srctype<0 || srctype>2 ){ + fossil_fatal("SRCTYPE should be a integer 0, 1, or 2"); + } + srcid = atoi(g.argv[3]); + blob_read_from_file(&in, g.argv[4], ExtFILE); + sqlite3_open(":memory:",&g.db); + if( zMTime==0 ) zMTime = "now"; + mtime = db_double(1721059.5,"SELECT julianday(%Q)",zMTime); + g.fSqlPrint = 1; + sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); + db_multi_exec( + "CREATE TEMP TABLE backlink(target,srctype,srcid,mtime);\n" + "CREATE TRIGGER backlink_insert BEFORE INSERT ON backlink BEGIN\n" + " SELECT print(" + " 'target='||quote(new.target)||" + " ' srctype='||quote(new.srctype)||" + " ' srcid='||quote(new.srcid)||" + " ' mtime='||datetime(new.mtime));\n" + " SELECT raise(ignore);\n" + "END;" + ); + backlink_extract(blob_str(&in),zMimetype,srcid,srctype,mtime,0); + blob_reset(&in); +} + + +/* +** COMMAND: test-wiki-relink +** +** Usage: %fossil test-wiki-relink WIKI-PAGE-NAME +** +** Run the backlink_wiki_refresh() procedure on the wiki page +** named. WIKI-PAGE-NAME can be a glob pattern or a prefix +** of the wiki page. +*/ +void test_wiki_relink_cmd(void){ + Stmt q; + db_find_and_open_repository(0, 0); + if( g.argc!=3 ) usage("WIKI-PAGE-NAME"); + db_prepare(&q, + "SELECT substr(tagname,6) FROM tag WHERE tagname GLOB 'wiki-%q*'", + g.argv[2] + ); + while( db_step(&q)==SQLITE_ROW ){ + const char *zPage = db_column_text(&q,0); + fossil_print("Relinking page: %s\n", zPage); + backlink_wiki_refresh(zPage); + } + db_finalize(&q); +} Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -2310,11 +2310,11 @@ ** SQL functions for debugging. ** ** The print() function writes its arguments on stdout, but only ** if the -sqlprint command-line option is turned on. */ -LOCAL void db_sql_print( +void db_sql_print( sqlite3_context *context, int argc, sqlite3_value **argv ){ int i; Index: src/info.c ================================================================== --- src/info.c +++ src/info.c @@ -302,75 +302,10 @@ |TIMELINE_CHPICK, 0, 0, 0, rid, rid2, 0); db_finalize(&q); } -/* -** Show a graph all wiki, tickets, and check-ins that refer to object zUuid. -** -** If zLabel is not NULL and the graph is not empty, then output zLabel as -** a prefix to the graph. -*/ -void render_backlink_graph(const char *zUuid, const char *zLabel){ - Blob sql; - Stmt q; - char *zGlob; - zGlob = mprintf("%.5s*", zUuid); - db_multi_exec( - "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" - "DELETE FROM ok;" - "INSERT OR IGNORE INTO ok" - " SELECT srcid FROM backlink" - " WHERE target GLOB %Q" - " AND %Q GLOB (target || '*');", - zGlob, zUuid - ); - if( !db_exists("SELECT 1 FROM ok") ) return; - if( zLabel ) cgi_printf("%s", zLabel); - blob_zero(&sql); - blob_append(&sql, timeline_query_for_www(), -1); - blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC"); - db_prepare(&q, "%s", blob_sql_text(&sql)); - www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, - 0, 0, 0, 0, 0, 0); - db_finalize(&q); -} - -/* -** WEBPAGE: test-backlinks -** -** Show a timeline of all check-ins and other events that have entries -** in the backlink table. This is used for testing the rendering -** of the "References" section of the /info page. -*/ -void backlink_timeline_page(void){ - Blob sql; - Stmt q; - - login_check_credentials(); - if( !g.perm.Read || !g.perm.RdTkt || !g.perm.RdWiki ){ - login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); - return; - } - style_header("Backlink Timeline (Internal Testing Use)"); - db_multi_exec( - "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" - "DELETE FROM ok;" - "INSERT OR IGNORE INTO ok" - " SELECT blob.rid FROM backlink, blob" - " WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')" - ); - blob_zero(&sql); - blob_append(&sql, timeline_query_for_www(), -1); - blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC"); - db_prepare(&q, "%s", blob_sql_text(&sql)); - www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, - 0, 0, 0, 0, 0, 0); - db_finalize(&q); - style_footer(); -} - /* ** Append the difference between artifacts to the output */ static void append_diff( Index: src/main.mk ================================================================== --- src/main.mk +++ src/main.mk @@ -18,10 +18,11 @@ SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/alerts.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ + $(SRCDIR)/backlink.c \ $(SRCDIR)/backoffice.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/bisect.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ @@ -250,10 +251,11 @@ TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/alerts_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ + $(OBJDIR)/backlink_.c \ $(OBJDIR)/backoffice_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ @@ -391,10 +393,11 @@ OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/alerts.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ + $(OBJDIR)/backlink.o \ $(OBJDIR)/backoffice.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ @@ -727,10 +730,11 @@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ + $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ @@ -900,10 +904,18 @@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers + +$(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(OBJDIR)/translate + $(OBJDIR)/translate $(SRCDIR)/backlink.c >$@ + +$(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c + +$(OBJDIR)/backlink.h: $(OBJDIR)/headers $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/backoffice.c >$@ $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h Index: src/makemake.tcl ================================================================== --- src/makemake.tcl +++ src/makemake.tcl @@ -29,10 +29,11 @@ set src { add alerts allrepo attach + backlink backoffice bag bisect blob branch Index: src/manifest.c ================================================================== --- src/manifest.c +++ src/manifest.c @@ -1828,19 +1828,30 @@ void manifest_crosslink_begin(void){ assert( manifest_crosslink_busy==0 ); manifest_crosslink_busy = 1; db_begin_transaction(); db_multi_exec( - "CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE);" + "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;" "CREATE TEMP TABLE time_fudge(" " mid INTEGER PRIMARY KEY," /* The rid of a manifest */ " m1 REAL," /* The timestamp on mid */ " cid INTEGER," /* A child or mid */ " m2 REAL" /* Timestamp on the child */ ");" ); } + +/* +** Add a new entry to the pending_xlink table. +*/ +static void add_pending_crosslink(char cType, const char *zId){ + assert( manifest_crosslink_busy==1 ); + db_multi_exec( + "INSERT OR IGNORE INTO pending_xlink VALUES('%c%q')", + cType, zId + ); +} #if INTERFACE /* Timestamps might be adjusted slightly to ensure that check-ins appear ** on the timeline in chronological order. This is the maximum amount ** of the adjustment window, in days. @@ -1879,20 +1890,28 @@ int rid = db_column_int(&q,0); const char *zValue = db_column_text(&q,1); manifest_reparent_checkin(rid, zValue); } db_finalize(&q); - db_prepare(&q, "SELECT uuid FROM pending_tkt"); + db_prepare(&q, "SELECT id FROM pending_xlink"); while( db_step(&q)==SQLITE_ROW ){ - const char *zUuid = db_column_text(&q, 0); - ticket_rebuild_entry(zUuid); - if( permitHooks && rc==TH_OK ){ - rc = xfer_run_script(zScript, zUuid, 0); + const char *zId = db_column_text(&q, 0); + char cType; + if( zId==0 || zId[0]==0 ) continue; + cType = zId[0]; + zId++; + if( cType=='t' ){ + ticket_rebuild_entry(zId); + if( permitHooks && rc==TH_OK ){ + rc = xfer_run_script(zScript, zId, 0); + } + }else if( cType=='w' ){ + backlink_wiki_refresh(zId); } } db_finalize(&q); - db_multi_exec("DROP TABLE pending_tkt"); + db_multi_exec("DROP TABLE pending_xlink"); /* If multiple check-ins happen close together in time, adjust their ** times by a few milliseconds to make sure they appear in chronological ** order. */ @@ -2162,11 +2181,11 @@ TAG_USER, rid, TAG_COMMENT, rid, p->rDate ); zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event" " WHERE rowid=last_insert_rowid()"); - wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE); + backlink_extract(zCom, 0, rid, BKLNK_COMMENT, p->rDate, 1); fossil_free(zCom); /* If this is a delta-manifest, record the fact that this repository ** contains delta manifests, to free the "commit" logic to generate ** new delta manifests. @@ -2227,10 +2246,11 @@ if( p->type==CFTYPE_WIKI ){ char *zTag = mprintf("wiki-%s", p->zWikiTitle); int tagid = tag_findid(zTag, 1); int prior; char *zComment; + const char *zPrefix; int nWiki; char zLength[40]; while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; nWiki = strlen(p->zWiki); sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); @@ -2243,16 +2263,40 @@ tagid, p->rDate ); if( prior ){ content_deltify(prior, &rid, 1, 0); } - if( nWiki>0 ){ - zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle); + if( nWiki<=0 ){ + zPrefix = "Deleted"; + }else if( !prior ){ + zPrefix = "Added"; }else{ - zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle); + zPrefix = "Changes to"; + } + switch( wiki_page_type(p->zWikiTitle) ){ + case WIKITYPE_CHECKIN: { + zComment = mprintf("%s wiki for check-in [%S]", zPrefix, + p->zWikiTitle+8); + break; + } + case WIKITYPE_BRANCH: { + zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]", + zPrefix, p->zWikiTitle+7, p->zWikiTitle+7); + break; + } + case WIKITYPE_TAG: { + zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]", + zPrefix, p->zWikiTitle+4, p->zWikiTitle+4); + break; + } + default: { + zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle); + break; + } } search_doc_touch('w',rid,p->zWikiTitle); + add_pending_crosslink('w',p->zWikiTitle); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment," " bgcolor,euser,ecomment)" "VALUES('w',%.17g,%d,%Q,%Q," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," @@ -2349,12 +2393,11 @@ Stmt qatt; assert( manifest_crosslink_busy==1 ); zTag = mprintf("tkt-%s", p->zTicketUuid); tag_insert(zTag, 1, 0, rid, p->rDate, rid); fossil_free(zTag); - db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", - p->zTicketUuid); + add_pending_crosslink('t',p->zTicketUuid); /* Locate and update comment for any attachments */ db_prepare(&qatt, "SELECT attachid, src, target, filename FROM attachment" " WHERE target=%Q", p->zTicketUuid Index: src/schema.c ================================================================== --- src/schema.c +++ src/schema.c @@ -407,11 +407,11 @@ @ -- the following table for that hyperlink. This table is used to @ -- facilitate the display of "back links". @ -- @ CREATE TABLE backlink( @ target TEXT, -- Where the hyperlink points to -@ srctype INT, -- 0: check-in 1: ticket 2: wiki +@ srctype INT, -- 0=comment 1=ticket 2=wiki. See BKLNK_* below. @ srcid INT, -- EVENT.OBJID for the source document @ mtime TIMESTAMP, -- time that the hyperlink was added. Julian day. @ UNIQUE(target, srctype, srcid) @ ); @ CREATE INDEX backlink_src ON backlink(srcid, srctype); @@ -476,10 +476,22 @@ @ PRIMARY KEY(parentid, childid) @ ) WITHOUT ROWID; @ CREATE INDEX cherrypick_cid ON cherrypick(childid); ; +/* +** Allowed values for backlink.srctype +*/ +#if INTERFACE +# define BKLNK_COMMENT 0 /* Check-in comment */ +# define BKLNK_TICKET 1 /* Ticket body or title */ +# define BKLNK_WIKI 2 /* Wiki */ +# define BKLNK_EVENT 3 /* Technote */ +# define BKLNK_FORUM 4 /* Forum post */ +# define ValidBklnk(X) (X>=0 && X<=4) /* True if backlink.srctype is valid */ +#endif + /* ** Predefined tagid values */ #if INTERFACE # define TAG_BGCOLOR 1 /* Set the background color for display */ Index: src/tag.c ================================================================== --- src/tag.c +++ src/tag.c @@ -220,11 +220,11 @@ if( zCol ){ db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d", zCol, zValue, rid); if( tagid==TAG_COMMENT ){ char *zCopy = mprintf("%s", zValue); - wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE); + backlink_extract(zCopy, 0, rid, BKLNK_COMMENT, mtime, 1); free(zCopy); } } if( tagid==TAG_DATE ){ db_multi_exec("UPDATE event " Index: src/timeline.c ================================================================== --- src/timeline.c +++ src/timeline.c @@ -116,10 +116,11 @@ #define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */ #define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */ #define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */ #define TIMELINE_NOTKT 0x2000000 /* Omit extra ticket classes */ #define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */ +#define TIMELINE_REFS 0x8000000 /* Output intended for References tab */ #endif /* ** Hash a string and use the hash to determine a background color. */ @@ -560,12 +561,27 @@ } if( zType[0]!='c' ){ /* Comments for anything other than a check-in are generated by ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ if( zType[0]=='w' ){ + const char *zCom = blob_str(&comment); + char *zWiki; wiki_hyperlink_override(zUuid); - wiki_convert(&comment, 0, WIKI_INLINE); + if( (tmFlags & TIMELINE_REFS)!=0 + && (zWiki = strstr(zCom,"wiki"))!=0 + ){ + /* The TIMELINE_REFS flag causes timeline comments of the + ** form "Changes to wiki..." or "Added wiki" to be changed + ** into just "Wiki..." */ + Blob rcom; + blob_init(&rcom, 0, 0); + blob_appendf(&rcom, "W%s", zWiki+1); + wiki_convert(&rcom, 0, WIKI_INLINE); + blob_reset(&rcom); + }else{ + wiki_convert(&comment, 0, WIKI_INLINE); + } wiki_hyperlink_override(0); }else{ wiki_convert(&comment, 0, WIKI_INLINE); } }else{ Index: src/tkt.c ================================================================== --- src/tkt.c +++ src/tkt.c @@ -194,10 +194,11 @@ static int ticket_insert(const Manifest *p, int rid, int tktid){ Blob sql1, sql2, sql3; Stmt q; int i, j; char *aUsed; + const char *zMimetype = 0; if( tktid==0 ){ db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " "VALUES(%Q, 0)", p->zTicketUuid); tktid = db_last_insert_rowid(); @@ -233,12 +234,22 @@ zUsedByName++; } blob_append_sql(&sql2, ",\"%w\"", zUsedByName); blob_append_sql(&sql3, ",%Q", p->aField[i].zValue); } - if( rid>0 ){ - wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0); + if( strcmp(zBaseName,"mimetype")==0 ){ + zMimetype = p->aField[i].zValue; + } + } + if( rid>0 ){ + for(i=0; inField; i++){ + const char *zName = p->aField[i].zName; + const char *zBaseName = zName[0]=='+' ? zName+1 : zName; + j = fieldId(zBaseName); + if( j<0 ) continue; + backlink_extract(p->aField[i].zValue, zMimetype, rid, BKLNK_TICKET, + p->rDate, i==0); } } blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid); db_prepare(&q, "%s", blob_sql_text(&sql1)); db_bind_double(&q, ":mtime", p->rDate); @@ -845,10 +856,14 @@ } /* ** Draw a timeline for a ticket with tag.tagid given by the tagid ** parameter. +** +** If zType[0]=='c' then only show check-ins associated with the +** ticket. For any other value of zType, show all events associated +** with the ticket. */ void tkt_draw_timeline(int tagid, const char *zType){ Stmt q; char *zFullUuid; char *zSQL; @@ -855,20 +870,26 @@ zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d", tagid); if( zType[0]=='c' ){ zSQL = mprintf( "%s AND event.objid IN " - " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " + " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " + "AND srctype=0 " "AND '%s' GLOB (target||'*')) " "ORDER BY mtime DESC", timeline_query_for_www(), zFullUuid, zFullUuid ); }else{ zSQL = mprintf( "%s AND event.objid IN " " (SELECT rid FROM tagxref WHERE tagid=%d" - " UNION SELECT srcid FROM backlink" + " UNION" + " SELECT CASE srctype WHEN 2 THEN" + " (SELECT rid FROM tagxref WHERE tagid=backlink.srcid" + " ORDER BY mtime DESC LIMIT 1)" + " ELSE srcid END" + " FROM backlink" " WHERE target GLOB '%.4s*'" " AND '%s' GLOB (target||'*')" " UNION SELECT attachid FROM attachment" " WHERE target=%Q) " "ORDER BY mtime DESC", @@ -875,21 +896,26 @@ timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid ); } db_prepare(&q, "%z", zSQL/*safe-for-%s*/); www_print_timeline(&q, - TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT, + TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT | + TIMELINE_REFS, 0, 0, 0, 0, 0, 0); db_finalize(&q); fossil_free(zFullUuid); } /* ** WEBPAGE: tkttimeline -** URL: /tkttimeline?name=TICKETUUID&y=TYPE +** URL: /tkttimeline/TICKETUUID ** ** Show the change history for a single ticket in timeline format. +** +** Query parameters: +** +** y=ci Show only check-ins associated with the ticket */ void tkttimeline_page(void){ char *zTitle; const char *zUuid; int tagid; Index: src/wiki.c ================================================================== --- src/wiki.c +++ src/wiki.c @@ -365,20 +365,22 @@ search_screen(SRCH_WIKI, 0); style_footer(); } /* Return values from wiki_page_type() */ -#define WIKITYPE_UNKNOWN (-1) -#define WIKITYPE_NORMAL 0 -#define WIKITYPE_BRANCH 1 -#define WIKITYPE_CHECKIN 2 -#define WIKITYPE_TAG 3 +#if INTERFACE +# define WIKITYPE_UNKNOWN (-1) +# define WIKITYPE_NORMAL 0 +# define WIKITYPE_BRANCH 1 +# define WIKITYPE_CHECKIN 2 +# define WIKITYPE_TAG 3 +#endif /* ** Figure out what type of wiki page we are dealing with. */ -static int wiki_page_type(const char *zPageName){ +int wiki_page_type(const char *zPageName){ if( db_get_boolean("wiki-about",1)==0 ){ return WIKITYPE_NORMAL; }else if( sqlite3_strglob("checkin/*", zPageName)==0 && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) Index: src/wikiformat.c ================================================================== --- src/wikiformat.c +++ src/wikiformat.c @@ -1869,14 +1869,11 @@ ** name. For each such hyperlink found, add an entry to the ** backlink table. */ void wiki_extract_links( char *z, /* The wiki text from which to extract links */ - int srcid, /* srcid field for new BACKLINK table entries */ - int srctype, /* srctype field for new BACKLINK table entries */ - double mtime, /* mtime field for new BACKLINK table entries */ - int replaceFlag, /* True first delete prior BACKLINK entries */ + Backlink *pBklnk, /* Backlink extraction context */ int flags /* wiki parsing flags */ ){ Renderer renderer; int tokenType; ParsedMarkup markup; @@ -1892,14 +1889,10 @@ if( wikiUsesHtml() ){ renderer.state |= WIKI_HTMLONLY; wikiHtmlOnly = 1; } inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0; - if( replaceFlag ){ - db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d", - srctype, srcid); - } while( z[0] ){ if( wikiHtmlOnly ){ n = nextRawToken(z, &renderer, &tokenType); }else{ @@ -1906,27 +1899,16 @@ n = nextWikiToken(z, &renderer, &tokenType); } switch( tokenType ){ case TOKEN_LINK: { char *zTarget; - int i, c; - char zLink[HNAME_MAX+4]; + int i; zTarget = &z[1]; for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){} while(i>1 && zTarget[i-1]==' '){ i--; } - c = zTarget[i]; - zTarget[i] = 0; - if( is_valid_hname(zTarget) ){ - memcpy(zLink, zTarget, i+1); - canonical16(zLink, i); - db_multi_exec( - "REPLACE INTO backlink(target,srctype,srcid,mtime)" - "VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime - ); - } - zTarget[i] = c; + backlink_create(pBklnk, zTarget, i); break; } case TOKEN_MARKUP: { const char *zId; int iDiv; Index: win/Makefile.dmc ================================================================== --- win/Makefile.dmc +++ win/Makefile.dmc @@ -28,13 +28,13 @@ SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen -SRC = add_.c alerts_.c allrepo_.c attach_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c +SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c -OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O +OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ @@ -49,11 +49,11 @@ $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res - +echo add alerts allrepo attach backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ + +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ @@ -152,10 +152,16 @@ $(OBJDIR)\attach$O : attach_.c attach.h $(TCC) -o$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c +translate$E $** > $@ + +$(OBJDIR)\backlink$O : backlink_.c backlink.h + $(TCC) -o$@ -c backlink_.c + +backlink_.c : $(SRCDIR)\backlink.c + +translate$E $** > $@ $(OBJDIR)\backoffice$O : backoffice_.c backoffice.h $(TCC) -o$@ -c backoffice_.c backoffice_.c : $(SRCDIR)\backoffice.c @@ -964,7 +970,7 @@ zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h - +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h + +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers Index: win/Makefile.mingw ================================================================== --- win/Makefile.mingw +++ win/Makefile.mingw @@ -440,10 +440,11 @@ SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/alerts.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ + $(SRCDIR)/backlink.c \ $(SRCDIR)/backoffice.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/bisect.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ @@ -672,10 +673,11 @@ TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/alerts_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ + $(OBJDIR)/backlink_.c \ $(OBJDIR)/backoffice_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ @@ -813,10 +815,11 @@ OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/alerts.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ + $(OBJDIR)/backlink.o \ $(OBJDIR)/backoffice.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ @@ -1174,10 +1177,11 @@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ + $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ @@ -1349,10 +1353,18 @@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers + +$(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE) + $(TRANSLATE) $(SRCDIR)/backlink.c >$@ + +$(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h + $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c + +$(OBJDIR)/backlink.h: $(OBJDIR)/headers $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h Index: win/Makefile.msc ================================================================== --- win/Makefile.msc +++ win/Makefile.msc @@ -348,10 +348,11 @@ SRC = add_.c \ alerts_.c \ allrepo_.c \ attach_.c \ + backlink_.c \ backoffice_.c \ bag_.c \ bisect_.c \ blob_.c \ branch_.c \ @@ -578,10 +579,11 @@ OBJ = $(OX)\add$O \ $(OX)\alerts$O \ $(OX)\allrepo$O \ $(OX)\attach$O \ + $(OX)\backlink$O \ $(OX)\backoffice$O \ $(OX)\bag$O \ $(OX)\bisect$O \ $(OX)\blob$O \ $(OX)\branch$O \ @@ -781,10 +783,11 @@ $(OX)\linkopts: $B\win\Makefile.msc echo $(OX)\add.obj > $@ echo $(OX)\alerts.obj >> $@ echo $(OX)\allrepo.obj >> $@ echo $(OX)\attach.obj >> $@ + echo $(OX)\backlink.obj >> $@ echo $(OX)\backoffice.obj >> $@ echo $(OX)\bag.obj >> $@ echo $(OX)\bisect.obj >> $@ echo $(OX)\blob.obj >> $@ echo $(OX)\branch.obj >> $@ @@ -1063,10 +1066,16 @@ $(OX)\attach$O : attach_.c attach.h $(TCC) /Fo$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c translate$E $** > $@ + +$(OX)\backlink$O : backlink_.c backlink.h + $(TCC) /Fo$@ -c backlink_.c + +backlink_.c : $(SRCDIR)\backlink.c + translate$E $** > $@ $(OX)\backoffice$O : backoffice_.c backoffice.h $(TCC) /Fo$@ -c backoffice_.c backoffice_.c : $(SRCDIR)\backoffice.c @@ -1882,10 +1891,11 @@ headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h makeheaders$E add_.c:add.h \ alerts_.c:alerts.h \ allrepo_.c:allrepo.h \ attach_.c:attach.h \ + backlink_.c:backlink.h \ backoffice_.c:backoffice.h \ bag_.c:bag.h \ bisect_.c:bisect.h \ blob_.c:blob.h \ branch_.c:branch.h \