Fossil SCM
Add a link to the [http://fuelscm.org/] project on the Fossil homepage.
Commit
85f265625bc7653657139e3125a2c6d8be2ba497
Parent
3e3bb5f71a61fa4…
2 files changed
+53
-37
+4
+53
-37
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -13,27 +13,32 @@ | ||
| 13 | 13 | ** [email protected] |
| 14 | 14 | ** http://www.hwaci.com/drh/ |
| 15 | 15 | ** |
| 16 | 16 | ******************************************************************************* |
| 17 | 17 | ** |
| 18 | -** This file contains code to implement the "/doc" web page and related | |
| 19 | -** pages. | |
| 18 | +** This file contains code to implement a very simple search function | |
| 19 | +** against timeline comments, checkin content, wiki pages, and/or tickets. | |
| 20 | +** | |
| 21 | +** The search is full-text like in that it is looking for words and ignores | |
| 22 | +** punctuation and capitalization. But it is more akin to "grep" in that | |
| 23 | +** it scans the entire corpus for the search, and it does not support the | |
| 24 | +** full functionality of FTS4. | |
| 20 | 25 | */ |
| 21 | 26 | #include "config.h" |
| 22 | 27 | #include "search.h" |
| 23 | 28 | #include <assert.h> |
| 24 | 29 | |
| 25 | 30 | #if INTERFACE |
| 26 | 31 | /* |
| 27 | -** A compiled search patter | |
| 32 | +** A compiled search pattern | |
| 28 | 33 | */ |
| 29 | 34 | struct Search { |
| 30 | - int nTerm; | |
| 31 | - struct srchTerm { | |
| 32 | - char *z; | |
| 33 | - int n; | |
| 34 | - } a[8]; | |
| 35 | + int nTerm; /* Number of search terms */ | |
| 36 | + struct srchTerm { /* For each search term */ | |
| 37 | + char *z; /* Text */ | |
| 38 | + int n; /* length */ | |
| 39 | + } a[8]; | |
| 35 | 40 | }; |
| 36 | 41 | #endif |
| 37 | 42 | |
| 38 | 43 | /* |
| 39 | 44 | ** Compile a search pattern |
| @@ -98,43 +103,47 @@ | ||
| 98 | 103 | ** * 10 bonus points if the first occurrence is an exact match |
| 99 | 104 | ** * 1 additional point for each subsequent match of the same word |
| 100 | 105 | ** * Extra points of two consecutive words of the pattern are consecutive |
| 101 | 106 | ** in the document |
| 102 | 107 | */ |
| 103 | -int search_score(Search *p, const char *zDoc){ | |
| 108 | +int search_score(Search *p, int nDoc, const char **azDoc){ | |
| 104 | 109 | int iPrev = 999; |
| 105 | 110 | int score = 10; |
| 106 | 111 | int iBonus = 0; |
| 107 | - int i, j; | |
| 112 | + int i, j, k; | |
| 113 | + const char *zDoc; | |
| 108 | 114 | unsigned char seen[8]; |
| 109 | 115 | |
| 110 | 116 | memset(seen, 0, sizeof(seen)); |
| 111 | - if( zDoc==0 ) return score; | |
| 112 | - for(i=0; zDoc[i]; i++){ | |
| 113 | - char c = zDoc[i]; | |
| 114 | - if( isBoundary[c&0xff] ) continue; | |
| 115 | - for(j=0; j<p->nTerm; j++){ | |
| 116 | - int n = p->a[j].n; | |
| 117 | - if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 ){ | |
| 118 | - score += 1; | |
| 119 | - if( !seen[j] ){ | |
| 120 | - if( isBoundary[zDoc[i+n]&0xff] ) score += 10; | |
| 121 | - seen[j] = 1; | |
| 122 | - } | |
| 123 | - if( j==iPrev+1 ){ | |
| 124 | - score += iBonus; | |
| 125 | - } | |
| 126 | - i += n-1; | |
| 127 | - iPrev = j; | |
| 128 | - iBonus = 50; | |
| 129 | - break; | |
| 130 | - } | |
| 131 | - } | |
| 132 | - iBonus /= 2; | |
| 133 | - while( !isBoundary[zDoc[i]&0xff] ){ i++; } | |
| 134 | - } | |
| 135 | - | |
| 117 | + for(k=0; k<nDoc; k++){ | |
| 118 | + zDoc = azDoc[k]; | |
| 119 | + if( zDoc==0 ) continue; | |
| 120 | + for(i=0; zDoc[i]; i++){ | |
| 121 | + char c = zDoc[i]; | |
| 122 | + if( isBoundary[c&0xff] ) continue; | |
| 123 | + for(j=0; j<p->nTerm; j++){ | |
| 124 | + int n = p->a[j].n; | |
| 125 | + if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 ){ | |
| 126 | + score += 1; | |
| 127 | + if( !seen[j] ){ | |
| 128 | + if( isBoundary[zDoc[i+n]&0xff] ) score += 10; | |
| 129 | + seen[j] = 1; | |
| 130 | + } | |
| 131 | + if( j==iPrev+1 ){ | |
| 132 | + score += iBonus; | |
| 133 | + } | |
| 134 | + i += n-1; | |
| 135 | + iPrev = j; | |
| 136 | + iBonus = 50; | |
| 137 | + break; | |
| 138 | + } | |
| 139 | + } | |
| 140 | + iBonus /= 2; | |
| 141 | + while( !isBoundary[zDoc[i]&0xff] ){ i++; } | |
| 142 | + } | |
| 143 | + } | |
| 144 | + | |
| 136 | 145 | /* Every term must be seen or else the score is zero */ |
| 137 | 146 | for(j=0; j<p->nTerm; j++){ |
| 138 | 147 | if( !seen[j] ) return 0; |
| 139 | 148 | } |
| 140 | 149 | |
| @@ -149,21 +158,28 @@ | ||
| 149 | 158 | sqlite3_context *context, |
| 150 | 159 | int argc, |
| 151 | 160 | sqlite3_value **argv |
| 152 | 161 | ){ |
| 153 | 162 | Search *p = (Search*)sqlite3_user_data(context); |
| 154 | - int score = search_score(p, (const char*)sqlite3_value_text(argv[0])); | |
| 163 | + conts char **azDoc; | |
| 164 | + int score; | |
| 165 | + int i; | |
| 166 | + | |
| 167 | + azDoc = fossil_malloc( sizeof(const char*)*(argc+1) ); | |
| 168 | + for(i=0; i<argc; i++) azDoc[i] = (const char*)sqlite3_value_text(argv[i]); | |
| 169 | + score = search_score(p, argc, azDoc); | |
| 170 | + fossil_free(azDoc); | |
| 155 | 171 | sqlite3_result_int(context, score); |
| 156 | 172 | } |
| 157 | 173 | |
| 158 | 174 | /* |
| 159 | 175 | ** Register the "score()" SQL function to score its input text |
| 160 | 176 | ** using the given Search object. Once this function is registered, |
| 161 | 177 | ** do not delete the Search object. |
| 162 | 178 | */ |
| 163 | 179 | void search_sql_setup(Search *p){ |
| 164 | - sqlite3_create_function(g.db, "score", 1, SQLITE_UTF8, p, | |
| 180 | + sqlite3_create_function(g.db, "score", -1, SQLITE_UTF8, p, | |
| 165 | 181 | search_score_sqlfunc, 0, 0); |
| 166 | 182 | } |
| 167 | 183 | |
| 168 | 184 | /* |
| 169 | 185 | ** Testing the search function. |
| 170 | 186 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -13,27 +13,32 @@ | |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file contains code to implement the "/doc" web page and related |
| 19 | ** pages. |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "search.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | #if INTERFACE |
| 26 | /* |
| 27 | ** A compiled search patter |
| 28 | */ |
| 29 | struct Search { |
| 30 | int nTerm; |
| 31 | struct srchTerm { |
| 32 | char *z; |
| 33 | int n; |
| 34 | } a[8]; |
| 35 | }; |
| 36 | #endif |
| 37 | |
| 38 | /* |
| 39 | ** Compile a search pattern |
| @@ -98,43 +103,47 @@ | |
| 98 | ** * 10 bonus points if the first occurrence is an exact match |
| 99 | ** * 1 additional point for each subsequent match of the same word |
| 100 | ** * Extra points of two consecutive words of the pattern are consecutive |
| 101 | ** in the document |
| 102 | */ |
| 103 | int search_score(Search *p, const char *zDoc){ |
| 104 | int iPrev = 999; |
| 105 | int score = 10; |
| 106 | int iBonus = 0; |
| 107 | int i, j; |
| 108 | unsigned char seen[8]; |
| 109 | |
| 110 | memset(seen, 0, sizeof(seen)); |
| 111 | if( zDoc==0 ) return score; |
| 112 | for(i=0; zDoc[i]; i++){ |
| 113 | char c = zDoc[i]; |
| 114 | if( isBoundary[c&0xff] ) continue; |
| 115 | for(j=0; j<p->nTerm; j++){ |
| 116 | int n = p->a[j].n; |
| 117 | if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 ){ |
| 118 | score += 1; |
| 119 | if( !seen[j] ){ |
| 120 | if( isBoundary[zDoc[i+n]&0xff] ) score += 10; |
| 121 | seen[j] = 1; |
| 122 | } |
| 123 | if( j==iPrev+1 ){ |
| 124 | score += iBonus; |
| 125 | } |
| 126 | i += n-1; |
| 127 | iPrev = j; |
| 128 | iBonus = 50; |
| 129 | break; |
| 130 | } |
| 131 | } |
| 132 | iBonus /= 2; |
| 133 | while( !isBoundary[zDoc[i]&0xff] ){ i++; } |
| 134 | } |
| 135 | |
| 136 | /* Every term must be seen or else the score is zero */ |
| 137 | for(j=0; j<p->nTerm; j++){ |
| 138 | if( !seen[j] ) return 0; |
| 139 | } |
| 140 | |
| @@ -149,21 +158,28 @@ | |
| 149 | sqlite3_context *context, |
| 150 | int argc, |
| 151 | sqlite3_value **argv |
| 152 | ){ |
| 153 | Search *p = (Search*)sqlite3_user_data(context); |
| 154 | int score = search_score(p, (const char*)sqlite3_value_text(argv[0])); |
| 155 | sqlite3_result_int(context, score); |
| 156 | } |
| 157 | |
| 158 | /* |
| 159 | ** Register the "score()" SQL function to score its input text |
| 160 | ** using the given Search object. Once this function is registered, |
| 161 | ** do not delete the Search object. |
| 162 | */ |
| 163 | void search_sql_setup(Search *p){ |
| 164 | sqlite3_create_function(g.db, "score", 1, SQLITE_UTF8, p, |
| 165 | search_score_sqlfunc, 0, 0); |
| 166 | } |
| 167 | |
| 168 | /* |
| 169 | ** Testing the search function. |
| 170 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -13,27 +13,32 @@ | |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file contains code to implement a very simple search function |
| 19 | ** against timeline comments, checkin content, wiki pages, and/or tickets. |
| 20 | ** |
| 21 | ** The search is full-text like in that it is looking for words and ignores |
| 22 | ** punctuation and capitalization. But it is more akin to "grep" in that |
| 23 | ** it scans the entire corpus for the search, and it does not support the |
| 24 | ** full functionality of FTS4. |
| 25 | */ |
| 26 | #include "config.h" |
| 27 | #include "search.h" |
| 28 | #include <assert.h> |
| 29 | |
| 30 | #if INTERFACE |
| 31 | /* |
| 32 | ** A compiled search pattern |
| 33 | */ |
| 34 | struct Search { |
| 35 | int nTerm; /* Number of search terms */ |
| 36 | struct srchTerm { /* For each search term */ |
| 37 | char *z; /* Text */ |
| 38 | int n; /* length */ |
| 39 | } a[8]; |
| 40 | }; |
| 41 | #endif |
| 42 | |
| 43 | /* |
| 44 | ** Compile a search pattern |
| @@ -98,43 +103,47 @@ | |
| 103 | ** * 10 bonus points if the first occurrence is an exact match |
| 104 | ** * 1 additional point for each subsequent match of the same word |
| 105 | ** * Extra points of two consecutive words of the pattern are consecutive |
| 106 | ** in the document |
| 107 | */ |
| 108 | int search_score(Search *p, int nDoc, const char **azDoc){ |
| 109 | int iPrev = 999; |
| 110 | int score = 10; |
| 111 | int iBonus = 0; |
| 112 | int i, j, k; |
| 113 | const char *zDoc; |
| 114 | unsigned char seen[8]; |
| 115 | |
| 116 | memset(seen, 0, sizeof(seen)); |
| 117 | for(k=0; k<nDoc; k++){ |
| 118 | zDoc = azDoc[k]; |
| 119 | if( zDoc==0 ) continue; |
| 120 | for(i=0; zDoc[i]; i++){ |
| 121 | char c = zDoc[i]; |
| 122 | if( isBoundary[c&0xff] ) continue; |
| 123 | for(j=0; j<p->nTerm; j++){ |
| 124 | int n = p->a[j].n; |
| 125 | if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 ){ |
| 126 | score += 1; |
| 127 | if( !seen[j] ){ |
| 128 | if( isBoundary[zDoc[i+n]&0xff] ) score += 10; |
| 129 | seen[j] = 1; |
| 130 | } |
| 131 | if( j==iPrev+1 ){ |
| 132 | score += iBonus; |
| 133 | } |
| 134 | i += n-1; |
| 135 | iPrev = j; |
| 136 | iBonus = 50; |
| 137 | break; |
| 138 | } |
| 139 | } |
| 140 | iBonus /= 2; |
| 141 | while( !isBoundary[zDoc[i]&0xff] ){ i++; } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | /* Every term must be seen or else the score is zero */ |
| 146 | for(j=0; j<p->nTerm; j++){ |
| 147 | if( !seen[j] ) return 0; |
| 148 | } |
| 149 | |
| @@ -149,21 +158,28 @@ | |
| 158 | sqlite3_context *context, |
| 159 | int argc, |
| 160 | sqlite3_value **argv |
| 161 | ){ |
| 162 | Search *p = (Search*)sqlite3_user_data(context); |
| 163 | conts char **azDoc; |
| 164 | int score; |
| 165 | int i; |
| 166 | |
| 167 | azDoc = fossil_malloc( sizeof(const char*)*(argc+1) ); |
| 168 | for(i=0; i<argc; i++) azDoc[i] = (const char*)sqlite3_value_text(argv[i]); |
| 169 | score = search_score(p, argc, azDoc); |
| 170 | fossil_free(azDoc); |
| 171 | sqlite3_result_int(context, score); |
| 172 | } |
| 173 | |
| 174 | /* |
| 175 | ** Register the "score()" SQL function to score its input text |
| 176 | ** using the given Search object. Once this function is registered, |
| 177 | ** do not delete the Search object. |
| 178 | */ |
| 179 | void search_sql_setup(Search *p){ |
| 180 | sqlite3_create_function(g.db, "score", -1, SQLITE_UTF8, p, |
| 181 | search_score_sqlfunc, 0, 0); |
| 182 | } |
| 183 | |
| 184 | /* |
| 185 | ** Testing the search function. |
| 186 |
+4
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -110,10 +110,14 @@ | ||
| 110 | 110 | a Fossil repository. |
| 111 | 111 | |
| 112 | 112 | <hr> |
| 113 | 113 | <h3>Links For Fossil Users:</h3> |
| 114 | 114 | |
| 115 | + * "Fuel" is cross-platform GUI front-end for Fossil | |
| 116 | + written in Qt. [http://fuelscm.org/]. | |
| 117 | + Fuel is an independent project run by a different group of | |
| 118 | + developers. | |
| 115 | 119 | * [./reviews.wiki | Testimonials] from satisfied fossil users and |
| 116 | 120 | [./quotes.wiki | Quotes] about Fossil and other DVCSes. |
| 117 | 121 | * [./faq.wiki | FAQ] |
| 118 | 122 | * The [./concepts.wiki | concepts] behind fossil |
| 119 | 123 | * [./quickstart.wiki | Quick Start] guide to using fossil |
| 120 | 124 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -110,10 +110,14 @@ | |
| 110 | a Fossil repository. |
| 111 | |
| 112 | <hr> |
| 113 | <h3>Links For Fossil Users:</h3> |
| 114 | |
| 115 | * [./reviews.wiki | Testimonials] from satisfied fossil users and |
| 116 | [./quotes.wiki | Quotes] about Fossil and other DVCSes. |
| 117 | * [./faq.wiki | FAQ] |
| 118 | * The [./concepts.wiki | concepts] behind fossil |
| 119 | * [./quickstart.wiki | Quick Start] guide to using fossil |
| 120 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -110,10 +110,14 @@ | |
| 110 | a Fossil repository. |
| 111 | |
| 112 | <hr> |
| 113 | <h3>Links For Fossil Users:</h3> |
| 114 | |
| 115 | * "Fuel" is cross-platform GUI front-end for Fossil |
| 116 | written in Qt. [http://fuelscm.org/]. |
| 117 | Fuel is an independent project run by a different group of |
| 118 | developers. |
| 119 | * [./reviews.wiki | Testimonials] from satisfied fossil users and |
| 120 | [./quotes.wiki | Quotes] about Fossil and other DVCSes. |
| 121 | * [./faq.wiki | FAQ] |
| 122 | * The [./concepts.wiki | concepts] behind fossil |
| 123 | * [./quickstart.wiki | Quick Start] guide to using fossil |
| 124 |