| | @@ -686,11 +686,14 @@ |
| 686 | 686 | ** |
| 687 | 687 | ** * It's uuid |
| 688 | 688 | ** * date of check-in |
| 689 | 689 | ** * Comment & user |
| 690 | 690 | */ |
| 691 | | -static void object_description(int rid, int linkToView){ |
| 691 | +static void object_description( |
| 692 | + int rid, /* The artifact ID */ |
| 693 | + int linkToView /* Add viewer link if true */ |
| 694 | +){ |
| 692 | 695 | Stmt q; |
| 693 | 696 | int cnt = 0; |
| 694 | 697 | int nWiki = 0; |
| 695 | 698 | db_prepare(&q, |
| 696 | 699 | "SELECT filename.name, datetime(event.mtime), substr(a.uuid,1,10)," |
| | @@ -710,14 +713,19 @@ |
| 710 | 713 | const char *zDate = db_column_text(&q, 1); |
| 711 | 714 | const char *zFuuid = db_column_text(&q, 2); |
| 712 | 715 | const char *zCom = db_column_text(&q, 3); |
| 713 | 716 | const char *zUser = db_column_text(&q, 4); |
| 714 | 717 | const char *zVers = db_column_text(&q, 5); |
| 715 | | - @ File <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a> |
| 718 | + if( cnt>0 ){ |
| 719 | + @ Also file |
| 720 | + }else{ |
| 721 | + @ File |
| 722 | + } |
| 723 | + @ <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a> |
| 716 | 724 | @ uuid %s(zFuuid) part of check-in |
| 717 | 725 | hyperlink_to_uuid(zVers); |
| 718 | | - @ %w(zCom) by %h(zUser) on %s(zDate) |
| 726 | + @ %w(zCom) by %h(zUser) on %s(zDate). |
| 719 | 727 | cnt++; |
| 720 | 728 | } |
| 721 | 729 | db_finalize(&q); |
| 722 | 730 | db_prepare(&q, |
| 723 | 731 | "SELECT substr(tagname, 6, 10000), datetime(event.mtime)," |
| | @@ -733,13 +741,17 @@ |
| 733 | 741 | while( db_step(&q)==SQLITE_ROW ){ |
| 734 | 742 | const char *zPagename = db_column_text(&q, 0); |
| 735 | 743 | const char *zDate = db_column_text(&q, 1); |
| 736 | 744 | const char *zUser = db_column_text(&q, 2); |
| 737 | 745 | const char *zUuid = db_column_text(&q, 3); |
| 738 | | - @ Wiki page |
| 746 | + if( cnt>0 ){ |
| 747 | + @ Also wiki page |
| 748 | + }else{ |
| 749 | + @ Wiki page |
| 750 | + } |
| 739 | 751 | @ [<a href="%s(g.zBaseURL)/wiki?name=%t(zPagename)">%h(zPagename)</a>] |
| 740 | | - @ uuid %s(zUuid) by %h(zUser) on %s(zDate) |
| 752 | + @ uuid %s(zUuid) by %h(zUser) on %s(zDate). |
| 741 | 753 | nWiki++; |
| 742 | 754 | cnt++; |
| 743 | 755 | } |
| 744 | 756 | db_finalize(&q); |
| 745 | 757 | if( nWiki==0 ){ |
| | @@ -754,10 +766,13 @@ |
| 754 | 766 | const char *zDate = db_column_text(&q, 0); |
| 755 | 767 | const char *zUuid = db_column_text(&q, 3); |
| 756 | 768 | const char *zUser = db_column_text(&q, 1); |
| 757 | 769 | const char *zCom = db_column_text(&q, 2); |
| 758 | 770 | const char *zType = db_column_text(&q, 4); |
| 771 | + if( cnt>0 ){ |
| 772 | + @ Also |
| 773 | + } |
| 759 | 774 | if( zType[0]=='w' ){ |
| 760 | 775 | @ Wiki edit |
| 761 | 776 | }else if( zType[0]=='t' ){ |
| 762 | 777 | @ Ticket change |
| 763 | 778 | }else if( zType[0]=='c' ){ |
| | @@ -764,11 +779,11 @@ |
| 764 | 779 | @ Manifest of baseline |
| 765 | 780 | }else{ |
| 766 | 781 | @ Control file referencing |
| 767 | 782 | } |
| 768 | 783 | hyperlink_to_uuid(zUuid); |
| 769 | | - @ %w(zCom) by %h(zUser) on %s(zDate) |
| 784 | + @ %w(zCom) by %h(zUser) on %s(zDate). |
| 770 | 785 | cnt++; |
| 771 | 786 | } |
| 772 | 787 | db_finalize(&q); |
| 773 | 788 | } |
| 774 | 789 | if( cnt==0 ){ |
| | @@ -812,10 +827,121 @@ |
| 812 | 827 | @ %h(blob_str(&diff)) |
| 813 | 828 | @ </pre></blockquote> |
| 814 | 829 | blob_reset(&diff); |
| 815 | 830 | style_footer(); |
| 816 | 831 | } |
| 832 | + |
| 833 | +/* |
| 834 | +** WEBPAGE: raw |
| 835 | +** URL: /raw?name=ARTIFACTID&m=TYPE |
| 836 | +** |
| 837 | +** Return the uninterpreted content of an artifact. Used primarily |
| 838 | +** to view artifacts that are images. |
| 839 | +*/ |
| 840 | +void rawartifact_page(void){ |
| 841 | + int rid; |
| 842 | + const char *zMime; |
| 843 | + Blob content; |
| 844 | + |
| 845 | + rid = name_to_rid(PD("name","0")); |
| 846 | + zMime = PD("m","application/x-fossil-artifact"); |
| 847 | + login_check_credentials(); |
| 848 | + if( !g.okRead ){ login_needed(); return; } |
| 849 | + if( rid==0 ){ cgi_redirect("/home"); } |
| 850 | + content_get(rid, &content); |
| 851 | + cgi_set_content_type(zMime); |
| 852 | + cgi_set_content(&content); |
| 853 | +} |
| 854 | + |
| 855 | +/* |
| 856 | +** Render a hex dump of a file. |
| 857 | +*/ |
| 858 | +static void hexdump(Blob *pBlob){ |
| 859 | + const unsigned char *x; |
| 860 | + int n, i, j, k; |
| 861 | + char zLine[100]; |
| 862 | + static const char zHex[] = "0123456789abcdef"; |
| 863 | + |
| 864 | + x = (const unsigned char*)blob_buffer(pBlob); |
| 865 | + n = blob_size(pBlob); |
| 866 | + for(i=0; i<n; i+=16){ |
| 867 | + j = 0; |
| 868 | + zLine[0] = zHex[(i>>24)&0xf]; |
| 869 | + zLine[1] = zHex[(i>>16)&0xf]; |
| 870 | + zLine[2] = zHex[(i>>8)&0xf]; |
| 871 | + zLine[3] = zHex[i&0xf]; |
| 872 | + zLine[4] = ':'; |
| 873 | + sprintf(zLine, "%04x: ", i); |
| 874 | + for(j=0; j<16; j++){ |
| 875 | + k = 5+j*3; |
| 876 | + zLine[k] = ' '; |
| 877 | + if( i+j<n ){ |
| 878 | + unsigned char c = x[i+j]; |
| 879 | + zLine[k+1] = zHex[c>>4]; |
| 880 | + zLine[k+2] = zHex[c&0xf]; |
| 881 | + }else{ |
| 882 | + zLine[k+1] = ' '; |
| 883 | + zLine[k+2] = ' '; |
| 884 | + } |
| 885 | + } |
| 886 | + zLine[53] = ' '; |
| 887 | + zLine[54] = ' '; |
| 888 | + for(j=0; j<16; j++){ |
| 889 | + k = j+55; |
| 890 | + if( i+j<n ){ |
| 891 | + unsigned char c = x[i+j]; |
| 892 | + if( c>=0x20 && c<=0x7e ){ |
| 893 | + zLine[k] = c; |
| 894 | + }else{ |
| 895 | + zLine[k] = '.'; |
| 896 | + } |
| 897 | + }else{ |
| 898 | + zLine[k] = 0; |
| 899 | + } |
| 900 | + } |
| 901 | + zLine[71] = 0; |
| 902 | + @ %h(zLine) |
| 903 | + } |
| 904 | +} |
| 905 | + |
| 906 | +/* |
| 907 | +** WEBPAGE: hexdump |
| 908 | +** URL: /hexdump?name=ARTIFACTID |
| 909 | +** |
| 910 | +** Show the complete content of a file identified by ARTIFACTID |
| 911 | +** as preformatted text. |
| 912 | +*/ |
| 913 | +void hexdump_page(void){ |
| 914 | + int rid; |
| 915 | + Blob content; |
| 916 | + |
| 917 | + rid = name_to_rid(PD("name","0")); |
| 918 | + login_check_credentials(); |
| 919 | + if( !g.okRead ){ login_needed(); return; } |
| 920 | + if( rid==0 ){ cgi_redirect("/home"); } |
| 921 | + if( g.okAdmin ){ |
| 922 | + const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 923 | + if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){ |
| 924 | + style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1", |
| 925 | + g.zTop, zUuid); |
| 926 | + }else{ |
| 927 | + style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", |
| 928 | + g.zTop, zUuid); |
| 929 | + } |
| 930 | + } |
| 931 | + style_header("Hex Artifact Content"); |
| 932 | + @ <h2>Hexadecimal Content Of:</h2> |
| 933 | + @ <blockquote> |
| 934 | + object_description(rid, 0); |
| 935 | + @ </blockquote> |
| 936 | + @ <hr> |
| 937 | + content_get(rid, &content); |
| 938 | + @ <blockquote><pre> |
| 939 | + hexdump(&content); |
| 940 | + @ </pre></blockquote> |
| 941 | + style_footer(); |
| 942 | +} |
| 817 | 943 | |
| 818 | 944 | /* |
| 819 | 945 | ** WEBPAGE: artifact |
| 820 | 946 | ** URL: /artifact?name=ARTIFACTID |
| 821 | 947 | ** |
| | @@ -823,10 +949,11 @@ |
| 823 | 949 | ** as preformatted text. |
| 824 | 950 | */ |
| 825 | 951 | void artifact_page(void){ |
| 826 | 952 | int rid; |
| 827 | 953 | Blob content; |
| 954 | + const char *zMime; |
| 828 | 955 | |
| 829 | 956 | rid = name_to_rid(PD("name","0")); |
| 830 | 957 | login_check_credentials(); |
| 831 | 958 | if( !g.okRead ){ login_needed(); return; } |
| 832 | 959 | if( rid==0 ){ cgi_redirect("/home"); } |
| | @@ -844,15 +971,27 @@ |
| 844 | 971 | @ <h2>Content Of:</h2> |
| 845 | 972 | @ <blockquote> |
| 846 | 973 | object_description(rid, 0); |
| 847 | 974 | @ </blockquote> |
| 848 | 975 | @ <hr> |
| 849 | | - @ <blockquote><pre> |
| 976 | + @ <blockquote> |
| 850 | 977 | content_get(rid, &content); |
| 851 | | - @ %h(blob_str(&content)) |
| 852 | | - @ </pre></blockquote> |
| 853 | | - blob_reset(&content); |
| 978 | + zMime = mimetype_from_content(&content); |
| 979 | + if( zMime==0 ){ |
| 980 | + @ <pre> |
| 981 | + @ %h(blob_str(&content)) |
| 982 | + @ </pre> |
| 983 | + style_submenu_element("Hex","Hex", "%s/hexdump?name=%d", g.zTop, rid); |
| 984 | + }else if( strncmp(zMime, "image/", 6)==0 ){ |
| 985 | + @ <img src="%s(g.zBaseURL)/raw?name=%d(rid)&m=%s(zMime)"></img> |
| 986 | + style_submenu_element("Hex","Hex", "%s/hexdump?name=%d", g.zTop, rid); |
| 987 | + }else{ |
| 988 | + @ <pre> |
| 989 | + hexdump(&content); |
| 990 | + @ </pre> |
| 991 | + } |
| 992 | + @ </blockquote> |
| 854 | 993 | style_footer(); |
| 855 | 994 | } |
| 856 | 995 | |
| 857 | 996 | /* |
| 858 | 997 | ** WEBPAGE: tinfo |
| 859 | 998 | |