Fossil SCM
Add new commands: test-var-list, test-var-get, and test-var-set. The original motivation was to make it easier to insert "css", "header", and "footer" values that people send in, but these commands seem useful for other kinds of experimentation. The are "test-*" commands because they are not intended to be accessed by ordinary users.
Commit
1e21dacecad2117f0b5286736e2045895704b340
Parent
9a07b24935c6c4b…
1 file changed
+191
+191
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -989,5 +989,196 @@ | ||
| 989 | 989 | fossil_fatal("METHOD should be one of:" |
| 990 | 990 | " export import merge pull push reset"); |
| 991 | 991 | } |
| 992 | 992 | configure_rebuild(); |
| 993 | 993 | } |
| 994 | + | |
| 995 | + | |
| 996 | +/* | |
| 997 | +** COMMAND: test-var-list | |
| 998 | +** | |
| 999 | +** Usage: %fossil test-var-list ?PATTERN? ?--unset? ?--mtime? | |
| 1000 | +** | |
| 1001 | +** Show the content of the CONFIG table in a repository. If PATTERN is | |
| 1002 | +** specified, then only show the entries that match that glob pattern. | |
| 1003 | +** Last modification time is shown if the --mtime option is present. | |
| 1004 | +** | |
| 1005 | +** If the --unset option is included, then entries are deleted rather than | |
| 1006 | +** being displayed. WARNING! This cannot be undone. Be sure you know what | |
| 1007 | +** you are doing! The --unset option only works if there is a PATTERN. | |
| 1008 | +** Probably you should run the command once without --unset to make sure | |
| 1009 | +** you know exactly what is being deleted. | |
| 1010 | +** | |
| 1011 | +** If not in an open check-out, use the -R REPO option to specify a | |
| 1012 | +** a repository. | |
| 1013 | +*/ | |
| 1014 | +void test_var_list_cmd(void){ | |
| 1015 | + Stmt q; | |
| 1016 | + int i, j; | |
| 1017 | + const char *zPattern = 0; | |
| 1018 | + int doUnset; | |
| 1019 | + int showMtime; | |
| 1020 | + Blob sql; | |
| 1021 | + Blob ans; | |
| 1022 | + unsigned char zTrans[1000]; | |
| 1023 | + | |
| 1024 | + doUnset = find_option("unset",0,0)!=0; | |
| 1025 | + showMtime = find_option("mtime",0,0)!=0; | |
| 1026 | + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); | |
| 1027 | + verify_all_options(); | |
| 1028 | + if( g.argc>=3 ){ | |
| 1029 | + zPattern = g.argv[2]; | |
| 1030 | + } | |
| 1031 | + blob_init(&sql,0,0); | |
| 1032 | + blob_appendf(&sql, "SELECT name, value, datetime(mtime,'unixepoch')" | |
| 1033 | + " FROM config"); | |
| 1034 | + if( zPattern ){ | |
| 1035 | + blob_appendf(&sql, " WHERE name GLOB %Q", zPattern); | |
| 1036 | + } | |
| 1037 | + if( showMtime ){ | |
| 1038 | + blob_appendf(&sql, " ORDER BY mtime, name"); | |
| 1039 | + }else{ | |
| 1040 | + blob_appendf(&sql, " ORDER BY name"); | |
| 1041 | + } | |
| 1042 | + db_prepare(&q, "%s", blob_str(&sql)/*safe-for-%s*/); | |
| 1043 | + blob_reset(&sql); | |
| 1044 | +#define MX_VAL 40 | |
| 1045 | +#define MX_NM 28 | |
| 1046 | +#define MX_LONGNM 60 | |
| 1047 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1048 | + const char *zName = db_column_text(&q,0); | |
| 1049 | + int nName = db_column_bytes(&q,0); | |
| 1050 | + const char *zValue = db_column_text(&q,1); | |
| 1051 | + int szValue = db_column_bytes(&q,1); | |
| 1052 | + const char *zMTime = db_column_text(&q,2); | |
| 1053 | + for(i=j=0; j<MX_VAL && zValue[i]; i++){ | |
| 1054 | + unsigned char c = (unsigned char)zValue[i]; | |
| 1055 | + if( c>=' ' && c<='~' ){ | |
| 1056 | + zTrans[j++] = c; | |
| 1057 | + }else{ | |
| 1058 | + zTrans[j++] = '\\'; | |
| 1059 | + if( c=='\n' ){ | |
| 1060 | + zTrans[j++] = 'n'; | |
| 1061 | + }else if( c=='\r' ){ | |
| 1062 | + zTrans[j++] = 'r'; | |
| 1063 | + }else if( c=='\t' ){ | |
| 1064 | + zTrans[j++] = 't'; | |
| 1065 | + }else{ | |
| 1066 | + zTrans[j++] = '0' + ((c>>6)&7); | |
| 1067 | + zTrans[j++] = '0' + ((c>>3)&7); | |
| 1068 | + zTrans[j++] = '0' + (c&7); | |
| 1069 | + } | |
| 1070 | + } | |
| 1071 | + } | |
| 1072 | + zTrans[j] = 0; | |
| 1073 | + if( i<szValue ){ | |
| 1074 | + sqlite3_snprintf(sizeof(zTrans)-j, (char*)zTrans+j, "...+%d", szValue-i); | |
| 1075 | + j += (int)strlen((char*)zTrans+j); | |
| 1076 | + } | |
| 1077 | + if( showMtime ){ | |
| 1078 | + fossil_print("%s:%*s%s\n", zName, 58-nName, "", zMTime); | |
| 1079 | + }else if( nName<MX_NM-2 ){ | |
| 1080 | + fossil_print("%s:%*s%s\n", zName, MX_NM-1-nName, "", zTrans); | |
| 1081 | + }else if( nName<MX_LONGNM-2 && j<10 ){ | |
| 1082 | + fossil_print("%s:%*s%s\n", zName, MX_LONGNM-1-nName, "", zTrans); | |
| 1083 | + }else{ | |
| 1084 | + fossil_print("%s:\n%*s%s\n", zName, MX_NM, "", zTrans); | |
| 1085 | + } | |
| 1086 | + } | |
| 1087 | + db_finalize(&q); | |
| 1088 | + if( zPattern && doUnset ){ | |
| 1089 | + prompt_user("Delete all of the above? (y/N)? ", &ans); | |
| 1090 | + if( blob_str(&ans)[0]=='y' || blob_str(&ans)[0]=='Y' ){ | |
| 1091 | + db_multi_exec("DELETE FROM config WHERE name GLOB %Q", zPattern); | |
| 1092 | + } | |
| 1093 | + blob_reset(&ans); | |
| 1094 | + } | |
| 1095 | +} | |
| 1096 | + | |
| 1097 | +/* | |
| 1098 | +** COMMAND: test-var-get | |
| 1099 | +** | |
| 1100 | +** Usage: %fossil test-var-get VAR ?FILE? | |
| 1101 | +** | |
| 1102 | +** Write the text of the VAR variable into FILE. If FILE is "-" | |
| 1103 | +** or is omitted then output goes to standard output. VAR can be a | |
| 1104 | +** GLOB pattern. | |
| 1105 | +** | |
| 1106 | +** If not in an open check-out, use the -R REPO option to specify a | |
| 1107 | +** a repository. | |
| 1108 | +*/ | |
| 1109 | +void test_var_get_cmd(void){ | |
| 1110 | + const char *zVar; | |
| 1111 | + const char *zFile; | |
| 1112 | + int n; | |
| 1113 | + Blob x; | |
| 1114 | + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); | |
| 1115 | + verify_all_options(); | |
| 1116 | + if( g.argc<3 ){ | |
| 1117 | + usage("VAR ?FILE?"); | |
| 1118 | + } | |
| 1119 | + zVar = g.argv[2]; | |
| 1120 | + zFile = g.argc>=4 ? g.argv[3] : "-"; | |
| 1121 | + n = db_int(0, "SELECT count(*) FROM config WHERE name GLOB %Q", zVar); | |
| 1122 | + if( n==0 ){ | |
| 1123 | + fossil_fatal("no match for %Q", zVar); | |
| 1124 | + } | |
| 1125 | + if( n>1 ){ | |
| 1126 | + fossil_fatal("multiple matches: %s", | |
| 1127 | + db_text(0, "SELECT group_concat(quote(name),', ') FROM (" | |
| 1128 | + " SELECT name FROM config WHERE name GLOB %Q ORDER BY 1)", | |
| 1129 | + zVar)); | |
| 1130 | + } | |
| 1131 | + blob_init(&x,0,0); | |
| 1132 | + db_blob(&x, "SELECT value FROM config WHERE name GLOB %Q", zVar); | |
| 1133 | + blob_write_to_file(&x, zFile); | |
| 1134 | +} | |
| 1135 | + | |
| 1136 | +/* | |
| 1137 | +** COMMAND: test-var-set | |
| 1138 | +** | |
| 1139 | +** Usage: %fossil test-var-set VAR ?VALUE? ?--file FILE? | |
| 1140 | +** | |
| 1141 | +** Store VALUE or the content of FILE (exactly one of which must be | |
| 1142 | +** supplied) into variable VAR. Use a FILE of "-" to read from | |
| 1143 | +** standard input. | |
| 1144 | +** | |
| 1145 | +** WARNING: changing the value of a variable can interfere with the | |
| 1146 | +** operation of Fossil. Be sure you know what you are doing. | |
| 1147 | +** | |
| 1148 | +** Use "--blob FILE" instead of "--file FILE" to load a binary blob | |
| 1149 | +** such as a GIF. | |
| 1150 | +*/ | |
| 1151 | +void test_var_set_cmd(void){ | |
| 1152 | + const char *zVar; | |
| 1153 | + const char *zFile; | |
| 1154 | + const char *zBlob; | |
| 1155 | + Blob x; | |
| 1156 | + Stmt ins; | |
| 1157 | + zFile = find_option("file",0,1); | |
| 1158 | + zBlob = find_option("blob",0,1); | |
| 1159 | + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); | |
| 1160 | + verify_all_options(); | |
| 1161 | + if( g.argc<3 || (zFile==0 && zBlob==0 && g.argc<4) ){ | |
| 1162 | + usage("VAR ?VALUE? ?--file FILE?"); | |
| 1163 | + } | |
| 1164 | + zVar = g.argv[2]; | |
| 1165 | + if( zFile ){ | |
| 1166 | + if( zBlob ) fossil_fatal("cannot do both --file or --blob"); | |
| 1167 | + blob_read_from_file(&x, zFile); | |
| 1168 | + }else if( zBlob ){ | |
| 1169 | + blob_read_from_file(&x, zBlob); | |
| 1170 | + }else{ | |
| 1171 | + blob_init(&x,g.argv[3],-1); | |
| 1172 | + } | |
| 1173 | + db_prepare(&ins, | |
| 1174 | + "REPLACE INTO config(name,value,mtime)" | |
| 1175 | + "VALUES(%Q,:val,now())", zVar); | |
| 1176 | + if( zBlob ){ | |
| 1177 | + db_bind_blob(&ins, ":val", &x); | |
| 1178 | + }else{ | |
| 1179 | + db_bind_text(&ins, ":val", blob_str(&x)); | |
| 1180 | + } | |
| 1181 | + db_step(&ins); | |
| 1182 | + db_finalize(&ins); | |
| 1183 | + blob_reset(&x); | |
| 1184 | +} | |
| 994 | 1185 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -989,5 +989,196 @@ | |
| 989 | fossil_fatal("METHOD should be one of:" |
| 990 | " export import merge pull push reset"); |
| 991 | } |
| 992 | configure_rebuild(); |
| 993 | } |
| 994 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -989,5 +989,196 @@ | |
| 989 | fossil_fatal("METHOD should be one of:" |
| 990 | " export import merge pull push reset"); |
| 991 | } |
| 992 | configure_rebuild(); |
| 993 | } |
| 994 | |
| 995 | |
| 996 | /* |
| 997 | ** COMMAND: test-var-list |
| 998 | ** |
| 999 | ** Usage: %fossil test-var-list ?PATTERN? ?--unset? ?--mtime? |
| 1000 | ** |
| 1001 | ** Show the content of the CONFIG table in a repository. If PATTERN is |
| 1002 | ** specified, then only show the entries that match that glob pattern. |
| 1003 | ** Last modification time is shown if the --mtime option is present. |
| 1004 | ** |
| 1005 | ** If the --unset option is included, then entries are deleted rather than |
| 1006 | ** being displayed. WARNING! This cannot be undone. Be sure you know what |
| 1007 | ** you are doing! The --unset option only works if there is a PATTERN. |
| 1008 | ** Probably you should run the command once without --unset to make sure |
| 1009 | ** you know exactly what is being deleted. |
| 1010 | ** |
| 1011 | ** If not in an open check-out, use the -R REPO option to specify a |
| 1012 | ** a repository. |
| 1013 | */ |
| 1014 | void test_var_list_cmd(void){ |
| 1015 | Stmt q; |
| 1016 | int i, j; |
| 1017 | const char *zPattern = 0; |
| 1018 | int doUnset; |
| 1019 | int showMtime; |
| 1020 | Blob sql; |
| 1021 | Blob ans; |
| 1022 | unsigned char zTrans[1000]; |
| 1023 | |
| 1024 | doUnset = find_option("unset",0,0)!=0; |
| 1025 | showMtime = find_option("mtime",0,0)!=0; |
| 1026 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1027 | verify_all_options(); |
| 1028 | if( g.argc>=3 ){ |
| 1029 | zPattern = g.argv[2]; |
| 1030 | } |
| 1031 | blob_init(&sql,0,0); |
| 1032 | blob_appendf(&sql, "SELECT name, value, datetime(mtime,'unixepoch')" |
| 1033 | " FROM config"); |
| 1034 | if( zPattern ){ |
| 1035 | blob_appendf(&sql, " WHERE name GLOB %Q", zPattern); |
| 1036 | } |
| 1037 | if( showMtime ){ |
| 1038 | blob_appendf(&sql, " ORDER BY mtime, name"); |
| 1039 | }else{ |
| 1040 | blob_appendf(&sql, " ORDER BY name"); |
| 1041 | } |
| 1042 | db_prepare(&q, "%s", blob_str(&sql)/*safe-for-%s*/); |
| 1043 | blob_reset(&sql); |
| 1044 | #define MX_VAL 40 |
| 1045 | #define MX_NM 28 |
| 1046 | #define MX_LONGNM 60 |
| 1047 | while( db_step(&q)==SQLITE_ROW ){ |
| 1048 | const char *zName = db_column_text(&q,0); |
| 1049 | int nName = db_column_bytes(&q,0); |
| 1050 | const char *zValue = db_column_text(&q,1); |
| 1051 | int szValue = db_column_bytes(&q,1); |
| 1052 | const char *zMTime = db_column_text(&q,2); |
| 1053 | for(i=j=0; j<MX_VAL && zValue[i]; i++){ |
| 1054 | unsigned char c = (unsigned char)zValue[i]; |
| 1055 | if( c>=' ' && c<='~' ){ |
| 1056 | zTrans[j++] = c; |
| 1057 | }else{ |
| 1058 | zTrans[j++] = '\\'; |
| 1059 | if( c=='\n' ){ |
| 1060 | zTrans[j++] = 'n'; |
| 1061 | }else if( c=='\r' ){ |
| 1062 | zTrans[j++] = 'r'; |
| 1063 | }else if( c=='\t' ){ |
| 1064 | zTrans[j++] = 't'; |
| 1065 | }else{ |
| 1066 | zTrans[j++] = '0' + ((c>>6)&7); |
| 1067 | zTrans[j++] = '0' + ((c>>3)&7); |
| 1068 | zTrans[j++] = '0' + (c&7); |
| 1069 | } |
| 1070 | } |
| 1071 | } |
| 1072 | zTrans[j] = 0; |
| 1073 | if( i<szValue ){ |
| 1074 | sqlite3_snprintf(sizeof(zTrans)-j, (char*)zTrans+j, "...+%d", szValue-i); |
| 1075 | j += (int)strlen((char*)zTrans+j); |
| 1076 | } |
| 1077 | if( showMtime ){ |
| 1078 | fossil_print("%s:%*s%s\n", zName, 58-nName, "", zMTime); |
| 1079 | }else if( nName<MX_NM-2 ){ |
| 1080 | fossil_print("%s:%*s%s\n", zName, MX_NM-1-nName, "", zTrans); |
| 1081 | }else if( nName<MX_LONGNM-2 && j<10 ){ |
| 1082 | fossil_print("%s:%*s%s\n", zName, MX_LONGNM-1-nName, "", zTrans); |
| 1083 | }else{ |
| 1084 | fossil_print("%s:\n%*s%s\n", zName, MX_NM, "", zTrans); |
| 1085 | } |
| 1086 | } |
| 1087 | db_finalize(&q); |
| 1088 | if( zPattern && doUnset ){ |
| 1089 | prompt_user("Delete all of the above? (y/N)? ", &ans); |
| 1090 | if( blob_str(&ans)[0]=='y' || blob_str(&ans)[0]=='Y' ){ |
| 1091 | db_multi_exec("DELETE FROM config WHERE name GLOB %Q", zPattern); |
| 1092 | } |
| 1093 | blob_reset(&ans); |
| 1094 | } |
| 1095 | } |
| 1096 | |
| 1097 | /* |
| 1098 | ** COMMAND: test-var-get |
| 1099 | ** |
| 1100 | ** Usage: %fossil test-var-get VAR ?FILE? |
| 1101 | ** |
| 1102 | ** Write the text of the VAR variable into FILE. If FILE is "-" |
| 1103 | ** or is omitted then output goes to standard output. VAR can be a |
| 1104 | ** GLOB pattern. |
| 1105 | ** |
| 1106 | ** If not in an open check-out, use the -R REPO option to specify a |
| 1107 | ** a repository. |
| 1108 | */ |
| 1109 | void test_var_get_cmd(void){ |
| 1110 | const char *zVar; |
| 1111 | const char *zFile; |
| 1112 | int n; |
| 1113 | Blob x; |
| 1114 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1115 | verify_all_options(); |
| 1116 | if( g.argc<3 ){ |
| 1117 | usage("VAR ?FILE?"); |
| 1118 | } |
| 1119 | zVar = g.argv[2]; |
| 1120 | zFile = g.argc>=4 ? g.argv[3] : "-"; |
| 1121 | n = db_int(0, "SELECT count(*) FROM config WHERE name GLOB %Q", zVar); |
| 1122 | if( n==0 ){ |
| 1123 | fossil_fatal("no match for %Q", zVar); |
| 1124 | } |
| 1125 | if( n>1 ){ |
| 1126 | fossil_fatal("multiple matches: %s", |
| 1127 | db_text(0, "SELECT group_concat(quote(name),', ') FROM (" |
| 1128 | " SELECT name FROM config WHERE name GLOB %Q ORDER BY 1)", |
| 1129 | zVar)); |
| 1130 | } |
| 1131 | blob_init(&x,0,0); |
| 1132 | db_blob(&x, "SELECT value FROM config WHERE name GLOB %Q", zVar); |
| 1133 | blob_write_to_file(&x, zFile); |
| 1134 | } |
| 1135 | |
| 1136 | /* |
| 1137 | ** COMMAND: test-var-set |
| 1138 | ** |
| 1139 | ** Usage: %fossil test-var-set VAR ?VALUE? ?--file FILE? |
| 1140 | ** |
| 1141 | ** Store VALUE or the content of FILE (exactly one of which must be |
| 1142 | ** supplied) into variable VAR. Use a FILE of "-" to read from |
| 1143 | ** standard input. |
| 1144 | ** |
| 1145 | ** WARNING: changing the value of a variable can interfere with the |
| 1146 | ** operation of Fossil. Be sure you know what you are doing. |
| 1147 | ** |
| 1148 | ** Use "--blob FILE" instead of "--file FILE" to load a binary blob |
| 1149 | ** such as a GIF. |
| 1150 | */ |
| 1151 | void test_var_set_cmd(void){ |
| 1152 | const char *zVar; |
| 1153 | const char *zFile; |
| 1154 | const char *zBlob; |
| 1155 | Blob x; |
| 1156 | Stmt ins; |
| 1157 | zFile = find_option("file",0,1); |
| 1158 | zBlob = find_option("blob",0,1); |
| 1159 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1160 | verify_all_options(); |
| 1161 | if( g.argc<3 || (zFile==0 && zBlob==0 && g.argc<4) ){ |
| 1162 | usage("VAR ?VALUE? ?--file FILE?"); |
| 1163 | } |
| 1164 | zVar = g.argv[2]; |
| 1165 | if( zFile ){ |
| 1166 | if( zBlob ) fossil_fatal("cannot do both --file or --blob"); |
| 1167 | blob_read_from_file(&x, zFile); |
| 1168 | }else if( zBlob ){ |
| 1169 | blob_read_from_file(&x, zBlob); |
| 1170 | }else{ |
| 1171 | blob_init(&x,g.argv[3],-1); |
| 1172 | } |
| 1173 | db_prepare(&ins, |
| 1174 | "REPLACE INTO config(name,value,mtime)" |
| 1175 | "VALUES(%Q,:val,now())", zVar); |
| 1176 | if( zBlob ){ |
| 1177 | db_bind_blob(&ins, ":val", &x); |
| 1178 | }else{ |
| 1179 | db_bind_text(&ins, ":val", blob_str(&x)); |
| 1180 | } |
| 1181 | db_step(&ins); |
| 1182 | db_finalize(&ins); |
| 1183 | blob_reset(&x); |
| 1184 | } |
| 1185 |