Fossil SCM
Simplified tarball basenames. The download file includes sufficient information to reconstruct the check-in hash, so the check-in hash does not need to be supplied separately, resulting in shorter URLs.
Commit
df28ba6539589a0091034913ce7975284b349b1141f75b2a6f675e74bf8ef57e
Parent
d6c9aee97df1277…
2 files changed
+3
-3
+71
-12
+3
-3
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -998,14 +998,14 @@ | ||
| 998 | 998 | @ <tr><th>Downloads:</th><td> |
| 999 | 999 | if( robot_would_be_restricted("download") ){ |
| 1000 | 1000 | @ See separate %z(href("%R/rchvdwnld/%!S",zUuid))download page</a> |
| 1001 | 1001 | }else{ |
| 1002 | 1002 | char *zBase = archive_base_name(rid); |
| 1003 | - @ %z(href("%R/tarball/%S/%s.tar.gz",zUuid,zBase))Tarball</a> | |
| 1004 | - @ | %z(href("%R/zip/%S/%s.zip",zUuid,zBase))ZIP archive</a> | |
| 1003 | + @ %z(href("%R/tarball/%s.tar.gz",zBase))Tarball</a> | |
| 1004 | + @ | %z(href("%R/zip/%s.zip",zBase))ZIP archive</a> | |
| 1005 | 1005 | if( g.zLogin!=0 ){ |
| 1006 | - @ | %z(href("%R/sqlar/%S/%s.sqlar",zUuid,zBase))\ | |
| 1006 | + @ | %z(href("%R/sqlar/%s.sqlar",zBase))\ | |
| 1007 | 1007 | @ SQL archive</a></td></tr> |
| 1008 | 1008 | } |
| 1009 | 1009 | fossil_free(zBase); |
| 1010 | 1010 | } |
| 1011 | 1011 | } |
| 1012 | 1012 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -998,14 +998,14 @@ | |
| 998 | @ <tr><th>Downloads:</th><td> |
| 999 | if( robot_would_be_restricted("download") ){ |
| 1000 | @ See separate %z(href("%R/rchvdwnld/%!S",zUuid))download page</a> |
| 1001 | }else{ |
| 1002 | char *zBase = archive_base_name(rid); |
| 1003 | @ %z(href("%R/tarball/%S/%s.tar.gz",zUuid,zBase))Tarball</a> |
| 1004 | @ | %z(href("%R/zip/%S/%s.zip",zUuid,zBase))ZIP archive</a> |
| 1005 | if( g.zLogin!=0 ){ |
| 1006 | @ | %z(href("%R/sqlar/%S/%s.sqlar",zUuid,zBase))\ |
| 1007 | @ SQL archive</a></td></tr> |
| 1008 | } |
| 1009 | fossil_free(zBase); |
| 1010 | } |
| 1011 | } |
| 1012 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -998,14 +998,14 @@ | |
| 998 | @ <tr><th>Downloads:</th><td> |
| 999 | if( robot_would_be_restricted("download") ){ |
| 1000 | @ See separate %z(href("%R/rchvdwnld/%!S",zUuid))download page</a> |
| 1001 | }else{ |
| 1002 | char *zBase = archive_base_name(rid); |
| 1003 | @ %z(href("%R/tarball/%s.tar.gz",zBase))Tarball</a> |
| 1004 | @ | %z(href("%R/zip/%s.zip",zBase))ZIP archive</a> |
| 1005 | if( g.zLogin!=0 ){ |
| 1006 | @ | %z(href("%R/sqlar/%s.sqlar",zBase))\ |
| 1007 | @ SQL archive</a></td></tr> |
| 1008 | } |
| 1009 | fossil_free(zBase); |
| 1010 | } |
| 1011 | } |
| 1012 |
+71
-12
| --- src/tar.c | ||
| +++ src/tar.c | ||
| @@ -704,27 +704,76 @@ | ||
| 704 | 704 | if( zOut ){ |
| 705 | 705 | blob_write_to_file(&tarball, zOut); |
| 706 | 706 | blob_reset(&tarball); |
| 707 | 707 | } |
| 708 | 708 | } |
| 709 | + | |
| 710 | +/* | |
| 711 | +** This is a helper routine for tar_uuid_from_name(). It handles | |
| 712 | +** the case where *pzName contains no "/" character. Check for | |
| 713 | +** format (3). Return the hash if the name matches format (3), | |
| 714 | +** or return NULL if it does not. | |
| 715 | +*/ | |
| 716 | +static char *format_three_parser(const char *zName){ | |
| 717 | + int iDot = 0; /* Index in zName[] of the first '.' */ | |
| 718 | + int iDash1 = 0; /* Index in zName[] of the '-' before the timestamp */ | |
| 719 | + int iDash2 = 0; /* Index in zName[] of the '-' between timestamp and hash */ | |
| 720 | + int nHash; /* Size of the hash */ | |
| 721 | + char *zHash; /* A copy of the hash value */ | |
| 722 | + char *zDate; /* Copy of the timestamp */ | |
| 723 | + char *zUuid; /* Final result */ | |
| 724 | + int i; /* Loop query */ | |
| 725 | + Stmt q; /* Query to verify that hash and timestamp agree */ | |
| 726 | + | |
| 727 | + for(i=0; zName[i]; i++){ | |
| 728 | + char c = zName[i]; | |
| 729 | + if( c=='.' ){ iDot = i; break; } | |
| 730 | + if( c=='-' ){ iDash1 = iDash2; iDash2 = i; } | |
| 731 | + if( !fossil_isalnum(c) && c!='_' && c!='-' ){ break; } | |
| 732 | + } | |
| 733 | + if( iDot==0 ) return 0; | |
| 734 | + if( iDash1==0 ) return 0; | |
| 735 | + nHash = iDot - iDash2 - 1; | |
| 736 | + if( nHash<8 ) return 0; /* HASH value too short */ | |
| 737 | + if( (iDash2 - iDash1)!=15 ) return 0; /* Wrong timestamp size */ | |
| 738 | + zHash = fossil_strndup(&zName[iDash2+1], nHash); | |
| 739 | + zDate = fossil_strndup(&zName[iDash1+1], 14); | |
| 740 | + db_prepare(&q, | |
| 741 | + "SELECT blob.uuid" | |
| 742 | + " FROM blob JOIN event ON event.objid=blob.rid" | |
| 743 | + " WHERE blob.uuid GLOB '%q*'" | |
| 744 | + " AND strftime('%%Y%%m%%d%%H%%M%%S',event.mtime)='%q'", | |
| 745 | + zHash, zDate | |
| 746 | + ); | |
| 747 | + fossil_free(zHash); | |
| 748 | + fossil_free(zDate); | |
| 749 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 750 | + zUuid = fossil_strdup(db_column_text(&q,0)); | |
| 751 | + }else{ | |
| 752 | + zUuid = 0; | |
| 753 | + } | |
| 754 | + db_finalize(&q); | |
| 755 | + return zUuid; | |
| 756 | +} | |
| 709 | 757 | |
| 710 | 758 | /* |
| 711 | 759 | ** Check to see if the input string is of one of the following |
| 712 | 760 | ** two the forms: |
| 713 | 761 | ** |
| 714 | 762 | ** check-in-name/filename.ext (1) |
| 715 | -** tag-name/check-in-name/filename.txt (2) | |
| 763 | +** tag-name/check-in-name/filename.ext (2) | |
| 764 | +** project-datetime-hash.ext (3) | |
| 716 | 765 | ** |
| 717 | 766 | ** In other words, check to see if the input string contains either |
| 718 | 767 | ** a check-in name or a tag-name and a check-in name separated by |
| 719 | -** a slash. There must be either 1 or 2 "/" characters. In the | |
| 768 | +** a slash. There must be between 0 or 2 "/" characters. In the | |
| 720 | 769 | ** second form, tag-name must be an individual tag (not a branch-tag) |
| 721 | 770 | ** that is found on the check-in identified by the check-in-name. |
| 722 | 771 | ** |
| 723 | 772 | ** If the condition is true, then: |
| 724 | 773 | ** |
| 725 | -** * Make *pzName point to the fielname suffix only | |
| 774 | +** * Make *pzName point to the filename suffix only | |
| 726 | 775 | ** * return a copy of the check-in name in memory from mprintf(). |
| 727 | 776 | ** |
| 728 | 777 | ** If the condition is false, leave *pzName unchanged and return either |
| 729 | 778 | ** NULL or an empty string. Normally NULL is returned, however an |
| 730 | 779 | ** empty string is returned for format (2) if check-in-name does not |
| @@ -736,10 +785,19 @@ | ||
| 736 | 785 | ** |
| 737 | 786 | ** Such URLs will pass through most anti-robot filters because of the |
| 738 | 787 | ** "/tarball/release" prefix will match the suggested "robot-exception" |
| 739 | 788 | ** pattern and can still refer to an historic release rather than just |
| 740 | 789 | ** the most recent release. |
| 790 | +** | |
| 791 | +** Format (3) is designed to allow URLs like this: | |
| 792 | +** | |
| 793 | +** /tarball/fossil-20251018193920-d6c9aee97df.tar.gz | |
| 794 | +** | |
| 795 | +** In other words, filename itself contains sufficient information to | |
| 796 | +** uniquely identify the check-in, including a timestamp of the form | |
| 797 | +** YYYYMMDDHHMMSS and a prefix of the check-in hash. The timestamp | |
| 798 | +** and hash must immediately preceed the first "." in the name. | |
| 741 | 799 | */ |
| 742 | 800 | char *tar_uuid_from_name(char **pzName){ |
| 743 | 801 | char *zName = *pzName; /* Original input */ |
| 744 | 802 | int n1 = 0; /* Bytes in first prefix (tag-name) */ |
| 745 | 803 | int n2 = 0; /* Bytes in second prefix (check-in-name) */ |
| @@ -755,11 +813,12 @@ | ||
| 755 | 813 | return 0; /* More than two "/" characters seen */ |
| 756 | 814 | } |
| 757 | 815 | } |
| 758 | 816 | } |
| 759 | 817 | if( n1==0 ){ |
| 760 | - return 0; /* No prefix of any kind */ | |
| 818 | + /* Check for format (3) */ | |
| 819 | + return format_three_parser(*pzName); | |
| 761 | 820 | } |
| 762 | 821 | if( zName[n+1]==0 ){ |
| 763 | 822 | return 0; /* No filename suffix */ |
| 764 | 823 | } |
| 765 | 824 | if( n2==0 ){ |
| @@ -1002,13 +1061,13 @@ | ||
| 1002 | 1061 | @ branch: \ |
| 1003 | 1062 | @ %z(href("%R/timeline?r=%t",zBrName))%h(zBrName)</a> |
| 1004 | 1063 | } |
| 1005 | 1064 | } |
| 1006 | 1065 | zNm = archive_base_name(rid); |
| 1007 | - @ %z(href("%R/tarball/%!S/%s.tar.gz",zUuid,zNm))\ | |
| 1066 | + @ %z(href("%R/tarball/%s.tar.gz",zNm))\ | |
| 1008 | 1067 | @ <button>Tarball</button></a> |
| 1009 | - @ %z(href("%R/zip/%!S/%s.zip",zUuid,zNm))\ | |
| 1068 | + @ %z(href("%R/zip/%s.zip",zNm))\ | |
| 1010 | 1069 | @ <button>ZIP Archive</button></a> |
| 1011 | 1070 | fossil_free(zBrName); |
| 1012 | 1071 | fossil_free(zNm); |
| 1013 | 1072 | } |
| 1014 | 1073 | } |
| @@ -1176,27 +1235,27 @@ | ||
| 1176 | 1235 | @ %z(href("%R/info/%!S",zUuid))%S(zUuid)</a></div> |
| 1177 | 1236 | @ <div class="accordion_panel"> |
| 1178 | 1237 | @ <table class="label-value"> |
| 1179 | 1238 | @ <tr> |
| 1180 | 1239 | @ <th>Tarball:</th> |
| 1181 | - @ <td>%z(href("%R/tarball/%!S/%s.tar.gz",zUuid,zBase))\ | |
| 1182 | - @ %s(g.zBaseURL)/tarball/%!S(zUuid)/%s(zBase).tar.gz</a></td> | |
| 1240 | + @ <td>%z(href("%R/tarball/%s.tar.gz",zBase))\ | |
| 1241 | + @ %s(g.zBaseURL)/tarball/%s(zBase).tar.gz</a></td> | |
| 1183 | 1242 | @ </tr> |
| 1184 | 1243 | @ |
| 1185 | 1244 | @ <tr> |
| 1186 | 1245 | @ <th>ZIP:</th> |
| 1187 | - @ <td>%z(href("%R/zip/%!S/%s.zip",zUuid,zBase))\ | |
| 1188 | - @ %s(g.zBaseURL)/zip/%!S(zUuid)/%s(zBase).zip</a></td> | |
| 1246 | + @ <td>%z(href("%R/zip/%s.zip",zBase))\ | |
| 1247 | + @ %s(g.zBaseURL)/zip/%s(zBase).zip</a></td> | |
| 1189 | 1248 | @ </tr> |
| 1190 | 1249 | @ |
| 1191 | 1250 | @ <tr> |
| 1192 | 1251 | @ <th>SQLAR:</th> |
| 1193 | - @ <td>%z(href("%R/sqlar/%!S/%s.sqlar",zUuid,zBase))\ | |
| 1194 | - @ %s(g.zBaseURL)/sqlar/%!S(zUuid)/%s(zBase).sqlar</a></td> | |
| 1252 | + @ <td>%z(href("%R/sqlar/%s.sqlar",zBase))\ | |
| 1253 | + @ %s(g.zBaseURL)/sqlar/%s(zBase).sqlar</a></td> | |
| 1195 | 1254 | @ </tr> |
| 1196 | 1255 | @ </table></div> |
| 1197 | 1256 | fossil_free(zBase); |
| 1198 | 1257 | @ <div class="section accordion">Context</div><div class="accordion_panel"> |
| 1199 | 1258 | render_checkin_context(rid, 0, 0, 0); |
| 1200 | 1259 | @ </div> |
| 1201 | 1260 | style_finish_page(); |
| 1202 | 1261 | } |
| 1203 | 1262 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -704,27 +704,76 @@ | |
| 704 | if( zOut ){ |
| 705 | blob_write_to_file(&tarball, zOut); |
| 706 | blob_reset(&tarball); |
| 707 | } |
| 708 | } |
| 709 | |
| 710 | /* |
| 711 | ** Check to see if the input string is of one of the following |
| 712 | ** two the forms: |
| 713 | ** |
| 714 | ** check-in-name/filename.ext (1) |
| 715 | ** tag-name/check-in-name/filename.txt (2) |
| 716 | ** |
| 717 | ** In other words, check to see if the input string contains either |
| 718 | ** a check-in name or a tag-name and a check-in name separated by |
| 719 | ** a slash. There must be either 1 or 2 "/" characters. In the |
| 720 | ** second form, tag-name must be an individual tag (not a branch-tag) |
| 721 | ** that is found on the check-in identified by the check-in-name. |
| 722 | ** |
| 723 | ** If the condition is true, then: |
| 724 | ** |
| 725 | ** * Make *pzName point to the fielname suffix only |
| 726 | ** * return a copy of the check-in name in memory from mprintf(). |
| 727 | ** |
| 728 | ** If the condition is false, leave *pzName unchanged and return either |
| 729 | ** NULL or an empty string. Normally NULL is returned, however an |
| 730 | ** empty string is returned for format (2) if check-in-name does not |
| @@ -736,10 +785,19 @@ | |
| 736 | ** |
| 737 | ** Such URLs will pass through most anti-robot filters because of the |
| 738 | ** "/tarball/release" prefix will match the suggested "robot-exception" |
| 739 | ** pattern and can still refer to an historic release rather than just |
| 740 | ** the most recent release. |
| 741 | */ |
| 742 | char *tar_uuid_from_name(char **pzName){ |
| 743 | char *zName = *pzName; /* Original input */ |
| 744 | int n1 = 0; /* Bytes in first prefix (tag-name) */ |
| 745 | int n2 = 0; /* Bytes in second prefix (check-in-name) */ |
| @@ -755,11 +813,12 @@ | |
| 755 | return 0; /* More than two "/" characters seen */ |
| 756 | } |
| 757 | } |
| 758 | } |
| 759 | if( n1==0 ){ |
| 760 | return 0; /* No prefix of any kind */ |
| 761 | } |
| 762 | if( zName[n+1]==0 ){ |
| 763 | return 0; /* No filename suffix */ |
| 764 | } |
| 765 | if( n2==0 ){ |
| @@ -1002,13 +1061,13 @@ | |
| 1002 | @ branch: \ |
| 1003 | @ %z(href("%R/timeline?r=%t",zBrName))%h(zBrName)</a> |
| 1004 | } |
| 1005 | } |
| 1006 | zNm = archive_base_name(rid); |
| 1007 | @ %z(href("%R/tarball/%!S/%s.tar.gz",zUuid,zNm))\ |
| 1008 | @ <button>Tarball</button></a> |
| 1009 | @ %z(href("%R/zip/%!S/%s.zip",zUuid,zNm))\ |
| 1010 | @ <button>ZIP Archive</button></a> |
| 1011 | fossil_free(zBrName); |
| 1012 | fossil_free(zNm); |
| 1013 | } |
| 1014 | } |
| @@ -1176,27 +1235,27 @@ | |
| 1176 | @ %z(href("%R/info/%!S",zUuid))%S(zUuid)</a></div> |
| 1177 | @ <div class="accordion_panel"> |
| 1178 | @ <table class="label-value"> |
| 1179 | @ <tr> |
| 1180 | @ <th>Tarball:</th> |
| 1181 | @ <td>%z(href("%R/tarball/%!S/%s.tar.gz",zUuid,zBase))\ |
| 1182 | @ %s(g.zBaseURL)/tarball/%!S(zUuid)/%s(zBase).tar.gz</a></td> |
| 1183 | @ </tr> |
| 1184 | @ |
| 1185 | @ <tr> |
| 1186 | @ <th>ZIP:</th> |
| 1187 | @ <td>%z(href("%R/zip/%!S/%s.zip",zUuid,zBase))\ |
| 1188 | @ %s(g.zBaseURL)/zip/%!S(zUuid)/%s(zBase).zip</a></td> |
| 1189 | @ </tr> |
| 1190 | @ |
| 1191 | @ <tr> |
| 1192 | @ <th>SQLAR:</th> |
| 1193 | @ <td>%z(href("%R/sqlar/%!S/%s.sqlar",zUuid,zBase))\ |
| 1194 | @ %s(g.zBaseURL)/sqlar/%!S(zUuid)/%s(zBase).sqlar</a></td> |
| 1195 | @ </tr> |
| 1196 | @ </table></div> |
| 1197 | fossil_free(zBase); |
| 1198 | @ <div class="section accordion">Context</div><div class="accordion_panel"> |
| 1199 | render_checkin_context(rid, 0, 0, 0); |
| 1200 | @ </div> |
| 1201 | style_finish_page(); |
| 1202 | } |
| 1203 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -704,27 +704,76 @@ | |
| 704 | if( zOut ){ |
| 705 | blob_write_to_file(&tarball, zOut); |
| 706 | blob_reset(&tarball); |
| 707 | } |
| 708 | } |
| 709 | |
| 710 | /* |
| 711 | ** This is a helper routine for tar_uuid_from_name(). It handles |
| 712 | ** the case where *pzName contains no "/" character. Check for |
| 713 | ** format (3). Return the hash if the name matches format (3), |
| 714 | ** or return NULL if it does not. |
| 715 | */ |
| 716 | static char *format_three_parser(const char *zName){ |
| 717 | int iDot = 0; /* Index in zName[] of the first '.' */ |
| 718 | int iDash1 = 0; /* Index in zName[] of the '-' before the timestamp */ |
| 719 | int iDash2 = 0; /* Index in zName[] of the '-' between timestamp and hash */ |
| 720 | int nHash; /* Size of the hash */ |
| 721 | char *zHash; /* A copy of the hash value */ |
| 722 | char *zDate; /* Copy of the timestamp */ |
| 723 | char *zUuid; /* Final result */ |
| 724 | int i; /* Loop query */ |
| 725 | Stmt q; /* Query to verify that hash and timestamp agree */ |
| 726 | |
| 727 | for(i=0; zName[i]; i++){ |
| 728 | char c = zName[i]; |
| 729 | if( c=='.' ){ iDot = i; break; } |
| 730 | if( c=='-' ){ iDash1 = iDash2; iDash2 = i; } |
| 731 | if( !fossil_isalnum(c) && c!='_' && c!='-' ){ break; } |
| 732 | } |
| 733 | if( iDot==0 ) return 0; |
| 734 | if( iDash1==0 ) return 0; |
| 735 | nHash = iDot - iDash2 - 1; |
| 736 | if( nHash<8 ) return 0; /* HASH value too short */ |
| 737 | if( (iDash2 - iDash1)!=15 ) return 0; /* Wrong timestamp size */ |
| 738 | zHash = fossil_strndup(&zName[iDash2+1], nHash); |
| 739 | zDate = fossil_strndup(&zName[iDash1+1], 14); |
| 740 | db_prepare(&q, |
| 741 | "SELECT blob.uuid" |
| 742 | " FROM blob JOIN event ON event.objid=blob.rid" |
| 743 | " WHERE blob.uuid GLOB '%q*'" |
| 744 | " AND strftime('%%Y%%m%%d%%H%%M%%S',event.mtime)='%q'", |
| 745 | zHash, zDate |
| 746 | ); |
| 747 | fossil_free(zHash); |
| 748 | fossil_free(zDate); |
| 749 | if( db_step(&q)==SQLITE_ROW ){ |
| 750 | zUuid = fossil_strdup(db_column_text(&q,0)); |
| 751 | }else{ |
| 752 | zUuid = 0; |
| 753 | } |
| 754 | db_finalize(&q); |
| 755 | return zUuid; |
| 756 | } |
| 757 | |
| 758 | /* |
| 759 | ** Check to see if the input string is of one of the following |
| 760 | ** two the forms: |
| 761 | ** |
| 762 | ** check-in-name/filename.ext (1) |
| 763 | ** tag-name/check-in-name/filename.ext (2) |
| 764 | ** project-datetime-hash.ext (3) |
| 765 | ** |
| 766 | ** In other words, check to see if the input string contains either |
| 767 | ** a check-in name or a tag-name and a check-in name separated by |
| 768 | ** a slash. There must be between 0 or 2 "/" characters. In the |
| 769 | ** second form, tag-name must be an individual tag (not a branch-tag) |
| 770 | ** that is found on the check-in identified by the check-in-name. |
| 771 | ** |
| 772 | ** If the condition is true, then: |
| 773 | ** |
| 774 | ** * Make *pzName point to the filename suffix only |
| 775 | ** * return a copy of the check-in name in memory from mprintf(). |
| 776 | ** |
| 777 | ** If the condition is false, leave *pzName unchanged and return either |
| 778 | ** NULL or an empty string. Normally NULL is returned, however an |
| 779 | ** empty string is returned for format (2) if check-in-name does not |
| @@ -736,10 +785,19 @@ | |
| 785 | ** |
| 786 | ** Such URLs will pass through most anti-robot filters because of the |
| 787 | ** "/tarball/release" prefix will match the suggested "robot-exception" |
| 788 | ** pattern and can still refer to an historic release rather than just |
| 789 | ** the most recent release. |
| 790 | ** |
| 791 | ** Format (3) is designed to allow URLs like this: |
| 792 | ** |
| 793 | ** /tarball/fossil-20251018193920-d6c9aee97df.tar.gz |
| 794 | ** |
| 795 | ** In other words, filename itself contains sufficient information to |
| 796 | ** uniquely identify the check-in, including a timestamp of the form |
| 797 | ** YYYYMMDDHHMMSS and a prefix of the check-in hash. The timestamp |
| 798 | ** and hash must immediately preceed the first "." in the name. |
| 799 | */ |
| 800 | char *tar_uuid_from_name(char **pzName){ |
| 801 | char *zName = *pzName; /* Original input */ |
| 802 | int n1 = 0; /* Bytes in first prefix (tag-name) */ |
| 803 | int n2 = 0; /* Bytes in second prefix (check-in-name) */ |
| @@ -755,11 +813,12 @@ | |
| 813 | return 0; /* More than two "/" characters seen */ |
| 814 | } |
| 815 | } |
| 816 | } |
| 817 | if( n1==0 ){ |
| 818 | /* Check for format (3) */ |
| 819 | return format_three_parser(*pzName); |
| 820 | } |
| 821 | if( zName[n+1]==0 ){ |
| 822 | return 0; /* No filename suffix */ |
| 823 | } |
| 824 | if( n2==0 ){ |
| @@ -1002,13 +1061,13 @@ | |
| 1061 | @ branch: \ |
| 1062 | @ %z(href("%R/timeline?r=%t",zBrName))%h(zBrName)</a> |
| 1063 | } |
| 1064 | } |
| 1065 | zNm = archive_base_name(rid); |
| 1066 | @ %z(href("%R/tarball/%s.tar.gz",zNm))\ |
| 1067 | @ <button>Tarball</button></a> |
| 1068 | @ %z(href("%R/zip/%s.zip",zNm))\ |
| 1069 | @ <button>ZIP Archive</button></a> |
| 1070 | fossil_free(zBrName); |
| 1071 | fossil_free(zNm); |
| 1072 | } |
| 1073 | } |
| @@ -1176,27 +1235,27 @@ | |
| 1235 | @ %z(href("%R/info/%!S",zUuid))%S(zUuid)</a></div> |
| 1236 | @ <div class="accordion_panel"> |
| 1237 | @ <table class="label-value"> |
| 1238 | @ <tr> |
| 1239 | @ <th>Tarball:</th> |
| 1240 | @ <td>%z(href("%R/tarball/%s.tar.gz",zBase))\ |
| 1241 | @ %s(g.zBaseURL)/tarball/%s(zBase).tar.gz</a></td> |
| 1242 | @ </tr> |
| 1243 | @ |
| 1244 | @ <tr> |
| 1245 | @ <th>ZIP:</th> |
| 1246 | @ <td>%z(href("%R/zip/%s.zip",zBase))\ |
| 1247 | @ %s(g.zBaseURL)/zip/%s(zBase).zip</a></td> |
| 1248 | @ </tr> |
| 1249 | @ |
| 1250 | @ <tr> |
| 1251 | @ <th>SQLAR:</th> |
| 1252 | @ <td>%z(href("%R/sqlar/%s.sqlar",zBase))\ |
| 1253 | @ %s(g.zBaseURL)/sqlar/%s(zBase).sqlar</a></td> |
| 1254 | @ </tr> |
| 1255 | @ </table></div> |
| 1256 | fossil_free(zBase); |
| 1257 | @ <div class="section accordion">Context</div><div class="accordion_panel"> |
| 1258 | render_checkin_context(rid, 0, 0, 0); |
| 1259 | @ </div> |
| 1260 | style_finish_page(); |
| 1261 | } |
| 1262 |