Bug Summary

File:libsynthesis/src/sysync_SDK/Sources/dbitem.cpp
Warning:line 846, column 27
Value stored to 'is0D' is never read

Annotated Source Code

1/*
2 * File: dbitem.cpp
3 *
4 * Author: Beat Forster (bfo@synthesis.ch)
5 *
6 *
7 * Example DBApi database adapter.
8 * TDBItem class for item handling
9 *
10 * Copyright (c) 2005-2011 by Synthesis AG + plan44.ch
11 *
12 */
13
14#include "sync_include.h" // import some global things
15#include "sync_dbapidef.h"
16
17#include "SDK_util.h" // include SDK utilities
18#include "SDK_support.h"
19#include "dbitem.h"
20
21#ifndef SYSYNC_ENGINE1
22 #include "stringutil.h" // local implementation of CStr <=> Str conversions
23 #include "timeutil.h" // local implementation for time routines
24#endif
25
26
27namespace sysync {
28
29#define MyDB"DBItem" "DBItem" // 'DBItem' debug name
30
31
32#define FFirst10000 10000 // start number for item name (will be incremented)
33#define ARR_Sep'\x1D' '\x1D' // Array element separator within textdb file
34#define ARR_SepS"\x1D" "\x1D" // ... the same as string
35
36
37
38// ---- utility functions ----------------------------------------------------------
39// Calculate the length of <fVal>'s array expansion
40static int ArrLen( cAppCharP fKey, cAppCharP fVal )
41{
42 cAppCharP z= "[0]";
43 cAppCharP x= "[x]:";
44 int len= strlen( fVal );
45
46 // some additional space is needed for array elements, calculate it first
47 int j= 0;
48 int i;
49 for (i= 0; i<len; i++) {
50 if (fVal[ i ]==ARR_Sep'\x1D') j++;
51 } // for
52
53 // yes, there are such elements => increase <newLen>
54 // don't forget element "[0]"
55 len = 0;
56 if (j>0) { len+= strlen( z ) + j*(strlen( fKey )+strlen( x )); // "yy[x]:"
57 if (j>10) len+= j-10; // "[xx]"
58 } // if
59
60 return len;
61} // ArrLen
62
63
64/*! Create an item field */
65void MakeField( TDBItemField* &act, cAppCharP field,
66 cAppCharP fieldEnd, int &len, bool convert )
67{
68 MakeObj ( act );
69 if (fieldEnd) act->field.assign( field, (unsigned int)(fieldEnd-field) );
70 else act->field.assign( field );
71
72 if (convert) { string s= ""; // don't touch array separator
73 StrToCStrAppend( act->field.c_str(), s, true, ARR_Sep'\x1D' );
74 act->field= s;
75 } // if
76
77//printf( "len=%3d field='%s'\n", len, act->field.c_str() );
78 len+= act->field.length()+1;
79} // MakeField
80
81
82// Get a token string
83static string Token_Str( string aToken )
84{
85 if (aToken.empty()) return "";
86 else return "[" + aToken + "]";
87} // Token_Str;
88
89
90// create a UTC token for time comparisons
91string CurrentTime( int secsOffs, bool dateOnly )
92{
93 string isoStr;
94 lineartime_t f= secondToLinearTimeFactor * secsOffs;
95
96 #ifdef SYSYNC_ENGINE1
97 timecontext_t tctx= TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ));
98 if (dateOnly) tctx= TCTX_DATEONLY;
99
100 //lineartime_t timestamp= getSystemNowAs( TCTX_UTC, (GZones*)aGZones );
101 lineartime_t timestamp= getSystemNowAs( TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)), NULL__null );
102 TimestampToISO8601Str( isoStr, timestamp + f, tctx, false, false );
103 #else
104 lineartime_t timestamp= utcNowAsLineartime();
105 timeStampToISO8601 ( timestamp + f, isoStr, dateOnly );
106 #endif
107
108 return isoStr;
109} // CurrentTime
110
111
112
113// Compare time of ISO8601 <aToken> with <aLastToken>/<aResumeToken>
114int CompareTokens( string aToken, string aLastToken, string aResumeToken )
115{
116 bool chg= aLastToken.empty() || // calculate newer condition
117 aLastToken < aToken;
118 bool res= !aResumeToken.empty() && // calculate resume condition
119 aResumeToken < aToken &&
120 aResumeToken > aLastToken;
121
122 int aStatus= ReadNextItem_Unchanged;
123 if (chg) aStatus= ReadNextItem_Changed;
124 if (res) aStatus= ReadNextItem_Resumed;
125 return aStatus;
126} // CompareTokens
127
128
129// -------------------------------------------------------------------------
130/* Combines <sKey> and <sField> into string <s>
131 * support for blobs, tz names and array fields
132 */
133string KeyAndField( cAppCharP sKey, cAppCharP sField )
134{
135 cAppCharP fs= strstr( sField, ";" ); // semicolon pattern: BLOB, TZNAME, ...
136 cAppCharP q = sField;
137 cAppCharP qE;
138
139 string s = sKey;
140 if (fs) { s+= fs; return s; } // with semicolon
141
142 int i= 0;
143 while (true) {
144 qE= strstr( q, ARR_SepS"\x1D" );
145 if (!qE && i==0) break;
146
147 s+= '[' + IntStr( i ) + ']'; if (!qE) break;
148
149 string tmp;
150 tmp.assign( q, (unsigned int)( qE-q ) );
151 s+= ':' + tmp + '\n';
152 i++;
153 q= qE+1;
154
155 s+= sKey;
156 } // loop
157
158 s+= ":";
159 s+= q;
160 return s;
161} // KeyAndField
162
163
164string KeyAndField( TDBItemField* actKey,
165 TDBItemField* actField ) {
166 return KeyAndField( actKey->field.c_str(),
167 actField->field.c_str() );
168} // KeyAndField
169
170
171void AddFields( string &aDat, string aAdd, cAppCharP param1,
172 cAppCharP param2,
173 cAppCharP param3,
174 cAppCharP param4,
175 cAppCharP param5 )
176{
177 string p;
178
179 if (aAdd.empty()) return;
180
181 int i= 1;
182 while (true) {
183 switch ( i ) {
184 case 1 : p= param1; break;
185 case 2 : p= param2; break;
186 case 3 : p= param3; break;
187 case 4 : p= param4; break;
188 case 5 : p= param5; break;
189 default: p= "";
190 } // switch
191
192 string chk= "%" + IntStr( i++ );
193 string::size_type pos= aAdd.find( chk,0 );
194 if (pos==string::npos) break;
195
196 aAdd.replace( pos,chk.length(), p );
197 } // loop
198
199 if (!aDat.empty()) {
200 cAppCharP c= aDat.c_str() + aDat.length()-1; // last char
201 if (*c!='\n') aDat+= '\n';
202 } // if
203
204 aDat+= aAdd;
205} // AddFields
206
207
208/* ------------------------------------------------------------------------ */
209//! assign ID and callback
210void TDBItem::init( cAppCharP aItemID, cAppCharP aParentID, void* aCB, TDBItem* aNext )
211{
212 itemID = aItemID;
213 parentID= aParentID;
214 fCB = aCB;
215 next = aNext;
216} // init
217
218
219//! assign ID and callback (overloaded)
220void TDBItem::init( cAppCharP aItemID, void* aCB, TDBItem* aNext ) {
221 init( aItemID,"", aCB, aNext );
222} // init
223
224
225// the combined item/parent string
226cAppCharP TDBItem::c_str()
227{
228 fID= itemID;
229 if (!parentID.empty()) { fID+= ","; fID+= parentID; }
230 return fID.c_str();
231} // c_str
232
233
234
235/* Get a specific item <actL>, which represents <itemID>,<parentID>.
236 * Returns error, if not found.
237 * If <first> is true (default), start at the beginning of the list,
238 * else continue
239 */
240TSyError TDBItem::GetItem( cItemID aID, TDBItem* &actL, bool first )
241{
242 char* p= aID->parent; if (!p) p= const_cast<char *>("");
243
244 if (first) actL= this;
245 while (ListNext( actL )) {
246 if (strcmp( aID->item,actL->itemID.c_str() )==0 &&
247 strcmp( p, actL->parentID.c_str() )==0) return LOCERR_OK;
248 } // while
249
250 actL= NULL__null;
251 return DB_NotFound;
252} // GetItem
253
254
255/* Overloaded for <aItemID> only w/o parent ID
256 * Get a specific item <actL>, which represents <itemID>
257 * Returns error, if not found.
258 */
259TSyError TDBItem::GetItem( cAppCharP aItemID, TDBItem* &actL, bool first )
260{
261 ItemID_Struct a; a.item = (char*)aItemID;
262 a.parent= NULL__null;
263 return GetItem( &a, actL, first );
264} // GetItem
265
266
267/* Overloaded for <aItemID> only w/o parent ID
268 * Get a specific item <actL>, which represents <itemID>
269 * Returns error, if not found.
270 */
271TSyError TDBItem::GetItem_2( cAppCharP aItemID, cAppCharP aField2,
272 TDBItem* mpL, TDBItem* &act )
273{
274 TSyError err= LOCERR_OK;
275 ItemID_Struct a; a.item = (char*)aItemID;
276 a.parent= NULL__null;
277
278 bool first= true;
279 string s;
280
281 while (true) {
282 err= GetItem( &a, act, first ); if (err) break; // not found
283 mpL->Field ( "2",act, s );
284 if (strcmp( aField2,s.c_str() )==0) break; // found
285 first= false;
286 } // loop
287
288 return err;
289} // GetItem_2
290
291
292
293TSyError TDBItem::DeleteItem( TDBItem* actL )
294{
295 TDBItem* prvL= actL;
296 ListBack( prvL, this ); // search for the previous element
297
298 prvL->next= actL->next;
299 actL->next= NULL__null; // avoid destroying the whole chain
300 delete actL;
301
302 fChanged= true;
303 return LOCERR_OK;
304} /* DeleteItem */
305
306
307
308TSyError TDBItem::DeleteItem( cItemID aID )
309{
310 TSyError err= DB_NotFound;
311
312 TDBItem* actI;
313 err= GetItem( aID, actI ); if (err) return err;
314
315 // item must be not available as parent element
316 TDBItem* actL= this;
317 while (ListNext( actL )) {
318 if (strcmp( aID->item,actL->parentID.c_str() )==0) return DB_Forbidden;
319 } // while
320
321 return DeleteItem( actI );
322} /* DeleteItem */
323
324
325
326TSyError TDBItem::DeleteItem( cAppCharP aItemID )
327{
328 ItemID_Struct a; a.item = (char*)aItemID;
329 a.parent= const_cast<char *>("");
330 return DeleteItem( &a );
331} // DeleteItem
332
333
334
335// parent element must be "" or exist
336TSyError TDBItem::ParentExist( cAppCharP aItemID, cAppCharP aParentID )
337{
338 if (*aParentID==0) return LOCERR_OK;
339 if (strcmp( aItemID,aParentID )==0) return DB_Forbidden; // no recursion allowed
340
341 TDBItem* actL= this;
342 while (ListNext( actL )) {
343 if (strcmp( aParentID,actL->itemID.c_str() )==0) return LOCERR_OK;
344 } // while
345
346 return DB_NotFound;
347} // ParentExists
348
349
350
351void TDBItem::CreateItem( string newItemID, string parentID, TDBItem* &actL )
352{
353 TDBItemField* actK;
354 TDBItemField* actF;
355
356 TDBItem* newL= new TDBItem; // create item root
357 newL->fCB= fCB; // inherit callback
358 actF= &newL->item;
359 newL->itemID = newItemID;
360 newL->parentID= parentID;
361 actL->next= newL;
362 ListNext( actL );
363
364 int n= 0; actK= &item;
365 while (ListNext( actK )) { // number of elements according to the key
366 MakeField ( actF, "", NULL__null, actL->len );
367 n++;
368 } // while
369
370 actL->len--; // termination is included already
371 fChanged= true;
372} // CreateItem
373
374
375
376/* The strategy is simple: Take ID="10000" as start, go thru the whole list.
377 * if found, increment. Insert new element at the end.
378 */
379TSyError TDBItem::CreateEmptyItem( ItemID aID, string &newItemID, TDBItem* &actL,
380 string newID )
381{
382 TSyError err;
383 int itemID= atoi( newID.c_str() );
384 if (itemID==0) itemID= atoi( F_First"10000" );
385
386 TDBItem* last;
387//TDBItem* newL;
388//TDBItemField* actK;
389//TDBItemField* actF;
390
391 // parent element must be "" or exist
392 err= ParentExist( "", aID->parent ); if (err) return err;
393
394 newItemID= aID->item;
395 if (newItemID.empty()) {
396 actL= this;
397 while (true) { last= actL;
398 if (!ListNext( actL )) break;
399
400 int itemNum= atoi( actL->itemID.c_str() );
401 if (itemNum>=itemID) itemID= itemNum+1; // assign largest unique number
402 } // while
403
404 newItemID= IntStr( itemID );
405 actL = last;
406 }
407 else {
408 actL= LastElem( this ); // get the last element
409 } // if
410
411
412 CreateItem( newItemID, aID->parent, actL );
413
414 /*
415 // create item root
416 newL= new TDBItem;
417 newL->fCB= fCB; // inherit callback
418 actF= &newL->item;
419 newL->itemID = newItemID;
420 newL->parentID= aID->parent;
421 actL->next= newL;
422 ListNext( actL );
423
424 int n= 0; actK= &item;
425 while (ListNext( actK )) { // number of elements according to the key
426 MakeField ( actF, "", NULL, actL->len );
427 n++;
428 } // while
429
430 actL->len--; // termination is included already
431 */
432
433 return LOCERR_OK;
434} // CreateEmptyItem
435
436
437TSyError TDBItem::CreateEmptyItem( cAppCharP aParentID, string &newItemID, TDBItem* &actL )
438{
439 ItemID_Struct a; a.item = const_cast<char *>("");
440 a.parent= (char*)aParentID;
441 return CreateEmptyItem ( &a, newItemID, actL );
442} // CreateEmptyItem
443
444
445TSyError TDBItem::CreateEmptyItem( string &newItemID, TDBItem* &actL ) {
446 return CreateEmptyItem( "", newItemID, actL );
447} // CreateEmptyItem
448
449
450/*!
451 * Check, if <aKey> can be interpreted as number
452 */
453static bool NumKey( cAppCharP aKey, int &iKey )
454{
455 iKey= atoi( aKey );
456 return iKey!=0 || strcmp( aKey,"0")==0;
457} // NumKey
458
459
460/* Update the specific field <fKey> with the new value <fVal>.
461 * Create the whole bunch of missing keys, if not yet available.
462 */
463TSyError TDBItem::UpdateField( void* aCB, cAppCharP fKey,
464 cAppCharP fVal, TDBItem* hdI, bool asName )
465{
466 TSyError err= DB_NotFound;
467 char* ts;
468 string s;
469 int iKey= 0, iLast= -1; // start value if data base is empty
470
471 TDBItemField* actK= &item; // the key
472 TDBItemField* lastK;
473 TDBItemField* actI= &hdI->item;
474 TDBItemField* lastI;
475
476 if (aCB==NULL__null) aCB= fCB; // get the default callback, if not overridden by <aCB>
477
478 // check, if the specific element is already existing
479 while (true) {
480 lastK= actK;
481 if (!ListNext( actK )) break;
482
483 lastI= actI; // a not existing field will be created (empty)
484 if (!ListNext( actI )) {
485 actI= lastI;
486 MakeField ( actI, "", NULL__null, hdI->len );
487 } // if
488
489 if (strcmp( fKey,actK->field.c_str())==0) {
490 int oldLen= actI->field.length();
491 int newLen= strlen( fVal ) + ArrLen( fKey,fVal );
492
493 hdI->len += newLen-oldLen; // adapt the whole length
494 actI->field= fVal; // and assign value of the new field
495 err= LOCERR_OK; // the new value is assigned, everything is ok
496 fChanged= true; // must be marked for a change
497 break;
498 } // if
499 } // while
500
501 // -----------------------------------------------------
502 if (err && !asName) {
503 if (!lastK->field.empty()) iLast= atoi( lastK->field.c_str() );
504 if (!NumKey( fKey,iKey )) err= DB_NotFound; // not a valid key
505 } // if
506
507 if (err!=DB_NotFound) {
508 s= KeyAndField( fKey,fVal );
509
510 // create name info
511 string vv= hdI->c_str();
512 if (!vv.empty()) vv= " " + Parans( vv ) + " ";
513
514 // create debug info <eInfo>
515 string eInfo;
516 if (err) eInfo= " err=" + IntStr( err );
517
518 DEBUG_Exotic_DB( aCB, MyDB"DBItem","UpdateField", "%s'%s'%s",
519 vv.c_str(), s.c_str(), eInfo.c_str() );
520 return err;
521 } // if
522
523 if (asName) iKey= iLast+1; // create just one element
524
525 // now add the missing key fields
526 int i;
527 for (i= iLast+1; i<=iKey; i++) {
528 string tmp= IntStr( i );
529 if (asName) ts= (char*)fKey;
530 else ts= (char*)tmp.c_str();
531 MakeField( lastK, ts, NULL__null, len ); // create a new key element
532 } // for
533
534 // and the same for each item
535 TDBItem* actV= this;
536 while (ListNext( actV )) {
537 actI= &actV->item;
538 while (true) {
539 lastI= actI;
540 if (!ListNext( actI )) {
541 for (i= iLast+1; i<=iKey; i++) MakeField( lastI, "", NULL__null, actV->len );
542 break;
543 } // if
544 } // loop
545 } // while
546
547 // the task is not yet done => do it again (recursively)
548 return UpdateField( aCB, fKey, fVal, hdI, asName );
549} // UpdateField
550
551
552
553TSyError TDBItem::Field( cAppCharP fKey, TDBItem* hdI, string &s )
554{
555 TDBItemField* actK= &item;
556 TDBItemField* actF= &hdI->item;
557
558 while (ListNext( actK,actF )) {
559 if (strcmp( actK->field.c_str(),fKey )==0) {
560 s= actF->field.c_str();
561 return LOCERR_OK;
562 } // if
563 } // while
564
565 return DB_NotFound;
566} // Field
567
568
569static void ReplaceArrElem( string &s, int n, string arrElem )
570{
571 int pos, v= 0;
572 bool fnd;
573
574 int i= n;
575 while (true) { pos= v;
576 v= s.find( ARR_Sep'\x1D', pos );
577 fnd= v!=(int)string::npos;
578 if (!fnd) v= s.length();
579
580 if (i<=0) break;
581
582 if (!fnd) {
583 if (arrElem.empty()) return; // adding not needed
584 s+= ARR_Sep'\x1D';
585 } // if
586
587 v++; i--;
588 } // while
589
590 s= s.substr( 0, pos ) + arrElem + s.substr( v, s.length()-v );
591} // ReplaceArrElem
592
593
594
595static void CutEmptyEnd( string &s )
596{
597 while (!s.empty()) {
598 uInt32 last= s.length()-1;
599 if (s.rfind( ARR_Sep'\x1D', last )!=last) break; // last char ?
600 s= s.substr ( 0, last ); // if yes, cut it
601 } // while
602} // CutEmptyEnd
603
604
605static int ArrIndex( cAppCharP q, cAppCharP qN )
606{
607 int n= 0;
608
609 if (qN) {
610 string s;
611 s.assign( q, (unsigned int)( qN-q ) );
612 CutBracks( s );
613 n= atoi ( s.c_str() );
614 } // if
615
616 return n;
617} // ArrIndex
618
619
620void TDBItem::Array_TDB( cAppCharP &q, cAppCharP aKey, TDBItem* hdI, string &aVal )
621{
622 cAppCharP qR;
623 cAppCharP qN;
624 cAppCharP qA;
625 bool firstElem= true; // true during first element handling
626 string s; // temporary local string
627 int n;
628
629//aVal= ""; // init string
630//string org;
631 Field( aKey, hdI, aVal ); // as it is now
632
633 while (*q!='\0') {
634 qN= strstr( q,":" );
635 qA= strstr( q,"[" );
636
637 if (!qN) break; // no more regular elements
638 if (!qA || qN<qA) break; // this is not an array element
639
640 if (!firstElem) {
641 s.assign( q, (unsigned int)( qA-q ) );
642 if (!(s==aKey)) break;
643
644 //if (strcmp( aKey, s.c_str() )!=0) break; // not the same key
645 } // if
646
647 n= ArrIndex( qA,qN ); // get the index of this field
648 if (qN) q= qN+1;
649
650 // get the string in-between ':' and '\r' or '\n'
651 qN= strstr( q,"\n" );
652 if (!qN) qN= (cAppCharP)q + strlen( q );
653 qR= qN-1;
654 if (*qR!='\r') qR= qN;
655
656 s.assign( q, (unsigned int)( qR-q ) );
657 /*
658 if (!firstElem) aVal+= ARR_Sep; // separator between elements, even if elements = ""
659 aVal+= s;
660 */
661
662 ReplaceArrElem( aVal, n, s );
663 CutEmptyEnd ( aVal );
664
665 q= qN; if (*q!='\0') q++;
666 firstElem= false;
667 } // while
668} // Array_TDB
669
670
671
672TSyError TDBItem::UpdateFields( void* aCB, cAppCharP aItemData, TDBItem* hdI, bool asName,
673 cAppCharP aNewToken )
674{
675 TSyError err= LOCERR_OK;
676 cAppCharP q = aItemData;
677 cAppCharP qR;
678 string fKey, fVal;
679 int iKey;
680
681 if (!hdI) { hdI= this; ListNext( hdI ); } // special case, if only one item
682 if (!asName) hdI->fToken= aNewToken;
683
684 while (*q!='\0') { // break the string <key>':'<value>'\n' into key and value
685 cAppCharP qN= strstr( q, StdPattern":" ); // normal notation
686 cAppCharP qA= strstr( q,ArrayPattern"[" ); // try array notation
687 //cAppCharP qB= strstr( q, BlobPattern ); // try blob notation
688 //cAppCharP qT= strstr( q, TZPattern ); // try tzname notation
689 cAppCharP qS= strstr( q, ";" ); // semicolon notation
690
691 bool isArr = qA && (!qN || qA<qN); // available and before ":"
692 if (isArr) qN= qA; if (!qN) { err= DB_Fatal; break; }
693
694 //bool isBlob= qB && (!qN || qB<qN); // available and before ":"
695 //if (isBlob) qN= qB; if (!qN) { err= DB_Fatal; break; }
696
697 //bool isTZ = qT && (!qN || qT<qN); // available and before ":"
698 //if (isTZ) qN= qT; if (!qN) { err= DB_Fatal; break; }
699
700 bool isSemi= qS && (!qN || qS<qN); // available and before ":"
701 if (isSemi) qN= qS; if (!qN) { err= DB_Fatal; break; }
702
703 fKey.assign( q, (unsigned int)( qN-q ) );
704 //q= qN; if (!isBlob && !isArr && !isTZ) q++;
705 q= qN; if (!isArr && !isSemi) q++;
706
707 if (isArr) {
708 Array_TDB( q, fKey.c_str(), hdI, fVal );
709 }
710 else { qN= strstr ( q,"\n" ); // allow "\r\n" (what we expect), and also "\n"
711 if (!qN) qN= (cAppCharP)q + strlen( q );
712 qR= qN-1;
713 if (*qR!='\r') qR= qN;
714 fVal.assign( q, (unsigned int)( qR-q ) );
715 q= qN; if (*q!='\0') q++;
716 } // if
717
718 asName|= !NumKey( fKey.c_str(), iKey ); // must interpret them as names !!
719 err= UpdateField( aCB, fKey.c_str(),fVal.c_str(), hdI, asName );
720 if (err) break;
721 } // while
722
723 fChanged= true;
724 return err;
725} // UpdateFields
726
727
728
729bool TDBItem::SameField( cAppCharP fKey,
730 cAppCharP fVal, TDBItem* hdI )
731{
732 string s;
733 TSyError err= Field( fKey, hdI, s );
734 return !err && strcmp( s.c_str(), fVal )==0;
735} // SameField
736
737
738
739void TDBItem::Disp_ItemData( void* aCB, cAppCharP title, cAppCharP aTxt, cAppCharP aItemData )
740{
741 DEBUG_Block ( aCB, title, "", aTxt );
742 DEBUG_ ( aCB, "%s", aItemData );
743 DEBUG_EndBlock( aCB, title );
744} // Disp_ItemData
745
746
747void TDBItem::Disp_Items( void* aCB, cAppCharP txt, bool allFields,
748 cAppCharP specificItem )
749{
750 cAppCharP DIT= "-Disp_Items"; // collapsed display with '-' at the beginning
751
752 string s= txt;
753 if (!s.empty()) s= "call=" + s;
754
755 if (aCB==NULL__null) aCB= fCB; // get the default callback, if not overridden by <aCB>
756 DEBUG_Block ( aCB, DIT, itemID.c_str(), s.c_str() ); // hierarchical log
757
758 TDBItem* actI= this;
759 while (ListNext( actI )) { // for each item do ...
760 if (*specificItem!=0 &&
761 strcmp( specificItem,actI->itemID.c_str() )!=0 ) continue;
762
763 s= Token_Str( actI->fToken );
764 DEBUG_( aCB, "%s= (%s) %s", this->c_str(), actI->c_str(), s.c_str() );
765
766 TDBItemField* actK= &item; // the key identifier
767 TDBItemField* actF= &actI->item; // the item current field
768
769 cAppCharP f;
770 while (ListNext( actK )) { // for each element do ...
771 if (ListNext( actF )) f= actF->field.c_str();
772 else f= ""; // "<???>";
773
774 if (allFields || !actF->field.empty()) { // display only under conditions
775 s= KeyAndField( actK->field.c_str(), f );
776 DEBUG_( aCB, "%s", s.c_str() );
777 } // if
778 } // while
779 } // while
780
781 DEBUG_EndBlock( aCB, DIT );
782} // Disp_Items
783
784
785
786static void AddKey( void* aCB, bool withKey, cAppCharP qA,
787 cAppCharP qR,
788 int n, TDBItemField* &actF, int &len )
789{
790 if (withKey)
791 MakeField( actF, qA,qR, len, true );
792 else { // just numbering, starting with "0"
793 string tmp= IntStr( n-2 );
794 MakeField( actF, tmp.c_str(),NULL__null, len, true );
795 DEBUG_Exotic_DB( aCB, MyDB"DBItem","AddKey", "'%s'", tmp.c_str() );
796 } // if
797} // AddKey
798
799
800TSyError TDBItem::LoadDB( bool withKey, cAppCharP aPrefix, void* aCB )
801{
802 cAppCharP LDB= "-LoadDB"; // collapsed display with '-' at the beginning
803 TSyError err= LOCERR_OK;
804
805 char ch, prv= '\0';
806 char* q;
807 char* qA;
808 char* qR;
809 char* qC;
810
811 // create root structure
812 TDBItem* actL= this;
813 TDBItem* actI= NULL__null;
814 TDBItem* newL= NULL__null;
815 TDBItemField* actF;
816 TDBItemField* actT;
817 bool first= true;
818 int n= 0, nMax= 0;
819
820 if (fLoaded) return LOCERR_OK;
821 if (withKey) init( aPrefix, aCB );
822
823 if (aCB==NULL__null) aCB= fCB; // get the default callback, if not overridden by <aCB>
824
825 FILE* f= fopen( fFileName.c_str(),"rb" );
826 if (!f) err= DB_NotFound; /* empty DB */
827
828 string s= "err=" + IntStr( err );
829 string rslt;
830 DEBUG_Block( aCB, LDB, fFileName.c_str(), s.c_str() ); // hierarchical log
831
832 if (f) {
833 while (true) { // loop ...
834 bool is0D= false; // last line ended with 0D ?
835
836 // get lines, make it compatible for Windows, Mac and Linux
837 q= &ch;
838 s= "";
839 while (true) {
840 if (fread( q, 1,1, f ) != 1) {
841 ; // error ignored
842 }
843
844 if (feof( f )) break;
845
846 if (*q=='\x0D') { is0D= true; break; }
Value stored to 'is0D' is never read
847 if (*q=='\x0A') {
848 if (!is0D) break;
849 *q= prv; is0D= false;
850 } // if
851
852 s += ch;
853 prv= ch;
854 } // while
855 if (s.empty() && feof( f )) break; // .. until end of file
856
857 q= (char*)s.c_str();
858 // remove possible UTF-8 lead-in
859 if ((q[0] & 0xFF) == 0xEF &&
860 (q[1] & 0xFF) == 0xBB &&
861 (q[2] & 0xFF) == 0xBF) {
862 q+=3; // skip UTF-8 lead-in
863 DEBUG_Exotic_DB( aCB, MyDB"DBItem","", "UTF-8 lead-in skipped" );
864 } // if
865
866 if (*q=='\0') continue; // empty line ?
867 ReplaceLoad( q, rslt ); // textdb specific => normal
868 q= (char*)rslt.c_str();
869
870 DEBUG_Exotic_DB( aCB, MyDB"DBItem","", "line='%s'", q );
871
872 // create index tree
873 if (first) {
874 actT= &actL->item;
875
876 n= 0; qA= q;
877 while ( *qA!='\0') {
878 qR= strstr( qA,"\t" );
879 if (!qR) qR= qA + strlen( qA ); // no tab to skip
880
881 if (n>1) // the index itself is not a field
882 AddKey( aCB, withKey, qA,qR, n, actT, actL->len );
883
884 n++;
885 if (*qR=='\0') break;
886 qA= qR+1;
887 } // while
888
889 nMax= n; // keep it for later appending of additional elements
890 actI= actL; // save it for later use
891 } // if
892
893 // create item elements
894 if (!first || !withKey) {
895 n= 0; qA= q;
896 while ( *qA!='\0') {
897 qR= strstr( qA,"\t" );
898 if (!qR) qR= qA + strlen( qA ); // no tab to skip
899
900 if (n==0) { newL= new TDBItem; // create new item root
901 newL->fCB= fCB; // inherit callback
902 actF= &newL->item;
903 qC= strstr( qA,"," );
904 if (!qC || qC>qR) qC= qR;
905
906 /* separate itemID/parentID, if comma separator available */
907 newL->itemID.assign ( qA, (unsigned int)(qC-qA) );
908 if (qC!=qR) qC++;
909 newL->parentID.assign( qC, (unsigned int)(qR-qC) );
910 actL->next= newL;
911 ListNext ( actL );
912 }
913 else if (n==1) newL->fToken.assign( qA, (unsigned int)(qR-qA) );
914 else {
915 MakeField( actF, qA,qR, actL->len, true );
916 //printf( "A: %3d\n", actL->len );
917 actL->len+= ArrLen( "xx", actF->field.c_str() );
918 //printf( "B: %3d\n", actL->len );
919 } // if
920
921 if (n>=nMax && actI!=NULL__null) {
922 if (n>1) {
923 //printf( "indexLenV=%d\n", actI->len );
924 AddKey( aCB, withKey, "???",NULL__null, n, actT, actI->len );
925 //printf( "indexLenN=%d\n", actI->len );
926 } // if
927
928 nMax++; // adapt the new limit
929 } // if
930
931 if (*qR=='\0') break;
932 qA= qR+1; n++;
933 } // while
934 } // if
935
936 actL->len--; // termination is included
937 //printf( "C: %3d\n", actL->len );
938 //printf( "D: '%s'\n", q );
939 first= false; // first run is done
940 } // while
941
942 fclose( f );
943 fLoaded = true;
944 fChanged= false;
945 } // if
946
947 DEBUG_EndBlock( aCB, LDB );
948 return err;
949} // LoadDB
950
951
952TSyError TDBItem::SaveDB( bool withKey, void* aCB )
953{
954 cAppCharP SDB= "-SaveDB"; // collapsed display with '-' at the beginning
955 TSyError err= LOCERR_OK;
956
957 if (!fChanged) return LOCERR_OK; // nothing changed, no saving
958 if (fFileName.empty()) return DB_NotFound;
959
960 if (aCB==NULL__null) aCB= fCB; // get the default callback, if not overridden by <aCB>
961
962 FILE* f= fopen( fFileName.c_str(),"wb" );
963 if (!f) err= DB_NotFound;
964
965 string s= "err=" + IntStr( err );
966 string rslt;
967 DEBUG_Block( aCB, SDB, fFileName.c_str(), s.c_str() ); // hierarchical log
968
969 if (f) {
970 fputs( "\xEF\xBB\xBF", f ); // UTF-8 lead-in
971 DEBUG_Exotic_DB( aCB, MyDB"DBItem","","UTF-8 lead-in written" );
972
973 string line;
974 TDBItem* actI= this;
975 while (withKey || ListNext( actI )) {
976 line = actI->c_str(); // starting with the <itemID>[","<parentID>]
977 line+= "\t";
978 line+= actI->fToken.c_str(); // append the timestamp
979
980 int n= 0;
981 TDBItemField* actF= &actI->item; // the item current field
982 while (ListNext( actF )) { s= ""; // tab separated
983 CStrToStrAppend( actF->field.c_str(), s, false, ARR_Sep'\x1D' ); // don't touch array separator
984 ReplaceSave ( s.c_str(), rslt ); // normal => textdb specific
985
986 line+= "\t" + rslt;
987 n++;
988 } // while
989
990 int ii= line.length(); // remove empty items at the end
991 while (ii>0) {
992 if (line[ --ii ]!='\t') break;
993 }
994 line= line.substr( 0, ii+1 );
995
996 // now log and write the whole line
997 DEBUG_DB( aCB, MyDB"DBItem","", "line='%s'", line.c_str() );
998 fprintf( f, "%s\r\n", line.c_str() );
999 withKey= false;
1000 } // while
1001
1002 fclose( f );
1003 fChanged= false;
1004 } // if
1005
1006 DEBUG_EndBlock( aCB, SDB );
1007 return err;
1008} // SaveDB
1009
1010
1011} /* namespace */
1012/* eof */