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   PCATLG cat = PlgGetCatalog(g);
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 rc, n = -1;
712 #if defined(_WIN32)
713 
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    rc, n = 0;
1045   size_t m = strlen(Direc);
1046 
1047   // Start searching files in the target directory.
1048 #if defined(_WIN32)
1049 	HANDLE h;
1050 
1051 #if defined(PATHMATCHSPEC)
1052 	if (!*Drive)
1053 		Path(g);
1054 
1055 	_makepath(Fpath, Drive, Direc, "*", "*");
1056 
1057 	h = FindFirstFile(Fpath, &FileData);
1058 
1059   if (h == INVALID_HANDLE_VALUE) {
1060 		rc = GetLastError();
1061 
1062 		if (rc != ERROR_FILE_NOT_FOUND) {
1063 			char buf[512];
1064 
1065 			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1066 				FORMAT_MESSAGE_IGNORE_INSERTS,
1067 				NULL, GetLastError(), 0, (LPTSTR)&buf, sizeof(buf), NULL);
1068 			sprintf(g->Message, MSG(BAD_FILE_HANDLE), buf);
1069 			return -1;
1070 		} // endif rc
1071 
1072 		return 0;
1073 	} // endif h
1074 
1075 	while (true) {
1076 		if ((FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
1077 			  *FileData.cFileName != '.') {
1078 			// Look in the name sub-directory
1079 			strcat(strcat(Direc, FileData.cFileName), "/");
1080 			n += FindInDir(g);
1081 			Direc[m] = '\0';         // Restore path
1082 		} else if (PathMatchSpec(FileData.cFileName, Fpath))
1083 			n++;
1084 
1085 		if (!FindNextFile(h, &FileData)) {
1086 			rc = GetLastError();
1087 
1088 			if (rc != ERROR_NO_MORE_FILES) {
1089 				sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
1090 				FindClose(h);
1091 				return -1;
1092 			} // endif rc
1093 
1094 			break;
1095 		} // endif Next
1096 
1097 	} // endwhile
1098 #else   // !PATHMATCHSPEC
1099 	h = FindFirstFile(Path(g), &FileData);
1100 
1101 	if (h == INVALID_HANDLE_VALUE) {
1102 		rc = GetLastError();
1103 
1104 		if (rc != ERROR_FILE_NOT_FOUND) {
1105 			char buf[512];
1106 
1107 			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1108 				FORMAT_MESSAGE_IGNORE_INSERTS,
1109 				NULL, GetLastError(), 0, (LPTSTR)&buf, sizeof(buf), NULL);
1110 			sprintf(g->Message, MSG(BAD_FILE_HANDLE), buf);
1111 			return -1;
1112 		} // endif rc
1113 
1114 		return 0;
1115 	} // endif hSearch
1116 
1117 	while (true) {
1118 		n++;
1119 
1120 		if (!FindNextFile(h, &FileData)) {
1121 			rc = GetLastError();
1122 
1123 			if (rc != ERROR_NO_MORE_FILES) {
1124 				sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
1125 				FindClose(h);
1126 				return -1;
1127 			} // endif rc
1128 
1129 			break;
1130 		} // endif Next
1131 
1132 	} // endwhile
1133 
1134 	// Now search files in sub-directories.
1135 	_makepath(Fpath, Drive, Direc, "*", ".");
1136 	h = FindFirstFile(Fpath, &FileData);
1137 
1138 	if (h != INVALID_HANDLE_VALUE) {
1139 		while (true) {
1140 			if ((FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
1141 				  *FileData.cFileName != '.') {
1142 				// Look in the name sub-directory
1143 				strcat(strcat(Direc, FileData.cFileName), "/");
1144 				n += FindInDir(g);
1145 				Direc[m] = '\0';         // Restore path
1146 			} // endif SUBDIR
1147 
1148 			if (!FindNextFile(h, &FileData))
1149 				break;
1150 
1151 		} // endwhile
1152 
1153 	} // endif h
1154 #endif  // !PATHMATCHSPEC
1155 
1156   // Close the search handle.
1157 	FindClose(h);
1158 #else   // !_WIN32
1159   int k;
1160   DIR *dir = opendir(Direc);
1161 
1162   if (!dir) {
1163     sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno));
1164     return -1;
1165     } // endif dir
1166 
1167   while ((Entry = readdir(dir))) {
1168     strcat(strcpy(Fpath, Direc), Entry->d_name);
1169 
1170     if (lstat(Fpath, &Fileinfo) < 0) {
1171       sprintf(g->Message, "%s: %s", Fpath, strerror(errno));
1172       return -1;
1173     } else if (S_ISDIR(Fileinfo.st_mode) && *Entry->d_name != '.') {
1174       // Look in the name sub-directory
1175       strcat(strcat(Direc, Entry->d_name), "/");
1176 
1177       if ((k = FindInDir(g)) < 0)
1178         return k;
1179       else
1180         n += k;
1181 
1182       Direc[m] = '\0';         // Restore path
1183     } else if (S_ISREG(Fileinfo.st_mode))
1184       // Test whether the file name matches the table name filter
1185       if (!fnmatch(Pattern, Entry->d_name, 0))
1186         n++;      // We have a match
1187 
1188     } // endwhile readdir
1189 
1190   // Close the DIR handle.
1191   closedir(dir);
1192 #endif  // !_WIN32
1193 
1194   return n;
1195   } // end of FindInDir
1196 
1197 /***********************************************************************/
1198 /*  DIR Access Method opening routine.                                 */
1199 /*  Open first file, other will be opened sequencially when reading.   */
1200 /***********************************************************************/
OpenDB(PGLOBAL g)1201 bool TDBSDR::OpenDB(PGLOBAL g)
1202   {
1203   if (!Sub) {
1204     Path(g);
1205     Sub = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR));
1206     Sub->Next = NULL;
1207     Sub->Prev = NULL;
1208 #if defined(_WIN32)
1209     Sub->H = INVALID_HANDLE_VALUE;
1210     Sub->Len = strlen(Direc);
1211 #else   // !_WIN32
1212     Sub->D = NULL;
1213     Sub->Len = 0;
1214 #endif  // !_WIN32
1215     } // endif To_Sub
1216 
1217   return TDBDIR::OpenDB(g);
1218   } // end of OpenDB
1219 
1220 /***********************************************************************/
1221 /*  Data Base read routine for SDR access method.                      */
1222 /***********************************************************************/
ReadDB(PGLOBAL g)1223 int TDBSDR::ReadDB(PGLOBAL g)
1224   {
1225   int rc;
1226 
1227 #if defined(_WIN32)
1228  again:
1229   rc = TDBDIR::ReadDB(g);
1230 
1231   if (rc == RC_EF) {
1232     // Are there more files in sub-directories
1233    retry:
1234     do {
1235       if (Sub->H == INVALID_HANDLE_VALUE) {
1236 //      _makepath(Fpath, Drive, Direc, "*", ".");		 why was this made?
1237 				_makepath(Fpath, Drive, Direc, "*", NULL);
1238 				Sub->H = FindFirstFile(Fpath, &FileData);
1239       } else if (!FindNextFile(Sub->H, &FileData)) {
1240         FindClose(Sub->H);
1241         Sub->H = INVALID_HANDLE_VALUE;
1242         *FileData.cFileName= '\0';
1243 				break;
1244       } // endif findnext
1245 
1246     } while(!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
1247     		(*FileData.cFileName == '.' &&
1248 			  (!FileData.cFileName[1] || FileData.cFileName[1] == '.')));
1249 
1250     if (Sub->H == INVALID_HANDLE_VALUE) {
1251       // No more sub-directories. Are we in a sub-directory?
1252       if (!Sub->Prev)
1253         return rc;               // No, all is finished
1254 
1255       // here we must continue in the parent directory
1256       Sub = Sub->Prev;
1257       goto retry;
1258     } else {
1259       // Search next sub-directory
1260       Direc[Sub->Len] = '\0';
1261 
1262       if (!Sub->Next) {
1263         PSUBDIR sup;
1264 
1265         sup = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR));
1266         sup->Next = NULL;
1267         sup->Prev = Sub;
1268         sup->H = INVALID_HANDLE_VALUE;
1269         Sub->Next = sup;
1270         } // endif Next
1271 
1272       Sub = Sub->Next;
1273       strcat(strcat(Direc, FileData.cFileName), "/");
1274       Sub->Len = strlen(Direc);
1275 
1276       // Reset Hsearch used by TDBDIR::ReadDB
1277 			FindClose(hSearch);
1278 			hSearch = INVALID_HANDLE_VALUE;
1279       goto again;
1280     } // endif H
1281 
1282     } // endif rc
1283 #else   // !_WIN32
1284   rc = RC_NF;
1285 
1286  again:
1287   if (!Sub->D)
1288     // Start searching files in the target directory.
1289     if (!(Sub->D = opendir(Direc))) {
1290       sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno));
1291       rc = RC_FX;
1292       } // endif dir
1293 
1294   while (rc == RC_NF)
1295     if ((Entry = readdir(Sub->D))) {
1296       // We need the Fileinfo structure to get info about the file
1297       strcat(strcpy(Fpath, Direc), Entry->d_name);
1298 
1299       if (lstat(Fpath, &Fileinfo) < 0) {
1300         sprintf(g->Message, "%s: %s", Fpath, strerror(errno));
1301         rc = RC_FX;
1302       } else if (S_ISDIR(Fileinfo.st_mode) && strcmp(Entry->d_name, ".")
1303 			                                     && strcmp(Entry->d_name, "..")) {
1304         // Look in the name sub-directory
1305         if (!Sub->Next) {
1306           PSUBDIR sup;
1307 
1308           sup = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR));
1309           sup->Next = NULL;
1310           sup->Prev = Sub;
1311           Sub->Next = sup;
1312           } // endif Next
1313 
1314         Sub = Sub->Next;
1315         Sub->D = NULL;
1316         Sub->Len = strlen(Direc);
1317         strcat(strcat(Direc, Entry->d_name), "/");
1318         goto again;
1319       } else if (S_ISREG(Fileinfo.st_mode))
1320         // Test whether the file name matches the table name filter
1321         if (!fnmatch(Pattern, Entry->d_name, 0)) {
1322           iFile++;      // We have a match
1323           _splitpath(Entry->d_name, NULL, NULL, Fname, Ftype);
1324           rc = RC_OK;
1325           } // endif fnmatch
1326 
1327     } else {
1328       // No more files. Close the DIR handle.
1329       closedir(Sub->D);
1330 
1331       // Are we in a sub-directory?
1332       if (Sub->Prev) {
1333         // Yes, we must continue in the parent directory
1334         Direc[Sub->Len] = '\0';
1335         Sub = Sub->Prev;
1336       } else
1337         rc = RC_EF;              // No, all is finished
1338 
1339     } // endif Entry
1340 
1341 #endif  // !_WIN32
1342 
1343   return rc;
1344   } // end of ReadDB
1345 
1346 #if 0
1347 /* ------------------------- Class TDBDHR ---------------------------- */
1348 
1349 /***********************************************************************/
1350 /*  TABDHR constructors.                                               */
1351 /***********************************************************************/
1352 TDBDHR::TDBDHR(PDHRDEF tdp) : TDBASE(tdp)
1353   {
1354   memset(&FileData, 0, sizeof(WIN32_FIND_DATA));
1355   Hsearch = INVALID_HANDLE_VALUE;
1356   iFile = 0;
1357   *Drive = '\0';
1358   *Direc = '\0';
1359   *Fname = '\0';
1360   *Ftype = '\0';
1361   } // end of TDBDHR standard constructor
1362 
1363 TDBDHR::TDBDHR(PTDBDHR tdbp) : TDBASE(tdbp)
1364   {
1365   FileData = tdbp->FileData;
1366   Hsearch = tdbp->Hsearch;
1367   iFile = tdbp->iFile;
1368   strcpy(Drive, tdbp->Drive);
1369   strcpy(Direc, tdbp->Direc);
1370   strcpy(Fname, tdbp->Fname);
1371   strcpy(Ftype, tdbp->Ftype);
1372   } // end of TDBDHR copy constructor
1373 
1374 // Method
1375 PTDB TDBDHR::Clone(PTABS t)
1376   {
1377   PTDB    tp;
1378   PGLOBAL g = t->G;        // Is this really useful ???
1379 
1380   tp = new(g) TDBDHR(this);
1381   tp->Columns = Columns;
1382   return tp;
1383   } // end of Clone
1384 
1385 /***********************************************************************/
1386 /*  Allocate DHR column description block.                             */
1387 /***********************************************************************/
1388 PCOL TDBDHR::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
1389   {
1390   return new(g) DHRCOL(cdp, this, cprec, n);
1391   } // end of MakeCol
1392 
1393 /***********************************************************************/
1394 /*  DHR GetMaxSize: returns the number of retrieved files.             */
1395 /***********************************************************************/
1396 int TDBDHR::GetMaxSize(PGLOBAL g)
1397   {
1398   if (MaxSize < 0) {
1399     char    filename[_MAX_PATH];
1400     int     i, rc;
1401     int    n = -1;
1402     HANDLE  h;
1403     PDBUSER dup = PlgGetUser(g);
1404 
1405     PlugSetPath(filename, To_File, dup->Path);
1406 
1407     // Start searching files in the target directory.
1408     h = FindFirstFile(filename, &FileData);
1409 
1410     if (h == INVALID_HANDLE_VALUE) {
1411       switch (rc = GetLastError()) {
1412         case ERROR_NO_MORE_FILES:
1413         case ERROR_FILE_NOT_FOUND:
1414           n = 0;
1415           break;
1416         default:
1417           FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1418                         FORMAT_MESSAGE_IGNORE_INSERTS,
1419                         NULL, rc, 0,
1420                         (LPTSTR)&filename, sizeof(filename), NULL);
1421           sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename);
1422         } // endswitch rc
1423 
1424     } else {
1425       for (n = 1;; n++)
1426         if (!FindNextFile(h, &FileData)) {
1427           rc = GetLastError();
1428 
1429           if (rc != ERROR_NO_MORE_FILES) {
1430             sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
1431             n = -1;
1432             } // endif rc
1433 
1434           break;
1435           } // endif FindNextFile
1436 
1437       // Close the search handle.
1438       if (!FindClose(h) && n != -1)
1439         strcpy(g->Message, MSG(SRCH_CLOSE_ERR));
1440 
1441     } // endif Hsearch
1442 
1443     MaxSize = n;
1444     } // endif MaxSize
1445 
1446   return MaxSize;
1447   } // end of GetMaxSize
1448 
1449 /***********************************************************************/
1450 /*  DHR Access Method opening routine.                                 */
1451 /*  Open first file, other will be opened sequencially when reading.   */
1452 /***********************************************************************/
1453 bool TDBDHR::OpenDB(PGLOBAL g)
1454   {
1455   if (trace(1))
1456     htrc("DHR OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
1457       this, Tdb_No, Use, Mode);
1458 
1459   if (Use == USE_OPEN) {
1460     /*******************************************************************/
1461     /*  Table already open, reopen it.                                 */
1462     /*******************************************************************/
1463     CloseDB(g);
1464     SetUse(USE_READY);
1465     } // endif use
1466 
1467   /*********************************************************************/
1468   /*  Direct access needed for join or sorting.                        */
1469   /*********************************************************************/
1470   if (NeedIndexing(g)) {
1471     // Direct access of DHR tables is not implemented yet
1472     sprintf(g->Message, MSG(NO_DIR_INDX_RD), "DHR");
1473     return true;
1474     } // endif NeedIndexing
1475 
1476   Use = USE_OPEN;
1477   return false;
1478   } // end of OpenDB
1479 
1480 /***********************************************************************/
1481 /*  Data Base read routine for DHR access method.                      */
1482 /***********************************************************************/
1483 int TDBDHR::ReadDB(PGLOBAL g)
1484   {
1485   int   rc = RC_OK;
1486   DWORD erc;
1487 
1488   if (Hsearch == INVALID_HANDLE_VALUE) {
1489     char   *filename[_MAX_PATH];
1490     PDBUSER dup = PlgGetUser(g);
1491 
1492     PlugSetPath(filename, To_File, dup->Path);
1493     _splitpath(filename, Drive, Direc, NULL, NULL);
1494 
1495     /*******************************************************************/
1496     /*  Start searching files in the target directory.                 */
1497     /*******************************************************************/
1498     Hsearch = FindFirstFile(filename, &FileData);
1499 
1500     if (Hsearch != INVALID_HANDLE_VALUE)
1501       iFile = 1;
1502     else switch (erc = GetLastError()) {
1503       case ERROR_NO_MORE_FILES:
1504       case ERROR_FILE_NOT_FOUND:
1505 //    case ERROR_PATH_NOT_FOUND:               ???????
1506         rc = RC_EF;
1507         break;
1508       default:
1509         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1510                       FORMAT_MESSAGE_IGNORE_INSERTS,
1511                       NULL, erc,  0,
1512                       (LPTSTR)&filename, sizeof(filename), NULL);
1513         sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename);
1514         rc = RC_FX;
1515       } // endswitch erc
1516 
1517   } else {
1518     if (!FindNextFile(Hsearch, &FileData)) {
1519       DWORD erc = GetLastError();
1520 
1521       if (erc != ERROR_NO_MORE_FILES) {
1522         sprintf(g->Message, MSG(NEXT_FILE_ERROR), erc);
1523         FindClose(Hsearch);
1524         rc = RC_FX;
1525       } else
1526         rc = RC_EF;
1527 
1528     } else
1529       iFile++;
1530 
1531   } // endif Hsearch
1532 
1533   if (rc == RC_OK)
1534     _splitpath(FileData.cFileName, NULL, NULL, Fname, Ftype);
1535 
1536   return rc;
1537   } // end of ReadDB
1538 
1539 /***********************************************************************/
1540 /*  Data Base close routine for MUL access method.                     */
1541 /***********************************************************************/
1542 void TDBDHR::CloseDB(PGLOBAL g)
1543   {
1544   // Close the search handle.
1545   if (!FindClose(Hsearch)) {
1546     strcpy(g->Message, MSG(SRCH_CLOSE_ERR));
1547 		throw GetAmType();
1548 	} // endif FindClose
1549 
1550   iFile = 0;
1551   Hsearch = INVALID_HANDLE_VALUE;
1552   } // end of CloseDB
1553 
1554 // ------------------------ DHRCOL functions ----------------------------
1555 
1556 /***********************************************************************/
1557 /*  DHRCOL public constructor.                                         */
1558 /***********************************************************************/
1559 DHRCOL::DHRCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
1560       : COLBLK(cdp, tdbp, i)
1561   {
1562   if (cprec) {
1563     Next = cprec->GetNext();
1564     cprec->SetNext(this);
1565   } else {
1566     Next = tdbp->GetColumns();
1567     tdbp->SetColumns(this);
1568   } // endif cprec
1569 
1570   // Set additional DHR access method information for column.
1571   N = cdp->GetOffset();
1572   } // end of DOSCOL constructor
1573 
1574 /***********************************************************************/
1575 /*  DHRCOL constructor used for copying columns.                       */
1576 /*  tdbp is the pointer to the new table descriptor.                   */
1577 /***********************************************************************/
1578 DHRCOL::DHRCOL(DHRCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
1579   {
1580   N = col1->N;
1581   } // end of DHRCOL copy constructor
1582 
1583 /***********************************************************************/
1584 /*  ReadColumn: what this routine does is to access the information    */
1585 /*  corresponding to this column and convert it to buffer type.        */
1586 /***********************************************************************/
1587 void DHRCOL::ReadColumn(PGLOBAL g)
1588   {
1589   int     rc;
1590   PTDBDHR tdbp = (PTDBDHR)To_Tdb;
1591 
1592   if (trace(1))
1593     htrc("DHR ReadColumn: col %s R%d use=%.4X status=%.4X type=%d N=%d\n",
1594       Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type, N);
1595 
1596   /*********************************************************************/
1597   /*  Retrieve the information corresponding to the column number.     */
1598   /*********************************************************************/
1599   switch (N) {
1600     case 0:                                // Drive
1601       Value->SetValue(Drive, _MAX_DRIVE);
1602       break;
1603     case 1:                                // Path
1604       Value->SetValue(Direc, _MAX_DHR);
1605       break;
1606     case 2:                                // Name
1607       Value->SetValue(Fname, _MAX_FNAME);
1608       break;
1609     case 3:                                // Extention
1610       Value->SetValue(Ftype, _MAX_EXT);
1611       break;
1612     case 4:                                // Extention
1613       Value->SetValue(tdbp->FileData.cAlternateFileName, 14);
1614       break;
1615     case 5:
1616       Value->SetValue(tdbp->FileData.dwFileAttributes);
1617       break;
1618     case 6:
1619       Value->SetValue(..................
1620   } // end of ReadColumn
1621 #endif // 0
1622 
1623