Fossil SCM
Rudimentary support for [wikipedia:interwiki_links|interwiki links].
Commit
3ca23edc8fe05300dc31dbc3d771e39a1a7fa99bde0712ffd799e2ac6dff3dcc
Parent
0ae2dbd40a3e335…
1 file changed
+96
+96
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -1197,10 +1197,100 @@ | ||
| 1197 | 1197 | ) ){ |
| 1198 | 1198 | return 0; |
| 1199 | 1199 | } |
| 1200 | 1200 | return wikiOverrideHash; |
| 1201 | 1201 | } |
| 1202 | + | |
| 1203 | +/* | |
| 1204 | +** If zTarget is an interwiki link, return a pointer to a URL for that | |
| 1205 | +** link target in memory obtained from fossil_malloc(). If zTarget is | |
| 1206 | +** not a valid interwiki link, return NULL. | |
| 1207 | +** | |
| 1208 | +** An interwiki link target is of the form: | |
| 1209 | +** | |
| 1210 | +** Code:PageName | |
| 1211 | +** | |
| 1212 | +** "Code" is a brief code that describes the intended target wiki. | |
| 1213 | +** Codes are assigned by "intermap:*" entries in the CONFIG table. | |
| 1214 | +** The link is only valid if there exists an try in the CONFIG table | |
| 1215 | +** that matches "intermap:Code". | |
| 1216 | +** | |
| 1217 | +** Each value of each intermap:Code entry in the CONFIG table is a JSON | |
| 1218 | +** object with the following fields: | |
| 1219 | +** | |
| 1220 | +** { | |
| 1221 | +** "base": Base URL for the remote site. | |
| 1222 | +** "hash": Append this to "base" for Hash targets. | |
| 1223 | +** "wiki": Append this to "base" for Wiki targets. | |
| 1224 | +** } | |
| 1225 | +** | |
| 1226 | +** If the remote wiki is Fossil, then the correct value for "hash" | |
| 1227 | +** is "/info/" and the correct value for "wiki" is "/wiki?name=". | |
| 1228 | +** If (for example) Wikipedia is the remote, then "hash" should be | |
| 1229 | +** omitted and the correct value for "wiki" is "/wiki/". | |
| 1230 | +** | |
| 1231 | +** PageName is link name of the target wiki. Several different forms | |
| 1232 | +** of PageName are recognized. | |
| 1233 | +** | |
| 1234 | +** Path If PageName is empty or begins with a "/" character, then | |
| 1235 | +** it is a pathname that is appended to "base". | |
| 1236 | +** | |
| 1237 | +** Hash If PageName is a hexadecimal string of 4 or more | |
| 1238 | +** characters, then PageName is appended to "hash" which | |
| 1239 | +** is then appended to "base". | |
| 1240 | +** | |
| 1241 | +** Wiki If PageName does not start with "/" and it is | |
| 1242 | +** not a hexadecimal string of 4 or more characters, then | |
| 1243 | +** PageName is appended to "wiki" and that combination is | |
| 1244 | +** appended to "base". | |
| 1245 | +** | |
| 1246 | +** See https://en.wikipedia.org/wiki/Interwiki_links for further information | |
| 1247 | +** on interwiki links. | |
| 1248 | +*/ | |
| 1249 | +static char *wiki_is_interwiki(const char *zTarget){ | |
| 1250 | + int nCode; | |
| 1251 | + int i; | |
| 1252 | + const char *zPage; | |
| 1253 | + int nPage; | |
| 1254 | + char *zUrl = 0; | |
| 1255 | + Stmt q; | |
| 1256 | + for(i=0; zTarget[i] && zTarget[i]!=':'; i++){} | |
| 1257 | + if( zTarget[i]==0 ) return 0; | |
| 1258 | + nCode = i; | |
| 1259 | + if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0; | |
| 1260 | + zPage = zTarget + nCode + 1; | |
| 1261 | + nPage = (int)strlen(zPage); | |
| 1262 | + db_prepare(&q, | |
| 1263 | + "SELECT json_extract(value,'$.base')," | |
| 1264 | + " json_extract(value,'$.hash')," | |
| 1265 | + " json_extract(value,'$.wiki')" | |
| 1266 | + " FROM config WHERE name=lower('interwiki:%.*q')", | |
| 1267 | + nCode, zTarget); | |
| 1268 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1269 | + const char *zBase = db_column_text(&q,0); | |
| 1270 | + if( zBase==0 || zBase[0]==0 ) break; | |
| 1271 | + if( nPage==0 || zPage[0]=='/' ){ | |
| 1272 | + /* Path */ | |
| 1273 | + zUrl = mprintf("%s%s", zBase, zPage); | |
| 1274 | + }else if( nPage>=4 && validate16(zPage,nPage) ){ | |
| 1275 | + /* Hash */ | |
| 1276 | + const char *zHash = db_column_text(&q,1); | |
| 1277 | + if( zHash && zHash[0] ){ | |
| 1278 | + zUrl = mprintf("%s%s%s", zBase, zHash, zPage); | |
| 1279 | + } | |
| 1280 | + }else{ | |
| 1281 | + /* Wiki */ | |
| 1282 | + const char *zWiki = db_column_text(&q,2); | |
| 1283 | + if( zWiki && zWiki[0] ){ | |
| 1284 | + zUrl = mprintf("%s%s%s", zBase, zWiki, zPage); | |
| 1285 | + } | |
| 1286 | + } | |
| 1287 | + break; | |
| 1288 | + } | |
| 1289 | + db_finalize(&q); | |
| 1290 | + return zUrl; | |
| 1291 | +} | |
| 1202 | 1292 | |
| 1203 | 1293 | /* |
| 1204 | 1294 | ** Resolve a hyperlink. The zTarget argument is the content of the [...] |
| 1205 | 1295 | ** in the wiki. Append to the output string whatever text is appropriate |
| 1206 | 1296 | ** for opening the hyperlink. Write into zClose[0...nClose-1] text that will |
| @@ -1230,10 +1320,12 @@ | ||
| 1230 | 1320 | ** |
| 1231 | 1321 | ** [WikiPageName] |
| 1232 | 1322 | ** [wiki:WikiPageName] |
| 1233 | 1323 | ** |
| 1234 | 1324 | ** [2010-02-27 07:13] |
| 1325 | +** | |
| 1326 | +** [InterMap:Link] -> Interwiki link | |
| 1235 | 1327 | */ |
| 1236 | 1328 | void wiki_resolve_hyperlink( |
| 1237 | 1329 | Blob *pOut, /* Write the HTML output here */ |
| 1238 | 1330 | int mFlags, /* Rendering option flags */ |
| 1239 | 1331 | const char *zTarget, /* Hyperlink target; text within [...] */ |
| @@ -1244,10 +1336,11 @@ | ||
| 1244 | 1336 | ){ |
| 1245 | 1337 | const char *zTerm = "</a>"; |
| 1246 | 1338 | const char *z; |
| 1247 | 1339 | char *zExtra = 0; |
| 1248 | 1340 | const char *zExtraNS = 0; |
| 1341 | + char *zRemote = 0; | |
| 1249 | 1342 | |
| 1250 | 1343 | if( zTitle ){ |
| 1251 | 1344 | zExtra = mprintf(" title='%h'", zTitle); |
| 1252 | 1345 | zExtraNS = zExtra+1; |
| 1253 | 1346 | } |
| @@ -1303,10 +1396,13 @@ | ||
| 1303 | 1396 | blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget)); |
| 1304 | 1397 | zTerm = "]</a>"; |
| 1305 | 1398 | }else{ |
| 1306 | 1399 | zTerm = ""; |
| 1307 | 1400 | } |
| 1401 | + }else if( (zRemote = wiki_is_interwiki(zTarget))!=0 ){ | |
| 1402 | + blob_appendf(pOut, "<a href=\"%z\"%s>", zRemote, zExtra); | |
| 1403 | + zTerm = "</a>"; | |
| 1308 | 1404 | }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){ |
| 1309 | 1405 | /* The link is to a valid wiki page name */ |
| 1310 | 1406 | const char *zOverride = wiki_is_overridden(zTarget); |
| 1311 | 1407 | if( zOverride ){ |
| 1312 | 1408 | blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra); |
| 1313 | 1409 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -1197,10 +1197,100 @@ | |
| 1197 | ) ){ |
| 1198 | return 0; |
| 1199 | } |
| 1200 | return wikiOverrideHash; |
| 1201 | } |
| 1202 | |
| 1203 | /* |
| 1204 | ** Resolve a hyperlink. The zTarget argument is the content of the [...] |
| 1205 | ** in the wiki. Append to the output string whatever text is appropriate |
| 1206 | ** for opening the hyperlink. Write into zClose[0...nClose-1] text that will |
| @@ -1230,10 +1320,12 @@ | |
| 1230 | ** |
| 1231 | ** [WikiPageName] |
| 1232 | ** [wiki:WikiPageName] |
| 1233 | ** |
| 1234 | ** [2010-02-27 07:13] |
| 1235 | */ |
| 1236 | void wiki_resolve_hyperlink( |
| 1237 | Blob *pOut, /* Write the HTML output here */ |
| 1238 | int mFlags, /* Rendering option flags */ |
| 1239 | const char *zTarget, /* Hyperlink target; text within [...] */ |
| @@ -1244,10 +1336,11 @@ | |
| 1244 | ){ |
| 1245 | const char *zTerm = "</a>"; |
| 1246 | const char *z; |
| 1247 | char *zExtra = 0; |
| 1248 | const char *zExtraNS = 0; |
| 1249 | |
| 1250 | if( zTitle ){ |
| 1251 | zExtra = mprintf(" title='%h'", zTitle); |
| 1252 | zExtraNS = zExtra+1; |
| 1253 | } |
| @@ -1303,10 +1396,13 @@ | |
| 1303 | blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget)); |
| 1304 | zTerm = "]</a>"; |
| 1305 | }else{ |
| 1306 | zTerm = ""; |
| 1307 | } |
| 1308 | }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){ |
| 1309 | /* The link is to a valid wiki page name */ |
| 1310 | const char *zOverride = wiki_is_overridden(zTarget); |
| 1311 | if( zOverride ){ |
| 1312 | blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra); |
| 1313 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -1197,10 +1197,100 @@ | |
| 1197 | ) ){ |
| 1198 | return 0; |
| 1199 | } |
| 1200 | return wikiOverrideHash; |
| 1201 | } |
| 1202 | |
| 1203 | /* |
| 1204 | ** If zTarget is an interwiki link, return a pointer to a URL for that |
| 1205 | ** link target in memory obtained from fossil_malloc(). If zTarget is |
| 1206 | ** not a valid interwiki link, return NULL. |
| 1207 | ** |
| 1208 | ** An interwiki link target is of the form: |
| 1209 | ** |
| 1210 | ** Code:PageName |
| 1211 | ** |
| 1212 | ** "Code" is a brief code that describes the intended target wiki. |
| 1213 | ** Codes are assigned by "intermap:*" entries in the CONFIG table. |
| 1214 | ** The link is only valid if there exists an try in the CONFIG table |
| 1215 | ** that matches "intermap:Code". |
| 1216 | ** |
| 1217 | ** Each value of each intermap:Code entry in the CONFIG table is a JSON |
| 1218 | ** object with the following fields: |
| 1219 | ** |
| 1220 | ** { |
| 1221 | ** "base": Base URL for the remote site. |
| 1222 | ** "hash": Append this to "base" for Hash targets. |
| 1223 | ** "wiki": Append this to "base" for Wiki targets. |
| 1224 | ** } |
| 1225 | ** |
| 1226 | ** If the remote wiki is Fossil, then the correct value for "hash" |
| 1227 | ** is "/info/" and the correct value for "wiki" is "/wiki?name=". |
| 1228 | ** If (for example) Wikipedia is the remote, then "hash" should be |
| 1229 | ** omitted and the correct value for "wiki" is "/wiki/". |
| 1230 | ** |
| 1231 | ** PageName is link name of the target wiki. Several different forms |
| 1232 | ** of PageName are recognized. |
| 1233 | ** |
| 1234 | ** Path If PageName is empty or begins with a "/" character, then |
| 1235 | ** it is a pathname that is appended to "base". |
| 1236 | ** |
| 1237 | ** Hash If PageName is a hexadecimal string of 4 or more |
| 1238 | ** characters, then PageName is appended to "hash" which |
| 1239 | ** is then appended to "base". |
| 1240 | ** |
| 1241 | ** Wiki If PageName does not start with "/" and it is |
| 1242 | ** not a hexadecimal string of 4 or more characters, then |
| 1243 | ** PageName is appended to "wiki" and that combination is |
| 1244 | ** appended to "base". |
| 1245 | ** |
| 1246 | ** See https://en.wikipedia.org/wiki/Interwiki_links for further information |
| 1247 | ** on interwiki links. |
| 1248 | */ |
| 1249 | static char *wiki_is_interwiki(const char *zTarget){ |
| 1250 | int nCode; |
| 1251 | int i; |
| 1252 | const char *zPage; |
| 1253 | int nPage; |
| 1254 | char *zUrl = 0; |
| 1255 | Stmt q; |
| 1256 | for(i=0; zTarget[i] && zTarget[i]!=':'; i++){} |
| 1257 | if( zTarget[i]==0 ) return 0; |
| 1258 | nCode = i; |
| 1259 | if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0; |
| 1260 | zPage = zTarget + nCode + 1; |
| 1261 | nPage = (int)strlen(zPage); |
| 1262 | db_prepare(&q, |
| 1263 | "SELECT json_extract(value,'$.base')," |
| 1264 | " json_extract(value,'$.hash')," |
| 1265 | " json_extract(value,'$.wiki')" |
| 1266 | " FROM config WHERE name=lower('interwiki:%.*q')", |
| 1267 | nCode, zTarget); |
| 1268 | while( db_step(&q)==SQLITE_ROW ){ |
| 1269 | const char *zBase = db_column_text(&q,0); |
| 1270 | if( zBase==0 || zBase[0]==0 ) break; |
| 1271 | if( nPage==0 || zPage[0]=='/' ){ |
| 1272 | /* Path */ |
| 1273 | zUrl = mprintf("%s%s", zBase, zPage); |
| 1274 | }else if( nPage>=4 && validate16(zPage,nPage) ){ |
| 1275 | /* Hash */ |
| 1276 | const char *zHash = db_column_text(&q,1); |
| 1277 | if( zHash && zHash[0] ){ |
| 1278 | zUrl = mprintf("%s%s%s", zBase, zHash, zPage); |
| 1279 | } |
| 1280 | }else{ |
| 1281 | /* Wiki */ |
| 1282 | const char *zWiki = db_column_text(&q,2); |
| 1283 | if( zWiki && zWiki[0] ){ |
| 1284 | zUrl = mprintf("%s%s%s", zBase, zWiki, zPage); |
| 1285 | } |
| 1286 | } |
| 1287 | break; |
| 1288 | } |
| 1289 | db_finalize(&q); |
| 1290 | return zUrl; |
| 1291 | } |
| 1292 | |
| 1293 | /* |
| 1294 | ** Resolve a hyperlink. The zTarget argument is the content of the [...] |
| 1295 | ** in the wiki. Append to the output string whatever text is appropriate |
| 1296 | ** for opening the hyperlink. Write into zClose[0...nClose-1] text that will |
| @@ -1230,10 +1320,12 @@ | |
| 1320 | ** |
| 1321 | ** [WikiPageName] |
| 1322 | ** [wiki:WikiPageName] |
| 1323 | ** |
| 1324 | ** [2010-02-27 07:13] |
| 1325 | ** |
| 1326 | ** [InterMap:Link] -> Interwiki link |
| 1327 | */ |
| 1328 | void wiki_resolve_hyperlink( |
| 1329 | Blob *pOut, /* Write the HTML output here */ |
| 1330 | int mFlags, /* Rendering option flags */ |
| 1331 | const char *zTarget, /* Hyperlink target; text within [...] */ |
| @@ -1244,10 +1336,11 @@ | |
| 1336 | ){ |
| 1337 | const char *zTerm = "</a>"; |
| 1338 | const char *z; |
| 1339 | char *zExtra = 0; |
| 1340 | const char *zExtraNS = 0; |
| 1341 | char *zRemote = 0; |
| 1342 | |
| 1343 | if( zTitle ){ |
| 1344 | zExtra = mprintf(" title='%h'", zTitle); |
| 1345 | zExtraNS = zExtra+1; |
| 1346 | } |
| @@ -1303,10 +1396,13 @@ | |
| 1396 | blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget)); |
| 1397 | zTerm = "]</a>"; |
| 1398 | }else{ |
| 1399 | zTerm = ""; |
| 1400 | } |
| 1401 | }else if( (zRemote = wiki_is_interwiki(zTarget))!=0 ){ |
| 1402 | blob_appendf(pOut, "<a href=\"%z\"%s>", zRemote, zExtra); |
| 1403 | zTerm = "</a>"; |
| 1404 | }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){ |
| 1405 | /* The link is to a valid wiki page name */ |
| 1406 | const char *zOverride = wiki_is_overridden(zTarget); |
| 1407 | if( zOverride ){ |
| 1408 | blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra); |
| 1409 |