| | @@ -26,62 +26,89 @@ |
| 26 | 26 | ** The list of database user-defined fields in the TICKET table. |
| 27 | 27 | ** The real table also contains some addition fields for internal |
| 28 | 28 | ** used. The internal-use fields begin with "tkt_". |
| 29 | 29 | */ |
| 30 | 30 | static int nField = 0; |
| 31 | | -static char **azField = 0; /* Names of database fields */ |
| 32 | | -static char **azValue = 0; /* Original values */ |
| 33 | | -static char **azAppend = 0; /* Value to be appended */ |
| 31 | +static struct tktFieldInfo { |
| 32 | + char *zName; /* Name of the database field */ |
| 33 | + char *zValue; /* Value to store */ |
| 34 | + char *zAppend; /* Value to append */ |
| 35 | + unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ |
| 36 | +} *aField; |
| 37 | +#define USEDBY_TICKET 01 |
| 38 | +#define USEDBY_TICKETCHNG 02 |
| 39 | +static int haveTicket = 0; /* True if the TICKET table exists */ |
| 40 | +static int haveTicketChng = 0; /* True if the TICKETCHNG table exists */ |
| 34 | 41 | |
| 35 | 42 | /* |
| 36 | | -** Compare two entries in azField for sorting purposes |
| 43 | +** Compare two entries in aField[] for sorting purposes |
| 37 | 44 | */ |
| 38 | 45 | static int nameCmpr(const void *a, const void *b){ |
| 39 | | - return fossil_strcmp(*(char**)a, *(char**)b); |
| 46 | + return fossil_strcmp(((const struct tktFieldInfo*)a)->zName, |
| 47 | + ((const struct tktFieldInfo*)b)->zName); |
| 48 | +} |
| 49 | + |
| 50 | +/* |
| 51 | +** Return the index into aField[] of the given field name. |
| 52 | +** Return -1 if zFieldName is not in aField[]. |
| 53 | +*/ |
| 54 | +static int fieldId(const char *zFieldName){ |
| 55 | + int i; |
| 56 | + for(i=0; i<nField; i++){ |
| 57 | + if( fossil_strcmp(aField[i].zName, zFieldName)==0 ) return i; |
| 58 | + } |
| 59 | + return -1; |
| 40 | 60 | } |
| 41 | 61 | |
| 42 | 62 | /* |
| 43 | | -** Obtain a list of all fields of the TICKET table. Put them |
| 44 | | -** in sorted order in azField[]. |
| 63 | +** Obtain a list of all fields of the TICKET and TICKETCHNG tables. Put them |
| 64 | +** in sorted order in aField[]. |
| 45 | 65 | ** |
| 46 | | -** Also allocate space for azValue[] and azAppend[] and initialize |
| 47 | | -** all the values there to zero. |
| 66 | +** The haveTicket and haveTicketChng variables are set to 1 if the TICKET and |
| 67 | +** TICKETCHANGE tables exist, respectively. |
| 48 | 68 | */ |
| 49 | 69 | static void getAllTicketFields(void){ |
| 50 | 70 | Stmt q; |
| 51 | 71 | int i; |
| 52 | | - if( nField>0 ) return; |
| 72 | + static int once = 0; |
| 73 | + if( once ) return; |
| 74 | + once = 1; |
| 53 | 75 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 54 | 76 | while( db_step(&q)==SQLITE_ROW ){ |
| 55 | | - const char *zField = db_column_text(&q, 1); |
| 56 | | - if( strncmp(zField,"tkt_",4)==0 ) continue; |
| 77 | + const char *zFieldName = db_column_text(&q, 1); |
| 78 | + haveTicket = 1; |
| 79 | + if( memcmp(zFieldName,"tkt_",4)==0 ) continue; |
| 80 | + if( nField%10==0 ){ |
| 81 | + aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 82 | + } |
| 83 | + aField[nField].zName = mprintf("%s", zFieldName); |
| 84 | + aField[nField].mUsed = USEDBY_TICKET; |
| 85 | + nField++; |
| 86 | + } |
| 87 | + db_finalize(&q); |
| 88 | + db_prepare(&q, "PRAGMA table_info(ticketchng)"); |
| 89 | + while( db_step(&q)==SQLITE_ROW ){ |
| 90 | + const char *zFieldName = db_column_text(&q, 1); |
| 91 | + haveTicketChng = 1; |
| 92 | + if( memcmp(zFieldName,"tkt_",4)==0 ) continue; |
| 93 | + if( (i = fieldId(zFieldName))>=0 ){ |
| 94 | + aField[i].mUsed |= USEDBY_TICKETCHNG; |
| 95 | + continue; |
| 96 | + } |
| 57 | 97 | if( nField%10==0 ){ |
| 58 | | - azField = fossil_realloc(azField, sizeof(azField)*3*(nField+10) ); |
| 98 | + aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 59 | 99 | } |
| 60 | | - azField[nField] = mprintf("%s", zField); |
| 100 | + aField[nField].zName = mprintf("%s", zFieldName); |
| 101 | + aField[nField].mUsed = USEDBY_TICKETCHNG; |
| 61 | 102 | nField++; |
| 62 | 103 | } |
| 63 | 104 | db_finalize(&q); |
| 64 | | - qsort(azField, nField, sizeof(azField[0]), nameCmpr); |
| 65 | | - azAppend = &azField[nField]; |
| 66 | | - memset(azAppend, 0, sizeof(azAppend[0])*nField); |
| 67 | | - azValue = &azAppend[nField]; |
| 68 | | - for(i=0; i<nField; i++){ |
| 69 | | - azValue[i] = ""; |
| 70 | | - } |
| 71 | | -} |
| 72 | | - |
| 73 | | -/* |
| 74 | | -** Return the index into azField[] of the given field name. |
| 75 | | -** Return -1 if zField is not in azField[]. |
| 76 | | -*/ |
| 77 | | -static int fieldId(const char *zField){ |
| 78 | | - int i; |
| 79 | | - for(i=0; i<nField; i++){ |
| 80 | | - if( fossil_strcmp(azField[i], zField)==0 ) return i; |
| 81 | | - } |
| 82 | | - return -1; |
| 105 | + qsort(aField, nField, sizeof(aField[0]), nameCmpr); |
| 106 | + for(i=0; i<nField; i++){ |
| 107 | + aField[i].zValue = ""; |
| 108 | + aField[i].zAppend = 0; |
| 109 | + } |
| 83 | 110 | } |
| 84 | 111 | |
| 85 | 112 | /* |
| 86 | 113 | ** Query the database for all TICKET fields for the specific |
| 87 | 114 | ** ticket whose name is given by the "name" CGI parameter. |
| | @@ -114,15 +141,12 @@ |
| 114 | 141 | if( zVal==0 ){ |
| 115 | 142 | zVal = ""; |
| 116 | 143 | }else if( strncmp(zName, "private_", 8)==0 ){ |
| 117 | 144 | zVal = zRevealed = db_reveal(zVal); |
| 118 | 145 | } |
| 119 | | - for(j=0; j<nField; j++){ |
| 120 | | - if( fossil_strcmp(azField[j],zName)==0 ){ |
| 121 | | - azValue[j] = mprintf("%s", zVal); |
| 122 | | - break; |
| 123 | | - } |
| 146 | + if( (j = fieldId(zName))>=0 ){ |
| 147 | + aField[j].zValue = mprintf("%s", zVal); |
| 124 | 148 | } |
| 125 | 149 | if( Th_Fetch(zName, &size)==0 ){ |
| 126 | 150 | Th_Store(zName, zVal); |
| 127 | 151 | } |
| 128 | 152 | free(zRevealed); |
| | @@ -157,56 +181,69 @@ |
| 157 | 181 | Th_Store(z, P(z)); |
| 158 | 182 | } |
| 159 | 183 | } |
| 160 | 184 | |
| 161 | 185 | /* |
| 162 | | -** Update an entry of the TICKET table according to the information |
| 163 | | -** in the control file given in p. Attempt to create the appropriate |
| 164 | | -** TICKET table entry if createFlag is true. If createFlag is false, |
| 165 | | -** that means we already know the entry exists and so we can save the |
| 166 | | -** work of trying to create it. |
| 186 | +** Update an entry of the TICKET and TICKETCHNG tables according to the |
| 187 | +** information in the ticket artifact given in p. Attempt to create |
| 188 | +** the appropriate TICKET table entry if tktid is zero. If tktid is nonzero |
| 189 | +** then it will be the ROWID of an existing TICKET entry. |
| 190 | +** |
| 191 | +** Parameter rid is the recordID for the ticket artifact in the BLOB table. |
| 167 | 192 | ** |
| 168 | | -** Return TRUE if a new TICKET entry was created and FALSE if an |
| 169 | | -** existing entry was revised. |
| 193 | +** Return the new rowid of the TICKET table entry. |
| 170 | 194 | */ |
| 171 | | -int ticket_insert(const Manifest *p, int createFlag, int rid){ |
| 172 | | - Blob sql; |
| 195 | +static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 196 | + Blob sql1, sql2, sql3; |
| 173 | 197 | Stmt q; |
| 174 | | - int i; |
| 175 | | - int rc = 0; |
| 198 | + int i, j; |
| 199 | + const char *zSep = "("; |
| 176 | 200 | |
| 177 | | - getAllTicketFields(); |
| 178 | | - if( createFlag ){ |
| 179 | | - db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) " |
| 201 | + if( tktid==0 ){ |
| 202 | + db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 180 | 203 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 181 | | - rc = db_changes(); |
| 204 | + tktid = db_last_insert_rowid(); |
| 182 | 205 | } |
| 183 | | - blob_zero(&sql); |
| 184 | | - blob_appendf(&sql, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); |
| 206 | + blob_zero(&sql1); |
| 207 | + blob_zero(&sql2); |
| 208 | + blob_zero(&sql3); |
| 209 | + blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); |
| 185 | 210 | for(i=0; i<p->nField; i++){ |
| 186 | 211 | const char *zName = p->aField[i].zName; |
| 187 | 212 | if( zName[0]=='+' ){ |
| 188 | 213 | zName++; |
| 189 | | - if( fieldId(zName)<0 ) continue; |
| 190 | | - blob_appendf(&sql,", %s=coalesce(%s,'') || %Q", |
| 191 | | - zName, zName, p->aField[i].zValue); |
| 214 | + if( (j = fieldId(zName))<0 ) continue; |
| 215 | + if( aField[j].mUsed & USEDBY_TICKET ){ |
| 216 | + blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q", |
| 217 | + zName, zName, p->aField[i].zValue); |
| 218 | + } |
| 192 | 219 | }else{ |
| 193 | | - if( fieldId(zName)<0 ) continue; |
| 194 | | - blob_appendf(&sql,", %s=%Q", zName, p->aField[i].zValue); |
| 220 | + if( (j = fieldId(zName))<0 ) continue; |
| 221 | + blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue); |
| 222 | + } |
| 223 | + if( aField[j].mUsed & USEDBY_TICKETCHNG ){ |
| 224 | + blob_appendf(&sql2, "%s%s", zSep, zName); |
| 225 | + blob_appendf(&sql3, "%s%Q", p->aField[i].zValue); |
| 226 | + zSep = ","; |
| 195 | 227 | } |
| 196 | 228 | if( rid>0 ){ |
| 197 | 229 | wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0); |
| 198 | 230 | } |
| 199 | 231 | } |
| 200 | | - blob_appendf(&sql, " WHERE tkt_uuid='%s' AND tkt_mtime<:mtime", |
| 201 | | - p->zTicketUuid); |
| 202 | | - db_prepare(&q, "%s", blob_str(&sql)); |
| 232 | + blob_appendf(&sql1, " WHERE tkt_id=%d", tktid); |
| 233 | + db_prepare(&q, "%s", blob_str(&sql1)); |
| 203 | 234 | db_bind_double(&q, ":mtime", p->rDate); |
| 204 | 235 | db_step(&q); |
| 205 | 236 | db_finalize(&q); |
| 206 | | - blob_reset(&sql); |
| 207 | | - return rc; |
| 237 | + blob_reset(&sql1); |
| 238 | + if( blob_size(&sql2)>0 ){ |
| 239 | + db_multi_exec("INSERT INTO ticketchng %s) VALUES %s)", |
| 240 | + blob_str(&sql2), blob_str(&sql3)); |
| 241 | + } |
| 242 | + blob_reset(&sql2); |
| 243 | + blob_reset(&sql3); |
| 244 | + return tktid; |
| 208 | 245 | } |
| 209 | 246 | |
| 210 | 247 | /* |
| 211 | 248 | ** Rebuild an entire entry in the TICKET table |
| 212 | 249 | */ |
| | @@ -213,67 +250,78 @@ |
| 213 | 250 | void ticket_rebuild_entry(const char *zTktUuid){ |
| 214 | 251 | char *zTag = mprintf("tkt-%s", zTktUuid); |
| 215 | 252 | int tagid = tag_findid(zTag, 1); |
| 216 | 253 | Stmt q; |
| 217 | 254 | Manifest *pTicket; |
| 255 | + int tktid; |
| 218 | 256 | int createFlag = 1; |
| 219 | 257 | |
| 220 | | - fossil_free(zTag); |
| 221 | | - db_multi_exec( |
| 222 | | - "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid |
| 223 | | - ); |
| 258 | + fossil_free(zTag); |
| 259 | + getAllTicketFields(); |
| 260 | + if( haveTicket==0 ) return; |
| 261 | + tktid = db_int(0, "SELECT tkt_id FROM ticket WHERE tkt_uuid=%Q", zTktUuid); |
| 262 | + if( haveTicketChng ){ |
| 263 | + db_multi_exec("DELETE FROM ticketchng WHERE tkt_id=%d;", tktid); |
| 264 | + } |
| 265 | + db_multi_exec("DELETE FROM ticket WHERE tkt_id=%d", tktid); |
| 266 | + tktid = 0; |
| 224 | 267 | db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid); |
| 225 | 268 | while( db_step(&q)==SQLITE_ROW ){ |
| 226 | 269 | int rid = db_column_int(&q, 0); |
| 227 | 270 | pTicket = manifest_get(rid, CFTYPE_TICKET); |
| 228 | 271 | if( pTicket ){ |
| 229 | | - ticket_insert(pTicket, createFlag, rid); |
| 272 | + tktid = ticket_insert(pTicket, rid, tktid); |
| 230 | 273 | manifest_ticket_event(rid, pTicket, createFlag, tagid); |
| 231 | 274 | manifest_destroy(pTicket); |
| 232 | 275 | } |
| 233 | 276 | createFlag = 0; |
| 234 | 277 | } |
| 235 | 278 | db_finalize(&q); |
| 236 | 279 | } |
| 237 | 280 | |
| 238 | 281 | /* |
| 239 | | -** Create the subscript interpreter and load the "common" code. |
| 282 | +** Create the TH1 interpreter and load the "common" code. |
| 240 | 283 | */ |
| 241 | 284 | void ticket_init(void){ |
| 242 | 285 | const char *zConfig; |
| 243 | 286 | Th_FossilInit(0, 0); |
| 244 | 287 | zConfig = ticket_common_code(); |
| 245 | 288 | Th_Eval(g.interp, 0, zConfig, -1); |
| 246 | 289 | } |
| 247 | 290 | |
| 248 | 291 | /* |
| 249 | | -** Create the subscript interpreter and load the "change" code. |
| 292 | +** Create the TH1 interpreter and load the "change" code. |
| 250 | 293 | */ |
| 251 | 294 | int ticket_change(void){ |
| 252 | 295 | const char *zConfig; |
| 253 | 296 | Th_FossilInit(0, 0); |
| 254 | 297 | zConfig = ticket_change_code(); |
| 255 | 298 | return Th_Eval(g.interp, 0, zConfig, -1); |
| 256 | 299 | } |
| 257 | 300 | |
| 258 | 301 | /* |
| 259 | | -** Recreate the ticket table. |
| 302 | +** Recreate the TICKET and TICKETCHNG tables. |
| 260 | 303 | */ |
| 261 | 304 | void ticket_create_table(int separateConnection){ |
| 262 | 305 | const char *zSql; |
| 263 | 306 | |
| 264 | | - db_multi_exec("DROP TABLE IF EXISTS ticket;"); |
| 307 | + db_multi_exec( |
| 308 | + "DROP TABLE IF EXISTS ticket;" |
| 309 | + "DROP TABLE IF EXISTS ticketchng;" |
| 310 | + ); |
| 265 | 311 | zSql = ticket_table_schema(); |
| 266 | 312 | if( separateConnection ){ |
| 313 | + db_end_transaction(0); |
| 267 | 314 | db_init_database(g.zRepositoryName, zSql, 0); |
| 268 | 315 | }else{ |
| 269 | 316 | db_multi_exec("%s", zSql); |
| 270 | 317 | } |
| 271 | 318 | } |
| 272 | 319 | |
| 273 | 320 | /* |
| 274 | | -** Repopulate the ticket table |
| 321 | +** Repopulate the TICKET and TICKETCHNG tables from scratch using all |
| 322 | +** available ticket artifacts. |
| 275 | 323 | */ |
| 276 | 324 | void ticket_rebuild(void){ |
| 277 | 325 | Stmt q; |
| 278 | 326 | ticket_create_table(1); |
| 279 | 327 | db_begin_transaction(); |
| | @@ -373,20 +421,20 @@ |
| 373 | 421 | if( g.thTrace ){ |
| 374 | 422 | Th_Trace("append_field %#h {%#h}<br />\n", |
| 375 | 423 | argl[1], argv[1], argl[2], argv[2]); |
| 376 | 424 | } |
| 377 | 425 | for(idx=0; idx<nField; idx++){ |
| 378 | | - if( strncmp(azField[idx], argv[1], argl[1])==0 |
| 379 | | - && azField[idx][argl[1]]==0 ){ |
| 426 | + if( memcmp(aField[idx].zName, argv[1], argl[1])==0 |
| 427 | + && aField[idx].zName[argl[1]]==0 ){ |
| 380 | 428 | break; |
| 381 | 429 | } |
| 382 | 430 | } |
| 383 | 431 | if( idx>=nField ){ |
| 384 | 432 | Th_ErrorMessage(g.interp, "no such TICKET column: ", argv[1], argl[1]); |
| 385 | 433 | return TH_ERROR; |
| 386 | 434 | } |
| 387 | | - azAppend[idx] = mprintf("%.*s", argl[2], argv[2]); |
| 435 | + aField[idx].zAppend = mprintf("%.*s", argl[2], argv[2]); |
| 388 | 436 | return TH_OK; |
| 389 | 437 | } |
| 390 | 438 | |
| 391 | 439 | /* |
| 392 | 440 | ** Write a ticket into the repository. |
| | @@ -447,29 +495,31 @@ |
| 447 | 495 | blob_zero(&tktchng); |
| 448 | 496 | zDate = date_in_standard_format("now"); |
| 449 | 497 | blob_appendf(&tktchng, "D %s\n", zDate); |
| 450 | 498 | free(zDate); |
| 451 | 499 | for(i=0; i<nField; i++){ |
| 452 | | - if( azAppend[i] ){ |
| 453 | | - blob_appendf(&tktchng, "J +%s %z\n", azField[i], |
| 454 | | - fossilize(azAppend[i], -1)); |
| 500 | + if( aField[i].zAppend ){ |
| 501 | + blob_appendf(&tktchng, "J +%s %z\n", aField[i].zName, |
| 502 | + fossilize(aField[i].zAppend, -1)); |
| 455 | 503 | ++nJ; |
| 456 | 504 | } |
| 457 | 505 | } |
| 458 | 506 | for(i=0; i<nField; i++){ |
| 459 | 507 | const char *zValue; |
| 460 | 508 | int nValue; |
| 461 | | - if( azAppend[i] ) continue; |
| 462 | | - zValue = Th_Fetch(azField[i], &nValue); |
| 509 | + if( aField[i].zAppend ) continue; |
| 510 | + zValue = Th_Fetch(aField[i].zName, &nValue); |
| 463 | 511 | if( zValue ){ |
| 464 | 512 | while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } |
| 465 | | - if( strncmp(zValue, azValue[i], nValue) || strlen(azValue[i])!=nValue ){ |
| 466 | | - if( strncmp(azField[i], "private_", 8)==0 ){ |
| 513 | + if( memcmp(zValue, aField[i].zValue, nValue)!=0 |
| 514 | + || strlen(aField[i].zValue)!=nValue |
| 515 | + ){ |
| 516 | + if( memcmp(aField[i].zName, "private_", 8)==0 ){ |
| 467 | 517 | zValue = db_conceal(zValue, nValue); |
| 468 | | - blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); |
| 518 | + blob_appendf(&tktchng, "J %s %s\n", aField[i].zName, zValue); |
| 469 | 519 | }else{ |
| 470 | | - blob_appendf(&tktchng, "J %s %#F\n", azField[i], nValue, zValue); |
| 520 | + blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue); |
| 471 | 521 | } |
| 472 | 522 | nJ++; |
| 473 | 523 | } |
| 474 | 524 | } |
| 475 | 525 | } |
| | @@ -642,18 +692,23 @@ |
| 642 | 692 | sqlite3_close(db); |
| 643 | 693 | return zErr; |
| 644 | 694 | } |
| 645 | 695 | rc = sqlite3_exec(db, "SELECT tkt_id, tkt_uuid, tkt_mtime FROM ticket", |
| 646 | 696 | 0, 0, 0); |
| 647 | | - sqlite3_close(db); |
| 648 | 697 | if( rc!=SQLITE_OK ){ |
| 649 | | - zErr = mprintf("schema fails to define a valid ticket table " |
| 650 | | - "containing all required fields"); |
| 651 | | - return zErr; |
| 698 | + zErr = mprintf("schema fails to define valid a TICKET " |
| 699 | + "table containing all required fields"); |
| 700 | + }else{ |
| 701 | + rc = sqlite3_exec(db, "SELECT tkt_id, tkt_mtime FROM ticketchng", 0,0,0); |
| 702 | + if( rc!=SQLITE_OK ){ |
| 703 | + zErr = mprintf("schema fails to define valid a TICKETCHNG " |
| 704 | + "table containing all required fields"); |
| 705 | + } |
| 652 | 706 | } |
| 707 | + sqlite3_close(db); |
| 653 | 708 | } |
| 654 | | - return 0; |
| 709 | + return zErr; |
| 655 | 710 | } |
| 656 | 711 | |
| 657 | 712 | /* |
| 658 | 713 | ** WEBPAGE: tkttimeline |
| 659 | 714 | ** URL: /tkttimeline?name=TICKETUUID&y=TYPE |
| | @@ -998,11 +1053,11 @@ |
| 998 | 1053 | int i; |
| 999 | 1054 | |
| 1000 | 1055 | /* read all available ticket fields */ |
| 1001 | 1056 | getAllTicketFields(); |
| 1002 | 1057 | for(i=0; i<nField; i++){ |
| 1003 | | - printf("%s\n",azField[i]); |
| 1058 | + printf("%s\n",aField[i].zName); |
| 1004 | 1059 | } |
| 1005 | 1060 | }else if( !strncmp(g.argv[3],"reports",n) ){ |
| 1006 | 1061 | rpt_list_reports(); |
| 1007 | 1062 | }else{ |
| 1008 | 1063 | fossil_fatal("unknown ticket list option '%s'!",g.argv[3]); |
| | @@ -1105,21 +1160,22 @@ |
| 1105 | 1160 | zShort[10] = 0; |
| 1106 | 1161 | if( zFile!=0 ){ |
| 1107 | 1162 | const char *zSrc = db_column_text(&q, 3); |
| 1108 | 1163 | const char *zUser = db_column_text(&q, 5); |
| 1109 | 1164 | if( zSrc==0 || zSrc[0]==0 ){ |
| 1110 | | - fossil_print("Delete attachment %h\n", zFile); |
| 1165 | + fossil_print("Delete attachment %s\n", zFile); |
| 1111 | 1166 | }else{ |
| 1112 | | - fossil_print("Add attachment %h\n", zFile); |
| 1167 | + fossil_print("Add attachment %s\n", zFile); |
| 1113 | 1168 | } |
| 1114 | | - fossil_print(" by %h on %h\n", zUser, zDate); |
| 1169 | + fossil_print(" by %s on %s\n", zUser, zDate); |
| 1115 | 1170 | }else{ |
| 1116 | 1171 | pTicket = manifest_get(rid, CFTYPE_TICKET); |
| 1117 | 1172 | if( pTicket ){ |
| 1118 | 1173 | int i; |
| 1119 | 1174 | |
| 1120 | | - fossil_print("Ticket Change by %h on %h:\n", pTicket->zUser, zDate); |
| 1175 | + fossil_print("Ticket Change by %s on %s:\n", |
| 1176 | + pTicket->zUser, zDate); |
| 1121 | 1177 | for(i=0; i<pTicket->nField; i++){ |
| 1122 | 1178 | Blob val; |
| 1123 | 1179 | const char *z; |
| 1124 | 1180 | z = pTicket->aField[i].zName; |
| 1125 | 1181 | blob_set(&val, pTicket->aField[i].zValue); |
| | @@ -1148,11 +1204,11 @@ |
| 1148 | 1204 | /* read all given ticket field/value pairs from command line */ |
| 1149 | 1205 | if( i==g.argc ){ |
| 1150 | 1206 | fossil_fatal("empty %s command aborted!",g.argv[2]); |
| 1151 | 1207 | } |
| 1152 | 1208 | getAllTicketFields(); |
| 1153 | | - /* read commandline and assign fields in the azValue array */ |
| 1209 | + /* read commandline and assign fields in the aField[].zValue array */ |
| 1154 | 1210 | while( i<g.argc ){ |
| 1155 | 1211 | char *zFName; |
| 1156 | 1212 | char *zFValue; |
| 1157 | 1213 | int j; |
| 1158 | 1214 | int append = 0; |
| | @@ -1173,13 +1229,13 @@ |
| 1173 | 1229 | j = fieldId(zFName); |
| 1174 | 1230 | if( j == -1 ){ |
| 1175 | 1231 | fossil_fatal("unknown field name '%s'!",zFName); |
| 1176 | 1232 | }else{ |
| 1177 | 1233 | if (append) { |
| 1178 | | - azAppend[j] = zFValue; |
| 1234 | + aField[j].zAppend = zFValue; |
| 1179 | 1235 | } else { |
| 1180 | | - azValue[j] = zFValue; |
| 1236 | + aField[j].zValue = zFValue; |
| 1181 | 1237 | } |
| 1182 | 1238 | } |
| 1183 | 1239 | } |
| 1184 | 1240 | |
| 1185 | 1241 | /* now add the needed artifacts to the repository */ |
| | @@ -1189,25 +1245,25 @@ |
| 1189 | 1245 | /* append defined elements */ |
| 1190 | 1246 | for(i=0; i<nField; i++){ |
| 1191 | 1247 | char *zValue = 0; |
| 1192 | 1248 | char *zPfx; |
| 1193 | 1249 | |
| 1194 | | - if (azAppend[i] && azAppend[i][0] ){ |
| 1250 | + if (aField[i].zAppend && aField[i].zAppend[0] ){ |
| 1195 | 1251 | zPfx = " +"; |
| 1196 | | - zValue = azAppend[i]; |
| 1197 | | - } else if( azValue[i] && azValue[i][0] ){ |
| 1252 | + zValue = aField[i].zAppend; |
| 1253 | + } else if( aField[i].zValue && aField[i].zValue[0] ){ |
| 1198 | 1254 | zPfx = " "; |
| 1199 | | - zValue = azValue[i]; |
| 1255 | + zValue = aField[i].zValue; |
| 1200 | 1256 | } else { |
| 1201 | 1257 | continue; |
| 1202 | 1258 | } |
| 1203 | | - if( strncmp(azField[i], "private_", 8)==0 ){ |
| 1259 | + if( memcmp(aField[i].zName, "private_", 8)==0 ){ |
| 1204 | 1260 | zValue = db_conceal(zValue, strlen(zValue)); |
| 1205 | | - blob_appendf(&tktchng, "J%s%s %s\n", zPfx, azField[i], zValue); |
| 1261 | + blob_appendf(&tktchng, "J%s%s %s\n", zPfx, aField[i].zName, zValue); |
| 1206 | 1262 | }else{ |
| 1207 | 1263 | blob_appendf(&tktchng, "J%s%s %#F\n", zPfx, |
| 1208 | | - azField[i], strlen(zValue), zValue); |
| 1264 | + aField[i].zName, strlen(zValue), zValue); |
| 1209 | 1265 | } |
| 1210 | 1266 | } |
| 1211 | 1267 | blob_appendf(&tktchng, "K %s\n", zTktUuid); |
| 1212 | 1268 | blob_appendf(&tktchng, "U %F\n", zUser); |
| 1213 | 1269 | md5sum_blob(&tktchng, &cksum); |
| 1214 | 1270 | |