2e9d52f27f 2007-10-02 drh: /* 2e9d52f27f 2007-10-02 drh: ** Copyright (c) 2007 D. Richard Hipp 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** This program is free software; you can redistribute it and/or 2e9d52f27f 2007-10-02 drh: ** modify it under the terms of the GNU General Public 2e9d52f27f 2007-10-02 drh: ** License version 2 as published by the Free Software Foundation. 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** This program is distributed in the hope that it will be useful, 2e9d52f27f 2007-10-02 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of 2e9d52f27f 2007-10-02 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 2e9d52f27f 2007-10-02 drh: ** General Public License for more details. 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** You should have received a copy of the GNU General Public 2e9d52f27f 2007-10-02 drh: ** License along with this library; if not, write to the 2e9d52f27f 2007-10-02 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, 2e9d52f27f 2007-10-02 drh: ** Boston, MA 02111-1307, USA. 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** Author contact information: 2e9d52f27f 2007-10-02 drh: ** drh@hwaci.com 2e9d52f27f 2007-10-02 drh: ** http://www.hwaci.com/drh/ 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ******************************************************************************* 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** This file contains code used to parser ticket configuration 2e9d52f27f 2007-10-02 drh: ** artificates. 2e9d52f27f 2007-10-02 drh: */ 2e9d52f27f 2007-10-02 drh: #include "config.h" 2e9d52f27f 2007-10-02 drh: #include "tktconf.h" 2e9d52f27f 2007-10-02 drh: #include <assert.h> 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: /* 2e9d52f27f 2007-10-02 drh: ** Return TRUE if the given token is a valid field name for 2e9d52f27f 2007-10-02 drh: ** the ticket table. The name must be all letters, digits, 2e9d52f27f 2007-10-02 drh: ** and underscores. 2e9d52f27f 2007-10-02 drh: */ 2e9d52f27f 2007-10-02 drh: static int is_valid_name(Blob *p){ 2e9d52f27f 2007-10-02 drh: const char *z = blob_buffer(p); 2e9d52f27f 2007-10-02 drh: int n = blob_size(p); 2e9d52f27f 2007-10-02 drh: int i; 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: for(i=0; i<n; i++){ 2e9d52f27f 2007-10-02 drh: if( !isalnum(z[i]) && z[i]!='_' ){ 2e9d52f27f 2007-10-02 drh: return 0; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: return 1; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: /* 2e9d52f27f 2007-10-02 drh: ** Return TRUE if the given token is a valid enumeration value. 2e9d52f27f 2007-10-02 drh: ** The token must consist of the following characters: 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** a-zA-Z0-9_%/.- 2e9d52f27f 2007-10-02 drh: */ 2e9d52f27f 2007-10-02 drh: static int is_valid_enum(Blob *p){ 2e9d52f27f 2007-10-02 drh: const char *z = blob_buffer(p); 2e9d52f27f 2007-10-02 drh: int n = blob_size(p); 2e9d52f27f 2007-10-02 drh: int i; 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: for(i=0; i<n; i++){ 2e9d52f27f 2007-10-02 drh: int c = z[i]; 2e9d52f27f 2007-10-02 drh: if( !isalnum(c) && c!='_' && c!='%' && c!='/' && c!='.' && c!='-' ){ 2e9d52f27f 2007-10-02 drh: return 0; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: return 1; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: /* 2e9d52f27f 2007-10-02 drh: ** A ticket configuration record is a single artifact that defines 2e9d52f27f 2007-10-02 drh: ** the ticket configuration for a server. The file format is as 2e9d52f27f 2007-10-02 drh: ** follows: 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** ticket-configuration 2e9d52f27f 2007-10-02 drh: ** field <fieldname> <fieldtype> <width> <param> ... 2e9d52f27f 2007-10-02 drh: ** template <type> <delimiter> 2e9d52f27f 2007-10-02 drh: ** <text> 0edee97370 2007-10-03 drh: ** description <delimiter> 0edee97370 2007-10-03 drh: ** <text> 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** All lines are separated by \n. Trailing whitespace is 2e9d52f27f 2007-10-02 drh: ** ignored. The first line must be "ticket-configuration". 2e9d52f27f 2007-10-02 drh: ** Subsequent lines are either "field" or "template" lines. 2e9d52f27f 2007-10-02 drh: ** There must be exactly three template lines and one or more 2e9d52f27f 2007-10-02 drh: ** field lines (usually more). 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** Field lines define the fields of the "ticket" table in the 2e9d52f27f 2007-10-02 drh: ** database. The fields appear in the table in the order in 2e9d52f27f 2007-10-02 drh: ** which they appear in the configuration artifact. The <fieldname> 2e9d52f27f 2007-10-02 drh: ** must consist of alphanumerics and underscores. <fieldtype> 0edee97370 2007-10-03 drh: ** is one of: text, ctext, enum, date, uuid, baseline, private. All 2e9d52f27f 2007-10-02 drh: ** types have at least a <width> parameter. Text and Ctext types 2e9d52f27f 2007-10-02 drh: ** have a height parameter. Enum has a list of allowed values. 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** The <type> of a template is one of: new, view, edit. There must 2e9d52f27f 2007-10-02 drh: ** be one template of each type. <delimiter> is an arbitrary string 2e9d52f27f 2007-10-02 drh: ** that terminates the template. The body of the template is subsequent 2e9d52f27f 2007-10-02 drh: ** lines of text up to but not including the <delimiter>. Trailing 2e9d52f27f 2007-10-02 drh: ** whitespace on the delimiter is ignored. 0edee97370 2007-10-03 drh: ** 0edee97370 2007-10-03 drh: ** There should be one description entry. The text that follows 0edee97370 2007-10-03 drh: ** is a human-readable plaintext description of this ticket 0edee97370 2007-10-03 drh: ** configuration. The description is visible to the administrator 0edee97370 2007-10-03 drh: ** and is used to help identify this configuration among several 0edee97370 2007-10-03 drh: ** options. The first line of the description is a one-line 0edee97370 2007-10-03 drh: ** summary. Subsequent lines are details. 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** The pConfig parameter is the complete text of the configuration 2e9d52f27f 2007-10-02 drh: ** file to be parsed. testFlag is 1 to cause the results to be printed 2e9d52f27f 2007-10-02 drh: ** on stdout or 0 to cause results to update the database. 2e9d52f27f 2007-10-02 drh: ** 2e9d52f27f 2007-10-02 drh: ** Return the number of errors. If there is an error and pErr!=NULL 2e9d52f27f 2007-10-02 drh: ** then leave an error message in pErr. We assume that pErr has already 2e9d52f27f 2007-10-02 drh: ** been initialized. 2e9d52f27f 2007-10-02 drh: */ 2e9d52f27f 2007-10-02 drh: int ticket_config_parse(Blob *pConfig, int testFlag, Blob *pErr){ 2e9d52f27f 2007-10-02 drh: int rc = 1; 2e9d52f27f 2007-10-02 drh: Blob line; 2e9d52f27f 2007-10-02 drh: Blob token; 2e9d52f27f 2007-10-02 drh: Blob name; 2e9d52f27f 2007-10-02 drh: Blob type; 2e9d52f27f 2007-10-02 drh: Blob arg; 2e9d52f27f 2007-10-02 drh: Blob sql; 2e9d52f27f 2007-10-02 drh: Blob tbldef; 2e9d52f27f 2007-10-02 drh: Blob err; 2e9d52f27f 2007-10-02 drh: int seen_template = 0; 2e9d52f27f 2007-10-02 drh: int lineno = 0; 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: blob_zero(&sql); 2e9d52f27f 2007-10-02 drh: blob_zero(&tbldef); 2e9d52f27f 2007-10-02 drh: blob_zero(&token); 2e9d52f27f 2007-10-02 drh: blob_zero(&name); 2e9d52f27f 2007-10-02 drh: blob_zero(&type); 2e9d52f27f 2007-10-02 drh: blob_zero(&arg); 2e9d52f27f 2007-10-02 drh: blob_zero(&err); 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: /* The configuration file must begin with a line that 2e9d52f27f 2007-10-02 drh: ** says "ticket-configuration" 2e9d52f27f 2007-10-02 drh: */ 2e9d52f27f 2007-10-02 drh: blob_line(pConfig, &line); 2e9d52f27f 2007-10-02 drh: blob_token(&line, &token); 2e9d52f27f 2007-10-02 drh: if( !blob_eq(&token, "ticket-configuration") ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "missing initialization keyword"); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: lineno++; 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: /* Begin accumulating SQL text that will implement the 2e9d52f27f 2007-10-02 drh: ** ticket configuration. tbldef will hold the ticket table 2e9d52f27f 2007-10-02 drh: ** definition. sql will hold text to initialize and define 2e9d52f27f 2007-10-02 drh: ** the tktfield table and to insert template text into the 2e9d52f27f 2007-10-02 drh: ** config table 2e9d52f27f 2007-10-02 drh: */ 2e9d52f27f 2007-10-02 drh: blob_appendf(&tbldef, 2e9d52f27f 2007-10-02 drh: "DROP TABLE IF EXISTS ticket;\n" 1e9c0e287e 2007-10-03 drh: "CREATE TABLE repository.ticket(\n" 2e9d52f27f 2007-10-02 drh: " tktid INTEGER PRIMARY KEY,\n" 2e9d52f27f 2007-10-02 drh: " tktuuid TEXT UNIQUE,\n" 2e9d52f27f 2007-10-02 drh: " starttime DATETIME,\n" 2e9d52f27f 2007-10-02 drh: " lastmod DATETIME" 2e9d52f27f 2007-10-02 drh: ); 2e9d52f27f 2007-10-02 drh: blob_appendf(&sql, 2e9d52f27f 2007-10-02 drh: "DROP TABLE IF EXISTS tktfield;\n" 1e9c0e287e 2007-10-03 drh: "CREATE TABLE repository.tktfield(\n" 2e9d52f27f 2007-10-02 drh: " fidx INTEGER PRIMARY KEY,\n" 2e9d52f27f 2007-10-02 drh: " name TEXT UNIQUE,\n" 2e9d52f27f 2007-10-02 drh: " type TEXT,\n" 2e9d52f27f 2007-10-02 drh: " width INTEGER,\n" 2e9d52f27f 2007-10-02 drh: " arg\n" 2e9d52f27f 2007-10-02 drh: ");\n" 2e9d52f27f 2007-10-02 drh: ); 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: /* Process the remainder of the configuration file (the part that 2e9d52f27f 2007-10-02 drh: ** comes after the "ticket-configuration" header) line by line 2e9d52f27f 2007-10-02 drh: */ 2e9d52f27f 2007-10-02 drh: while( blob_line(pConfig, &line) ){ 2e9d52f27f 2007-10-02 drh: char *z; 2e9d52f27f 2007-10-02 drh: lineno++; 2e9d52f27f 2007-10-02 drh: if( blob_token(&line, &token)==0 ){ 2e9d52f27f 2007-10-02 drh: /* Ignore blank lines */ 2e9d52f27f 2007-10-02 drh: continue; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: z = blob_buffer(&token); 2e9d52f27f 2007-10-02 drh: if( z[0]=='#' ){ 2e9d52f27f 2007-10-02 drh: /* Ignore comment lines */ 2e9d52f27f 2007-10-02 drh: continue; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: /* 2e9d52f27f 2007-10-02 drh: ** field <name> <type> <width> <args...> 2e9d52f27f 2007-10-02 drh: */ 2e9d52f27f 2007-10-02 drh: if( blob_eq(&token, "field") 2e9d52f27f 2007-10-02 drh: && blob_token(&line,&name) 2e9d52f27f 2007-10-02 drh: && blob_token(&line,&type) 2e9d52f27f 2007-10-02 drh: && blob_token(&line,&arg) 2e9d52f27f 2007-10-02 drh: ){ 2e9d52f27f 2007-10-02 drh: int width; 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: if( !is_valid_name(&name) ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "invalid field name: %b", &name); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: if( !blob_is_int(&arg, &width) ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "invalid field width: %b", &arg); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: if( width<1 || width>200 ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "width less than 1 or greater than 200"); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: blob_appendf(&tbldef, ",\n tkt_%b", &name); 0edee97370 2007-10-03 drh: if( blob_eq(&type,"text") || blob_eq(&type,"ctext") 0edee97370 2007-10-03 drh: || blob_eq(&type,"private") ){ 2e9d52f27f 2007-10-02 drh: int height; 2e9d52f27f 2007-10-02 drh: if( !blob_token(&line, &arg) || !blob_is_int(&arg, &height) ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "invalid height: %b", &arg); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: if( height<1 || height>1000 ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "height less than 1 or greater than 1000"); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: blob_appendf(&sql, 2e9d52f27f 2007-10-02 drh: "INSERT INTO tktfield(name,type,width,arg)" 2e9d52f27f 2007-10-02 drh: "VALUES('%b','%b',%d,%d);\n", 2e9d52f27f 2007-10-02 drh: &name, &type, width, height 2e9d52f27f 2007-10-02 drh: ); 2e9d52f27f 2007-10-02 drh: }else if( blob_eq(&type,"enum") ){ 2e9d52f27f 2007-10-02 drh: int cnt = 0; 2e9d52f27f 2007-10-02 drh: const char *zDelim = "'"; 2e9d52f27f 2007-10-02 drh: blob_appendf(&sql, 2e9d52f27f 2007-10-02 drh: "INSERT INTO tktfield(name,type,width,arg)" 2e9d52f27f 2007-10-02 drh: "VALUES('%b','%b',%d,", 2e9d52f27f 2007-10-02 drh: &name, &type, width 2e9d52f27f 2007-10-02 drh: ); 2e9d52f27f 2007-10-02 drh: while( blob_token(&line, &arg) ){ 2e9d52f27f 2007-10-02 drh: if( !is_valid_enum(&arg) ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "invalid enumeration value: %b", &arg); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: cnt++; 2e9d52f27f 2007-10-02 drh: blob_appendf(&sql, "%s%b", zDelim, &arg); 2e9d52f27f 2007-10-02 drh: zDelim = " "; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: if( cnt<2 ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "less than 2 enumeration values"); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: blob_appendf(&sql,"');\n"); 2e9d52f27f 2007-10-02 drh: }else if( blob_eq(&type,"uuid") || blob_eq(&type,"baseline") || 2e9d52f27f 2007-10-02 drh: blob_eq(&type,"date") ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&sql, 2e9d52f27f 2007-10-02 drh: "INSERT INTO tktfield(name,type,width)" 2e9d52f27f 2007-10-02 drh: "VALUES('%b','%b',%d);\n", 2e9d52f27f 2007-10-02 drh: &name, &type, width 2e9d52f27f 2007-10-02 drh: ); 2e9d52f27f 2007-10-02 drh: }else{ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "unknown field type: %b", &type); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 0edee97370 2007-10-03 drh: }else 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: /* 2e9d52f27f 2007-10-02 drh: ** template <type> <delimiter> 2e9d52f27f 2007-10-02 drh: ** <text> 2e9d52f27f 2007-10-02 drh: */ 0edee97370 2007-10-03 drh: if( blob_eq(&token, "template") 0edee97370 2007-10-03 drh: && blob_token(&line, &type) 0edee97370 2007-10-03 drh: && blob_token(&line, &arg) 2e9d52f27f 2007-10-02 drh: ){ 2e9d52f27f 2007-10-02 drh: int idx; 2e9d52f27f 2007-10-02 drh: Blob content; 2e9d52f27f 2007-10-02 drh: int start; 2e9d52f27f 2007-10-02 drh: int end; 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: if( blob_eq(&type,"new") ){ 2e9d52f27f 2007-10-02 drh: idx = 0; 2e9d52f27f 2007-10-02 drh: }else if( blob_eq(&type, "view") ){ 2e9d52f27f 2007-10-02 drh: idx = 1; 2e9d52f27f 2007-10-02 drh: }else if( blob_eq(&type, "edit") ){ 2e9d52f27f 2007-10-02 drh: idx = 2; 2e9d52f27f 2007-10-02 drh: }else{ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "unknown template type: %b", &type); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: if( (seen_template & (1<<idx))!=0 ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "more than one %b template", &type); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: seen_template |= 1<<idx; 2e9d52f27f 2007-10-02 drh: start = end = blob_tell(pConfig); 2e9d52f27f 2007-10-02 drh: while( blob_line(pConfig, &line) ){ 2e9d52f27f 2007-10-02 drh: blob_token(&line, &token); 2e9d52f27f 2007-10-02 drh: if( blob_compare(&token, &arg)==0 ) break; 2e9d52f27f 2007-10-02 drh: end = blob_tell(pConfig); 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: blob_init(&content, &blob_buffer(pConfig)[start], end - start); 2e9d52f27f 2007-10-02 drh: blob_appendf(&sql, 2e9d52f27f 2007-10-02 drh: "REPLACE INTO config(name, value) VALUES('tkt-%b-template',%B);\n", 2e9d52f27f 2007-10-02 drh: &type, &content 2e9d52f27f 2007-10-02 drh: ); 2e9d52f27f 2007-10-02 drh: blob_reset(&content); 0edee97370 2007-10-03 drh: }else 0edee97370 2007-10-03 drh: 0edee97370 2007-10-03 drh: /* 0edee97370 2007-10-03 drh: ** description <delimiter> 0edee97370 2007-10-03 drh: ** <text> 0edee97370 2007-10-03 drh: */ 0edee97370 2007-10-03 drh: if( blob_eq(&token, "description") 0edee97370 2007-10-03 drh: && blob_token(&line, &arg) 0edee97370 2007-10-03 drh: ){ 0edee97370 2007-10-03 drh: Blob content; 0edee97370 2007-10-03 drh: int start; 0edee97370 2007-10-03 drh: int end; 0edee97370 2007-10-03 drh: 0edee97370 2007-10-03 drh: start = end = blob_tell(pConfig); 0edee97370 2007-10-03 drh: while( blob_line(pConfig, &line) ){ 0edee97370 2007-10-03 drh: blob_token(&line, &token); 0edee97370 2007-10-03 drh: if( blob_compare(&token, &arg)==0 ) break; 0edee97370 2007-10-03 drh: end = blob_tell(pConfig); 0edee97370 2007-10-03 drh: } 0edee97370 2007-10-03 drh: blob_init(&content, &blob_buffer(pConfig)[start], end - start); 0edee97370 2007-10-03 drh: blob_appendf(&sql, 0edee97370 2007-10-03 drh: "REPLACE INTO config(name, value) VALUES('tkt-desc',%B);\n", 0edee97370 2007-10-03 drh: &content 0edee97370 2007-10-03 drh: ); 0edee97370 2007-10-03 drh: blob_reset(&content); 0edee97370 2007-10-03 drh: }else 0edee97370 2007-10-03 drh: 0edee97370 2007-10-03 drh: { 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "unknown command: %b", &token); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: if( seen_template != 0x7 ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(&err, "missing templates"); 2e9d52f27f 2007-10-02 drh: goto bad_config_file; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: /* If we reach this point it means the parse was successful 2e9d52f27f 2007-10-02 drh: */ 2e9d52f27f 2007-10-02 drh: rc = 0; 2e9d52f27f 2007-10-02 drh: blob_appendf(&tbldef, "\n);\n"); 2e9d52f27f 2007-10-02 drh: if( testFlag ){ 2e9d52f27f 2007-10-02 drh: blob_write_to_file(&tbldef, "-"); 2e9d52f27f 2007-10-02 drh: blob_write_to_file(&sql, "-"); 2e9d52f27f 2007-10-02 drh: }else{ 2e9d52f27f 2007-10-02 drh: db_multi_exec("%b", &tbldef); 2e9d52f27f 2007-10-02 drh: db_multi_exec("%b", &sql); 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: bad_config_file: 2e9d52f27f 2007-10-02 drh: if( rc && pErr ){ 2e9d52f27f 2007-10-02 drh: blob_appendf(pErr, "line %d: %b", lineno, &err); 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: blob_reset(&token); 2e9d52f27f 2007-10-02 drh: blob_reset(&line); 2e9d52f27f 2007-10-02 drh: blob_reset(&name); 2e9d52f27f 2007-10-02 drh: blob_reset(&type); 2e9d52f27f 2007-10-02 drh: blob_reset(&arg); 2e9d52f27f 2007-10-02 drh: blob_reset(&sql); 2e9d52f27f 2007-10-02 drh: blob_reset(&tbldef); 2e9d52f27f 2007-10-02 drh: blob_reset(&err); 2e9d52f27f 2007-10-02 drh: return rc; 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: 2e9d52f27f 2007-10-02 drh: /* 2e9d52f27f 2007-10-02 drh: ** COMMAND: test-tktconfig-parse 2e9d52f27f 2007-10-02 drh: */ 1e9c0e287e 2007-10-03 drh: void test_tktconfig_parse_cmd(void){ 2e9d52f27f 2007-10-02 drh: Blob config, err; 2e9d52f27f 2007-10-02 drh: if( g.argc!=3 ){ 2e9d52f27f 2007-10-02 drh: usage("FILENAME"); 2e9d52f27f 2007-10-02 drh: } 2e9d52f27f 2007-10-02 drh: blob_read_from_file(&config, g.argv[2]); 2e9d52f27f 2007-10-02 drh: blob_zero(&err); 2e9d52f27f 2007-10-02 drh: ticket_config_parse(&config, 1, &err); 2e9d52f27f 2007-10-02 drh: if( blob_size(&err) ){ 2e9d52f27f 2007-10-02 drh: blob_write_to_file(&err, "-"); 2e9d52f27f 2007-10-02 drh: } 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: /* 1e9c0e287e 2007-10-03 drh: ** COMMAND: test-tktconfig-import 1e9c0e287e 2007-10-03 drh: */ 1e9c0e287e 2007-10-03 drh: void test_tktconfig_import_cmd(void){ 1e9c0e287e 2007-10-03 drh: Blob config, err; 1e9c0e287e 2007-10-03 drh: db_must_be_within_tree(); 1e9c0e287e 2007-10-03 drh: if( g.argc!=3 ){ 1e9c0e287e 2007-10-03 drh: usage("FILENAME"); 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: blob_read_from_file(&config, g.argv[2]); 1e9c0e287e 2007-10-03 drh: blob_zero(&err); 1e9c0e287e 2007-10-03 drh: db_begin_transaction(); 1e9c0e287e 2007-10-03 drh: ticket_config_parse(&config, 0, &err); 1e9c0e287e 2007-10-03 drh: db_end_transaction(0); 0edee97370 2007-10-03 drh: if( blob_size(&err) ){ 0edee97370 2007-10-03 drh: blob_write_to_file(&err, "-"); 0edee97370 2007-10-03 drh: } 0edee97370 2007-10-03 drh: } 0edee97370 2007-10-03 drh: 0edee97370 2007-10-03 drh: /* 0edee97370 2007-10-03 drh: ** Load the default ticket configuration. 0edee97370 2007-10-03 drh: */ 0edee97370 2007-10-03 drh: void ticket_load_default_config(void){ 0edee97370 2007-10-03 drh: static const char zDefaultConfig[] = 0edee97370 2007-10-03 drh: @ ticket-configuration 0edee97370 2007-10-03 drh: @ description END-OF-DESCRIPTION 0edee97370 2007-10-03 drh: @ Default Ticket Configuration 0edee97370 2007-10-03 drh: @ The default ticket configuration for new projects 0edee97370 2007-10-03 drh: @ END-OF-DESCRIPTION 0edee97370 2007-10-03 drh: @ ##################################################################### 0edee97370 2007-10-03 drh: @ field title text 60 1 0edee97370 2007-10-03 drh: @ field comment ctext 80 20 0edee97370 2007-10-03 drh: @ field assignedto text 20 1 0edee97370 2007-10-03 drh: @ field subsystem text 20 1 0edee97370 2007-10-03 drh: @ field type enum 12 Code Build_Problem Documentation Feature_Request Incident 0edee97370 2007-10-03 drh: @ field priority enum 10 High Medium Low 0edee97370 2007-10-03 drh: @ field severity enum 10 Critical Severe Important Minor Cosmetic 0edee97370 2007-10-03 drh: @ field sesolution enum 20 Open Fixed Rejected Unable_To_Reproduce Works_As_Designed External_Bug Not_A_Bug Duplicate Overcome_By_Events Drive_By_Patch 0edee97370 2007-10-03 drh: @ field status enum 10 Open Verified In_Process Deferred Fixed Tested Closed 0edee97370 2007-10-03 drh: @ field contact private 50 1 0edee97370 2007-10-03 drh: @ field foundin text 30 1 0edee97370 2007-10-03 drh: @ field assocvers baseline 50 0edee97370 2007-10-03 drh: @ field presentin uuid 50 0edee97370 2007-10-03 drh: @ field fixedin uuid 50 0edee97370 2007-10-03 drh: @ field dueby date 20 0edee97370 2007-10-03 drh: @ field deferuntil date 20 0edee97370 2007-10-03 drh: @ ###################################################################### 0edee97370 2007-10-03 drh: @ template new END-OF-NEW-TEMPLATE 0edee97370 2007-10-03 drh: @ <table cellpadding="5"> 0edee97370 2007-10-03 drh: @ <tr> 0edee97370 2007-10-03 drh: @ <td cellpadding="2"> 0edee97370 2007-10-03 drh: @ Enter a one-line summary of the problem:<br> 0edee97370 2007-10-03 drh: @ [entrywidget title] 0edee97370 2007-10-03 drh: @ </td> 0edee97370 2007-10-03 drh: @ </tr> 0edee97370 2007-10-03 drh: @ 0edee97370 2007-10-03 drh: @ <tr> 0edee97370 2007-10-03 drh: @ <td align="right">Type: 0edee97370 2007-10-03 drh: @ [entrywidget type] 0edee97370 2007-10-03 drh: @ </td> 0edee97370 2007-10-03 drh: @ <td>What type of ticket is this?</td> 0edee97370 2007-10-03 drh: @ </tr> 0edee97370 2007-10-03 drh: @ 0edee97370 2007-10-03 drh: @ <tr> 0edee97370 2007-10-03 drh: @ <td align="right">Version: 0edee97370 2007-10-03 drh: @ [entrywidget foundin] 0edee97370 2007-10-03 drh: @ </td> 0edee97370 2007-10-03 drh: @ <td>In what version or build number do you observer the problem?</td> 0edee97370 2007-10-03 drh: @ </tr> 0edee97370 2007-10-03 drh: @ 0edee97370 2007-10-03 drh: @ <tr> 0edee97370 2007-10-03 drh: @ <td align="right">Severity: 0edee97370 2007-10-03 drh: @ [entrywidget severity] 0edee97370 2007-10-03 drh: @ </td> 0edee97370 2007-10-03 drh: @ <td>How debilitating is the problem? How badly does the problem 0edee97370 2007-10-03 drh: @ effect the operation of the product?</td> 0edee97370 2007-10-03 drh: @ </tr> 0edee97370 2007-10-03 drh: @ 0edee97370 2007-10-03 drh: @ <tr> 0edee97370 2007-10-03 drh: @ <td colspan="2"> 0edee97370 2007-10-03 drh: @ Enter a detailed description of the problem. 0edee97370 2007-10-03 drh: @ For code defects, be sure to provide details on exactly how 0edee97370 2007-10-03 drh: @ the problem can be reproduced. Provide as much detail as 0edee97370 2007-10-03 drh: @ possible. 0edee97370 2007-10-03 drh: @ <br> 0edee97370 2007-10-03 drh: @ [entrywidget comment noappend] 0edee97370 2007-10-03 drh: @ [ifpreview comment] 0edee97370 2007-10-03 drh: @ <hr> 0edee97370 2007-10-03 drh: @ [viewwidget comment] 0edee97370 2007-10-03 drh: @ </hr> 0edee97370 2007-10-03 drh: @ </tr> 0edee97370 2007-10-03 drh: @ 0edee97370 2007-10-03 drh: @ <tr> 0edee97370 2007-10-03 drh: @ <td align="right"> 0edee97370 2007-10-03 drh: @ [submitbutton] 0edee97370 2007-10-03 drh: @ </td> 0edee97370 2007-10-03 drh: @ <td>After filling in the information above, press this button to create 0edee97370 2007-10-03 drh: @ the new ticket</td> 0edee97370 2007-10-03 drh: @ </tr> 0edee97370 2007-10-03 drh: @ </table> 0edee97370 2007-10-03 drh: @ [defaultvalue status Open] 0edee97370 2007-10-03 drh: @ [defaultvalue resolution Open] 0edee97370 2007-10-03 drh: @ END-OF-NEW-TEMPLATE 0edee97370 2007-10-03 drh: @ ###################################################################### 0edee97370 2007-10-03 drh: @ template edit END-OF-EDIT-TEMPLATE 0edee97370 2007-10-03 drh: @ <table cellpadding="5"> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Title:</td><td> 0edee97370 2007-10-03 drh: @ [entrywidget title] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Status:</td><td> 0edee97370 2007-10-03 drh: @ [entrywidget status] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Type:</td><td> 0edee97370 2007-10-03 drh: @ [entrywidget type] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Severity:</td><td> 0edee97370 2007-10-03 drh: @ [entrywidget severity] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Priority:</td><td> 0edee97370 2007-10-03 drh: @ [entrywidget priority] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Resolution:</td><td> 0edee97370 2007-10-03 drh: @ [entrywidget resolution] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Subsystem:</td><td> 0edee97370 2007-10-03 drh: @ [entrywidget subsystem] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Assigned To:</td><td> 0edee97370 2007-10-03 drh: @ [entrywidget assignedto] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Contact:</td><td> 0edee97370 2007-10-03 drh: @ [entrywidget contact] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Version Found In:</td><td> 0edee97370 2007-10-03 drh: @ [entrywidget foundin] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td colspan="2"> 0edee97370 2007-10-03 drh: @ [ifappend comment] 0edee97370 2007-10-03 drh: @ New Remarks:<br> 0edee97370 2007-10-03 drh: @ [appendwidget comment] 0edee97370 2007-10-03 drh: @ [else] 0edee97370 2007-10-03 drh: @ Description And Comments:<br> 0edee97370 2007-10-03 drh: @ [entrywidget comment] 0edee97370 2007-10-03 drh: @ [endif] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right"></td><td> 0edee97370 2007-10-03 drh: @ [submitbutton] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ </table> 0edee97370 2007-10-03 drh: @ END-OF-EDIT-TEMPLATE 0edee97370 2007-10-03 drh: @ ###################################################################### 0edee97370 2007-10-03 drh: @ template view END-OF-VIEW-TEMPLATE 0edee97370 2007-10-03 drh: @ <table cellpadding="5"> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Title:</td><td> 0edee97370 2007-10-03 drh: @ [viewwidget title] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Status:</td><td> 0edee97370 2007-10-03 drh: @ [viewwidget status] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Type:</td><td> 0edee97370 2007-10-03 drh: @ [viewwidget type] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Severity:</td><td> 0edee97370 2007-10-03 drh: @ [viewwidget severity] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Priority:</td><td> 0edee97370 2007-10-03 drh: @ [viewwidget priority] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Resolution:</td><td> 0edee97370 2007-10-03 drh: @ [viewwidget resolution] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Subsystem:</td><td> 0edee97370 2007-10-03 drh: @ [viewwidget subsystem] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Assigned To:</td><td> 0edee97370 2007-10-03 drh: @ [viewwidget assignedto] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Contact:</td><td> 0edee97370 2007-10-03 drh: @ [viewwidget contact] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td align="right">Version Found In:</td><td> 0edee97370 2007-10-03 drh: @ [viewwidget foundin] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ <tr><td colspan="2"> 0edee97370 2007-10-03 drh: @ Description And Comments:<br> 0edee97370 2007-10-03 drh: @ [viewwidget comment] 0edee97370 2007-10-03 drh: @ </td></tr> 0edee97370 2007-10-03 drh: @ </table> 0edee97370 2007-10-03 drh: @ END-OF-VIEW-TEMPLATE 0edee97370 2007-10-03 drh: ; 0edee97370 2007-10-03 drh: Blob config, errmsg; 0edee97370 2007-10-03 drh: blob_init(&config, zDefaultConfig, sizeof(zDefaultConfig)-1); 0edee97370 2007-10-03 drh: db_begin_transaction(); 0edee97370 2007-10-03 drh: blob_zero(&errmsg); 0edee97370 2007-10-03 drh: ticket_config_parse(&config, 0, &errmsg); 0edee97370 2007-10-03 drh: if( blob_size(&errmsg) ){ 0edee97370 2007-10-03 drh: fossil_fatal("%b", &errmsg); 0edee97370 2007-10-03 drh: } 0edee97370 2007-10-03 drh: db_end_transaction(0); 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: 1e9c0e287e 2007-10-03 drh: /* 1e9c0e287e 2007-10-03 drh: ** Return the length of a string without its trailing whitespace. 1e9c0e287e 2007-10-03 drh: */ 1e9c0e287e 2007-10-03 drh: static int non_whitespace_length(const char *z){ 1e9c0e287e 2007-10-03 drh: int n = strlen(z); 1e9c0e287e 2007-10-03 drh: while( n>0 && isspace(z[n-1]) ){ n--; } 1e9c0e287e 2007-10-03 drh: return n; 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: 1e9c0e287e 2007-10-03 drh: /* 1e9c0e287e 2007-10-03 drh: ** Fill the given Blob with text that describes the current 1e9c0e287e 2007-10-03 drh: ** ticket configuration. This is the inverse of ticket_config_parse() 1e9c0e287e 2007-10-03 drh: */ 1e9c0e287e 2007-10-03 drh: void ticket_config_render(Blob *pOut){ 1e9c0e287e 2007-10-03 drh: char *zDelim; 1e9c0e287e 2007-10-03 drh: char *zContent; 1e9c0e287e 2007-10-03 drh: Stmt q; 1e9c0e287e 2007-10-03 drh: int n; 1e9c0e287e 2007-10-03 drh: 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "ticket-configuration\n"); 1e9c0e287e 2007-10-03 drh: zDelim = db_text(0, "SELECT '--end-of-text--' || hex(random(20))"); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "###################################################\n"); 1e9c0e287e 2007-10-03 drh: db_prepare(&q, "SELECT name, type, width, arg FROM tktfield"); 1e9c0e287e 2007-10-03 drh: while( db_step(&q)==SQLITE_ROW ){ 1e9c0e287e 2007-10-03 drh: const char *zName = db_column_text(&q, 0); 1e9c0e287e 2007-10-03 drh: const char *zType = db_column_text(&q, 1); 1e9c0e287e 2007-10-03 drh: int width = db_column_int(&q, 2); 1e9c0e287e 2007-10-03 drh: const char *zArg = db_column_text(&q, 3); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "field %s %s %d %s\n", zName, zType, width, zArg); 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: db_finalize(&q); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "###################################################\n"); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "template new %s\n", zDelim); 1e9c0e287e 2007-10-03 drh: zContent = db_get("tkt-new-template", 0); 1e9c0e287e 2007-10-03 drh: if( zContent ){ 1e9c0e287e 2007-10-03 drh: n = non_whitespace_length(zContent); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "%.*s\n", n, zContent); 1e9c0e287e 2007-10-03 drh: free(zContent); 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "%s\n", zDelim); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "###################################################\n"); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "template edit %s\n", zDelim); 1e9c0e287e 2007-10-03 drh: zContent = db_get("tkt-edit-template", 0); 1e9c0e287e 2007-10-03 drh: if( zContent ){ 1e9c0e287e 2007-10-03 drh: n = non_whitespace_length(zContent); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "%.*s\n", n, zContent); 1e9c0e287e 2007-10-03 drh: free(zContent); 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "%s\n", zDelim); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "###################################################\n"); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "template view %s\n", zDelim); 1e9c0e287e 2007-10-03 drh: zContent = db_get("tkt-view-template", 0); 1e9c0e287e 2007-10-03 drh: if( zContent ){ 1e9c0e287e 2007-10-03 drh: n = non_whitespace_length(zContent); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "%.*s\n", n, zContent); 1e9c0e287e 2007-10-03 drh: free(zContent); 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "%s\n", zDelim); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "###################################################\n"); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "description %s\n", zDelim); 1e9c0e287e 2007-10-03 drh: zContent = db_get("tkt-desc", 0); 1e9c0e287e 2007-10-03 drh: if( zContent ){ 1e9c0e287e 2007-10-03 drh: n = non_whitespace_length(zContent); 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "%.*s\n", n, zContent); 1e9c0e287e 2007-10-03 drh: free(zContent); 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: blob_appendf(pOut, "%s\n", zDelim); 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: 1e9c0e287e 2007-10-03 drh: /* 1e9c0e287e 2007-10-03 drh: ** COMMAND: test-tktconfig-export 1e9c0e287e 2007-10-03 drh: ** Write the current ticket configuration out to a file. 1e9c0e287e 2007-10-03 drh: */ 1e9c0e287e 2007-10-03 drh: void tktconfig_render_cmd(void){ 1e9c0e287e 2007-10-03 drh: Blob config; 1e9c0e287e 2007-10-03 drh: 1e9c0e287e 2007-10-03 drh: db_must_be_within_tree(); 1e9c0e287e 2007-10-03 drh: if( g.argc!=3 ){ 1e9c0e287e 2007-10-03 drh: usage("FILENAME"); 1e9c0e287e 2007-10-03 drh: } 1e9c0e287e 2007-10-03 drh: blob_zero(&config); 1e9c0e287e 2007-10-03 drh: ticket_config_render(&config); 1e9c0e287e 2007-10-03 drh: blob_write_to_file(&config, g.argv[2]); 1e9c0e287e 2007-10-03 drh: blob_reset(&config); 2e9d52f27f 2007-10-02 drh: }