1 | /************* TabSys C++ Program Source Code File (.CPP) **************/ |
2 | /* PROGRAM NAME: TABSYS */ |
3 | /* ------------- */ |
4 | /* Version 2.4 */ |
5 | /* */ |
6 | /* Author Olivier BERTRAND 2004-2017 */ |
7 | /* */ |
8 | /* This program are the INI/CFG tables classes. */ |
9 | /***********************************************************************/ |
10 | |
11 | /***********************************************************************/ |
12 | /* Include relevant sections of the System header files. */ |
13 | /***********************************************************************/ |
14 | #include "my_global.h" |
15 | #if defined(__WIN__) |
16 | #if defined(__BORLANDC__) |
17 | #define __MFC_COMPAT__ // To define min/max as macro |
18 | #endif // __BORLANDC__ |
19 | //#include <windows.h> |
20 | #else // !__WIN__ |
21 | #if defined(UNIX) |
22 | #include <errno.h> |
23 | #include <unistd.h> |
24 | #else // !UNIX |
25 | #include <io.h> |
26 | #endif // !UNIX |
27 | #include <fcntl.h> |
28 | #endif // !__WIN__ |
29 | |
30 | /***********************************************************************/ |
31 | /* Include application header files: */ |
32 | /* global.h is header containing all global declarations. */ |
33 | /* plgdbsem.h is header containing the DB application declarations. */ |
34 | /* tabdos.h is header containing the TABDOS class declarations. */ |
35 | /***********************************************************************/ |
36 | #include "global.h" |
37 | #include "plgdbsem.h" |
38 | #include "reldef.h" |
39 | #if !defined(__WIN__) |
40 | #include "osutil.h" |
41 | #endif // !__WIN__ |
42 | #include "filamtxt.h" |
43 | #include "tabdos.h" |
44 | #include "tabsys.h" |
45 | #include "tabmul.h" |
46 | #include "inihandl.h" |
47 | |
48 | #define CSZ 36 // Column section name length |
49 | #define CDZ 256 // Column definition length |
50 | |
51 | #if !defined(__WIN__) |
52 | #define GetPrivateProfileSectionNames(S,L,I) \ |
53 | GetPrivateProfileString(NULL,NULL,"",S,L,I) |
54 | #endif // !__WIN__ |
55 | |
56 | /* -------------- Implementation of the INI classes ------------------ */ |
57 | |
58 | /***********************************************************************/ |
59 | /* Constructor. */ |
60 | /***********************************************************************/ |
61 | INIDEF::INIDEF(void) |
62 | { |
63 | Pseudo = 3; |
64 | Fn = NULL; |
65 | Xname = NULL; |
66 | Layout = '?'; |
67 | Ln = 0; |
68 | } // end of INIDEF constructor |
69 | |
70 | /***********************************************************************/ |
71 | /* DefineAM: define specific AM block values from XDB file. */ |
72 | /***********************************************************************/ |
73 | bool INIDEF::DefineAM(PGLOBAL g, LPCSTR, int) |
74 | { |
75 | char buf[8]; |
76 | |
77 | Fn = GetStringCatInfo(g, "Filename" , NULL); |
78 | GetCharCatInfo("Layout" , "C" , buf, sizeof(buf)); |
79 | Layout = toupper(*buf); |
80 | |
81 | if (Fn) { |
82 | char *p = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); |
83 | |
84 | PlugSetPath(p, Fn, GetPath()); |
85 | Fn = p; |
86 | } else { |
87 | strcpy(g->Message, MSG(MISSING_FNAME)); |
88 | return true; |
89 | } // endif Fn |
90 | |
91 | Ln = GetSizeCatInfo("Secsize" , "8K" ); |
92 | Desc = Fn; |
93 | return false; |
94 | } // end of DefineAM |
95 | |
96 | /***********************************************************************/ |
97 | /* GetTable: makes a new TDB of the proper type. */ |
98 | /***********************************************************************/ |
99 | PTDB INIDEF::GetTable(PGLOBAL g, MODE) |
100 | { |
101 | PTDBASE tdbp; |
102 | |
103 | if (Layout == 'C') |
104 | tdbp = new(g) TDBINI(this); |
105 | else |
106 | tdbp = new(g) TDBXIN(this); |
107 | |
108 | if (Multiple) |
109 | tdbp = new(g) TDBMUL(tdbp); // No block optimization yet |
110 | |
111 | return tdbp; |
112 | } // end of GetTable |
113 | |
114 | #if 0 |
115 | /***********************************************************************/ |
116 | /* DeleteTableFile: Delete INI table files using platform API. */ |
117 | /***********************************************************************/ |
118 | bool INIDEF::DeleteTableFile(PGLOBAL g) |
119 | { |
120 | char filename[_MAX_PATH]; |
121 | bool rc; |
122 | |
123 | // Delete the INI table file if not protected |
124 | if (!IsReadOnly()) { |
125 | PlugSetPath(filename, Fn, GetPath()); |
126 | #if defined(__WIN__) |
127 | rc = !DeleteFile(filename); |
128 | #else // UNIX |
129 | rc = remove(filename); |
130 | #endif // UNIX |
131 | } else |
132 | rc =true; |
133 | |
134 | return rc; // Return true if error |
135 | } // end of DeleteTableFile |
136 | #endif // 0 |
137 | |
138 | /* ------------------------------------------------------------------- */ |
139 | |
140 | /***********************************************************************/ |
141 | /* Implementation of the TDBINI class. */ |
142 | /***********************************************************************/ |
143 | TDBINI::TDBINI(PINIDEF tdp) : TDBASE(tdp) |
144 | { |
145 | Ifile = tdp->Fn; |
146 | Seclist = NULL; |
147 | Section = NULL; |
148 | Seclen = tdp->Ln; |
149 | N = 0; |
150 | } // end of TDBINI constructor |
151 | |
152 | TDBINI::TDBINI(PTDBINI tdbp) : TDBASE(tdbp) |
153 | { |
154 | Ifile = tdbp->Ifile; |
155 | Seclist = tdbp->Seclist; |
156 | Section = tdbp->Section; |
157 | Seclen = tdbp->Seclen; |
158 | N = tdbp->N; |
159 | } // end of TDBINI copy constructor |
160 | |
161 | // Is this really useful ??? |
162 | PTDB TDBINI::Clone(PTABS t) |
163 | { |
164 | PTDB tp; |
165 | PINICOL cp1, cp2; |
166 | PGLOBAL g = t->G; |
167 | |
168 | tp = new(g) TDBINI(this); |
169 | |
170 | for (cp1 = (PINICOL)Columns; cp1; cp1 = (PINICOL)cp1->GetNext()) { |
171 | cp2 = new(g) INICOL(cp1, tp); // Make a copy |
172 | NewPointer(t, cp1, cp2); |
173 | } // endfor cp1 |
174 | |
175 | return tp; |
176 | } // end of Clone |
177 | |
178 | /***********************************************************************/ |
179 | /* Get the section list from the INI file. */ |
180 | /***********************************************************************/ |
181 | char *TDBINI::GetSeclist(PGLOBAL g) |
182 | { |
183 | if (trace(1)) |
184 | htrc("GetSeclist: Seclist=%p\n" , Seclist); |
185 | |
186 | if (!Seclist) { |
187 | // Result will be retrieved from the INI file |
188 | Seclist = (char*)PlugSubAlloc(g, NULL, Seclen); |
189 | GetPrivateProfileSectionNames(Seclist, Seclen, Ifile); |
190 | } // endif Seclist |
191 | |
192 | return Seclist; |
193 | } // end of GetSeclist |
194 | |
195 | /***********************************************************************/ |
196 | /* Allocate INI column description block. */ |
197 | /***********************************************************************/ |
198 | PCOL TDBINI::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) |
199 | { |
200 | return new(g) INICOL(cdp, this, cprec, n); |
201 | } // end of MakeCol |
202 | |
203 | /***********************************************************************/ |
204 | /* INI Cardinality: returns the number of sections in the INI file. */ |
205 | /***********************************************************************/ |
206 | int TDBINI::Cardinality(PGLOBAL g) |
207 | { |
208 | if (!g) |
209 | return 1; |
210 | |
211 | if (Cardinal < 0) { |
212 | // Count the number of sections from the section list |
213 | char *p = GetSeclist(g); |
214 | |
215 | Cardinal = 0; |
216 | |
217 | if (p) |
218 | for (; *p; p += (strlen(p) + 1)) |
219 | Cardinal++; |
220 | |
221 | } // endif Cardinal |
222 | |
223 | return Cardinal; |
224 | } // end of Cardinality |
225 | |
226 | /***********************************************************************/ |
227 | /* INI GetMaxSize: returns the table cardinality. */ |
228 | /***********************************************************************/ |
229 | int TDBINI::GetMaxSize(PGLOBAL g) |
230 | { |
231 | if (MaxSize < 0) |
232 | MaxSize = Cardinality(g); |
233 | |
234 | return MaxSize; |
235 | } // end of GetMaxSize |
236 | |
237 | /***********************************************************************/ |
238 | /* INI Access Method opening routine. */ |
239 | /***********************************************************************/ |
240 | bool TDBINI::OpenDB(PGLOBAL g) |
241 | { |
242 | PINICOL colp; |
243 | |
244 | if (Use == USE_OPEN) { |
245 | #if 0 |
246 | if (To_Kindex) |
247 | /*****************************************************************/ |
248 | /* Table is to be accessed through a sorted index table. */ |
249 | /*****************************************************************/ |
250 | To_Kindex->Reset(); |
251 | #endif // 0 |
252 | Section = NULL; |
253 | N = 0; |
254 | return false; |
255 | } // endif use |
256 | |
257 | /*********************************************************************/ |
258 | /* OpenDB: initialize the INI file processing. */ |
259 | /*********************************************************************/ |
260 | GetSeclist(g); |
261 | Use = USE_OPEN; // Do it now in case we are recursively called |
262 | |
263 | /*********************************************************************/ |
264 | /* Allocate the buffers that will contain key values. */ |
265 | /*********************************************************************/ |
266 | for (colp = (PINICOL)Columns; colp; colp = (PINICOL)colp->GetNext()) |
267 | if (!colp->IsSpecial()) // Not a pseudo column |
268 | colp->AllocBuf(g); |
269 | |
270 | if (trace(1)) |
271 | htrc("INI OpenDB: seclist=%s seclen=%d ifile=%s\n" , |
272 | Seclist, Seclen, Ifile); |
273 | |
274 | return false; |
275 | } // end of OpenDB |
276 | |
277 | /***********************************************************************/ |
278 | /* Data Base read routine for INI access method. */ |
279 | /***********************************************************************/ |
280 | int TDBINI::ReadDB(PGLOBAL) |
281 | { |
282 | /*********************************************************************/ |
283 | /* Now start the pseudo reading process. */ |
284 | /*********************************************************************/ |
285 | if (!Section) |
286 | Section = Seclist; |
287 | else |
288 | Section += (strlen(Section) + 1); |
289 | |
290 | if (trace(2)) |
291 | htrc("INI ReadDB: section=%s N=%d\n" , Section, N); |
292 | |
293 | N++; |
294 | return (*Section) ? RC_OK : RC_EF; |
295 | } // end of ReadDB |
296 | |
297 | /***********************************************************************/ |
298 | /* WriteDB: Data Base write routine for INI access methods. */ |
299 | /***********************************************************************/ |
300 | int TDBINI::WriteDB(PGLOBAL) |
301 | { |
302 | // This is to check that section name was given when inserting |
303 | if (Mode == MODE_INSERT) |
304 | Section = NULL; |
305 | |
306 | // Nothing else to do because all was done in WriteColumn |
307 | return RC_OK; |
308 | } // end of WriteDB |
309 | |
310 | /***********************************************************************/ |
311 | /* Data Base delete line routine for INI access methods. */ |
312 | /***********************************************************************/ |
313 | int TDBINI::DeleteDB(PGLOBAL g, int irc) |
314 | { |
315 | switch (irc) { |
316 | case RC_EF: |
317 | break; |
318 | case RC_FX: |
319 | while (ReadDB(g) == RC_OK) |
320 | if (!WritePrivateProfileString(Section, NULL, NULL, Ifile)) { |
321 | sprintf(g->Message, "Error %d accessing %s" , |
322 | GetLastError(), Ifile); |
323 | return RC_FX; |
324 | } // endif |
325 | |
326 | break; |
327 | default: |
328 | if (!Section) { |
329 | strcpy(g->Message, MSG(NO_SECTION_NAME)); |
330 | return RC_FX; |
331 | } else |
332 | if (!WritePrivateProfileString(Section, NULL, NULL, Ifile)) { |
333 | sprintf(g->Message, "Error %d accessing %s" , |
334 | GetLastError(), Ifile); |
335 | return RC_FX; |
336 | } // endif rc |
337 | |
338 | } // endswitch irc |
339 | |
340 | return RC_OK; |
341 | } // end of DeleteDB |
342 | |
343 | /***********************************************************************/ |
344 | /* Data Base close routine for INI access methods. */ |
345 | /***********************************************************************/ |
346 | void TDBINI::CloseDB(PGLOBAL) |
347 | { |
348 | #if !defined(__WIN__) |
349 | PROFILE_Close(Ifile); |
350 | #endif // !__WIN__ |
351 | } // end of CloseDB |
352 | |
353 | // ------------------------ INICOL functions ---------------------------- |
354 | |
355 | /***********************************************************************/ |
356 | /* INICOL public constructor. */ |
357 | /***********************************************************************/ |
358 | INICOL::INICOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ) |
359 | : COLBLK(cdp, tdbp, i) |
360 | { |
361 | if (cprec) { |
362 | Next = cprec->GetNext(); |
363 | cprec->SetNext(this); |
364 | } else { |
365 | Next = tdbp->GetColumns(); |
366 | tdbp->SetColumns(this); |
367 | } // endif cprec |
368 | |
369 | // Set additional INI access method information for column. |
370 | Valbuf = NULL; |
371 | Flag = cdp->GetOffset(); |
372 | Long = cdp->GetLong(); |
373 | To_Val = NULL; |
374 | } // end of INICOL constructor |
375 | |
376 | /***********************************************************************/ |
377 | /* INICOL constructor used for copying columns. */ |
378 | /* tdbp is the pointer to the new table descriptor. */ |
379 | /***********************************************************************/ |
380 | INICOL::INICOL(INICOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) |
381 | { |
382 | Valbuf = col1->Valbuf; |
383 | Flag = col1->Flag; |
384 | Long = col1->Long; |
385 | To_Val = col1->To_Val; |
386 | } // end of INICOL copy constructor |
387 | |
388 | /***********************************************************************/ |
389 | /* Allocate a buffer of the proper size. */ |
390 | /***********************************************************************/ |
391 | void INICOL::AllocBuf(PGLOBAL g) |
392 | { |
393 | if (!Valbuf) |
394 | Valbuf = (char*)PlugSubAlloc(g, NULL, Long + 1); |
395 | |
396 | } // end of AllocBuf |
397 | |
398 | /***********************************************************************/ |
399 | /* SetBuffer: prepare a column block for write operation. */ |
400 | /***********************************************************************/ |
401 | bool INICOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) |
402 | { |
403 | if (!(To_Val = value)) { |
404 | sprintf(g->Message, MSG(VALUE_ERROR), Name); |
405 | return true; |
406 | } else if (Buf_Type == value->GetType()) { |
407 | // Values are of the (good) column type |
408 | if (Buf_Type == TYPE_DATE) { |
409 | // If any of the date values is formatted |
410 | // output format must be set for the receiving table |
411 | if (GetDomain() || ((DTVAL *)value)->IsFormatted()) |
412 | goto newval; // This will make a new value; |
413 | |
414 | } else if (Buf_Type == TYPE_DOUBLE || Buf_Type == TYPE_DECIM) |
415 | // Float values must be written with the correct (column) precision |
416 | // Note: maybe this should be forced by ShowValue instead of this ? |
417 | value->SetPrec(GetScale()); |
418 | |
419 | Value = value; // Directly access the external value |
420 | } else { |
421 | // Values are not of the (good) column type |
422 | if (check) { |
423 | sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name, |
424 | GetTypeName(Buf_Type), GetTypeName(value->GetType())); |
425 | return true; |
426 | } // endif check |
427 | |
428 | newval: |
429 | if (InitValue(g)) // Allocate the matching value block |
430 | return true; |
431 | |
432 | } // endif's Value, Buf_Type |
433 | |
434 | // Allocate the internal value buffer |
435 | AllocBuf(g); |
436 | |
437 | // Because Colblk's have been made from a copy of the original TDB in |
438 | // case of Update, we must reset them to point to the original one. |
439 | if (To_Tdb->GetOrig()) |
440 | To_Tdb = (PTDB)To_Tdb->GetOrig(); |
441 | |
442 | // Set the Column |
443 | Status = (ok) ? BUF_EMPTY : BUF_NO; |
444 | return false; |
445 | } // end of SetBuffer |
446 | |
447 | /***********************************************************************/ |
448 | /* ReadColumn: what this routine does is to access the key buffer set */ |
449 | /* from the corresponding section, extract from it the key value */ |
450 | /* corresponding to this column name and convert it to buffer type. */ |
451 | /***********************************************************************/ |
452 | void INICOL::ReadColumn(PGLOBAL) |
453 | { |
454 | PTDBINI tdbp = (PTDBINI)To_Tdb; |
455 | |
456 | if (trace(2)) |
457 | htrc("INI ReadColumn: col %s R%d flag=%d\n" , |
458 | Name, tdbp->GetTdb_No(), Flag); |
459 | |
460 | /*********************************************************************/ |
461 | /* Get the key value from the INI file. */ |
462 | /*********************************************************************/ |
463 | switch (Flag) { |
464 | case 1: |
465 | strncpy(Valbuf, tdbp->Section, Long); // Section name |
466 | Valbuf[Long] = '\0'; |
467 | break; |
468 | default: |
469 | GetPrivateProfileString(tdbp->Section, Name, "\b" , |
470 | Valbuf, Long + 1, tdbp->Ifile); |
471 | break; |
472 | } // endswitch Flag |
473 | |
474 | // Missing keys are interpreted as null values |
475 | if (!strcmp(Valbuf, "\b" )) { |
476 | if (Nullable) |
477 | Value->SetNull(true); |
478 | |
479 | Value->Reset(); // Null value |
480 | } else |
481 | Value->SetValue_psz(Valbuf); |
482 | |
483 | } // end of ReadColumn |
484 | |
485 | /***********************************************************************/ |
486 | /* WriteColumn: what this routine does is to access the last line */ |
487 | /* read from the corresponding table, and rewrite the field */ |
488 | /* corresponding to this column from the column buffer and type. */ |
489 | /***********************************************************************/ |
490 | void INICOL::WriteColumn(PGLOBAL g) |
491 | { |
492 | char *p; |
493 | bool rc; |
494 | PTDBINI tdbp = (PTDBINI)To_Tdb; |
495 | |
496 | if (trace(2)) |
497 | htrc("INI WriteColumn: col %s R%d coluse=%.4X status=%.4X\n" , |
498 | Name, tdbp->GetTdb_No(), ColUse, Status); |
499 | |
500 | /*********************************************************************/ |
501 | /* Get the string representation of Value according to column type. */ |
502 | /*********************************************************************/ |
503 | if (Value != To_Val) |
504 | Value->SetValue_pval(To_Val, false); // Convert the updated value |
505 | |
506 | // Null key are missing keys |
507 | if (Value->IsNull()) |
508 | return; |
509 | |
510 | p = Value->GetCharString(Valbuf); |
511 | |
512 | if (strlen(p) > (unsigned)Long) { |
513 | sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long); |
514 | throw 31; |
515 | } else if (Flag == 1) { |
516 | if (tdbp->Mode == MODE_UPDATE) { |
517 | strcpy(g->Message, MSG(NO_SEC_UPDATE)); |
518 | throw 31; |
519 | } else if (*p) { |
520 | tdbp->Section = p; |
521 | } else |
522 | tdbp->Section = NULL; |
523 | |
524 | return; |
525 | } else if (!tdbp->Section) { |
526 | strcpy(g->Message, MSG(SEC_NAME_FIRST)); |
527 | throw 31; |
528 | } // endif's |
529 | |
530 | /*********************************************************************/ |
531 | /* Updating must be done only when not in checking pass. */ |
532 | /*********************************************************************/ |
533 | if (Status) { |
534 | rc = WritePrivateProfileString(tdbp->Section, Name, p, tdbp->Ifile); |
535 | |
536 | if (!rc) { |
537 | sprintf(g->Message, "Error %d writing to %s" , |
538 | GetLastError(), tdbp->Ifile); |
539 | throw 31; |
540 | } // endif rc |
541 | |
542 | } // endif Status |
543 | |
544 | } // end of WriteColumn |
545 | |
546 | /* ------------------------------------------------------------------- */ |
547 | |
548 | /***********************************************************************/ |
549 | /* Implementation of the TDBXIN class. */ |
550 | /***********************************************************************/ |
551 | TDBXIN::TDBXIN(PINIDEF tdp) : TDBINI(tdp) |
552 | { |
553 | Keylist = NULL; |
554 | Keycur = NULL; |
555 | Keylen = Seclen; |
556 | Oldsec = -1; |
557 | } // end of TDBXIN constructor |
558 | |
559 | TDBXIN::TDBXIN(PTDBXIN tdbp) : TDBINI(tdbp) |
560 | { |
561 | Keylist = tdbp->Keylist; |
562 | Keycur = tdbp->Keycur; |
563 | Keylen = tdbp->Keylen; |
564 | Oldsec = tdbp->Oldsec; |
565 | } // end of TDBXIN copy constructor |
566 | |
567 | // Is this really useful ??? |
568 | PTDB TDBXIN::Clone(PTABS t) |
569 | { |
570 | PTDB tp; |
571 | PXINCOL cp1, cp2; |
572 | PGLOBAL g = t->G; |
573 | |
574 | tp = new(g) TDBXIN(this); |
575 | |
576 | for (cp1 = (PXINCOL)Columns; cp1; cp1 = (PXINCOL)cp1->GetNext()) { |
577 | cp2 = new(g) XINCOL(cp1, tp); // Make a copy |
578 | NewPointer(t, cp1, cp2); |
579 | } // endfor cp1 |
580 | |
581 | return tp; |
582 | } // end of Clone |
583 | |
584 | /***********************************************************************/ |
585 | /* Get the key list from the INI file. */ |
586 | /***********************************************************************/ |
587 | char *TDBXIN::GetKeylist(PGLOBAL g, char *sec) |
588 | { |
589 | if (!Keylist) |
590 | Keylist = (char*)PlugSubAlloc(g, NULL, Keylen); |
591 | |
592 | GetPrivateProfileString(sec, NULL, "" , Keylist, Keylen, Ifile); |
593 | return Keylist; |
594 | } // end of GetKeylist |
595 | |
596 | /***********************************************************************/ |
597 | /* Allocate XIN column description block. */ |
598 | /***********************************************************************/ |
599 | PCOL TDBXIN::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) |
600 | { |
601 | return new(g) XINCOL(cdp, this, cprec, n); |
602 | } // end of MakeCol |
603 | |
604 | /***********************************************************************/ |
605 | /* XIN Cardinality: returns the number of keys in the XIN file. */ |
606 | /***********************************************************************/ |
607 | int TDBXIN::Cardinality(PGLOBAL g) |
608 | { |
609 | if (!g) |
610 | return 1; |
611 | |
612 | if (Cardinal < 0) { |
613 | // Count the number of keys from the section list |
614 | char *k, *p = GetSeclist(g); |
615 | |
616 | Cardinal = 0; |
617 | |
618 | if (p) |
619 | for (; *p; p += (strlen(p) + 1)) |
620 | for (k = GetKeylist(g, p); *k; k += (strlen(k) + 1)) |
621 | Cardinal++; |
622 | |
623 | } // endif Cardinal |
624 | |
625 | return Cardinal; |
626 | } // end of Cardinality |
627 | |
628 | /***********************************************************************/ |
629 | /* Record position is Section+Key. */ |
630 | /***********************************************************************/ |
631 | int TDBXIN::GetRecpos(void) |
632 | { |
633 | union { |
634 | short X[2]; // Section and Key offsets |
635 | int Xpos; // File position |
636 | }; // end of union |
637 | |
638 | X[0] = (short)(Section - Seclist); |
639 | X[1] = (short)(Keycur - Keylist); |
640 | return Xpos; |
641 | } // end of GetRecpos |
642 | |
643 | /***********************************************************************/ |
644 | /* Record position is Section+Key. */ |
645 | /***********************************************************************/ |
646 | bool TDBXIN::SetRecpos(PGLOBAL g, int recpos) |
647 | { |
648 | union { |
649 | short X[2]; // Section and Key offsets |
650 | int Xpos; // File position |
651 | }; // end of union |
652 | |
653 | Xpos = recpos; |
654 | |
655 | if (X[0] != Oldsec) { |
656 | Section = Seclist + X[0]; |
657 | Keycur = GetKeylist(g, Section) + X[1]; |
658 | Oldsec = X[0]; |
659 | } else |
660 | Keycur = Keylist + X[1]; |
661 | |
662 | return false; |
663 | } // end of SetRecpos |
664 | |
665 | /***********************************************************************/ |
666 | /* XIN Access Method opening routine. */ |
667 | /***********************************************************************/ |
668 | bool TDBXIN::OpenDB(PGLOBAL g) |
669 | { |
670 | Oldsec = -1; // To replace the table at its beginning |
671 | return TDBINI::OpenDB(g); |
672 | } // end of OpenDB |
673 | |
674 | /***********************************************************************/ |
675 | /* Data Base read routine for XIN access method. */ |
676 | /***********************************************************************/ |
677 | int TDBXIN::ReadDB(PGLOBAL g) |
678 | { |
679 | /*********************************************************************/ |
680 | /* Now start the pseudo reading process. */ |
681 | /*********************************************************************/ |
682 | #if 0 // XIN tables are not indexable |
683 | if (To_Kindex) { |
684 | /*******************************************************************/ |
685 | /* Reading is by an index table. */ |
686 | /*******************************************************************/ |
687 | int recpos = To_Kindex->Fetch(g); |
688 | |
689 | switch (recpos) { |
690 | case -1: // End of file reached |
691 | return RC_EF; |
692 | case -2: // No match for join |
693 | return RC_NF; |
694 | case -3: // Same record as last non null one |
695 | return RC_OK; |
696 | default: |
697 | SetRecpos(g, recpos); |
698 | } // endswitch recpos |
699 | |
700 | } else { |
701 | #endif // 0 |
702 | do { |
703 | if (!Keycur || !*Keycur) { |
704 | if (!Section) |
705 | Section = Seclist; |
706 | else |
707 | Section += (strlen(Section) + 1); |
708 | |
709 | if (*Section) |
710 | Keycur = GetKeylist(g, Section); |
711 | else |
712 | return RC_EF; |
713 | |
714 | } else |
715 | Keycur += (strlen(Keycur) + 1); |
716 | |
717 | } while (!*Keycur); |
718 | |
719 | N++; |
720 | //} // endif To_Kindex |
721 | |
722 | return RC_OK; |
723 | } // end of ReadDB |
724 | |
725 | /***********************************************************************/ |
726 | /* WriteDB: Data Base write routine for XIN access methods. */ |
727 | /***********************************************************************/ |
728 | int TDBXIN::WriteDB(PGLOBAL) |
729 | { |
730 | // To check that section and key names were given when inserting |
731 | if (Mode == MODE_INSERT) { |
732 | Section = NULL; |
733 | Keycur = NULL; |
734 | } // endif Mode |
735 | |
736 | // Nothing else to do because all was done in WriteColumn |
737 | return RC_OK; |
738 | } // end of WriteDB |
739 | |
740 | /***********************************************************************/ |
741 | /* Data Base delete line routine for XIN access methods. */ |
742 | /***********************************************************************/ |
743 | int TDBXIN::DeleteDB(PGLOBAL g, int irc) |
744 | { |
745 | if (irc == RC_EF) { |
746 | } else if (irc == RC_FX) { |
747 | for (Section = Seclist; *Section; Section += (strlen(Section) + 1)) |
748 | if (!WritePrivateProfileString(Section, NULL, NULL, Ifile)) { |
749 | sprintf(g->Message, "Error %d accessing %s" , |
750 | GetLastError(), Ifile); |
751 | return RC_FX; |
752 | } // endif |
753 | |
754 | } else if (!Section) { |
755 | strcpy(g->Message, MSG(NO_SECTION_NAME)); |
756 | return RC_FX; |
757 | } else |
758 | if (!WritePrivateProfileString(Section, Keycur, NULL, Ifile)) { |
759 | sprintf(g->Message, "Error %d accessing %s" , |
760 | GetLastError(), Ifile); |
761 | return RC_FX; |
762 | } // endif |
763 | |
764 | return RC_OK; |
765 | } // end of DeleteDB |
766 | |
767 | // ------------------------ XINCOL functions ---------------------------- |
768 | |
769 | /***********************************************************************/ |
770 | /* XINCOL public constructor. */ |
771 | /***********************************************************************/ |
772 | XINCOL::XINCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am) |
773 | : INICOL(cdp, tdbp, cprec, i, am) |
774 | { |
775 | } // end of XINCOL constructor |
776 | |
777 | /***********************************************************************/ |
778 | /* XINCOL constructor used for copying columns. */ |
779 | /* tdbp is the pointer to the new table descriptor. */ |
780 | /***********************************************************************/ |
781 | XINCOL::XINCOL(XINCOL *col1, PTDB tdbp) : INICOL(col1, tdbp) |
782 | { |
783 | } // end of XINCOL copy constructor |
784 | |
785 | /***********************************************************************/ |
786 | /* ReadColumn: what this routine does is to access the key buffer set */ |
787 | /* from the corresponding section, extract from it the key value */ |
788 | /* corresponding to this column name and convert it to buffer type. */ |
789 | /***********************************************************************/ |
790 | void XINCOL::ReadColumn(PGLOBAL) |
791 | { |
792 | PTDBXIN tdbp = (PTDBXIN)To_Tdb; |
793 | |
794 | /*********************************************************************/ |
795 | /* Get the key value from the XIN file. */ |
796 | /*********************************************************************/ |
797 | switch (Flag) { |
798 | case 1: |
799 | strncpy(Valbuf, tdbp->Section, Long); // Section name |
800 | Valbuf[Long] = '\0'; |
801 | break; |
802 | case 2: |
803 | strncpy(Valbuf, tdbp->Keycur, Long); // Key name |
804 | Valbuf[Long] = '\0'; |
805 | break; |
806 | default: |
807 | GetPrivateProfileString(tdbp->Section, tdbp->Keycur, "" , |
808 | Valbuf, Long + 1, tdbp->Ifile); |
809 | break; |
810 | } // endswitch Flag |
811 | |
812 | Value->SetValue_psz(Valbuf); |
813 | } // end of ReadColumn |
814 | |
815 | /***********************************************************************/ |
816 | /* WriteColumn: what this routine does is to access the last line */ |
817 | /* read from the corresponding table, and rewrite the field */ |
818 | /* corresponding to this column from the column buffer and type. */ |
819 | /***********************************************************************/ |
820 | void XINCOL::WriteColumn(PGLOBAL g) |
821 | { |
822 | char *p; |
823 | bool rc; |
824 | PTDBXIN tdbp = (PTDBXIN)To_Tdb; |
825 | |
826 | if (trace(2)) |
827 | htrc("XIN WriteColumn: col %s R%d coluse=%.4X status=%.4X\n" , |
828 | Name, tdbp->GetTdb_No(), ColUse, Status); |
829 | |
830 | /*********************************************************************/ |
831 | /* Get the string representation of Value according to column type. */ |
832 | /*********************************************************************/ |
833 | if (Value != To_Val) |
834 | Value->SetValue_pval(To_Val, false); // Convert the updated value |
835 | |
836 | p = Value->GetCharString(Valbuf); |
837 | |
838 | if (strlen(p) > (unsigned)Long) { |
839 | sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long); |
840 | throw 31; |
841 | } else if (Flag == 1) { |
842 | if (tdbp->Mode == MODE_UPDATE) { |
843 | strcpy(g->Message, MSG(NO_SEC_UPDATE)); |
844 | throw 31; |
845 | } else if (*p) { |
846 | tdbp->Section = p; |
847 | } else |
848 | tdbp->Section = NULL; |
849 | |
850 | return; |
851 | } else if (Flag == 2) { |
852 | if (tdbp->Mode == MODE_UPDATE) { |
853 | strcpy(g->Message, MSG(NO_KEY_UPDATE)); |
854 | throw 31; |
855 | } else if (*p) { |
856 | tdbp->Keycur = p; |
857 | } else |
858 | tdbp->Keycur = NULL; |
859 | |
860 | return; |
861 | } else if (!tdbp->Section || !tdbp->Keycur) { |
862 | strcpy(g->Message, MSG(SEC_KEY_FIRST)); |
863 | throw 31; |
864 | } // endif's |
865 | |
866 | /*********************************************************************/ |
867 | /* Updating must be done only when not in checking pass. */ |
868 | /*********************************************************************/ |
869 | if (Status) { |
870 | rc = WritePrivateProfileString(tdbp->Section, tdbp->Keycur, p, tdbp->Ifile); |
871 | |
872 | if (!rc) { |
873 | sprintf(g->Message, "Error %d writing to %s" , |
874 | GetLastError(), tdbp->Ifile); |
875 | throw 31; |
876 | } // endif rc |
877 | |
878 | } // endif Status |
879 | |
880 | } // end of WriteColumn |
881 | |
882 | /* ------------------------ End of System ---------------------------- */ |
883 | |
884 | |
885 | |