Fossil SCM
added -q to ticket set|add, corrected set/add command line parser
Commit
08726b95be4cba6923fbbfcbe9c3d03a386d9954
Parent
82559a701f6c5fb…
1 file changed
+136
-114
+136
-114
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -865,162 +865,184 @@ | ||
| 865 | 865 | ** |
| 866 | 866 | ** %fossil ticket list |
| 867 | 867 | ** |
| 868 | 868 | ** list all columns, defined in the ticket table |
| 869 | 869 | ** |
| 870 | -** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE ... ? | |
| 870 | +** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? | |
| 871 | +** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? | |
| 871 | 872 | ** |
| 872 | 873 | ** change ticket identified by TICKETUUID and set the value of |
| 873 | 874 | ** field FIELD to VALUE. Valid field descriptions are: |
| 874 | 875 | ** status, type, severity, priority, resolution, |
| 875 | 876 | ** foundin, private_contact, resolution, title or comment |
| 876 | 877 | ** Field names given above are the ones, defined in a standard |
| 877 | 878 | ** fossil environment. If you have added, deleted columns, you |
| 878 | 879 | ** change the all your configured columns. |
| 879 | 880 | ** You can use more than one field/value pair on the commandline. |
| 881 | +** Using -q|--quote enables the special character decoding as | |
| 882 | +** in "ticket list". So it's possible, to set multiline text or | |
| 883 | +** text with special characters. | |
| 880 | 884 | ** |
| 881 | -** %fossil ticket add FIELD VALUE ?FIELD VALUE ... ? | |
| 885 | +** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? | |
| 882 | 886 | ** |
| 883 | 887 | ** like set, but create a new ticket with the given values. |
| 884 | 888 | ** |
| 885 | 889 | ** The values in set|add are not validated against the definitions |
| 886 | -** given in the "Ticket Common Script". | |
| 890 | +** given in "Ticket Common Script". | |
| 887 | 891 | */ |
| 888 | 892 | void ticket_cmd(void){ |
| 889 | 893 | int n; |
| 890 | 894 | |
| 895 | + /* do some ints, we want to be inside a checkout */ | |
| 891 | 896 | db_must_be_within_tree(); |
| 892 | 897 | db_find_and_open_repository(1); |
| 893 | - | |
| 894 | 898 | user_select(); |
| 895 | 899 | /* |
| 896 | 900 | ** Check that the user exists. |
| 897 | 901 | */ |
| 898 | 902 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 899 | 903 | fossil_fatal("no such user: %s", g.zLogin); |
| 900 | 904 | } |
| 901 | 905 | |
| 902 | 906 | if( g.argc<3 ){ |
| 903 | - usage("add|set|show"); | |
| 907 | + usage("add|list|set|show"); | |
| 904 | 908 | }else{ |
| 905 | 909 | n = strlen(g.argv[2]); |
| 906 | 910 | if( n==1 && g.argv[2][0]=='s' ){ |
| 907 | - usage("ticket show|set|add"); | |
| 911 | + /* set/show cannot be distinguished, so show the usage */ | |
| 912 | + usage("add|list|set|show"); | |
| 908 | 913 | }else if( strncmp(g.argv[2],"list",n)==0 ){ |
| 914 | + /* simply show all field names */ | |
| 909 | 915 | int i; |
| 910 | 916 | |
| 911 | 917 | /* read all available ticket fields */ |
| 912 | 918 | getAllTicketFields(); |
| 913 | 919 | for(i=0; i<nField; i++){ |
| 914 | 920 | printf("%s\n",azField[i]); |
| 915 | 921 | } |
| 916 | - }else if( strncmp(g.argv[2],"show",n)==0 ){ | |
| 917 | - if( g.argc==3 ){ | |
| 918 | - usage("ticket show REPORTNR"); | |
| 919 | - }else{ | |
| 920 | - const char *zRep = 0; | |
| 921 | - const char *zSep = 0; | |
| 922 | - const char *zFilterUuid = 0; | |
| 923 | - tTktShowEncoding tktEncoding; | |
| 924 | - | |
| 925 | - zSep = find_option("limit","l",1); | |
| 926 | - tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; | |
| 927 | - zRep = g.argv[3]; | |
| 928 | - if( !strcmp(zRep,"0") ){ | |
| 929 | - zRep = 0; | |
| 930 | - } | |
| 931 | - if( g.argc>4 ){ | |
| 932 | - zFilterUuid = g.argv[4]; | |
| 933 | - } | |
| 934 | - | |
| 935 | - rptshow( zRep, zSep, zFilterUuid, tktEncoding ); | |
| 936 | - | |
| 937 | - } | |
| 938 | - }else{ | |
| 939 | - enum { set,add,err } eCmd = err; | |
| 940 | - int i; | |
| 941 | - int rid; | |
| 942 | - const char *zTktUuid; | |
| 943 | - Blob tktchng, cksum; | |
| 944 | - | |
| 945 | - /* get command type (set/add) and get uuid, if needed for set */ | |
| 946 | - if( strncmp(g.argv[2],"set",n)==0 ){ | |
| 947 | - eCmd = add; | |
| 948 | - if( g.argc==3 ){ | |
| 949 | - fossil_fatal("set: missing TICKETUUID!"); | |
| 950 | - } | |
| 951 | - zTktUuid = db_text(0, | |
| 952 | - "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3] | |
| 953 | - ); | |
| 954 | - if( !zTktUuid ){ | |
| 955 | - fossil_fatal("unknown ticket: '%s'!",g.argv[3]); | |
| 956 | - } | |
| 957 | - i=4; | |
| 958 | - }else if( strncmp(g.argv[2],"add",n)==0 ){ | |
| 959 | - eCmd = set; | |
| 960 | - zTktUuid = 0; | |
| 961 | - i = 3; | |
| 962 | - zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); | |
| 963 | - } | |
| 964 | - if( eCmd==err ){ | |
| 965 | - fossil_fatal("%s: unknown ticket command",g.argv[2]); | |
| 966 | - } | |
| 967 | - if( i==g.argc ){ | |
| 968 | - fossil_fatal("empty %s command aborted!",g.argv[2]); | |
| 969 | - } | |
| 970 | - /* read all available ticket fields */ | |
| 971 | - getAllTicketFields(); | |
| 972 | - /* read commandline and assign fields in the azValue array */ | |
| 973 | - while( i<g.argc ){ | |
| 974 | - char *zFName; | |
| 975 | - char *zFValue; | |
| 976 | - int j; | |
| 977 | - | |
| 978 | - zFName = g.argv[i++]; | |
| 979 | - if( i==g.argc ){ | |
| 980 | - fossil_fatal("missing value for '%s'!",zFName); | |
| 981 | - } | |
| 982 | - zFValue = g.argv[i++]; | |
| 983 | - j = fieldId(zFName); | |
| 984 | - if( j == -1 ){ | |
| 985 | - fossil_fatal("unknown field name '%s'!",zFName); | |
| 986 | - }else{ | |
| 987 | - azValue[j] = zFValue; | |
| 988 | - } | |
| 989 | - } | |
| 990 | - blob_zero(&tktchng); | |
| 991 | - { /* add the time to the ticket manifest */ | |
| 992 | - char *zDate; | |
| 993 | - | |
| 994 | - zDate = db_text(0, "SELECT datetime('now')"); | |
| 995 | - zDate[10] = 'T'; | |
| 996 | - blob_appendf(&tktchng, "D %s\n", zDate); | |
| 997 | - free(zDate); | |
| 998 | - } | |
| 999 | - /* append defined elements */ | |
| 1000 | - for(i=0; i<nField; i++){ | |
| 1001 | - char *zValue; | |
| 1002 | - | |
| 1003 | - zValue = azValue[i]; | |
| 1004 | - if( azValue[i] && azValue[i][0] ){ | |
| 1005 | - if( strncmp(azField[i], "private_", 8)==0 ){ | |
| 1006 | - zValue = db_conceal(zValue, strlen(zValue)); | |
| 1007 | - blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); | |
| 1008 | - }else{ | |
| 1009 | - blob_appendf(&tktchng, "J %s %#F\n", azField[i], strlen(zValue), zValue); | |
| 1010 | - } | |
| 1011 | - } | |
| 1012 | - } | |
| 1013 | - blob_appendf(&tktchng, "K %s\n", zTktUuid); | |
| 1014 | - blob_appendf(&tktchng, "U %F\n", g.zLogin); | |
| 1015 | - md5sum_blob(&tktchng, &cksum); | |
| 1016 | - blob_appendf(&tktchng, "Z %b\n", &cksum); | |
| 1017 | - rid = content_put(&tktchng, 0, 0); | |
| 1018 | - if( rid==0 ){ | |
| 1019 | - fossil_panic("trouble committing ticket: %s", g.zErrMsg); | |
| 1020 | - } | |
| 1021 | - manifest_crosslink_begin(); | |
| 1022 | - manifest_crosslink(rid, &tktchng); | |
| 1023 | - manifest_crosslink_end(); | |
| 922 | + }else{ | |
| 923 | + /* add a new ticket or set fields on existing tickets */ | |
| 924 | + tTktShowEncoding tktEncoding; | |
| 925 | + | |
| 926 | + tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; | |
| 927 | + | |
| 928 | + if( strncmp(g.argv[2],"show",n)==0 ){ | |
| 929 | + if( g.argc==3 ){ | |
| 930 | + usage("show REPORTNR"); | |
| 931 | + }else{ | |
| 932 | + const char *zRep = 0; | |
| 933 | + const char *zSep = 0; | |
| 934 | + const char *zFilterUuid = 0; | |
| 935 | + | |
| 936 | + zSep = find_option("limit","l",1); | |
| 937 | + zRep = g.argv[3]; | |
| 938 | + if( !strcmp(zRep,"0") ){ | |
| 939 | + zRep = 0; | |
| 940 | + } | |
| 941 | + if( g.argc>4 ){ | |
| 942 | + zFilterUuid = g.argv[4]; | |
| 943 | + } | |
| 944 | + | |
| 945 | + rptshow( zRep, zSep, zFilterUuid, tktEncoding ); | |
| 946 | + | |
| 947 | + } | |
| 948 | + }else{ | |
| 949 | + /* add a new ticket or update an existing ticket */ | |
| 950 | + enum { set,add,err } eCmd = err; | |
| 951 | + int i; | |
| 952 | + int rid; | |
| 953 | + const char *zTktUuid; | |
| 954 | + Blob tktchng, cksum; | |
| 955 | + | |
| 956 | + /* get command type (set/add) and get uuid, if needed for set */ | |
| 957 | + if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){ | |
| 958 | + eCmd = set; | |
| 959 | + if( g.argc==3 ){ | |
| 960 | + usage("set TICKETUUID"); | |
| 961 | + } | |
| 962 | + zTktUuid = db_text(0, | |
| 963 | + "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3] | |
| 964 | + ); | |
| 965 | + if( !zTktUuid ){ | |
| 966 | + fossil_fatal("unknown ticket: '%s'!",g.argv[3]); | |
| 967 | + } | |
| 968 | + i=4; | |
| 969 | + }else if( strncmp(g.argv[2],"add",n)==0 ){ | |
| 970 | + eCmd = add; | |
| 971 | + i = 3; | |
| 972 | + zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); | |
| 973 | + } | |
| 974 | + /* none of set/add, so show the usage! */ | |
| 975 | + if( eCmd==err ){ | |
| 976 | + usage("add|list|set|show"); | |
| 977 | + } | |
| 978 | + | |
| 979 | + /* read all given ticket field/value pairs from command line */ | |
| 980 | + if( i==g.argc ){ | |
| 981 | + fossil_fatal("empty %s command aborted!",g.argv[2]); | |
| 982 | + } | |
| 983 | + getAllTicketFields(); | |
| 984 | + /* read commandline and assign fields in the azValue array */ | |
| 985 | + while( i<g.argc ){ | |
| 986 | + char *zFName; | |
| 987 | + char *zFValue; | |
| 988 | + int j; | |
| 989 | + | |
| 990 | + zFName = g.argv[i++]; | |
| 991 | + if( i==g.argc ){ | |
| 992 | + fossil_fatal("missing value for '%s'!",zFName); | |
| 993 | + } | |
| 994 | + zFValue = g.argv[i++]; | |
| 995 | + j = fieldId(zFName); | |
| 996 | + if( tktEncoding == tktFossilize ){ | |
| 997 | + zFValue=mprintf("%s",zFValue); | |
| 998 | + defossilize(zFValue); | |
| 999 | + } | |
| 1000 | + if( j == -1 ){ | |
| 1001 | + fossil_fatal("unknown field name '%s'!",zFName); | |
| 1002 | + }else{ | |
| 1003 | + azValue[j] = zFValue; | |
| 1004 | + } | |
| 1005 | + } | |
| 1006 | + | |
| 1007 | + /* now add the needed artifacts to the repository */ | |
| 1008 | + blob_zero(&tktchng); | |
| 1009 | + { /* add the time to the ticket manifest */ | |
| 1010 | + char *zDate; | |
| 1011 | + | |
| 1012 | + zDate = db_text(0, "SELECT datetime('now')"); | |
| 1013 | + zDate[10] = 'T'; | |
| 1014 | + blob_appendf(&tktchng, "D %s\n", zDate); | |
| 1015 | + free(zDate); | |
| 1016 | + } | |
| 1017 | + /* append defined elements */ | |
| 1018 | + for(i=0; i<nField; i++){ | |
| 1019 | + char *zValue; | |
| 1020 | + | |
| 1021 | + zValue = azValue[i]; | |
| 1022 | + if( azValue[i] && azValue[i][0] ){ | |
| 1023 | + if( strncmp(azField[i], "private_", 8)==0 ){ | |
| 1024 | + zValue = db_conceal(zValue, strlen(zValue)); | |
| 1025 | + blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); | |
| 1026 | + }else{ | |
| 1027 | + blob_appendf(&tktchng, "J %s %#F\n", azField[i], strlen(zValue), zValue); | |
| 1028 | + } | |
| 1029 | + if( tktEncoding == tktFossilize ){ | |
| 1030 | + free(azValue[i]); | |
| 1031 | + } | |
| 1032 | + } | |
| 1033 | + } | |
| 1034 | + blob_appendf(&tktchng, "K %s\n", zTktUuid); | |
| 1035 | + blob_appendf(&tktchng, "U %F\n", g.zLogin); | |
| 1036 | + md5sum_blob(&tktchng, &cksum); | |
| 1037 | + blob_appendf(&tktchng, "Z %b\n", &cksum); | |
| 1038 | + rid = content_put(&tktchng, 0, 0); | |
| 1039 | + if( rid==0 ){ | |
| 1040 | + fossil_panic("trouble committing ticket: %s", g.zErrMsg); | |
| 1041 | + } | |
| 1042 | + manifest_crosslink_begin(); | |
| 1043 | + manifest_crosslink(rid, &tktchng); | |
| 1044 | + manifest_crosslink_end(); | |
| 1045 | + } | |
| 1024 | 1046 | } |
| 1025 | 1047 | } |
| 1026 | 1048 | } |
| 1027 | 1049 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -865,162 +865,184 @@ | |
| 865 | ** |
| 866 | ** %fossil ticket list |
| 867 | ** |
| 868 | ** list all columns, defined in the ticket table |
| 869 | ** |
| 870 | ** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE ... ? |
| 871 | ** |
| 872 | ** change ticket identified by TICKETUUID and set the value of |
| 873 | ** field FIELD to VALUE. Valid field descriptions are: |
| 874 | ** status, type, severity, priority, resolution, |
| 875 | ** foundin, private_contact, resolution, title or comment |
| 876 | ** Field names given above are the ones, defined in a standard |
| 877 | ** fossil environment. If you have added, deleted columns, you |
| 878 | ** change the all your configured columns. |
| 879 | ** You can use more than one field/value pair on the commandline. |
| 880 | ** |
| 881 | ** %fossil ticket add FIELD VALUE ?FIELD VALUE ... ? |
| 882 | ** |
| 883 | ** like set, but create a new ticket with the given values. |
| 884 | ** |
| 885 | ** The values in set|add are not validated against the definitions |
| 886 | ** given in the "Ticket Common Script". |
| 887 | */ |
| 888 | void ticket_cmd(void){ |
| 889 | int n; |
| 890 | |
| 891 | db_must_be_within_tree(); |
| 892 | db_find_and_open_repository(1); |
| 893 | |
| 894 | user_select(); |
| 895 | /* |
| 896 | ** Check that the user exists. |
| 897 | */ |
| 898 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 899 | fossil_fatal("no such user: %s", g.zLogin); |
| 900 | } |
| 901 | |
| 902 | if( g.argc<3 ){ |
| 903 | usage("add|set|show"); |
| 904 | }else{ |
| 905 | n = strlen(g.argv[2]); |
| 906 | if( n==1 && g.argv[2][0]=='s' ){ |
| 907 | usage("ticket show|set|add"); |
| 908 | }else if( strncmp(g.argv[2],"list",n)==0 ){ |
| 909 | int i; |
| 910 | |
| 911 | /* read all available ticket fields */ |
| 912 | getAllTicketFields(); |
| 913 | for(i=0; i<nField; i++){ |
| 914 | printf("%s\n",azField[i]); |
| 915 | } |
| 916 | }else if( strncmp(g.argv[2],"show",n)==0 ){ |
| 917 | if( g.argc==3 ){ |
| 918 | usage("ticket show REPORTNR"); |
| 919 | }else{ |
| 920 | const char *zRep = 0; |
| 921 | const char *zSep = 0; |
| 922 | const char *zFilterUuid = 0; |
| 923 | tTktShowEncoding tktEncoding; |
| 924 | |
| 925 | zSep = find_option("limit","l",1); |
| 926 | tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; |
| 927 | zRep = g.argv[3]; |
| 928 | if( !strcmp(zRep,"0") ){ |
| 929 | zRep = 0; |
| 930 | } |
| 931 | if( g.argc>4 ){ |
| 932 | zFilterUuid = g.argv[4]; |
| 933 | } |
| 934 | |
| 935 | rptshow( zRep, zSep, zFilterUuid, tktEncoding ); |
| 936 | |
| 937 | } |
| 938 | }else{ |
| 939 | enum { set,add,err } eCmd = err; |
| 940 | int i; |
| 941 | int rid; |
| 942 | const char *zTktUuid; |
| 943 | Blob tktchng, cksum; |
| 944 | |
| 945 | /* get command type (set/add) and get uuid, if needed for set */ |
| 946 | if( strncmp(g.argv[2],"set",n)==0 ){ |
| 947 | eCmd = add; |
| 948 | if( g.argc==3 ){ |
| 949 | fossil_fatal("set: missing TICKETUUID!"); |
| 950 | } |
| 951 | zTktUuid = db_text(0, |
| 952 | "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3] |
| 953 | ); |
| 954 | if( !zTktUuid ){ |
| 955 | fossil_fatal("unknown ticket: '%s'!",g.argv[3]); |
| 956 | } |
| 957 | i=4; |
| 958 | }else if( strncmp(g.argv[2],"add",n)==0 ){ |
| 959 | eCmd = set; |
| 960 | zTktUuid = 0; |
| 961 | i = 3; |
| 962 | zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); |
| 963 | } |
| 964 | if( eCmd==err ){ |
| 965 | fossil_fatal("%s: unknown ticket command",g.argv[2]); |
| 966 | } |
| 967 | if( i==g.argc ){ |
| 968 | fossil_fatal("empty %s command aborted!",g.argv[2]); |
| 969 | } |
| 970 | /* read all available ticket fields */ |
| 971 | getAllTicketFields(); |
| 972 | /* read commandline and assign fields in the azValue array */ |
| 973 | while( i<g.argc ){ |
| 974 | char *zFName; |
| 975 | char *zFValue; |
| 976 | int j; |
| 977 | |
| 978 | zFName = g.argv[i++]; |
| 979 | if( i==g.argc ){ |
| 980 | fossil_fatal("missing value for '%s'!",zFName); |
| 981 | } |
| 982 | zFValue = g.argv[i++]; |
| 983 | j = fieldId(zFName); |
| 984 | if( j == -1 ){ |
| 985 | fossil_fatal("unknown field name '%s'!",zFName); |
| 986 | }else{ |
| 987 | azValue[j] = zFValue; |
| 988 | } |
| 989 | } |
| 990 | blob_zero(&tktchng); |
| 991 | { /* add the time to the ticket manifest */ |
| 992 | char *zDate; |
| 993 | |
| 994 | zDate = db_text(0, "SELECT datetime('now')"); |
| 995 | zDate[10] = 'T'; |
| 996 | blob_appendf(&tktchng, "D %s\n", zDate); |
| 997 | free(zDate); |
| 998 | } |
| 999 | /* append defined elements */ |
| 1000 | for(i=0; i<nField; i++){ |
| 1001 | char *zValue; |
| 1002 | |
| 1003 | zValue = azValue[i]; |
| 1004 | if( azValue[i] && azValue[i][0] ){ |
| 1005 | if( strncmp(azField[i], "private_", 8)==0 ){ |
| 1006 | zValue = db_conceal(zValue, strlen(zValue)); |
| 1007 | blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); |
| 1008 | }else{ |
| 1009 | blob_appendf(&tktchng, "J %s %#F\n", azField[i], strlen(zValue), zValue); |
| 1010 | } |
| 1011 | } |
| 1012 | } |
| 1013 | blob_appendf(&tktchng, "K %s\n", zTktUuid); |
| 1014 | blob_appendf(&tktchng, "U %F\n", g.zLogin); |
| 1015 | md5sum_blob(&tktchng, &cksum); |
| 1016 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 1017 | rid = content_put(&tktchng, 0, 0); |
| 1018 | if( rid==0 ){ |
| 1019 | fossil_panic("trouble committing ticket: %s", g.zErrMsg); |
| 1020 | } |
| 1021 | manifest_crosslink_begin(); |
| 1022 | manifest_crosslink(rid, &tktchng); |
| 1023 | manifest_crosslink_end(); |
| 1024 | } |
| 1025 | } |
| 1026 | } |
| 1027 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -865,162 +865,184 @@ | |
| 865 | ** |
| 866 | ** %fossil ticket list |
| 867 | ** |
| 868 | ** list all columns, defined in the ticket table |
| 869 | ** |
| 870 | ** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? |
| 871 | ** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? |
| 872 | ** |
| 873 | ** change ticket identified by TICKETUUID and set the value of |
| 874 | ** field FIELD to VALUE. Valid field descriptions are: |
| 875 | ** status, type, severity, priority, resolution, |
| 876 | ** foundin, private_contact, resolution, title or comment |
| 877 | ** Field names given above are the ones, defined in a standard |
| 878 | ** fossil environment. If you have added, deleted columns, you |
| 879 | ** change the all your configured columns. |
| 880 | ** You can use more than one field/value pair on the commandline. |
| 881 | ** Using -q|--quote enables the special character decoding as |
| 882 | ** in "ticket list". So it's possible, to set multiline text or |
| 883 | ** text with special characters. |
| 884 | ** |
| 885 | ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? |
| 886 | ** |
| 887 | ** like set, but create a new ticket with the given values. |
| 888 | ** |
| 889 | ** The values in set|add are not validated against the definitions |
| 890 | ** given in "Ticket Common Script". |
| 891 | */ |
| 892 | void ticket_cmd(void){ |
| 893 | int n; |
| 894 | |
| 895 | /* do some ints, we want to be inside a checkout */ |
| 896 | db_must_be_within_tree(); |
| 897 | db_find_and_open_repository(1); |
| 898 | user_select(); |
| 899 | /* |
| 900 | ** Check that the user exists. |
| 901 | */ |
| 902 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 903 | fossil_fatal("no such user: %s", g.zLogin); |
| 904 | } |
| 905 | |
| 906 | if( g.argc<3 ){ |
| 907 | usage("add|list|set|show"); |
| 908 | }else{ |
| 909 | n = strlen(g.argv[2]); |
| 910 | if( n==1 && g.argv[2][0]=='s' ){ |
| 911 | /* set/show cannot be distinguished, so show the usage */ |
| 912 | usage("add|list|set|show"); |
| 913 | }else if( strncmp(g.argv[2],"list",n)==0 ){ |
| 914 | /* simply show all field names */ |
| 915 | int i; |
| 916 | |
| 917 | /* read all available ticket fields */ |
| 918 | getAllTicketFields(); |
| 919 | for(i=0; i<nField; i++){ |
| 920 | printf("%s\n",azField[i]); |
| 921 | } |
| 922 | }else{ |
| 923 | /* add a new ticket or set fields on existing tickets */ |
| 924 | tTktShowEncoding tktEncoding; |
| 925 | |
| 926 | tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; |
| 927 | |
| 928 | if( strncmp(g.argv[2],"show",n)==0 ){ |
| 929 | if( g.argc==3 ){ |
| 930 | usage("show REPORTNR"); |
| 931 | }else{ |
| 932 | const char *zRep = 0; |
| 933 | const char *zSep = 0; |
| 934 | const char *zFilterUuid = 0; |
| 935 | |
| 936 | zSep = find_option("limit","l",1); |
| 937 | zRep = g.argv[3]; |
| 938 | if( !strcmp(zRep,"0") ){ |
| 939 | zRep = 0; |
| 940 | } |
| 941 | if( g.argc>4 ){ |
| 942 | zFilterUuid = g.argv[4]; |
| 943 | } |
| 944 | |
| 945 | rptshow( zRep, zSep, zFilterUuid, tktEncoding ); |
| 946 | |
| 947 | } |
| 948 | }else{ |
| 949 | /* add a new ticket or update an existing ticket */ |
| 950 | enum { set,add,err } eCmd = err; |
| 951 | int i; |
| 952 | int rid; |
| 953 | const char *zTktUuid; |
| 954 | Blob tktchng, cksum; |
| 955 | |
| 956 | /* get command type (set/add) and get uuid, if needed for set */ |
| 957 | if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){ |
| 958 | eCmd = set; |
| 959 | if( g.argc==3 ){ |
| 960 | usage("set TICKETUUID"); |
| 961 | } |
| 962 | zTktUuid = db_text(0, |
| 963 | "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3] |
| 964 | ); |
| 965 | if( !zTktUuid ){ |
| 966 | fossil_fatal("unknown ticket: '%s'!",g.argv[3]); |
| 967 | } |
| 968 | i=4; |
| 969 | }else if( strncmp(g.argv[2],"add",n)==0 ){ |
| 970 | eCmd = add; |
| 971 | i = 3; |
| 972 | zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); |
| 973 | } |
| 974 | /* none of set/add, so show the usage! */ |
| 975 | if( eCmd==err ){ |
| 976 | usage("add|list|set|show"); |
| 977 | } |
| 978 | |
| 979 | /* read all given ticket field/value pairs from command line */ |
| 980 | if( i==g.argc ){ |
| 981 | fossil_fatal("empty %s command aborted!",g.argv[2]); |
| 982 | } |
| 983 | getAllTicketFields(); |
| 984 | /* read commandline and assign fields in the azValue array */ |
| 985 | while( i<g.argc ){ |
| 986 | char *zFName; |
| 987 | char *zFValue; |
| 988 | int j; |
| 989 | |
| 990 | zFName = g.argv[i++]; |
| 991 | if( i==g.argc ){ |
| 992 | fossil_fatal("missing value for '%s'!",zFName); |
| 993 | } |
| 994 | zFValue = g.argv[i++]; |
| 995 | j = fieldId(zFName); |
| 996 | if( tktEncoding == tktFossilize ){ |
| 997 | zFValue=mprintf("%s",zFValue); |
| 998 | defossilize(zFValue); |
| 999 | } |
| 1000 | if( j == -1 ){ |
| 1001 | fossil_fatal("unknown field name '%s'!",zFName); |
| 1002 | }else{ |
| 1003 | azValue[j] = zFValue; |
| 1004 | } |
| 1005 | } |
| 1006 | |
| 1007 | /* now add the needed artifacts to the repository */ |
| 1008 | blob_zero(&tktchng); |
| 1009 | { /* add the time to the ticket manifest */ |
| 1010 | char *zDate; |
| 1011 | |
| 1012 | zDate = db_text(0, "SELECT datetime('now')"); |
| 1013 | zDate[10] = 'T'; |
| 1014 | blob_appendf(&tktchng, "D %s\n", zDate); |
| 1015 | free(zDate); |
| 1016 | } |
| 1017 | /* append defined elements */ |
| 1018 | for(i=0; i<nField; i++){ |
| 1019 | char *zValue; |
| 1020 | |
| 1021 | zValue = azValue[i]; |
| 1022 | if( azValue[i] && azValue[i][0] ){ |
| 1023 | if( strncmp(azField[i], "private_", 8)==0 ){ |
| 1024 | zValue = db_conceal(zValue, strlen(zValue)); |
| 1025 | blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); |
| 1026 | }else{ |
| 1027 | blob_appendf(&tktchng, "J %s %#F\n", azField[i], strlen(zValue), zValue); |
| 1028 | } |
| 1029 | if( tktEncoding == tktFossilize ){ |
| 1030 | free(azValue[i]); |
| 1031 | } |
| 1032 | } |
| 1033 | } |
| 1034 | blob_appendf(&tktchng, "K %s\n", zTktUuid); |
| 1035 | blob_appendf(&tktchng, "U %F\n", g.zLogin); |
| 1036 | md5sum_blob(&tktchng, &cksum); |
| 1037 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 1038 | rid = content_put(&tktchng, 0, 0); |
| 1039 | if( rid==0 ){ |
| 1040 | fossil_panic("trouble committing ticket: %s", g.zErrMsg); |
| 1041 | } |
| 1042 | manifest_crosslink_begin(); |
| 1043 | manifest_crosslink(rid, &tktchng); |
| 1044 | manifest_crosslink_end(); |
| 1045 | } |
| 1046 | } |
| 1047 | } |
| 1048 | } |
| 1049 |