File: | libsynthesis/src/sysync_SDK/Sources/dbitem.cpp |
Warning: | line 846, column 27 Value stored to 'is0D' is never read |
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 | |
27 | namespace 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 |
40 | static 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 */ |
65 | void 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 |
83 | static 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 |
91 | string 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> |
114 | int 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 | */ |
133 | string 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 | |
164 | string KeyAndField( TDBItemField* actKey, |
165 | TDBItemField* actField ) { |
166 | return KeyAndField( actKey->field.c_str(), |
167 | actField->field.c_str() ); |
168 | } // KeyAndField |
169 | |
170 | |
171 | void 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 |
210 | void 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) |
220 | void TDBItem::init( cAppCharP aItemID, void* aCB, TDBItem* aNext ) { |
221 | init( aItemID,"", aCB, aNext ); |
222 | } // init |
223 | |
224 | |
225 | // the combined item/parent string |
226 | cAppCharP 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 | */ |
240 | TSyError 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 | */ |
259 | TSyError 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 | */ |
271 | TSyError 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 | |
293 | TSyError 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 | |
308 | TSyError 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 | |
326 | TSyError 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 |
336 | TSyError 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 | |
351 | void 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 | */ |
379 | TSyError 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 | |
437 | TSyError 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 | |
445 | TSyError 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 | */ |
453 | static 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 | */ |
463 | TSyError 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 | |
553 | TSyError 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 | |
569 | static 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 | |
595 | static 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 | |
605 | static 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 | |
620 | void 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 | |
672 | TSyError 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 | |
729 | bool 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 | |
739 | void 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 | |
747 | void 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 | |
786 | static 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 | |
800 | TSyError 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 | |
952 | TSyError 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 */ |