1 /************* TabMul C++ Program Source Code File (.CPP) **************/
2 /* PROGRAM NAME: TABMUL                                                */
3 /* -------------                                                       */
4 /*  Version 1.9                                                        */
5 /*                                                                     */
6 /* COPYRIGHT:                                                          */
7 /* ----------                                                          */
8 /*  (C) Copyright to PlugDB Software Development          2003 - 2017  */
9 /*  Author: Olivier BERTRAND                                           */
10 /*                                                                     */
11 /* WHAT THIS PROGRAM DOES:                                             */
12 /* -----------------------                                             */
13 /*  This program are the TDBMUL class DB routines.                     */
14 /*                                                                     */
15 /* WHAT YOU NEED TO COMPILE THIS PROGRAM:                              */
16 /* --------------------------------------                              */
17 /*                                                                     */
18 /*  REQUIRED FILES:                                                    */
19 /*  ---------------                                                    */
20 /*    TABMUL.CPP     - Source code                                     */
21 /*    PLGDBSEM.H     - DB application declaration file                 */
22 /*    TABDOS.H       - TABDOS classes declaration file                 */
23 /*    TABMUL.H       - TABFIX classes declaration file                 */
24 /*    GLOBAL.H       - Global declaration file                         */
25 /*                                                                     */
26 /*  REQUIRED LIBRARIES:                                                */
27 /*  -------------------                                                */
28 /*    Large model C library                                            */
29 /*                                                                     */
30 /*  REQUIRED PROGRAMS:                                                 */
31 /*  ------------------                                                 */
32 /*    IBM, Borland, GNU or Microsoft C++ Compiler and Linker           */
33 /*                                                                     */
34 /***********************************************************************/
35 
36 /***********************************************************************/
37 /*  Include relevant section of system dependant header files.         */
38 /***********************************************************************/
39 #include "my_global.h"
40 #if defined(_WIN32)
41 #include <stdlib.h>
42 #include <stdio.h>
43 #if defined(__BORLANDC__)
44 #define __MFC_COMPAT__                   // To define min/max as macro
45 #endif
46 //#include <windows.h>
47 #if defined(PATHMATCHSPEC)
48 #include "Shlwapi.h"
49 //using namespace std;
50 #pragma comment(lib,"shlwapi.lib")
51 #endif   //	PATHMATCHSPEC
52 #else
53 #if defined(UNIX)
54 #include <fnmatch.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include "osutil.h"
60 #else
61 //#include <io.h>
62 #endif
63 //#include <fcntl.h>
64 #endif
65 
66 /***********************************************************************/
67 /*  Include application header files:                                  */
68 /***********************************************************************/
69 #include "global.h"      // global declarations
70 #include "plgdbsem.h"    // DB application declarations
71 #include "reldef.h"      // DB definition declares
72 #include "filamtxt.h"
73 #include "tabdos.h"      // TDBDOS and DOSCOL class dcls
74 #include "tabmul.h"      // TDBMUL and MULCOL classes dcls
75 
76 /* ------------------------- Class TDBMUL ---------------------------- */
77 
78 /***********************************************************************/
79 /*  TABMUL constructors.                                               */
80 /***********************************************************************/
TDBMUL(PTDB tdbp)81 TDBMUL::TDBMUL(PTDB tdbp) : TDBASE(tdbp->GetDef())
82   {
83   Tdbp = tdbp;
84   Filenames = NULL;
85   Rows = 0;
86   Mul = tdbp->GetDef()->GetMultiple();
87   NumFiles = 0;
88   iFile = 0;
89   } // end of TDBMUL standard constructor
90 
TDBMUL(PTDBMUL tdbp)91 TDBMUL::TDBMUL(PTDBMUL tdbp) : TDBASE(tdbp)
92   {
93   Tdbp = tdbp->Tdbp;
94   Filenames = tdbp->Filenames;
95   Rows = tdbp->Rows;
96   Mul = tdbp->Mul;
97   NumFiles = tdbp->NumFiles;
98   iFile = tdbp->iFile;
99   } // end of TDBMUL copy constructor
100 
101 // Method
Clone(PTABS t)102 PTDB TDBMUL::Clone(PTABS t)
103   {
104   PTDBMUL tp;
105   PGLOBAL g = t->G;        // Is this really useful ???
106 
107   tp = new(g) TDBMUL(this);
108   tp->Tdbp = Tdbp->Clone(t);
109   tp->Columns = tp->Tdbp->GetColumns();
110   return tp;
111   } // end of Clone
112 
Duplicate(PGLOBAL g)113 PTDB TDBMUL::Duplicate(PGLOBAL g)
114   {
115   PTDBMUL tmup = new(g) TDBMUL(this);
116 
117   tmup->Tdbp = Tdbp->Duplicate(g);
118   return tmup;
119   } // end of Duplicate
120 
121 /***********************************************************************/
122 /*  Initializes the table filename list.                               */
123 /*  Note: tables created by concatenating the file components without  */
124 /*  specifying the LRECL value (that should be restricted to _MAX_PATH)*/
125 /*  have a LRECL that is the sum of the lengths of all components.     */
126 /*  This is why we use a big filename array to take care of that.      */
127 /***********************************************************************/
InitFileNames(PGLOBAL g)128 bool TDBMUL::InitFileNames(PGLOBAL g)
129   {
130 #define PFNZ  4096
131 #define FNSZ  (_MAX_DRIVE+_MAX_DIR+_MAX_FNAME+_MAX_EXT)
132 	PTDBDIR dirp;
133 	PSZ     pfn[PFNZ];
134   PSZ     filename;
135   int     rc, n = 0;
136 
137   if (trace(1))
138     htrc("in InitFileName: fn[]=%d\n", FNSZ);
139 
140   filename = (char*)PlugSubAlloc(g, NULL, FNSZ);
141 
142   // The sub table may need to refer to the Table original block
143   Tdbp->SetTable(To_Table);         // Was not set at construction
144 
145   PlugSetPath(filename, Tdbp->GetFile(g), Tdbp->GetPath());
146 
147   if (trace(1))
148     htrc("InitFileName: fn='%s'\n", filename);
149 
150   if (Mul != 2) {
151     /*******************************************************************/
152     /*  To_File is a multiple name with special characters             */
153     /*******************************************************************/
154 		if (Mul == 1)
155 			dirp = new(g) TDBDIR(PlugDup(g, filename));
156 		else // Mul == 3 (Subdir)
157 		  dirp = new(g) TDBSDR(PlugDup(g, filename));
158 
159 		if (dirp->OpenDB(g))
160 			return true;
161 
162 		if (trace(1) && Mul == 3) {
163 			int nf = ((PTDBSDR)dirp)->FindInDir(g);
164 			htrc("Number of files = %d\n", nf);
165 		} // endif trace
166 
167 		while (true)
168 			if ((rc = dirp->ReadDB(g)) == RC_OK) {
169 #if defined(_WIN32)
170 				strcat(strcpy(filename, dirp->Drive), dirp->Direc);
171 #else   // !_WIN32
172 				strcpy(filename, dirp->Direc);
173 #endif  // !_WIN32
174 				strcat(strcat(filename, dirp->Fname), dirp->Ftype);
175 				pfn[n++] = PlugDup(g, filename);
176 			} else
177 				break;
178 
179 		dirp->CloseDB(g);
180 
181 		if (rc == RC_FX)
182 			return true;
183 
184   } else {
185     /*******************************************************************/
186     /*  To_File is the name of a file containing the file name list    */
187     /*******************************************************************/
188     char *p;
189     FILE *stream;
190 
191     if (!(stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "r")))
192       return true;
193 
194     while (n < PFNZ) {
195       if (!fgets(filename, FNSZ, stream)) {
196         fclose(stream);
197         break;
198         } // endif fgets
199 
200       p = filename + strlen(filename) - 1;
201 
202 #if !defined(_WIN32)
203       // Data files can be imported from Windows (having CRLF)
204       if (*p == '\n' || *p == '\r') {
205         // is this enough for Unix ???
206         p--;          // Eliminate ending CR or LF character
207 
208         if (p >= filename)
209           // is this enough for Unix ???
210           if (*p == '\n' || *p == '\r')
211             p--;    // Eliminate ending CR or LF character
212 
213         } // endif p
214 
215 #else
216       if (*p == '\n')
217         p--;        // Eliminate ending new-line character
218 #endif
219       // Trim rightmost blanks
220       for (; p >= filename && *p == ' '; p--) ;
221 
222       *(++p) = '\0';
223 
224       // Suballocate the file name
225       pfn[n++] = PlugDup(g, filename);
226     } // endfor n
227 
228   } // endif Mul
229 
230   if (n) {
231     Filenames = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*));
232 
233     for (int i = 0; i < n; i++)
234       Filenames[i] = pfn[i];
235 
236   } else {
237     Filenames = (char**)PlugSubAlloc(g, NULL, sizeof(char*));
238     Filenames[0] = NULL;
239   } // endif n
240 
241   NumFiles = n;
242   return false;
243   } // end of InitFileNames
244 
245 /***********************************************************************/
246 /*  The table column list is the sub-table column list.                */
247 /***********************************************************************/
ColDB(PGLOBAL g,PSZ name,int num)248 PCOL TDBMUL::ColDB(PGLOBAL g, PSZ name, int num)
249   {
250   PCOL cp;
251 
252   /*********************************************************************/
253   /*  Because special columns are directly added to the MUL block,     */
254   /*  make sure that the sub-table has the same column list, before    */
255   /*  and after the call to the ColDB function.                        */
256   /*********************************************************************/
257   Tdbp->SetColumns(Columns);
258   cp = Tdbp->ColDB(g, name, num);
259   Columns = Tdbp->GetColumns();
260   return cp;
261 } // end of ColDB
262 
263 /***********************************************************************/
264 /*  MUL GetProgMax: get the max value for progress information.        */
265 /***********************************************************************/
GetProgMax(PGLOBAL g)266 int TDBMUL::GetProgMax(PGLOBAL g)
267   {
268   if (!Filenames && InitFileNames(g))
269     return -1;
270 
271   return NumFiles;                // This is a temporary setting
272   } // end of GetProgMax
273 
274 /***********************************************************************/
275 /*  MUL GetProgCur: get the current value for progress information.    */
276 /***********************************************************************/
GetProgCur(void)277 int TDBMUL::GetProgCur(void)
278   {
279   return iFile;                   // This is a temporary setting
280   } // end of GetProgMax
281 
282 /***********************************************************************/
283 /*  MUL Cardinality: returns table cardinality in number of rows.      */
284 /*  This function can be called with a null argument to test the       */
285 /*  availability of Cardinality implementation (1 yes, 0 no).          */
286 /*  Can be used on Multiple FIX table only.                            */
287 /***********************************************************************/
Cardinality(PGLOBAL g)288 int TDBMUL::Cardinality(PGLOBAL g)
289   {
290   if (!g)
291     return Tdbp->Cardinality(g);
292 
293   if (!Filenames && InitFileNames(g))
294     return -1;
295 
296   int n, card = 0;
297 
298   for (int i = 0; i < NumFiles; i++) {
299     Tdbp->SetFile(g, Filenames[i]);
300     Tdbp->ResetSize();
301 
302     if ((n = Tdbp->Cardinality(g)) < 0) {
303 //    strcpy(g->Message, MSG(BAD_CARDINALITY));
304       return -1;
305       } // endif n
306 
307     card += n;
308     } // endfor i
309 
310   return card;
311   } // end of Cardinality
312 
313 /***********************************************************************/
314 /*  Sum up the sizes of all sub-tables.                                */
315 /***********************************************************************/
GetMaxSize(PGLOBAL g)316 int TDBMUL::GetMaxSize(PGLOBAL g)
317   {
318   if (MaxSize < 0) {
319     int i;
320     int mxsz;
321 
322     if (trace(1))
323       htrc("TDBMUL::GetMaxSize: Filenames=%p\n", Filenames);
324 
325     if (!Filenames && InitFileNames(g))
326       return -1;
327 
328     if (Use == USE_OPEN) {
329       strcpy(g->Message, MSG(MAXSIZE_ERROR));
330       return -1;
331     } else
332       MaxSize = 0;
333 
334     for (i = 0; i < NumFiles; i++) {
335       Tdbp->SetFile(g, Filenames[i]);
336       Tdbp->ResetSize();
337 
338       if ((mxsz = Tdbp->GetMaxSize(g)) < 0) {
339         MaxSize = -1;
340         return mxsz;
341         } // endif mxsz
342 
343       MaxSize += mxsz;
344       } // endfor i
345 
346     } // endif MaxSize
347 
348   return MaxSize;
349   } // end of GetMaxSize
350 
351 /***********************************************************************/
352 /*  Reset read/write position values.                                  */
353 /***********************************************************************/
ResetDB(void)354 void TDBMUL::ResetDB(void)
355   {
356   for (PCOL colp = Columns; colp; colp = colp->GetNext())
357     if (colp->GetAmType() == TYPE_AM_FILID)
358       colp->COLBLK::Reset();
359 
360   Tdbp->ResetDB();
361   } // end of ResetDB
362 
363 /***********************************************************************/
364 /*  Returns RowId if b is false or Rownum if b is true.                */
365 /***********************************************************************/
RowNumber(PGLOBAL g,bool b)366 int TDBMUL::RowNumber(PGLOBAL g, bool b)
367   {
368   return ((b) ? 0 : Rows)
369        + ((iFile < NumFiles) ? Tdbp->RowNumber(g, b) : 1);
370   } // end of RowNumber
371 
372 /***********************************************************************/
373 /*  MUL Access Method opening routine.                                 */
374 /*  Open first file, other will be opened sequencially when reading.   */
375 /***********************************************************************/
OpenDB(PGLOBAL g)376 bool TDBMUL::OpenDB(PGLOBAL g)
377   {
378   if (trace(1))
379     htrc("MUL OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d\n",
380       this, Tdb_No, Use, To_Key_Col, Mode);
381 
382   if (Use == USE_OPEN) {
383     /*******************************************************************/
384     /*  Table already open, replace it at its beginning.               */
385     /*******************************************************************/
386     if (Filenames[iFile = 0]) {
387       Tdbp->CloseDB(g);
388       Tdbp->SetUse(USE_READY);
389       Tdbp->SetFile(g, Filenames[iFile = 0]);
390       Tdbp->ResetSize();
391       Rows = 0;
392       ResetDB();
393       return Tdbp->OpenDB(g);  // Re-open with new file name
394     } else
395       return false;
396 
397     } // endif use
398 
399   /*********************************************************************/
400   /*  We need to calculate MaxSize before opening the query.           */
401   /*********************************************************************/
402   if (GetMaxSize(g) < 0)
403     return true;
404 
405   /*********************************************************************/
406   /*  Open the first table file of the list.                           */
407   /*********************************************************************/
408 //if (!Filenames && InitFileNames(g))     // was done in GetMaxSize
409 //  return true;
410 
411   if (Filenames[iFile = 0]) {
412     Tdbp->SetFile(g, Filenames[0]);
413     Tdbp->SetMode(Mode);
414     Tdbp->ResetDB();
415     Tdbp->ResetSize();
416 
417     if (Tdbp->OpenDB(g))
418       return true;
419 
420     } // endif *Filenames
421 
422   Use = USE_OPEN;
423   return false;
424   } // end of OpenDB
425 
426 /***********************************************************************/
427 /*  ReadDB: Data Base read routine for MUL access method.              */
428 /***********************************************************************/
ReadDB(PGLOBAL g)429 int TDBMUL::ReadDB(PGLOBAL g)
430   {
431   int rc;
432 
433   if (NumFiles == 0)
434     return RC_EF;
435   else if (To_Kindex) {
436     /*******************************************************************/
437     /*  Reading is by an index table.                                  */
438     /*******************************************************************/
439     strcpy(g->Message, MSG(NO_INDEX_READ));
440     rc = RC_FX;
441   } else {
442     /*******************************************************************/
443     /*  Now start the reading process.                                 */
444     /*******************************************************************/
445    retry:
446     rc = Tdbp->ReadDB(g);
447 
448     if (rc == RC_EF) {
449       if (Tdbp->GetDef()->GetPseudo() & 1)
450         // Total number of rows met so far
451         Rows += Tdbp->RowNumber(g) - 1;
452 
453       if (++iFile < NumFiles) {
454         /***************************************************************/
455         /*  Continue reading from next table file.                     */
456         /***************************************************************/
457         Tdbp->CloseDB(g);
458         Tdbp->SetUse(USE_READY);
459         Tdbp->SetFile(g, Filenames[iFile]);
460         Tdbp->ResetSize();
461         ResetDB();
462 
463         if (Tdbp->OpenDB(g))     // Re-open with new file name
464           return RC_FX;
465 
466         goto retry;
467         } // endif iFile
468 
469     } else if (rc == RC_FX)
470       strcat(strcat(strcat(g->Message, " ("), Tdbp->GetFile(g)), ")");
471 
472   } // endif To_Kindex
473 
474   return rc;
475   } // end of ReadDB
476 
477 /***********************************************************************/
478 /*  Data Base write routine for MUL access method.                     */
479 /***********************************************************************/
WriteDB(PGLOBAL g)480 int TDBMUL::WriteDB(PGLOBAL g)
481   {
482   return Tdbp->WriteDB(g);
483 //  strcpy(g->Message, MSG(TABMUL_READONLY));
484 //  return RC_FX;                    // NIY
485   } // end of WriteDB
486 
487 /***********************************************************************/
488 /*  Data Base delete line routine for MUL access method.               */
489 /***********************************************************************/
DeleteDB(PGLOBAL g,int)490 int TDBMUL::DeleteDB(PGLOBAL g, int)
491   {
492   // When implementing DELETE_MODE InitFileNames must be updated to
493   // eliminate CRLF under Windows if the file is read in binary.
494   strcpy(g->Message, MSG(TABMUL_READONLY));
495   return RC_FX;                                      // NIY
496   } // end of DeleteDB
497 
498 /***********************************************************************/
499 /*  Data Base close routine for MUL access method.                     */
500 /***********************************************************************/
CloseDB(PGLOBAL g)501 void TDBMUL::CloseDB(PGLOBAL g)
502   {
503   if (NumFiles > 0) {
504     Tdbp->CloseDB(g);
505     iFile = NumFiles;
506     } // endif NumFiles
507 
508   } // end of CloseDB
509 
510 #if 0
511 /* ------------------------- Class TDBMSD ---------------------------- */
512 
513 	// Method
514 PTDB TDBMSD::Clone(PTABS t)
515 {
516 	PTDBMSD tp;
517 	PGLOBAL g = t->G;        // Is this really useful ???
518 
519 	tp = new(g) TDBMSD(this);
520 	tp->Tdbp = Tdbp->Clone(t);
521 	tp->Columns = tp->Tdbp->GetColumns();
522 	return tp;
523 } // end of Clone
524 
525 PTDB TDBMSD::Duplicate(PGLOBAL g)
526 {
527 	PTDBMSD tmup = new(g) TDBMSD(this);
528 
529 	tmup->Tdbp = Tdbp->Duplicate(g);
530 	return tmup;
531 } // end of Duplicate
532 
533 /***********************************************************************/
534 /*  Initializes the table filename list.                               */
535 /*  Note: tables created by concatenating the file components without  */
536 /*  specifying the LRECL value (that should be restricted to _MAX_PATH)*/
537 /*  have a LRECL that is the sum of the lengths of all components.     */
538 /*  This is why we use a big filename array to take care of that.      */
539 /***********************************************************************/
540 bool TDBMSD::InitFileNames(PGLOBAL g)
541 {
542 #define PFNZ  4096
543 #define FNSZ  (_MAX_DRIVE+_MAX_DIR+_MAX_FNAME+_MAX_EXT)
544 	PTDBSDR dirp;
545 	PSZ     pfn[PFNZ];
546 	PSZ     filename;
547 	int     rc, n = 0;
548 
549 	if (trace(1))
550 		htrc("in InitFileName: fn[]=%d\n", FNSZ);
551 
552 	filename = (char*)PlugSubAlloc(g, NULL, FNSZ);
553 
554 	// The sub table may need to refer to the Table original block
555 	Tdbp->SetTable(To_Table);         // Was not set at construction
556 
557 	PlugSetPath(filename, Tdbp->GetFile(g), Tdbp->GetPath());
558 
559 	if (trace(1))
560 		htrc("InitFileName: fn='%s'\n", filename);
561 
562 	dirp = new(g) TDBSDR(filename);
563 
564 	if (dirp->OpenDB(g))
565 		return true;
566 
567 	while (true)
568 		if ((rc = dirp->ReadDB(g)) == RC_OK) {
569 #if defined(_WIN32)
570 			strcat(strcpy(filename, dirp->Drive), dirp->Direc);
571 #else   // !_WIN32
572 			strcpy(filename, dirp->Direc);
573 #endif  // !_WIN32
574 			strcat(strcat(filename, dirp->Fname), dirp->Ftype);
575 			pfn[n++] = PlugDup(g, filename);
576 		} else
577 			break;
578 
579 	if (rc == RC_FX)
580 		return true;
581 
582 	if (n) {
583 	  Filenames = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*));
584 
585 	  for (int i = 0; i < n; i++)
586 		  Filenames[i] = pfn[i];
587 
588 	} else {
589 	  Filenames = (char**)PlugSubAlloc(g, NULL, sizeof(char*));
590 	  Filenames[0] = NULL;
591 	} // endif n
592 
593 	NumFiles = n;
594 	return false;
595 } // end of InitFileNames
596 #endif // 0
597 
598 	/* --------------------------- Class DIRDEF -------------------------- */
599 
600 /***********************************************************************/
601 /*  DefineAM: define specific AM block values from XDB file.           */
602 /***********************************************************************/
DefineAM(PGLOBAL g,LPCSTR,int)603 bool DIRDEF::DefineAM(PGLOBAL g, LPCSTR, int)
604   {
605   Desc = Fn = GetStringCatInfo(g, "Filename", NULL);
606   Incl = GetBoolCatInfo("Subdir", false);
607 	Huge = GetBoolCatInfo("Huge", false);
608 	Nodir = GetBoolCatInfo("Nodir", true);
609   return false;
610   } // end of DefineAM
611 
612 /***********************************************************************/
613 /*  GetTable: makes a new Table Description Block.                     */
614 /***********************************************************************/
GetTable(PGLOBAL g,MODE)615 PTDB DIRDEF::GetTable(PGLOBAL g, MODE)
616   {
617 #if 0
618   if (Huge)
619     return new(g) TDBDHR(this);        // Not implemented yet
620   else
621 #endif
622   if (Incl)
623     return new(g) TDBSDR(this);        // Including sub-directory files
624   else
625     return new(g) TDBDIR(this);    // Not Including sub-directory files
626 
627   } // end of GetTable
628 
629 /* ------------------------- Class TDBDIR ---------------------------- */
630 
631 /***********************************************************************/
632 /*  TABDIR constructors.                                               */
633 /***********************************************************************/
Init(void)634 void TDBDIR::Init(void)
635 {
636 	iFile = 0;
637 #if defined(_WIN32)
638 	Dvalp = NULL;
639 	memset(&FileData, 0, sizeof(_finddata_t));
640 	hSearch = INVALID_HANDLE_VALUE;
641 	*Drive = '\0';
642 #else   // !_WIN32
643 	memset(&Fileinfo, 0, sizeof(struct stat));
644 	Entry = NULL;
645 	Dir = NULL;
646 	Done = false;
647 	*Pattern = '\0';
648 #endif  // !_WIN32
649 	*Fpath = '\0';
650 	*Direc = '\0';
651 	*Fname = '\0';
652 	*Ftype = '\0';
653 }	// end of Init
654 
TDBDIR(PDIRDEF tdp)655 TDBDIR::TDBDIR(PDIRDEF tdp) : TDBASE(tdp)
656 {
657   To_File = tdp->Fn;
658 	Nodir = tdp->Nodir;
659 	Init();
660 } // end of TDBDIR standard constructor
661 
TDBDIR(PSZ fpat)662 TDBDIR::TDBDIR(PSZ fpat) : TDBASE((PTABDEF)NULL)
663 {
664 	To_File = fpat;
665 	Nodir = true;
666 	Init();
667 } // end of TDBDIR constructor
668 
669 /***********************************************************************/
670 /*  Initialize/get the components of the search file pattern.          */
671 /***********************************************************************/
Path(PGLOBAL g)672 char* TDBDIR::Path(PGLOBAL g)
673   {
674     (void) PlgGetCatalog(g);                    // XXX Should be removed?
675     PTABDEF defp = (PTABDEF)To_Def;
676 
677 #if defined(_WIN32)
678   if (!*Drive) {
679     PlugSetPath(Fpath, To_File, defp ? defp->GetPath() : NULL);
680     _splitpath(Fpath, Drive, Direc, Fname, Ftype);
681   } else
682     _makepath(Fpath, Drive, Direc, Fname, Ftype); // Usefull for TDBSDR
683 
684   return Fpath;
685 #else   // !_WIN32
686   if (!Done) {
687     PlugSetPath(Fpath, To_File, defp ? defp->GetPath() : NULL);
688     _splitpath(Fpath, NULL, Direc, Fname, Ftype);
689     strcat(strcpy(Pattern, Fname), Ftype);
690     Done = true;
691     } // endif Done
692 
693   return Pattern;
694 #endif  // !_WIN32
695   } // end of Path
696 
697 /***********************************************************************/
698 /*  Allocate DIR column description block.                             */
699 /***********************************************************************/
MakeCol(PGLOBAL g,PCOLDEF cdp,PCOL cprec,int n)700 PCOL TDBDIR::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
701   {
702   return new(g) DIRCOL(cdp, this, cprec, n);
703   } // end of MakeCol
704 
705 /***********************************************************************/
706 /*  DIR GetMaxSize: returns the number of retrieved files.             */
707 /***********************************************************************/
GetMaxSize(PGLOBAL g)708 int TDBDIR::GetMaxSize(PGLOBAL g)
709   {
710   if (MaxSize < 0) {
711     int n = -1;
712 #if defined(_WIN32)
713     int rc;
714     // Start searching files in the target directory.
715 		hSearch = FindFirstFile(Path(g), &FileData);
716 
717     if (hSearch == INVALID_HANDLE_VALUE) {
718 			rc = GetLastError();
719 
720 			if (rc != ERROR_FILE_NOT_FOUND) {
721 				char buf[512];
722 
723 				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
724 					            FORMAT_MESSAGE_IGNORE_INSERTS,
725 					NULL, GetLastError(), 0, (LPTSTR)&buf, sizeof(buf), NULL);
726 				sprintf(g->Message, MSG(BAD_FILE_HANDLE), buf);
727 				return -1;
728 			} // endif rc
729 
730 			return 0;
731 		} // endif hSearch
732 
733 		while (true) {
734 			if (!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
735 				n++;
736 
737 			if (!FindNextFile(hSearch, &FileData)) {
738 				rc = GetLastError();
739 
740 				if (rc != ERROR_NO_MORE_FILES) {
741 					sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
742 					FindClose(hSearch);
743 					return -1;
744 				} // endif rc
745 
746 				break;
747 			} // endif Next
748 
749 		} // endwhile
750 
751     // Close the search handle.
752 		FindClose(hSearch);
753 #else   // !_WIN32
754     Path(g);
755 
756     // Start searching files in the target directory.
757     if (!(Dir = opendir(Direc))) {
758       sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno));
759       return -1;
760       } // endif dir
761 
762     while ((Entry = readdir(Dir))) {
763       strcat(strcpy(Fpath, Direc), Entry->d_name);
764 
765       if (lstat(Fpath, &Fileinfo) < 0) {
766         sprintf(g->Message, "%s: %s", Fpath, strerror(errno));
767         return -1;
768       } else if (S_ISREG(Fileinfo.st_mode))
769         // Test whether the file name matches the table name filter
770         if (!fnmatch(Pattern, Entry->d_name, 0))
771           n++;      // We have a match
772 
773       } // endwhile Entry
774 
775     // Close the DIR handle.
776     closedir(Dir);
777 #endif  // !_WIN32
778     MaxSize = n;
779     } // endif MaxSize
780 
781   return MaxSize;
782   } // end of GetMaxSize
783 
784 /***********************************************************************/
785 /*  DIR Access Method opening routine.                                 */
786 /*  Open first file, other will be opened sequencially when reading.   */
787 /***********************************************************************/
OpenDB(PGLOBAL g)788 bool TDBDIR::OpenDB(PGLOBAL g)
789   {
790   if (trace(1))
791     htrc("DIR OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
792       this, Tdb_No, Use, Mode);
793 
794   if (Use == USE_OPEN) {
795     /*******************************************************************/
796     /*  Table already open, reopen it.                                 */
797     /*******************************************************************/
798     CloseDB(g);
799     SetUse(USE_READY);
800     } // endif use
801 
802   Use = USE_OPEN;
803 #if !defined(_WIN32)
804   Path(g);                          // Be sure it is done
805   Dir = NULL;                       // For ReadDB
806 #endif   // !_WIN32
807   return false;
808   } // end of OpenDB
809 
810 /***********************************************************************/
811 /*  Data Base read routine for DIR access method.                      */
812 /***********************************************************************/
ReadDB(PGLOBAL g)813 int TDBDIR::ReadDB(PGLOBAL g)
814   {
815   int rc = RC_OK;
816 
817 #if defined(_WIN32)
818 	do {
819 		if (hSearch == INVALID_HANDLE_VALUE) {
820 			/*****************************************************************/
821 			/*  Start searching files in the target directory. The use of    */
822 			/*  the Path function is required when called from TDBSDR.       */
823 			/*****************************************************************/
824 			hSearch = FindFirstFile(Path(g), &FileData);
825 
826 			if (hSearch == INVALID_HANDLE_VALUE) {
827 				rc = RC_EF;
828 				break;
829 			} else
830 				iFile++;
831 
832 		} else {
833 			if (!FindNextFile(hSearch, &FileData)) {
834 				// Restore file name and type pattern
835 				_splitpath(To_File, NULL, NULL, Fname, Ftype);
836 				rc = RC_EF;
837 				break;
838 			} else
839 				iFile++;
840 
841 		} // endif hSearch
842 
843 	} while (Nodir && FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
844 
845   if (rc == RC_OK)
846     _splitpath(FileData.cFileName, NULL, NULL, Fname, Ftype);
847 
848 #else   // !Win32
849   rc = RC_NF;
850 
851   if (!Dir)
852     // Start searching files in the target directory.
853     if (!(Dir = opendir(Direc))) {
854       sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno));
855       rc = RC_FX;
856       } // endif dir
857 
858   while (rc == RC_NF)
859     if ((Entry = readdir(Dir))) {
860       // We need the Fileinfo structure to get info about the file
861       strcat(strcpy(Fpath, Direc), Entry->d_name);
862 
863       if (lstat(Fpath, &Fileinfo) < 0) {
864         sprintf(g->Message, "%s: %s", Fpath, strerror(errno));
865         rc = RC_FX;
866       } else if (S_ISREG(Fileinfo.st_mode))
867         // Test whether the file name matches the table name filter
868         if (!fnmatch(Pattern, Entry->d_name, 0)) {
869           iFile++;      // We have a match
870           _splitpath(Entry->d_name, NULL, NULL, Fname, Ftype);
871           rc = RC_OK;
872           } // endif fnmatch
873 
874     } else {
875       // Restore file name and type pattern
876       _splitpath(To_File, NULL, NULL, Fname, Ftype);
877       rc = RC_EF;
878     } // endif Entry
879 
880 #endif  // !_WIN32
881 
882   return rc;
883   } // end of ReadDB
884 
885 /***********************************************************************/
886 /*  Data Base write routine for DIR access method.                     */
887 /***********************************************************************/
WriteDB(PGLOBAL g)888 int TDBDIR::WriteDB(PGLOBAL g)
889   {
890   strcpy(g->Message, MSG(TABDIR_READONLY));
891   return RC_FX;                    // NIY
892   } // end of WriteDB
893 
894 /***********************************************************************/
895 /*  Data Base delete line routine for DIR access method.               */
896 /***********************************************************************/
DeleteDB(PGLOBAL g,int)897 int TDBDIR::DeleteDB(PGLOBAL g, int)
898   {
899   strcpy(g->Message, MSG(TABDIR_READONLY));
900   return RC_FX;                                      // NIY
901   } // end of DeleteDB
902 
903 /***********************************************************************/
904 /*  Data Base close routine for MUL access method.                     */
905 /***********************************************************************/
CloseDB(PGLOBAL)906 void TDBDIR::CloseDB(PGLOBAL)
907   {
908 #if defined(_WIN32)
909   // Close the search handle.
910   FindClose(hSearch);
911 	hSearch = INVALID_HANDLE_VALUE;
912 #else   // !_WIN32
913   // Close the DIR handle
914   if (Dir) {
915     closedir(Dir);
916     Dir = NULL;
917     } // endif dir
918 #endif  // !_WIN32
919   iFile = 0;
920   } // end of CloseDB
921 
922 // ------------------------ DIRCOL functions ----------------------------
923 
924 /***********************************************************************/
925 /*  DIRCOL public constructor.                                         */
926 /***********************************************************************/
DIRCOL(PCOLDEF cdp,PTDB tdbp,PCOL cprec,int i,PCSZ)927 DIRCOL::DIRCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ)
928   : COLBLK(cdp, tdbp, i)
929   {
930   if (cprec) {
931     Next = cprec->GetNext();
932     cprec->SetNext(this);
933   } else {
934     Next = tdbp->GetColumns();
935     tdbp->SetColumns(this);
936   } // endif cprec
937 
938   // Set additional DIR access method information for column.
939 	Tdbp = (PTDBDIR)tdbp;
940   N = cdp->GetOffset();
941   } // end of DIRCOL constructor
942 
943 /***********************************************************************/
944 /*  DIRCOL constructor used for copying columns.                       */
945 /*  tdbp is the pointer to the new table descriptor.                   */
946 /***********************************************************************/
DIRCOL(DIRCOL * col1,PTDB tdbp)947 DIRCOL::DIRCOL(DIRCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
948   {
949 	Tdbp = (PTDBDIR)tdbp;
950 	N = col1->N;
951   } // end of DIRCOL copy constructor
952 
953 #if defined(_WIN32)
954 /***********************************************************************/
955 /*  Retrieve time information from FileData.                           */
956 /***********************************************************************/
SetTimeValue(PGLOBAL g,FILETIME & ftime)957 void DIRCOL::SetTimeValue(PGLOBAL g, FILETIME& ftime)
958 {
959 	char       tsp[24];
960 	SYSTEMTIME stp;
961 
962 	if (FileTimeToSystemTime(&ftime, &stp)) {
963 		sprintf(tsp, "%04d-%02d-%02d %02d:%02d:%02d",
964 			stp.wYear, stp.wMonth, stp.wDay, stp.wHour, stp.wMinute, stp.wSecond);
965 
966 		if (Value->GetType() != TYPE_STRING) {
967 			if (!Tdbp->Dvalp)
968 				Tdbp->Dvalp = AllocateValue(g, TYPE_DATE, 20, 0, false,
969 					"YYYY-MM-DD hh:mm:ss");
970 
971 			Tdbp->Dvalp->SetValue_psz(tsp);
972 			Value->SetValue_pval(Tdbp->Dvalp);
973 		} else
974 			Value->SetValue_psz(tsp);
975 
976 	} else
977 		Value->Reset();
978 
979 } // end of SetTimeValue
980 #endif   // _WIN32
981 
982 /***********************************************************************/
983 /*  ReadColumn: what this routine does is to access the information    */
984 /*  corresponding to this column and convert it to buffer type.        */
985 /***********************************************************************/
ReadColumn(PGLOBAL g)986 void DIRCOL::ReadColumn(PGLOBAL g)
987 	{
988   if (trace(1))
989     htrc("DIR ReadColumn: col %s R%d use=%.4X status=%.4X type=%d N=%d\n",
990       Name, Tdbp->GetTdb_No(), ColUse, Status, Buf_Type, N);
991 
992   /*********************************************************************/
993   /*  Retrieve the information corresponding to the column number.     */
994   /*********************************************************************/
995   switch (N) {
996 #if defined(_WIN32)
997     case  0: Value->SetValue_psz(Tdbp->Drive); break;
998 #endif   // _WIN32
999     case  1: Value->SetValue_psz(Tdbp->Direc); break;
1000     case  2: Value->SetValue_psz(Tdbp->Fname); break;
1001     case  3: Value->SetValue_psz(Tdbp->Ftype); break;
1002 #if defined(_WIN32)
1003     case  4: Value->SetValue((int)Tdbp->FileData.dwFileAttributes); break;
1004 		case  5: Value->SetValue((int)Tdbp->FileData.nFileSizeLow); break;
1005     case  6: SetTimeValue(g, Tdbp->FileData.ftLastWriteTime);   break;
1006     case  7: SetTimeValue(g, Tdbp->FileData.ftCreationTime);    break;
1007     case  8: SetTimeValue(g, Tdbp->FileData.ftLastAccessTime);  break;
1008 #else   // !_WIN32
1009     case  4: Value->SetValue((int)Tdbp->Fileinfo.st_mode);  break;
1010     case  5: Value->SetValue((int)Tdbp->Fileinfo.st_size);  break;
1011     case  6: Value->SetValue((int)Tdbp->Fileinfo.st_mtime); break;
1012     case  7: Value->SetValue((int)Tdbp->Fileinfo.st_ctime); break;
1013     case  8: Value->SetValue((int)Tdbp->Fileinfo.st_atime); break;
1014     case  9: Value->SetValue((int)Tdbp->Fileinfo.st_uid);   break;
1015     case 10: Value->SetValue((int)Tdbp->Fileinfo.st_gid);   break;
1016 #endif  // !_WIN32
1017     default:
1018       sprintf(g->Message, MSG(INV_DIRCOL_OFST), N);
1019 			throw GetAmType();
1020 	} // endswitch N
1021 
1022   } // end of ReadColumn
1023 
1024 /* ------------------------- Class TDBSDR ---------------------------- */
1025 
1026 /***********************************************************************/
1027 /*  SDR GetMaxSize: returns the number of retrieved files.             */
1028 /***********************************************************************/
GetMaxSize(PGLOBAL g)1029 int TDBSDR::GetMaxSize(PGLOBAL g)
1030   {
1031   if (MaxSize < 0) {
1032     Path(g);
1033     MaxSize = FindInDir(g);
1034     } // endif MaxSize
1035 
1036   return MaxSize;
1037   } // end of GetMaxSize
1038 
1039 /***********************************************************************/
1040 /*  SDR FindInDir: returns the number of retrieved files.              */
1041 /***********************************************************************/
FindInDir(PGLOBAL g)1042 int TDBSDR::FindInDir(PGLOBAL g)
1043   {
1044   int    n = 0;
1045   size_t m = strlen(Direc);
1046 
1047   // Start searching files in the target directory.
1048 #if defined(_WIN32)
1049 	int rc;
1050 	HANDLE h;
1051 
1052 #if defined(PATHMATCHSPEC)
1053 	if (!*Drive)
1054 		Path(g);
1055 
1056 	_makepath(Fpath, Drive, Direc, "*", "*");
1057 
1058 	h = FindFirstFile(Fpath, &FileData);
1059 
1060   if (h == INVALID_HANDLE_VALUE) {
1061 		rc = GetLastError();
1062 
1063 		if (rc != ERROR_FILE_NOT_FOUND) {
1064 			char buf[512];
1065 
1066 			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1067 				FORMAT_MESSAGE_IGNORE_INSERTS,
1068 				NULL, GetLastError(), 0, (LPTSTR)&buf, sizeof(buf), NULL);
1069 			sprintf(g->Message, MSG(BAD_FILE_HANDLE), buf);
1070 			return -1;
1071 		} // endif rc
1072 
1073 		return 0;
1074 	} // endif h
1075 
1076 	while (true) {
1077 		if ((FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
1078 			  *FileData.cFileName != '.') {
1079 			// Look in the name sub-directory
1080 			strcat(strcat(Direc, FileData.cFileName), "/");
1081 			n += FindInDir(g);
1082 			Direc[m] = '\0';         // Restore path
1083 		} else if (PathMatchSpec(FileData.cFileName, Fpath))
1084 			n++;
1085 
1086 		if (!FindNextFile(h, &FileData)) {
1087 			rc = GetLastError();
1088 
1089 			if (rc != ERROR_NO_MORE_FILES) {
1090 				sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
1091 				FindClose(h);
1092 				return -1;
1093 			} // endif rc
1094 
1095 			break;
1096 		} // endif Next
1097 
1098 	} // endwhile
1099 #else   // !PATHMATCHSPEC
1100 	h = FindFirstFile(Path(g), &FileData);
1101 
1102 	if (h == INVALID_HANDLE_VALUE) {
1103 		rc = GetLastError();
1104 
1105 		if (rc != ERROR_FILE_NOT_FOUND) {
1106 			char buf[512];
1107 
1108 			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1109 				FORMAT_MESSAGE_IGNORE_INSERTS,
1110 				NULL, GetLastError(), 0, (LPTSTR)&buf, sizeof(buf), NULL);
1111 			sprintf(g->Message, MSG(BAD_FILE_HANDLE), buf);
1112 			return -1;
1113 		} // endif rc
1114 
1115 		return 0;
1116 	} // endif hSearch
1117 
1118 	while (true) {
1119 		n++;
1120 
1121 		if (!FindNextFile(h, &FileData)) {
1122 			rc = GetLastError();
1123 
1124 			if (rc != ERROR_NO_MORE_FILES) {
1125 				sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
1126 				FindClose(h);
1127 				return -1;
1128 			} // endif rc
1129 
1130 			break;
1131 		} // endif Next
1132 
1133 	} // endwhile
1134 
1135 	// Now search files in sub-directories.
1136 	_makepath(Fpath, Drive, Direc, "*", ".");
1137 	h = FindFirstFile(Fpath, &FileData);
1138 
1139 	if (h != INVALID_HANDLE_VALUE) {
1140 		while (true) {
1141 			if ((FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
1142 				  *FileData.cFileName != '.') {
1143 				// Look in the name sub-directory
1144 				strcat(strcat(Direc, FileData.cFileName), "/");
1145 				n += FindInDir(g);
1146 				Direc[m] = '\0';         // Restore path
1147 			} // endif SUBDIR
1148 
1149 			if (!FindNextFile(h, &FileData))
1150 				break;
1151 
1152 		} // endwhile
1153 
1154 	} // endif h
1155 #endif  // !PATHMATCHSPEC
1156 
1157   // Close the search handle.
1158 	FindClose(h);
1159 #else   // !_WIN32
1160   int k;
1161   DIR *dir = opendir(Direc);
1162 
1163   if (!dir) {
1164     sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno));
1165     return -1;
1166     } // endif dir
1167 
1168   while ((Entry = readdir(dir))) {
1169     strcat(strcpy(Fpath, Direc), Entry->d_name);
1170 
1171     if (lstat(Fpath, &Fileinfo) < 0) {
1172       sprintf(g->Message, "%s: %s", Fpath, strerror(errno));
1173       return -1;
1174     } else if (S_ISDIR(Fileinfo.st_mode) && *Entry->d_name != '.') {
1175       // Look in the name sub-directory
1176       strcat(strcat(Direc, Entry->d_name), "/");
1177 
1178       if ((k= FindInDir(g)) < 0)
1179         return k;
1180       else
1181         n += k;
1182 
1183       Direc[m] = '\0';         // Restore path
1184     } else if (S_ISREG(Fileinfo.st_mode))
1185       // Test whether the file name matches the table name filter
1186       if (!fnmatch(Pattern, Entry->d_name, 0))
1187         n++;      // We have a match
1188 
1189     } // endwhile readdir
1190 
1191   // Close the DIR handle.
1192   closedir(dir);
1193 #endif  // !_WIN32
1194 
1195   return n;
1196   } // end of FindInDir
1197 
1198 /***********************************************************************/
1199 /*  DIR Access Method opening routine.                                 */
1200 /*  Open first file, other will be opened sequencially when reading.   */
1201 /***********************************************************************/
OpenDB(PGLOBAL g)1202 bool TDBSDR::OpenDB(PGLOBAL g)
1203   {
1204   if (!Sub) {
1205     Path(g);
1206     Sub = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR));
1207     Sub->Next = NULL;
1208     Sub->Prev = NULL;
1209 #if defined(_WIN32)
1210     Sub->H = INVALID_HANDLE_VALUE;
1211     Sub->Len = strlen(Direc);
1212 #else   // !_WIN32
1213     Sub->D = NULL;
1214     Sub->Len = 0;
1215 #endif  // !_WIN32
1216     } // endif To_Sub
1217 
1218   return TDBDIR::OpenDB(g);
1219   } // end of OpenDB
1220 
1221 /***********************************************************************/
1222 /*  Data Base read routine for SDR access method.                      */
1223 /***********************************************************************/
ReadDB(PGLOBAL g)1224 int TDBSDR::ReadDB(PGLOBAL g)
1225   {
1226   int rc;
1227 
1228 #if defined(_WIN32)
1229  again:
1230   rc = TDBDIR::ReadDB(g);
1231 
1232   if (rc == RC_EF) {
1233     // Are there more files in sub-directories
1234    retry:
1235     do {
1236       if (Sub->H == INVALID_HANDLE_VALUE) {
1237 //      _makepath(Fpath, Drive, Direc, "*", ".");		 why was this made?
1238 				_makepath(Fpath, Drive, Direc, "*", NULL);
1239 				Sub->H = FindFirstFile(Fpath, &FileData);
1240       } else if (!FindNextFile(Sub->H, &FileData)) {
1241         FindClose(Sub->H);
1242         Sub->H = INVALID_HANDLE_VALUE;
1243         *FileData.cFileName= '\0';
1244 				break;
1245       } // endif findnext
1246 
1247     } while(!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
1248     		(*FileData.cFileName == '.' &&
1249 			  (!FileData.cFileName[1] || FileData.cFileName[1] == '.')));
1250 
1251     if (Sub->H == INVALID_HANDLE_VALUE) {
1252       // No more sub-directories. Are we in a sub-directory?
1253       if (!Sub->Prev)
1254         return rc;               // No, all is finished
1255 
1256       // here we must continue in the parent directory
1257       Sub = Sub->Prev;
1258       goto retry;
1259     } else {
1260       // Search next sub-directory
1261       Direc[Sub->Len] = '\0';
1262 
1263       if (!Sub->Next) {
1264         PSUBDIR sup;
1265 
1266         sup = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR));
1267         sup->Next = NULL;
1268         sup->Prev = Sub;
1269         sup->H = INVALID_HANDLE_VALUE;
1270         Sub->Next = sup;
1271         } // endif Next
1272 
1273       Sub = Sub->Next;
1274       strcat(strcat(Direc, FileData.cFileName), "/");
1275       Sub->Len = strlen(Direc);
1276 
1277       // Reset Hsearch used by TDBDIR::ReadDB
1278 			FindClose(hSearch);
1279 			hSearch = INVALID_HANDLE_VALUE;
1280       goto again;
1281     } // endif H
1282 
1283     } // endif rc
1284 #else   // !_WIN32
1285   rc = RC_NF;
1286 
1287  again:
1288   if (!Sub->D)
1289     // Start searching files in the target directory.
1290     if (!(Sub->D = opendir(Direc))) {
1291       sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno));
1292       rc = RC_FX;
1293       } // endif dir
1294 
1295   while (rc == RC_NF)
1296     if ((Entry = readdir(Sub->D))) {
1297       // We need the Fileinfo structure to get info about the file
1298       strcat(strcpy(Fpath, Direc), Entry->d_name);
1299 
1300       if (lstat(Fpath, &Fileinfo) < 0) {
1301         sprintf(g->Message, "%s: %s", Fpath, strerror(errno));
1302         rc = RC_FX;
1303       } else if (S_ISDIR(Fileinfo.st_mode) && strcmp(Entry->d_name, ".")
1304 			                                     && strcmp(Entry->d_name, "..")) {
1305         // Look in the name sub-directory
1306         if (!Sub->Next) {
1307           PSUBDIR sup;
1308 
1309           sup = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR));
1310           sup->Next = NULL;
1311           sup->Prev = Sub;
1312           Sub->Next = sup;
1313           } // endif Next
1314 
1315         Sub = Sub->Next;
1316         Sub->D = NULL;
1317         Sub->Len = strlen(Direc);
1318         strcat(strcat(Direc, Entry->d_name), "/");
1319         goto again;
1320       } else if (S_ISREG(Fileinfo.st_mode))
1321         // Test whether the file name matches the table name filter
1322         if (!fnmatch(Pattern, Entry->d_name, 0)) {
1323           iFile++;      // We have a match
1324           _splitpath(Entry->d_name, NULL, NULL, Fname, Ftype);
1325           rc = RC_OK;
1326           } // endif fnmatch
1327 
1328     } else {
1329       // No more files. Close the DIR handle.
1330       closedir(Sub->D);
1331 
1332       // Are we in a sub-directory?
1333       if (Sub->Prev) {
1334         // Yes, we must continue in the parent directory
1335         Direc[Sub->Len] = '\0';
1336         Sub = Sub->Prev;
1337       } else
1338         rc = RC_EF;              // No, all is finished
1339 
1340     } // endif Entry
1341 
1342 #endif  // !_WIN32
1343 
1344   return rc;
1345   } // end of ReadDB
1346 
1347 #if 0
1348 /* ------------------------- Class TDBDHR ---------------------------- */
1349 
1350 /***********************************************************************/
1351 /*  TABDHR constructors.                                               */
1352 /***********************************************************************/
1353 TDBDHR::TDBDHR(PDHRDEF tdp) : TDBASE(tdp)
1354   {
1355   memset(&FileData, 0, sizeof(WIN32_FIND_DATA));
1356   Hsearch = INVALID_HANDLE_VALUE;
1357   iFile = 0;
1358   *Drive = '\0';
1359   *Direc = '\0';
1360   *Fname = '\0';
1361   *Ftype = '\0';
1362   } // end of TDBDHR standard constructor
1363 
1364 TDBDHR::TDBDHR(PTDBDHR tdbp) : TDBASE(tdbp)
1365   {
1366   FileData = tdbp->FileData;
1367   Hsearch = tdbp->Hsearch;
1368   iFile = tdbp->iFile;
1369   strcpy(Drive, tdbp->Drive);
1370   strcpy(Direc, tdbp->Direc);
1371   strcpy(Fname, tdbp->Fname);
1372   strcpy(Ftype, tdbp->Ftype);
1373   } // end of TDBDHR copy constructor
1374 
1375 // Method
1376 PTDB TDBDHR::Clone(PTABS t)
1377   {
1378   PTDB    tp;
1379   PGLOBAL g = t->G;        // Is this really useful ???
1380 
1381   tp = new(g) TDBDHR(this);
1382   tp->Columns = Columns;
1383   return tp;
1384   } // end of Clone
1385 
1386 /***********************************************************************/
1387 /*  Allocate DHR column description block.                             */
1388 /***********************************************************************/
1389 PCOL TDBDHR::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
1390   {
1391   return new(g) DHRCOL(cdp, this, cprec, n);
1392   } // end of MakeCol
1393 
1394 /***********************************************************************/
1395 /*  DHR GetMaxSize: returns the number of retrieved files.             */
1396 /***********************************************************************/
1397 int TDBDHR::GetMaxSize(PGLOBAL g)
1398   {
1399   if (MaxSize < 0) {
1400     char    filename[_MAX_PATH];
1401     int     i, rc;
1402     int    n = -1;
1403     HANDLE  h;
1404     PDBUSER dup = PlgGetUser(g);
1405 
1406     PlugSetPath(filename, To_File, dup->Path);
1407 
1408     // Start searching files in the target directory.
1409     h = FindFirstFile(filename, &FileData);
1410 
1411     if (h == INVALID_HANDLE_VALUE) {
1412       switch (rc = GetLastError()) {
1413         case ERROR_NO_MORE_FILES:
1414         case ERROR_FILE_NOT_FOUND:
1415           n = 0;
1416           break;
1417         default:
1418           FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1419                         FORMAT_MESSAGE_IGNORE_INSERTS,
1420                         NULL, rc, 0,
1421                         (LPTSTR)&filename, sizeof(filename), NULL);
1422           sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename);
1423         } // endswitch rc
1424 
1425     } else {
1426       for (n = 1;; n++)
1427         if (!FindNextFile(h, &FileData)) {
1428           rc = GetLastError();
1429 
1430           if (rc != ERROR_NO_MORE_FILES) {
1431             sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
1432             n = -1;
1433             } // endif rc
1434 
1435           break;
1436           } // endif FindNextFile
1437 
1438       // Close the search handle.
1439       if (!FindClose(h) && n != -1)
1440         strcpy(g->Message, MSG(SRCH_CLOSE_ERR));
1441 
1442     } // endif Hsearch
1443 
1444     MaxSize = n;
1445     } // endif MaxSize
1446 
1447   return MaxSize;
1448   } // end of GetMaxSize
1449 
1450 /***********************************************************************/
1451 /*  DHR Access Method opening routine.                                 */
1452 /*  Open first file, other will be opened sequencially when reading.   */
1453 /***********************************************************************/
1454 bool TDBDHR::OpenDB(PGLOBAL g)
1455   {
1456   if (trace(1))
1457     htrc("DHR OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
1458       this, Tdb_No, Use, Mode);
1459 
1460   if (Use == USE_OPEN) {
1461     /*******************************************************************/
1462     /*  Table already open, reopen it.                                 */
1463     /*******************************************************************/
1464     CloseDB(g);
1465     SetUse(USE_READY);
1466     } // endif use
1467 
1468   /*********************************************************************/
1469   /*  Direct access needed for join or sorting.                        */
1470   /*********************************************************************/
1471   if (NeedIndexing(g)) {
1472     // Direct access of DHR tables is not implemented yet
1473     sprintf(g->Message, MSG(NO_DIR_INDX_RD), "DHR");
1474     return true;
1475     } // endif NeedIndexing
1476 
1477   Use = USE_OPEN;
1478   return false;
1479   } // end of OpenDB
1480 
1481 /***********************************************************************/
1482 /*  Data Base read routine for DHR access method.                      */
1483 /***********************************************************************/
1484 int TDBDHR::ReadDB(PGLOBAL g)
1485   {
1486   int   rc = RC_OK;
1487   DWORD erc;
1488 
1489   if (Hsearch == INVALID_HANDLE_VALUE) {
1490     char   *filename[_MAX_PATH];
1491     PDBUSER dup = PlgGetUser(g);
1492 
1493     PlugSetPath(filename, To_File, dup->Path);
1494     _splitpath(filename, Drive, Direc, NULL, NULL);
1495 
1496     /*******************************************************************/
1497     /*  Start searching files in the target directory.                 */
1498     /*******************************************************************/
1499     Hsearch = FindFirstFile(filename, &FileData);
1500 
1501     if (Hsearch != INVALID_HANDLE_VALUE)
1502       iFile = 1;
1503     else switch (erc = GetLastError()) {
1504       case ERROR_NO_MORE_FILES:
1505       case ERROR_FILE_NOT_FOUND:
1506 //    case ERROR_PATH_NOT_FOUND:               ???????
1507         rc = RC_EF;
1508         break;
1509       default:
1510         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1511                       FORMAT_MESSAGE_IGNORE_INSERTS,
1512                       NULL, erc,  0,
1513                       (LPTSTR)&filename, sizeof(filename), NULL);
1514         sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename);
1515         rc = RC_FX;
1516       } // endswitch erc
1517 
1518   } else {
1519     if (!FindNextFile(Hsearch, &FileData)) {
1520       DWORD erc = GetLastError();
1521 
1522       if (erc != ERROR_NO_MORE_FILES) {
1523         sprintf(g->Message, MSG(NEXT_FILE_ERROR), erc);
1524         FindClose(Hsearch);
1525         rc = RC_FX;
1526       } else
1527         rc = RC_EF;
1528 
1529     } else
1530       iFile++;
1531 
1532   } // endif Hsearch
1533 
1534   if (rc == RC_OK)
1535     _splitpath(FileData.cFileName, NULL, NULL, Fname, Ftype);
1536 
1537   return rc;
1538   } // end of ReadDB
1539 
1540 /***********************************************************************/
1541 /*  Data Base close routine for MUL access method.                     */
1542 /***********************************************************************/
1543 void TDBDHR::CloseDB(PGLOBAL g)
1544   {
1545   // Close the search handle.
1546   if (!FindClose(Hsearch)) {
1547     strcpy(g->Message, MSG(SRCH_CLOSE_ERR));
1548 		throw GetAmType();
1549 	} // endif FindClose
1550 
1551   iFile = 0;
1552   Hsearch = INVALID_HANDLE_VALUE;
1553   } // end of CloseDB
1554 
1555 // ------------------------ DHRCOL functions ----------------------------
1556 
1557 /***********************************************************************/
1558 /*  DHRCOL public constructor.                                         */
1559 /***********************************************************************/
1560 DHRCOL::DHRCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
1561       : COLBLK(cdp, tdbp, i)
1562   {
1563   if (cprec) {
1564     Next = cprec->GetNext();
1565     cprec->SetNext(this);
1566   } else {
1567     Next = tdbp->GetColumns();
1568     tdbp->SetColumns(this);
1569   } // endif cprec
1570 
1571   // Set additional DHR access method information for column.
1572   N = cdp->GetOffset();
1573   } // end of DOSCOL constructor
1574 
1575 /***********************************************************************/
1576 /*  DHRCOL constructor used for copying columns.                       */
1577 /*  tdbp is the pointer to the new table descriptor.                   */
1578 /***********************************************************************/
1579 DHRCOL::DHRCOL(DHRCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
1580   {
1581   N = col1->N;
1582   } // end of DHRCOL copy constructor
1583 
1584 /***********************************************************************/
1585 /*  ReadColumn: what this routine does is to access the information    */
1586 /*  corresponding to this column and convert it to buffer type.        */
1587 /***********************************************************************/
1588 void DHRCOL::ReadColumn(PGLOBAL g)
1589   {
1590   int     rc;
1591   PTDBDHR tdbp = (PTDBDHR)To_Tdb;
1592 
1593   if (trace(1))
1594     htrc("DHR ReadColumn: col %s R%d use=%.4X status=%.4X type=%d N=%d\n",
1595       Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type, N);
1596 
1597   /*********************************************************************/
1598   /*  Retrieve the information corresponding to the column number.     */
1599   /*********************************************************************/
1600   switch (N) {
1601     case 0:                                // Drive
1602       Value->SetValue(Drive, _MAX_DRIVE);
1603       break;
1604     case 1:                                // Path
1605       Value->SetValue(Direc, _MAX_DHR);
1606       break;
1607     case 2:                                // Name
1608       Value->SetValue(Fname, _MAX_FNAME);
1609       break;
1610     case 3:                                // Extention
1611       Value->SetValue(Ftype, _MAX_EXT);
1612       break;
1613     case 4:                                // Extention
1614       Value->SetValue(tdbp->FileData.cAlternateFileName, 14);
1615       break;
1616     case 5:
1617       Value->SetValue(tdbp->FileData.dwFileAttributes);
1618       break;
1619     case 6:
1620       Value->SetValue(..................
1621   } // end of ReadColumn
1622 #endif // 0
1623 
1624