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(_WIN32)
16 #if defined(__BORLANDC__)
17 #define __MFC_COMPAT__                   // To define min/max as macro
18 #endif   // __BORLANDC__
19 //#include <windows.h>
20 #else   // !_WIN32
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  // !_WIN32
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(_WIN32)
40 #include "osutil.h"
41 #endif   // !_WIN32
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(_WIN32)
52 #define GetPrivateProfileSectionNames(S,L,I)  \
53         GetPrivateProfileString(NULL,NULL,"",S,L,I)
54 #endif   // !_WIN32
55 
56 /* -------------- Implementation of the INI classes ------------------ */
57 
58 /***********************************************************************/
59 /*  Constructor.                                                       */
60 /***********************************************************************/
INIDEF(void)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 /***********************************************************************/
DefineAM(PGLOBAL g,LPCSTR,int)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 /***********************************************************************/
GetTable(PGLOBAL g,MODE)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(_WIN32)
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 /***********************************************************************/
TDBINI(PINIDEF tdp)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 
TDBINI(PTDBINI tdbp)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 ???
Clone(PTABS t)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 /***********************************************************************/
GetSeclist(PGLOBAL g)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 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)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 /***********************************************************************/
Cardinality(PGLOBAL g)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 /***********************************************************************/
GetMaxSize(PGLOBAL g)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 /***********************************************************************/
OpenDB(PGLOBAL g)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 /***********************************************************************/
ReadDB(PGLOBAL)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 /***********************************************************************/
WriteDB(PGLOBAL)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 /***********************************************************************/
DeleteDB(PGLOBAL g,int irc)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 /***********************************************************************/
CloseDB(PGLOBAL)346 void TDBINI::CloseDB(PGLOBAL)
347   {
348 #if !defined(_WIN32)
349   PROFILE_Close(Ifile);
350 #endif   // !_WIN32
351   } // end of CloseDB
352 
353 // ------------------------ INICOL functions ----------------------------
354 
355 /***********************************************************************/
356 /*  INICOL public constructor.                                         */
357 /***********************************************************************/
INICOL(PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i,PCSZ)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 /***********************************************************************/
INICOL(INICOL * col1,PTDB tdbp)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 /***********************************************************************/
AllocBuf(PGLOBAL g)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 /***********************************************************************/
SetBuffer(PGLOBAL g,PVAL value,bool ok,bool check)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 /***********************************************************************/
ReadColumn(PGLOBAL)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 /***********************************************************************/
WriteColumn(PGLOBAL g)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 /***********************************************************************/
TDBXIN(PINIDEF tdp)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 
TDBXIN(PTDBXIN tdbp)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 ???
Clone(PTABS t)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 /***********************************************************************/
GetKeylist(PGLOBAL g,char * sec)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 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)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 /***********************************************************************/
Cardinality(PGLOBAL g)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 /***********************************************************************/
GetRecpos(void)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 /***********************************************************************/
SetRecpos(PGLOBAL g,int recpos)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 /***********************************************************************/
OpenDB(PGLOBAL g)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 /***********************************************************************/
ReadDB(PGLOBAL g)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