mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-30 18:36:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1614 lines
		
	
	
	
		
			51 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1614 lines
		
	
	
	
		
			51 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /********** PlgDBUtl Fpe C++ Program Source Code File (.CPP) ***********/
 | |
| /* PROGRAM NAME: PLGDBUTL                                              */
 | |
| /* -------------                                                       */
 | |
| /*  Version 4.1                                                        */
 | |
| /*                                                                     */
 | |
| /* COPYRIGHT:                                                          */
 | |
| /* ----------                                                          */
 | |
| /*  (C) Copyright to the author Olivier BERTRAND          1998-2020    */
 | |
| /*                                                                     */
 | |
| /* WHAT THIS PROGRAM DOES:                                             */
 | |
| /* -----------------------                                             */
 | |
| /*  Utility functions used by DB semantic routines.                    */
 | |
| /*                                                                     */
 | |
| /* WHAT YOU NEED TO COMPILE THIS PROGRAM:                              */
 | |
| /* --------------------------------------                              */
 | |
| /*                                                                     */
 | |
| /*  REQUIRED FILES:                                                    */
 | |
| /*  ---------------                                                    */
 | |
| /*  See Readme.C for a list and description of required SYSTEM files.  */
 | |
| /*                                                                     */
 | |
| /*    PLGDBUTL.C     - Source code                                     */
 | |
| /*    GLOBAL.H       - Global declaration file                         */
 | |
| /*    PLGDBSEM.H     - DB application declaration file                 */
 | |
| /*                                                                     */
 | |
| /*  REQUIRED LIBRARIES:                                                */
 | |
| /*  -------------------                                                */
 | |
| /*    OS2.LIB        - OS2 libray                                      */
 | |
| /*    LLIBCE.LIB     - Protect mode/standard combined large model C    */
 | |
| /*                     library                                         */
 | |
| /*                                                                     */
 | |
| /*  REQUIRED PROGRAMS:                                                 */
 | |
| /*  ------------------                                                 */
 | |
| /*    IBM, MS, Borland or GNU C++ Compiler                             */
 | |
| /*    IBM, MS, Borland or GNU Linker                                   */
 | |
| /***********************************************************************/
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Include relevant MariaDB header file.                              */
 | |
| /***********************************************************************/
 | |
| #include "my_global.h"
 | |
| #include "my_pthread.h"
 | |
| #if defined(_WIN32)
 | |
| #include <io.h>
 | |
| #include <fcntl.h>
 | |
| #include <errno.h>
 | |
| #define BIGMEM         1048576            // 1 Megabyte
 | |
| #else     // !_WIN32
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| //#if defined(THREAD)
 | |
| #include <pthread.h>
 | |
| //#endif   // THREAD
 | |
| #include <stdarg.h>
 | |
| #define BIGMEM      2147483647            // Max int value
 | |
| #endif    // !_WIN32
 | |
| #include <locale.h>
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Include application header files                                   */
 | |
| /***********************************************************************/
 | |
| #include "global.h"    // header containing all global declarations.
 | |
| #include "plgdbsem.h"  // header containing the DB applic. declarations.
 | |
| #include "preparse.h"  // For DATPAR
 | |
| #include "osutil.h"
 | |
| #include "maputil.h"
 | |
| #include "catalog.h"
 | |
| #include "colblk.h"
 | |
| #include "xtable.h"    // header of TBX, TDB and TDBASE classes
 | |
| #include "tabcol.h"    // header of XTAB and COLUMN classes
 | |
| #include "valblk.h"
 | |
| #include "rcmsg.h"
 | |
| #ifdef ZIP_SUPPORT
 | |
| #include "filamzip.h"
 | |
| #endif // ZIP_SUPPORT
 | |
| #ifdef JAVA_SUPPORT
 | |
| #include "javaconn.h"
 | |
| #endif // JAVA_SUPPORT
 | |
| #ifdef CMGO_SUPPORT
 | |
| #include "cmgoconn.h"
 | |
| #endif // JAVA_SUPPORT
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  DB static variables.                                               */
 | |
| /***********************************************************************/
 | |
| bool  Initdone = false;
 | |
| bool  plugin = false;  // True when called by the XDB plugin handler 
 | |
| 
 | |
| extern "C" {
 | |
| extern char version[];
 | |
| } // extern "C"
 | |
| 
 | |
| //#if defined(_WIN32)
 | |
| //extern CRITICAL_SECTION parsec;      // Used calling the Flex parser
 | |
| //#else   // !_WIN32
 | |
| extern pthread_mutex_t parmut;
 | |
| //#endif  // !_WIN32
 | |
| 
 | |
| // The debug trace used by the main thread
 | |
| FILE *pfile = NULL;
 | |
| 
 | |
| MBLOCK Nmblk = {NULL, false, 0, false, NULL};   // Used to init MBLOCK's
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Routines called externally and internally by utility routines.     */
 | |
| /***********************************************************************/
 | |
| bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
 | |
| bool EvalLikePattern(LPCSTR, LPCSTR);
 | |
| void PlugConvertConstant(PGLOBAL, void* &, short&);
 | |
| 
 | |
| #ifdef DOMDOC_SUPPORT
 | |
| void CloseXMLFile(PGLOBAL, PFBLOCK, bool);
 | |
| #endif   // DOMDOC_SUPPORT
 | |
| 
 | |
| #ifdef LIBXML2_SUPPORT
 | |
| #include "libdoc.h"
 | |
| #endif   // LIBXML2_SUPPORT
 | |
| 
 | |
| #ifdef ODBC_SUPPORT
 | |
| void OdbcClose(PGLOBAL g, PFBLOCK fp);
 | |
| #endif   // ODBC_SUPPORT
 | |
| 
 | |
| /***********************************************************************/
 | |
| /* Routines for file IO with error reporting to g->Message             */
 | |
| /* Note: errno and strerror must be called before the message file     */
 | |
| /* is read in the case of XMSG compile.                                */
 | |
| /***********************************************************************/
 | |
| static void global_open_error_msg(GLOBAL *g, int msgid, const char *path, 
 | |
|                                                         const char *mode)
 | |
| {
 | |
|   int  len, rno= (int)errno;
 | |
|   char errmsg[256]= "";
 | |
| 
 | |
|   strncat(errmsg, strerror(errno), 255);
 | |
| 
 | |
|   switch (msgid)
 | |
|   {
 | |
|     case MSGID_CANNOT_OPEN:
 | |
|       len= snprintf(g->Message, sizeof(g->Message) - 1,
 | |
|                     MSG(CANNOT_OPEN), // Cannot open %s
 | |
|                     path);
 | |
|       break;
 | |
| 
 | |
|     case MSGID_OPEN_MODE_ERROR:
 | |
|       len= snprintf(g->Message, sizeof(g->Message) - 1,
 | |
|                     MSG(OPEN_MODE_ERROR), // "Open(%s) error %d on %s"
 | |
|                     mode, rno, path);
 | |
|       break;
 | |
| 
 | |
|     case MSGID_OPEN_MODE_STRERROR:
 | |
|       {char fmt[256];
 | |
|       strcat(strcpy(fmt, MSG(OPEN_MODE_ERROR)), ": %s");
 | |
|       len= snprintf(g->Message, sizeof(g->Message) - 1,
 | |
|                     fmt, // Open(%s) error %d on %s: %s
 | |
|                     mode, rno, path, errmsg);
 | |
|       }break;
 | |
| 
 | |
|     case MSGID_OPEN_STRERROR:
 | |
|       len= snprintf(g->Message, sizeof(g->Message) - 1,
 | |
|                     MSG(OPEN_STRERROR), // "open error: %s"
 | |
|                     errmsg);
 | |
|       break;
 | |
| 
 | |
|     case MSGID_OPEN_ERROR_AND_STRERROR:
 | |
|       len= snprintf(g->Message, sizeof(g->Message) - 1,
 | |
|                     //OPEN_ERROR does not work, as it wants mode %d (not %s)
 | |
|                     //MSG(OPEN_ERROR) "%s",// "Open error %d in mode %d on %s: %s"
 | |
|                     "Open error %d in mode %s on %s: %s",
 | |
|                     rno, mode, path, errmsg);
 | |
|       break;
 | |
| 
 | |
|     case MSGID_OPEN_EMPTY_FILE:
 | |
|       len= snprintf(g->Message, sizeof(g->Message) - 1,
 | |
|                     MSG(OPEN_EMPTY_FILE), // "Opening empty file %s: %s"
 | |
|                     path, errmsg);
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       DBUG_ASSERT(0);
 | |
|       /* Fall through*/
 | |
|     case 0:
 | |
|       len= 0;
 | |
|   }
 | |
|   g->Message[len]= '\0';
 | |
| }
 | |
| 
 | |
| 
 | |
| FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode)
 | |
| {
 | |
|   FILE *f;
 | |
|   if (!(f= fopen(path, mode)))
 | |
|     global_open_error_msg(g, msgid, path, mode);
 | |
|   return f;
 | |
| }
 | |
| 
 | |
| 
 | |
| int global_open(GLOBAL *g, int msgid, const char *path, int flags)
 | |
| {
 | |
|   int h;
 | |
|   if ((h= open(path, flags)) <= 0)
 | |
|     global_open_error_msg(g, msgid, path, "");
 | |
|   return h;
 | |
| }
 | |
| 
 | |
| 
 | |
| int global_open(GLOBAL *g, int msgid, const char *path, int flags, int mode)
 | |
| {
 | |
|   int h;
 | |
|   if ((h= open(path, flags, mode)) <= 0)
 | |
|   {
 | |
|     char modestr[64];
 | |
|     snprintf(modestr, sizeof(modestr), "%d", mode);
 | |
|     global_open_error_msg(g, msgid, path, modestr);
 | |
|   }
 | |
|   return h;
 | |
| }
 | |
| 
 | |
| DllExport void SetTrc(void)
 | |
| {
 | |
|   // If tracing is on, debug must be initialized.
 | |
|   debug = pfile;
 | |
| } // end of SetTrc
 | |
| 
 | |
| /**************************************************************************/
 | |
| /*  SubAllocate the result structure that will contain result data.       */
 | |
| /**************************************************************************/
 | |
| PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
 | |
|                        int *buftyp, XFLD *fldtyp, 
 | |
|                        unsigned int *length, bool blank, bool nonull)
 | |
| {
 | |
|   char     cname[NAM_LEN+1];
 | |
|   int      i;
 | |
|   PCOLRES *pcrp, crp;
 | |
|   PQRYRES  qrp;
 | |
| 
 | |
| 	try {
 | |
| 		/**********************************************************************/
 | |
| 		/*  Allocate the structure used to contain the result set.            */
 | |
| 		/**********************************************************************/
 | |
| 		qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
 | |
| 		pcrp = &qrp->Colresp;
 | |
| 		qrp->Continued = false;
 | |
| 		qrp->Truncated = false;
 | |
| 		qrp->Info = false;
 | |
| 		qrp->Suball = true;
 | |
| 		qrp->Maxres = maxres;
 | |
| 		qrp->Maxsize = 0;
 | |
| 		qrp->Nblin = 0;
 | |
| 		qrp->Nbcol = 0;                                     // will be ncol
 | |
| 		qrp->Cursor = 0;
 | |
| 		qrp->BadLines = 0;
 | |
| 
 | |
| 		for (i = 0; i < ncol; i++) {
 | |
| 			*pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
 | |
| 			crp = *pcrp;
 | |
| 			pcrp = &crp->Next;
 | |
| 			memset(crp, 0, sizeof(COLRES));
 | |
| 			crp->Colp = NULL;
 | |
| 			crp->Ncol = ++qrp->Nbcol;
 | |
| 			crp->Type = buftyp[i];
 | |
| 			crp->Length = length[i];
 | |
| 			crp->Clen = GetTypeSize(crp->Type, length[i]);
 | |
| 			crp->Prec = 0;
 | |
| 
 | |
| 			if (ids > 0) {
 | |
| #if defined(XMSG)
 | |
| 				// Get header from message file
 | |
| 				strncpy(cname, PlugReadMessage(g, ids + crp->Ncol, NULL), NAM_LEN);
 | |
| 				cname[NAM_LEN] = 0;					// for truncated long names
 | |
| #else   // !XMSG
 | |
| 				GetRcString(ids + crp->Ncol, cname, sizeof(cname));
 | |
| #endif  // !XMSG
 | |
| 				crp->Name = (PSZ)PlugDup(g, cname);
 | |
| 			} else
 | |
| 				crp->Name = NULL;           // Will be set by caller
 | |
| 
 | |
| 			if (fldtyp)
 | |
| 				crp->Fld = fldtyp[i];
 | |
| 			else
 | |
| 				crp->Fld = FLD_NO;
 | |
| 
 | |
| 			// Allocate the Value Block that will contain data
 | |
| 			if (crp->Length || nonull)
 | |
| 				crp->Kdata = AllocValBlock(g, NULL, crp->Type, maxres,
 | |
| 					crp->Length, 0, true, blank, false);
 | |
| 			else
 | |
| 				crp->Kdata = NULL;
 | |
| 
 | |
| 			if (trace(1))
 | |
| 				htrc("Column(%d) %s type=%d len=%d value=%p\n",
 | |
| 					crp->Ncol, crp->Name, crp->Type, crp->Length, crp->Kdata);
 | |
| 
 | |
| 		} // endfor i
 | |
| 
 | |
| 		*pcrp = NULL;
 | |
| 
 | |
| 	} catch (int n) {
 | |
|   	htrc("Exception %d: %s\n", n, g->Message);
 | |
| 	  qrp = NULL;
 | |
|   } catch (const char *msg) {
 | |
|   	strcpy(g->Message, msg);
 | |
| 	  htrc("%s\n", g->Message);
 | |
| 	  qrp = NULL;
 | |
|   } // end catch
 | |
| 
 | |
| 	return qrp;
 | |
| } // end of PlgAllocResult
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Allocate and initialize the new DB User Block.                     */
 | |
| /***********************************************************************/
 | |
| PDBUSER PlgMakeUser(PGLOBAL g)
 | |
|   {
 | |
|   PDBUSER dbuserp;
 | |
| 
 | |
|   if (!(dbuserp = (PDBUSER)malloc(sizeof(DBUSERBLK)))) {
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(MALLOC_ERROR), "PlgMakeUser");
 | |
|     return NULL;
 | |
|     } // endif dbuserp
 | |
| 
 | |
|   memset(dbuserp, 0, sizeof(DBUSERBLK));
 | |
|   dbuserp->Maxbmp = MAXBMP;
 | |
| //dbuserp->UseTemp = TMP_AUTO;
 | |
|   dbuserp->Check = CHK_ALL;
 | |
|   strcpy(dbuserp->Server, "CONNECT");
 | |
|   return dbuserp;
 | |
|   } // end of PlgMakeUser
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PlgGetUser: returns DBUSER block pointer.                          */
 | |
| /***********************************************************************/
 | |
| PDBUSER PlgGetUser(PGLOBAL g)
 | |
|   {
 | |
|   PDBUSER dup = (PDBUSER)((g->Activityp) ? g->Activityp->Aptr : NULL);
 | |
| 
 | |
|   if (!dup)
 | |
|     strcpy(g->Message, MSG(APPL_NOT_INIT));
 | |
| 
 | |
|   return dup;
 | |
|   } // end of PlgGetUser
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PlgGetCatalog: returns CATALOG class pointer.                      */
 | |
| /***********************************************************************/
 | |
| PCATLG PlgGetCatalog(PGLOBAL g, bool jump)
 | |
|   {
 | |
|   PDBUSER dbuserp = PlgGetUser(g);
 | |
|   PCATLG  cat = (dbuserp) ? dbuserp->Catalog : NULL;
 | |
| 
 | |
|   if (!cat && jump) {
 | |
|     // Raise exception so caller doesn't have to check return value
 | |
|     strcpy(g->Message, MSG(NO_ACTIVE_DB));
 | |
| 		throw 1;
 | |
| 	} // endif cat
 | |
| 
 | |
|   return cat;
 | |
|   } // end of PlgGetCatalog
 | |
| 
 | |
| #if 0
 | |
| /***********************************************************************/
 | |
| /*  PlgGetDataPath: returns the default data path.                     */
 | |
| /***********************************************************************/
 | |
| char *PlgGetDataPath(PGLOBAL g)
 | |
|   {
 | |
|   PCATLG cat = PlgGetCatalog(g, false);
 | |
| 
 | |
|   return (cat) ? cat->GetDataPath() : NULL;
 | |
|   } // end of PlgGetDataPath
 | |
| #endif // 0
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  This function returns a database path.                             */
 | |
| /***********************************************************************/
 | |
| char *SetPath(PGLOBAL g, const char *path)
 | |
| {
 | |
|   char *buf= NULL;
 | |
| 
 | |
| 	if (path) {
 | |
| 		size_t len = strlen(path) + (*path != '.' ? 4 : 1);
 | |
| 
 | |
| 		if (!(buf = (char*)PlgDBSubAlloc(g, NULL, len)))
 | |
| 			return NULL;
 | |
| 
 | |
| 		if (PlugIsAbsolutePath(path)) {
 | |
| 			snprintf(buf, len, "%s", path);
 | |
| 			return buf;
 | |
| 		} // endif path
 | |
| 
 | |
| 		if (*path != '.') {
 | |
| #if defined(_WIN32)
 | |
| 			const char *s = "\\";
 | |
| #else   // !_WIN32
 | |
| 			const char *s = "/";
 | |
| #endif  // !_WIN32
 | |
| 			snprintf(buf, len, ".%s%s%s", s, path, s);
 | |
| 		} else
 | |
| 			snprintf(buf, len, "%s", path);
 | |
| 
 | |
| 	} // endif path
 | |
| 
 | |
|   return buf;
 | |
| } // end of SetPath
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Extract from a path name the required component.                   */
 | |
| /*  This function assumes there is enough space in the buffer.         */
 | |
| /***********************************************************************/
 | |
| char *ExtractFromPath(PGLOBAL g, char *pBuff, char *FileName, OPVAL op)
 | |
|   {
 | |
|   char *drive = NULL, *direc = NULL, *fname = NULL, *ftype = NULL;
 | |
| 
 | |
|   switch (op) {           // Determine which part to extract
 | |
| #if defined(_WIN32)
 | |
|     case OP_FDISK: drive = pBuff; break;
 | |
| #endif   // !UNIX
 | |
|     case OP_FPATH: direc = pBuff; break;
 | |
|     case OP_FNAME: fname = pBuff; break;
 | |
|     case OP_FTYPE: ftype = pBuff; break;
 | |
|     default:
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(INVALID_OPER), op, "ExtractFromPath");
 | |
|       return NULL;
 | |
|     } // endswitch op
 | |
| 
 | |
|   // Now do the extraction
 | |
|   _splitpath(FileName, drive, direc, fname, ftype);
 | |
|   return pBuff;
 | |
|   } // end of PlgExtractFromPath
 | |
| 
 | |
| 
 | |
| #ifdef NOT_USED
 | |
| /***********************************************************************/
 | |
| /*  Check the occurrence and matching of a pattern against a string.    */
 | |
| /*  Because this function is only used for catalog name checking,      */
 | |
| /*  it must be case insensitive.                                       */
 | |
| /***********************************************************************/
 | |
| static bool PlugCheckPattern(PGLOBAL g, LPCSTR string, LPCSTR pat)
 | |
|   {
 | |
|   if (pat && strlen(pat)) {
 | |
|     // This leaves 2048 bytes (MAX_STR / 2) for each components
 | |
|     LPSTR name = g->Message + MAX_STR / 2;
 | |
| 
 | |
|     strlwr(strcpy(name, string));
 | |
|     strlwr(strcpy(g->Message, pat));         // Can be modified by Eval
 | |
|     return EvalLikePattern(name, g->Message);
 | |
|   } else
 | |
|     return true;
 | |
| 
 | |
|   } // end of PlugCheckPattern
 | |
| #endif /* NOT_USED */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PlugEvalLike: evaluates a LIKE clause.                             */
 | |
| /*  Syntaxe: M like P escape C. strg->M, pat->P, C not implemented yet */
 | |
| /***********************************************************************/
 | |
| bool PlugEvalLike(PGLOBAL g, LPCSTR strg, LPCSTR pat, bool ci)
 | |
|   {
 | |
|   char *tp, *sp;
 | |
|   bool  b;
 | |
| 
 | |
|   if (trace(2))
 | |
|     htrc("LIKE: strg='%s' pattern='%s'\n", strg, pat);
 | |
| 
 | |
|   if (ci) {                        /* Case insensitive test             */
 | |
|     if (strlen(pat) + strlen(strg) + 1 < MAX_STR)
 | |
|       tp = g->Message;
 | |
|     else if (!(tp = new char[strlen(pat) + strlen(strg) + 2])) {
 | |
|       strcpy(g->Message, MSG(NEW_RETURN_NULL));
 | |
| 			throw (int)OP_LIKE;
 | |
| 		} /* endif tp */
 | |
|     
 | |
|     sp = tp + strlen(pat) + 1;
 | |
|     strlwr(strcpy(tp, pat));      /* Make a lower case copy of pat     */
 | |
|     strlwr(strcpy(sp, strg));     /* Make a lower case copy of strg    */
 | |
|   } else {                        /* Case sensitive test               */
 | |
|     if (strlen(pat) < MAX_STR)    /* In most of the case for small pat */
 | |
|       tp = g->Message;            /* Use this as temporary work space. */
 | |
|     else if (!(tp = new char[strlen(pat) + 1])) {
 | |
|       strcpy(g->Message, MSG(NEW_RETURN_NULL));
 | |
| 			throw (int)OP_LIKE;
 | |
| 		} /* endif tp */
 | |
|     
 | |
|     strcpy(tp, pat);                  /* Make a copy to be worked into */
 | |
|     sp = (char*)strg;
 | |
|   } /* endif ci */
 | |
| 
 | |
|   b = EvalLikePattern(sp, tp);
 | |
| 
 | |
|   if (tp != g->Message)               /* If working space was obtained */
 | |
|     delete [] tp;                     /* by the use of new, delete it. */
 | |
| 
 | |
|   return (b);
 | |
|   } /* end of PlugEvalLike */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  M and P are variable length character string. If M and P are zero  */
 | |
| /*  length strings then the Like predicate is true.                    */
 | |
| /*                                                                     */
 | |
| /*  The Like predicate is true if:                                     */
 | |
| /*                                                                     */
 | |
| /*  1- A subtring of M is a sequence of 0 or more contiguous <CR> of M */
 | |
| /*     and each <CR> of M is part of exactly one substring.            */
 | |
| /*                                                                     */
 | |
| /*  2- If the i-th <subtring-specifyer> of P is an <arbitrary-char-    */
 | |
| /*     specifier>, the i-th subtring of M is any single <CR>.          */
 | |
| /*                                                                     */
 | |
| /*  3- If the i-th <subtring-specifyer> of P is an <arbitrary-string-  */
 | |
| /*     specifier>, then the i-th subtring of M is any sequence of zero */
 | |
| /*     or more <CR>.                                                   */
 | |
| /*                                                                     */
 | |
| /*  4- If the i-th <subtring-specifyer> of P is neither an <arbitrary- */
 | |
| /*     character-specifier> nor an <arbitrary-string-specifier>, then  */
 | |
| /*     the i-th substring of M is equal to that <substring-specifier>  */
 | |
| /*     according to the collating sequence of the <like-predicate>,    */
 | |
| /*     without the appending of <space-character>, and has the same    */
 | |
| /*     length as that <substring-specifier>.                           */
 | |
| /*                                                                     */
 | |
| /*  5- The number of substrings of M is equal to the number of         */
 | |
| /*     <subtring-specifiers> of P.                                     */
 | |
| /*                                                                     */
 | |
| /*  Otherwise M like P is false.                                       */
 | |
| /***********************************************************************/
 | |
| bool EvalLikePattern(LPCSTR sp, LPCSTR tp)
 | |
|   {
 | |
|   LPSTR p;
 | |
|   char  c;
 | |
|   ssize_t   n;
 | |
|   bool  b, t = false;
 | |
| 
 | |
|   if (trace(2))
 | |
|     htrc("Eval Like: sp=%s tp=%s\n", 
 | |
|          (sp) ? sp : "Null", (tp) ? tp : "Null");
 | |
| 
 | |
|   /********************************************************************/
 | |
|   /*  If pattern is void, Like is true only if string is also void.   */
 | |
|   /********************************************************************/
 | |
|   if (!*tp)
 | |
|     return (!*sp);
 | |
| 
 | |
|   /********************************************************************/
 | |
|   /*  Analyse eventual arbitrary specifications ahead of pattern.     */
 | |
|   /********************************************************************/
 | |
|   for (p = (LPSTR)tp; p;)
 | |
|     switch (*p) {                     /*   it can contain % and/or _   */
 | |
|       case '%':                       /* An % has been found           */
 | |
|         t = true;                     /* Note eventual character skip  */
 | |
|         p++;
 | |
|         break;
 | |
|       case '_':                       /* An _ has been found           */
 | |
|         if (*sp) {                    /* If more character in string   */
 | |
|           sp++;                       /*   skip it                     */
 | |
|           p++;
 | |
|         } else
 | |
|           return false;               /* Like condition is not met     */
 | |
| 
 | |
|         break;
 | |
|       default:
 | |
|         tp = p;                       /* Point to rest of template     */
 | |
|         p = NULL;                     /* To stop For loop              */
 | |
|         break;
 | |
|       } /* endswitch */
 | |
| 
 | |
|   if ((p = (LPSTR)strpbrk(tp, "%_"))) /* Get position of next % or _   */
 | |
|     n = p - tp;
 | |
|   else
 | |
|     n = strlen(tp);                   /* Get length of pattern head    */
 | |
| 
 | |
|   if (trace(2))
 | |
|     htrc(" testing: t=%d sp=%s tp=%s p=%p\n", t, sp, tp, p);
 | |
| 
 | |
|   if (n > (signed)strlen(sp))         /* If head is longer than strg   */
 | |
|     b = false;                        /* Like condition is not met     */
 | |
|   else if (n == 0)                    /* If void <substring-specifier> */
 | |
|     b = (t || !*sp);                  /*   true if %  or void strg.    */
 | |
|   else if (!t) {
 | |
|     /*******************************************************************/
 | |
|     /*  No character to skip, check occurrence of <subtring-specifier>  */
 | |
|     /*  at the very beginning of remaining string.                     */
 | |
|     /*******************************************************************/
 | |
|     if (p) {
 | |
|       if ((b = !strncmp(sp, tp, n)))
 | |
|         b = EvalLikePattern(sp + n, p);
 | |
| 
 | |
|     } else
 | |
|       b = !strcmp(sp, tp);            /*   strg and tmp heads match    */
 | |
| 
 | |
|   } else
 | |
|     if (p)
 | |
|       /*****************************************************************/
 | |
|       /*  Here is the case explaining why we need a recursive routine. */
 | |
|       /*  The test must be done not only against the first occurrence   */
 | |
|       /*  of the <substring-specifier> in the remaining string,        */
 | |
|       /*  but also with all eventual succeeding ones.                  */
 | |
|       /*****************************************************************/
 | |
|       for (b = false, c = *p; !b && (signed)strlen(sp) >= n; sp++) {
 | |
|         *p = '\0';                    /* Separate pattern header       */
 | |
| 
 | |
|         if ((sp = strstr(sp, tp))) {
 | |
|           *p = c;
 | |
|           b = EvalLikePattern(sp + n, p);
 | |
|         } else {
 | |
|           *p = c;
 | |
|           b = false;
 | |
|           break;
 | |
|         } /* endif s */
 | |
| 
 | |
|         } /* endfor b, sp */
 | |
| 
 | |
|     else {
 | |
|       sp += (strlen(sp) - n);
 | |
|       b = !strcmp(sp, tp);
 | |
|     } /* endif p */
 | |
| 
 | |
|   if (trace(2))
 | |
|     htrc(" done: b=%d n=%d sp=%s tp=%s\n",
 | |
|           b, n, (sp) ? sp : "Null", tp);
 | |
| 
 | |
|   return (b);
 | |
|   } /* end of EvalLikePattern */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  MakeEscape: Escape some characters in a string.                    */
 | |
| /***********************************************************************/
 | |
| char *MakeEscape(PGLOBAL g, char* str, char q)
 | |
|   {
 | |
|   char *bufp;
 | |
|   int i, k, n = 0, len = (int)strlen(str);
 | |
| 
 | |
|   for (i = 0; i < len; i++)
 | |
|     if (str[i] == q || str[i] == '\\')
 | |
|       n++;
 | |
| 
 | |
|   if (!n)
 | |
|     return str;
 | |
|   else
 | |
|     bufp = (char*)PlugSubAlloc(g, NULL, len + n + 1);
 | |
| 
 | |
|   for (i = k = 0; i < len; i++) {
 | |
|     if (str[i] == q || str[i] == '\\')
 | |
|       bufp[k++] = '\\';
 | |
| 
 | |
|     bufp[k++] = str[i];
 | |
|     } // endfor i
 | |
| 
 | |
|   bufp[k] = 0;
 | |
|   return bufp;
 | |
|   } /* end of MakeEscape */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PlugConvertConstant: convert a Plug constant to an Xobject.        */
 | |
| /***********************************************************************/
 | |
| void PlugConvertConstant(PGLOBAL g, void* & value, short& type)
 | |
|   {
 | |
|   if (trace(1))
 | |
|     htrc("PlugConvertConstant: value=%p type=%hd\n", value, type);
 | |
| 
 | |
|   if (type != TYPE_XOBJECT) {
 | |
|     value = new(g) CONSTANT(g, value, type);
 | |
|     type = TYPE_XOBJECT;
 | |
|     } // endif type
 | |
| 
 | |
|   } // end of PlugConvertConstant
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Call the Flex preparser to convert a date format to a sscanf input */
 | |
| /*  format and a Strftime output format. Flag if not 0 indicates that  */
 | |
| /*  non quoted blanks are not included in the output format.           */
 | |
| /***********************************************************************/
 | |
| PDTP MakeDateFormat(PGLOBAL g, PCSZ dfmt, bool in, bool out, int flag)
 | |
| {
 | |
| 	int  rc;
 | |
|   PDTP pdp = (PDTP)PlugSubAlloc(g, NULL, sizeof(DATPAR));
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("MakeDateFormat: dfmt=%s\n", dfmt);
 | |
| 
 | |
|   memset(pdp, 0, sizeof(DATPAR));
 | |
|   pdp->Format = pdp->Curp = PlugDup(g, dfmt);
 | |
|   pdp->Outsize = 2 * strlen(dfmt) + 1;
 | |
| 
 | |
|   if (in)
 | |
|     pdp->InFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
 | |
| 
 | |
|   if (out)
 | |
|     pdp->OutFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
 | |
| 
 | |
|   pdp->Flag = flag;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /* Call the FLEX generated parser. In multi-threading mode the next  */
 | |
|   /* instruction is protected by mutex fmdflex using static variables. */
 | |
|   /*********************************************************************/
 | |
|   pthread_mutex_lock(&parmut);
 | |
|   rc = fmdflex(pdp);
 | |
|   pthread_mutex_unlock(&parmut);
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("Done: in=%s out=%s rc=%d\n", SVP(pdp->InFmt), SVP(pdp->OutFmt), rc);
 | |
| 
 | |
|   return pdp;
 | |
| } // end of MakeDateFormat
 | |
| 
 | |
| /***********************************************************************/
 | |
| /* Extract the date from a formatted string according to format.       */
 | |
| /***********************************************************************/
 | |
| int ExtractDate(char *dts, PDTP pdp, int defy, int val[6])
 | |
|   {
 | |
| 	PCSZ fmt;
 | |
| 	char c, d, e, W[8][12];
 | |
|   int  i, k, m, numval;
 | |
|   int  n, y = 30;
 | |
|   bool b = true;           // true for null dates
 | |
| 
 | |
|   if (pdp)
 | |
|     fmt = pdp->InFmt;
 | |
|   else            // assume standard MySQL date format
 | |
|     fmt = "%4d-%2d-%2d %2d:%2d:%2d";
 | |
| 
 | |
|   if (trace(2))
 | |
|     htrc("ExtractDate: dts=%s fmt=%s defy=%d\n", dts, fmt, defy);
 | |
| 
 | |
|   // Set default values for time only use
 | |
|   if (defy) {
 | |
|     // This may be a default value for year
 | |
|     y = defy;
 | |
|     val[0] = y;
 | |
|     y = (y < 100) ? y : 30;
 | |
|   } else
 | |
|     val[0] = 70;
 | |
| 
 | |
|   val[1] = 1;
 | |
|   val[2] = 1;
 | |
| 
 | |
|   for (i = 3; i < 6; i++)
 | |
|     val[i] = 0;
 | |
| 
 | |
|   numval = 0;
 | |
| 
 | |
|   // Get the date field parse it with derived input format
 | |
|   m = sscanf(dts, fmt, W[0], W[1], W[2], W[3], W[4], W[5], W[6], W[7]);
 | |
| 
 | |
|   if (m > pdp->Num)
 | |
|     m = pdp->Num;
 | |
| 
 | |
|   for (i = 0; i < m; i++) {
 | |
|     if ((n = *(int*)W[i]))
 | |
|       b = false;
 | |
| 
 | |
|     switch (k = pdp->Index[i]) {
 | |
|       case 0:
 | |
|         if (n < y)
 | |
|           n += 100;
 | |
| 
 | |
|         val[0] = n;
 | |
|         numval = MY_MAX(numval, 1);
 | |
|         break;
 | |
|       case 1:
 | |
|       case 2:
 | |
|       case 3:
 | |
|       case 4:
 | |
|       case 5:
 | |
|         val[k] = n;
 | |
|         numval = MY_MAX(numval, k + 1);
 | |
|         break;
 | |
|       case -1:
 | |
|         c = toupper(W[i][0]);
 | |
|         d = toupper(W[i][1]);
 | |
|         e = toupper(W[i][2]);
 | |
| 
 | |
|         switch (c) {
 | |
|           case 'J':
 | |
|             n = (d == 'A') ? 1
 | |
|               : (e == 'N') ? 6 : 7; break;
 | |
|           case 'F': n =  2; break;
 | |
|           case 'M':
 | |
|             n = (e == 'R') ? 3 : 5; break;
 | |
|           case 'A':
 | |
|             n = (d == 'P') ? 4 : 8; break;
 | |
|             break;
 | |
|           case 'S': n =  9; break;
 | |
|           case 'O': n = 10; break;
 | |
|           case 'N': n = 11; break;
 | |
|           case 'D': n = 12; break;
 | |
|           } /* endswitch c */
 | |
| 
 | |
|         val[1] = n;
 | |
|         numval = MY_MAX(numval, 2);
 | |
|         break;
 | |
|       case -6:
 | |
|         c = toupper(W[i][0]);
 | |
|         n = val[3] % 12;
 | |
| 
 | |
|         if (c == 'P')
 | |
|           n += 12;
 | |
| 
 | |
|         val[3] = n;
 | |
|         break;
 | |
|       } // endswitch Plugpar
 | |
| 
 | |
|     } // endfor i
 | |
| 
 | |
|   if (trace(2))
 | |
|     htrc("numval=%d val=(%d,%d,%d,%d,%d,%d)\n",
 | |
|           numval, val[0], val[1], val[2], val[3], val[4], val[5]); 
 | |
| 
 | |
|   return (b) ? 0 : numval;
 | |
|   } // end of ExtractDate
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Open file routine: the purpose of this routine is to make a list   */
 | |
| /*  of all open file so they can be closed in SQLINIT on error jump.   */
 | |
| /***********************************************************************/
 | |
| FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype)
 | |
|   {
 | |
|   FILE     *fop;
 | |
|   PFBLOCK   fp;
 | |
|   PDBUSER   dbuserp = (PDBUSER)g->Activityp->Aptr;
 | |
| 
 | |
|   if (trace(1)) {
 | |
|     htrc("PlugOpenFile: fname=%s ftype=%s\n", fname, ftype);
 | |
|     htrc("dbuserp=%p\n", dbuserp);
 | |
|     } // endif trace
 | |
| 
 | |
|   if ((fop= global_fopen(g, MSGID_OPEN_MODE_STRERROR, fname, ftype)) != NULL) {
 | |
|     if (trace(1))
 | |
|       htrc(" fop=%p\n", fop);
 | |
| 
 | |
|     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc(" fp=%p\n", fp);
 | |
| 
 | |
|     // fname may be in volatile memory such as stack
 | |
|     fp->Fname = PlugDup(g, fname);
 | |
|     fp->Count = 1;
 | |
|     fp->Type = TYPE_FB_FILE;
 | |
|     fp->File = fop;
 | |
|     fp->Mode = MODE_ANY;                        // ???
 | |
|     fp->Next = dbuserp->Openlist;
 | |
|     dbuserp->Openlist = fp;
 | |
|     } /* endif fop */
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc(" returning fop=%p\n", fop);
 | |
| 
 | |
|   return (fop);
 | |
|   } // end of PlugOpenFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Close file routine: the purpose of this routine is to avoid        */
 | |
| /*  double closing that freeze the system on some Unix platforms.      */
 | |
| /***********************************************************************/
 | |
| FILE *PlugReopenFile(PGLOBAL g, PFBLOCK fp, LPCSTR md)
 | |
|   {
 | |
|   FILE *fop;
 | |
| 
 | |
|   if ((fop = global_fopen(g, MSGID_OPEN_MODE_STRERROR, fp->Fname, md))) {
 | |
|     fp->Count = 1;
 | |
|     fp->Type = TYPE_FB_FILE;
 | |
|     fp->File = fop;
 | |
|     } /* endif fop */
 | |
| 
 | |
|   return (fop);
 | |
|   } // end of PlugOpenFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Close file routine: the purpose of this routine is to avoid        */
 | |
| /*  double closing that freeze the system on some Unix platforms.      */
 | |
| /***********************************************************************/
 | |
| int PlugCloseFile(PGLOBAL g, PFBLOCK fp, bool all)
 | |
|   {
 | |
|   int rc = 0;
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("PlugCloseFile: fp=%p count=%hd type=%hd\n",
 | |
|           fp, ((fp) ? fp->Count : 0), ((fp) ? fp->Type : 0));
 | |
| 
 | |
|   if (!fp || !fp->Count)
 | |
|     return rc;
 | |
| 
 | |
|   switch (fp->Type) {
 | |
|     case TYPE_FB_FILE:
 | |
|       if (fclose((FILE *)fp->File) == EOF)
 | |
|         rc = errno;
 | |
| 
 | |
|       fp->File = NULL;
 | |
|       fp->Mode = MODE_ANY;
 | |
|       fp->Count = 0;
 | |
|       break;
 | |
|     case TYPE_FB_MAP:
 | |
|       if ((fp->Count = (all) ? 0 : fp->Count - 1))
 | |
|         break;
 | |
| 
 | |
|       if (CloseMemMap(fp->Memory, fp->Length))
 | |
|         rc = (int)GetLastError();
 | |
| 
 | |
|       fp->Memory = NULL;
 | |
|       fp->Mode = MODE_ANY;
 | |
|       // fall through
 | |
|     case TYPE_FB_HANDLE:
 | |
|       if (fp->Handle && fp->Handle != INVALID_HANDLE_VALUE)
 | |
|         if (CloseFileHandle(fp->Handle))
 | |
|           rc = (rc) ? rc : (int)GetLastError();
 | |
| 
 | |
|       fp->Handle = INVALID_HANDLE_VALUE;
 | |
|       fp->Mode = MODE_ANY;
 | |
|       fp->Count = 0;
 | |
|       break;
 | |
| #ifdef DOMDOC_SUPPORT
 | |
|     case TYPE_FB_XML:
 | |
|       CloseXMLFile(g, fp, all);
 | |
|       break;
 | |
| #endif   // DOMDOC_SUPPORT
 | |
| #ifdef LIBXML2_SUPPORT
 | |
|     case TYPE_FB_XML2:
 | |
|       CloseXML2File(g, fp, all);
 | |
|       break;
 | |
| #endif   // LIBXML2_SUPPORT
 | |
| #ifdef ODBC_SUPPORT
 | |
| 		case TYPE_FB_ODBC:
 | |
| 			OdbcClose(g, fp);
 | |
| 			fp->Count = 0;
 | |
| 			fp->File = NULL;
 | |
| 			break;
 | |
| #endif   // ODBC_SUPPORT
 | |
| #ifdef ZIP_SUPPORT
 | |
| 		case TYPE_FB_ZIP:
 | |
| 			if (fp->Mode == MODE_INSERT)
 | |
| 				((ZIPUTIL*)fp->File)->close();
 | |
| 			else
 | |
| 				((UNZIPUTL*)fp->File)->close();
 | |
| 
 | |
| 			fp->Memory = NULL;
 | |
| 			fp->Mode = MODE_ANY;
 | |
| 			fp->Count = 0;
 | |
| 			fp->File = NULL;
 | |
| 			break;
 | |
| #endif   // ZIP_SUPPORT
 | |
| #ifdef JAVA_SUPPORT
 | |
| 		case TYPE_FB_JAVA:
 | |
| 			((JAVAConn*)fp->File)->Close();
 | |
| 			fp->Count = 0;
 | |
| 			fp->File = NULL;
 | |
| 			break;
 | |
| #endif   // JAVA_SUPPORT
 | |
| #ifdef CMGO_SUPPORT
 | |
| 		case TYPE_FB_MONGO:
 | |
| 			((CMgoConn*)fp->File)->Close();
 | |
| 			fp->Count = 0;
 | |
| 			fp->File = NULL;
 | |
| 			break;
 | |
| #endif   // JAVA_SUPPORT
 | |
| 		default:
 | |
|       rc = RC_FX;
 | |
|     } // endswitch Type
 | |
| 
 | |
|   return rc;
 | |
|   } // end of PlugCloseFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PlugCleanup: Cleanup remaining items of a SQL query.               */
 | |
| /***********************************************************************/
 | |
| void PlugCleanup(PGLOBAL g, bool dofree)
 | |
|   {
 | |
|   PCATLG  cat;
 | |
|   PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
 | |
| 
 | |
|   // The test on Catalog is to avoid a Windows bug that can make
 | |
|   // LoadString in PlugGetMessage to fail in some case
 | |
|   if (!dbuserp || !(cat = dbuserp->Catalog))
 | |
|     return;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Close eventually still open/mapped files.                        */
 | |
|   /*********************************************************************/
 | |
|   for (PFBLOCK fp = dbuserp->Openlist; fp; fp = fp->Next)
 | |
|     PlugCloseFile(g, fp, true);
 | |
| 
 | |
|   dbuserp->Openlist = NULL;
 | |
| 
 | |
|   if (dofree) {
 | |
|     /*******************************************************************/
 | |
|     /*  Cleanup any non suballocated memory still not freed.           */
 | |
|     /*******************************************************************/
 | |
|     for (PMBLOCK mp = dbuserp->Memlist; mp; mp = mp->Next)
 | |
|       PlgDBfree(*mp);
 | |
| 
 | |
|     dbuserp->Memlist = NULL;
 | |
| 
 | |
|     /*******************************************************************/
 | |
|     /*  If not using permanent storage catalog, reset volatile values. */
 | |
|     /*******************************************************************/
 | |
|     cat->Reset();
 | |
| 
 | |
|     /*******************************************************************/
 | |
|     /*  This is the place to reset the pointer on domains.             */
 | |
|     /*******************************************************************/
 | |
|     dbuserp->Subcor = false;
 | |
|     dbuserp->Step = "New query";     // was STEP(PARSING_QUERY);
 | |
|     dbuserp->ProgMax = dbuserp->ProgCur = dbuserp->ProgSav = 0;
 | |
|     } // endif dofree
 | |
| 
 | |
|   } // end of PlugCleanup
 | |
| 
 | |
| #if 0
 | |
| /***********************************************************************/
 | |
| /*  That stupid Windows 98 does not provide this function.             */
 | |
| /***********************************************************************/
 | |
| bool WritePrivateProfileInt(LPCSTR sec, LPCSTR key, int n, LPCSTR ini)
 | |
|   {
 | |
|   char buf[12];
 | |
| 
 | |
|   sprintf(buf, "%d", n);
 | |
|   return WritePrivateProfileString(sec, key, buf, ini);
 | |
|   } // end of WritePrivateProfileInt
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Retrieve a size from an INI file with eventual K or M following.   */
 | |
| /***********************************************************************/
 | |
| int GetIniSize(char *section, char *key, char *def, char *ini)
 | |
|   {
 | |
|   char c, buff[32];
 | |
|   int  i;
 | |
|   int  n = 0;
 | |
| 
 | |
|   GetPrivateProfileString(section, key, def, buff, sizeof(buff), ini);
 | |
| 
 | |
|   if ((i = sscanf(buff, " %d %c ", &n, &c)) == 2)
 | |
|     switch (toupper(c)) {
 | |
|       case 'M':
 | |
|         n *= 1024;
 | |
|       case 'K':
 | |
|         n *= 1024;
 | |
|       } // endswitch c
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("GetIniSize: key=%s buff=%s i=%d n=%d\n", key, buff, i, n);
 | |
| 
 | |
|   return n;
 | |
|   } // end of GetIniSize
 | |
| 
 | |
| /***********************************************************************/
 | |
| /* Allocate a string retrieved from an INI file and return its address */
 | |
| /***********************************************************************/
 | |
| DllExport PSZ GetIniString(PGLOBAL g, void *mp, LPCSTR sec, LPCSTR key,
 | |
|                                                 LPCSTR def, LPCSTR ini)
 | |
|   {
 | |
|   char  buff[_MAX_PATH];
 | |
|   PSZ   p;
 | |
|   int   n, m = sizeof(buff);
 | |
|   char *buf = buff;
 | |
| 
 | |
| #if defined(_DEBUG)
 | |
|   assert (sec && key);
 | |
| #endif
 | |
| 
 | |
|  again:
 | |
|   n = GetPrivateProfileString(sec, key, def, buf, m, ini);
 | |
| 
 | |
|   if (n == m - 1) {
 | |
|     // String may have been truncated, make sure to have all
 | |
|     if (buf != buff)
 | |
|       delete [] buf;
 | |
| 
 | |
|     m *= 2;
 | |
|     buf = new char[m];
 | |
|     goto again;
 | |
|     } // endif n
 | |
| 
 | |
|   p = (PSZ)PlugSubAlloc(g, mp, n + 1);
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("GetIniString: sec=%s key=%s buf=%s\n", sec, key, buf);
 | |
| 
 | |
|   strcpy(p, buf);
 | |
| 
 | |
|   if (buf != buff)
 | |
|     delete [] buf;
 | |
| 
 | |
|   return p;
 | |
|   } // end of GetIniString
 | |
| #endif // 0
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  GetAmName: return the name correponding to an AM code.             */
 | |
| /***********************************************************************/
 | |
| char *GetAmName(PGLOBAL g, AMT am, void *memp)
 | |
|   {
 | |
|   char *amn= (char*)PlugSubAlloc(g, memp, 16);
 | |
| 
 | |
|   switch (am) {
 | |
|     case TYPE_AM_ERROR: strcpy(amn, "ERROR"); break;
 | |
|     case TYPE_AM_ROWID: strcpy(amn, "ROWID"); break;
 | |
|     case TYPE_AM_FILID: strcpy(amn, "FILID"); break;
 | |
|     case TYPE_AM_VIEW:  strcpy(amn, "VIEW");  break;
 | |
|     case TYPE_AM_COUNT: strcpy(amn, "COUNT"); break;
 | |
|     case TYPE_AM_DCD:   strcpy(amn, "DCD");   break;
 | |
|     case TYPE_AM_CMS:   strcpy(amn, "CMS");   break;
 | |
|     case TYPE_AM_MAP:   strcpy(amn, "MAP");   break;
 | |
|     case TYPE_AM_FMT:   strcpy(amn, "FMT");   break;
 | |
|     case TYPE_AM_CSV:   strcpy(amn, "CSV");   break;
 | |
|     case TYPE_AM_MCV:   strcpy(amn, "MCV");   break;
 | |
|     case TYPE_AM_DOS:   strcpy(amn, "DOS");   break;
 | |
|     case TYPE_AM_FIX:   strcpy(amn, "FIX");   break;
 | |
|     case TYPE_AM_BIN:   strcpy(amn, "BIN");   break;
 | |
|     case TYPE_AM_VCT:   strcpy(amn, "VEC");   break;
 | |
|     case TYPE_AM_VMP:   strcpy(amn, "VMP");   break;
 | |
|     case TYPE_AM_DBF:   strcpy(amn, "DBF");   break;
 | |
|     case TYPE_AM_QRY:   strcpy(amn, "QRY");   break;
 | |
|     case TYPE_AM_SQL:   strcpy(amn, "SQL");   break;
 | |
|     case TYPE_AM_PLG:   strcpy(amn, "PLG");   break;
 | |
|     case TYPE_AM_PLM:   strcpy(amn, "PLM");   break;
 | |
|     case TYPE_AM_DOM:   strcpy(amn, "DOM");   break;
 | |
|     case TYPE_AM_DIR:   strcpy(amn, "DIR");   break;
 | |
|     case TYPE_AM_ODBC:  strcpy(amn, "ODBC");  break;
 | |
| 		case TYPE_AM_JDBC:  strcpy(amn, "JDBC");  break;
 | |
| 		case TYPE_AM_MAC:   strcpy(amn, "MAC");   break;
 | |
|     case TYPE_AM_OEM:   strcpy(amn, "OEM");   break;
 | |
|     case TYPE_AM_OUT:   strcpy(amn, "OUT");   break;
 | |
|     default:           sprintf(amn, "OEM(%d)", am);
 | |
|     } // endswitch am
 | |
| 
 | |
|   return amn;
 | |
|   } // end of GetAmName
 | |
| 
 | |
| #if defined(SE_CATCH)
 | |
| /***********************************************************************/
 | |
| /*  GetExceptionDesc: return the description of an exception code.     */
 | |
| /***********************************************************************/
 | |
| char *GetExceptionDesc(PGLOBAL g, unsigned int e)
 | |
|   {
 | |
|   char *p;
 | |
| 
 | |
|   switch (e) {
 | |
|     case EXCEPTION_GUARD_PAGE:
 | |
|       p = MSG(GUARD_PAGE);
 | |
|       break;
 | |
|     case EXCEPTION_DATATYPE_MISALIGNMENT:
 | |
|       p = MSG(DATA_MISALIGN);
 | |
|       break;
 | |
|     case EXCEPTION_BREAKPOINT:
 | |
|       p = MSG(BREAKPOINT);
 | |
|       break;
 | |
|     case EXCEPTION_SINGLE_STEP:
 | |
|       p = MSG(SINGLE_STEP);
 | |
|       break;
 | |
|     case EXCEPTION_ACCESS_VIOLATION:
 | |
|       p = MSG(ACCESS_VIOLATN);
 | |
|       break;
 | |
|     case EXCEPTION_IN_PAGE_ERROR:
 | |
|       p = MSG(PAGE_ERROR);
 | |
|       break;
 | |
|     case EXCEPTION_INVALID_HANDLE:
 | |
|       p = MSG(INVALID_HANDLE);
 | |
|       break;
 | |
|     case EXCEPTION_ILLEGAL_INSTRUCTION:
 | |
|       p = MSG(ILLEGAL_INSTR);
 | |
|       break;
 | |
|     case EXCEPTION_NONCONTINUABLE_EXCEPTION:
 | |
|       p = MSG(NONCONT_EXCEPT);
 | |
|       break;
 | |
|     case EXCEPTION_INVALID_DISPOSITION:
 | |
|       p = MSG(INVALID_DISP);
 | |
|       break;
 | |
|     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
 | |
|       p = MSG(ARRAY_BNDS_EXCD);
 | |
|       break;
 | |
|     case EXCEPTION_FLT_DENORMAL_OPERAND:
 | |
|       p = MSG(FLT_DENORMAL_OP);
 | |
|       break;
 | |
|     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
 | |
|       p = MSG(FLT_ZERO_DIVIDE);
 | |
|       break;
 | |
|     case EXCEPTION_FLT_INEXACT_RESULT:
 | |
|       p = MSG(FLT_BAD_RESULT);
 | |
|       break;
 | |
|     case EXCEPTION_FLT_INVALID_OPERATION:
 | |
|       p = MSG(FLT_INVALID_OP);
 | |
|       break;
 | |
|     case EXCEPTION_FLT_OVERFLOW:
 | |
|       p = MSG(FLT_OVERFLOW);
 | |
|       break;
 | |
|     case EXCEPTION_FLT_STACK_CHECK:
 | |
|       p = MSG(FLT_STACK_CHECK);
 | |
|       break;
 | |
|     case EXCEPTION_FLT_UNDERFLOW:
 | |
|       p = MSG(FLT_UNDERFLOW);
 | |
|       break;
 | |
|     case EXCEPTION_INT_DIVIDE_BY_ZERO:
 | |
|       p = MSG(INT_ZERO_DIVIDE);
 | |
|       break;
 | |
|     case EXCEPTION_INT_OVERFLOW:
 | |
|       p = MSG(INT_OVERFLOW);
 | |
|       break;
 | |
|     case EXCEPTION_PRIV_INSTRUCTION:
 | |
|       p = MSG(PRIV_INSTR);
 | |
|       break;
 | |
|     case EXCEPTION_STACK_OVERFLOW:
 | |
|       p = MSG(STACK_OVERFLOW);
 | |
|       break;
 | |
|     case CONTROL_C_EXIT:
 | |
|       p = MSG(CONTROL_C_EXIT);
 | |
|       break;
 | |
|     case STATUS_NO_MEMORY:
 | |
|       p = MSG(NO_MEMORY);
 | |
|       break;
 | |
|     default:
 | |
|       p = MSG(UNKNOWN_EXCPT);
 | |
|       break;
 | |
|     } // endswitch nSE
 | |
| 
 | |
|   return p;
 | |
|   } // end of GetExceptionDesc
 | |
| #endif   // SE_CATCH
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PlgDBalloc: allocates or suballocates memory conditionally.        */
 | |
| /*  If mp.Sub is true at entry, this forces suballocation.             */
 | |
| /*  If the memory is allocated, makes an entry in an allocation list   */
 | |
| /*  so it can be freed at the normal or error query completion.        */
 | |
| /***********************************************************************/
 | |
| void *PlgDBalloc(PGLOBAL g, void *area, MBLOCK& mp)
 | |
| {
 | |
| //bool        b;
 | |
|   size_t      maxsub, minsub;
 | |
|   void       *arp = (area) ? area : g->Sarea;
 | |
|   PPOOLHEADER pph = (PPOOLHEADER)arp;
 | |
| 
 | |
|   if (mp.Memp) {
 | |
|     // This is a reallocation. If this block is not suballocated, it
 | |
|     // was already placed in the chain of memory blocks and we must
 | |
|     // not do it again as it can trigger a loop when freeing them.
 | |
|     // Note: this works if blocks can be reallocated only once.
 | |
|     // Otherwise a new boolean must be added to the block that
 | |
|     // indicate that it is chained, or a test on the whole chain be
 | |
|     // done to check whether the block is already there.
 | |
| //  b = mp.Sub;
 | |
|     mp.Sub = false;    // Restrict suballocation to one quarter
 | |
|   } // endif Memp
 | |
| 
 | |
|   // Suballoc when possible if mp.Sub is initially true, but leaving
 | |
|   // a minimum amount of storage for future operations such as the
 | |
|   // optimize recalculation after insert; otherwise
 | |
|   // suballoc only if size is smaller than one quarter of free mem.
 | |
|   minsub = (pph->FreeBlk + pph->To_Free + 524248) >> 2;
 | |
|   maxsub = (pph->FreeBlk < minsub) ? 0 : pph->FreeBlk - minsub;
 | |
|   mp.Sub = mp.Size <= ((mp.Sub) ? maxsub : (maxsub >> 2));
 | |
| 
 | |
| 	if (trace(2))
 | |
| 		htrc("PlgDBalloc: in %p size=%zd used=%zd free=%zd sub=%d\n",
 | |
| 			arp, mp.Size, pph->To_Free, pph->FreeBlk, mp.Sub);
 | |
| 
 | |
| 	if (!mp.Sub) {
 | |
|     // For allocations greater than one fourth of remaining storage
 | |
|     // in the area, do allocate from virtual storage.
 | |
| 		const char*v = "malloc";
 | |
| #if defined(_WIN32)
 | |
| 		if (mp.Size >= BIGMEM) {
 | |
| 			v = "VirtualAlloc";
 | |
| 			mp.Memp = VirtualAlloc(NULL, mp.Size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
 | |
| 		} else
 | |
| #endif
 | |
|       mp.Memp = malloc(mp.Size);
 | |
| 
 | |
| 		if (trace(8))
 | |
| 			htrc("PlgDBalloc: %s(%zd) at %p\n", v, mp.Size, mp.Memp);
 | |
| 
 | |
| 		if (!mp.Inlist && mp.Memp) {
 | |
|       // New allocated block, put it in the memory block chain.
 | |
|       PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
 | |
| 
 | |
|       mp.Next = dbuserp->Memlist;
 | |
|       dbuserp->Memlist = ∓
 | |
|       mp.Inlist = true;
 | |
|     } // endif mp
 | |
| 
 | |
|   } else
 | |
|     // Suballocating is Ok.
 | |
|     mp.Memp = PlugSubAlloc(g, area, mp.Size);
 | |
| 
 | |
|   return mp.Memp;
 | |
| } // end of PlgDBalloc
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PlgDBrealloc: reallocates memory conditionally.                    */
 | |
| /*  Note that this routine can fail only when block size is increased  */
 | |
| /*  because otherwise we keep the old storage on failure.              */
 | |
| /***********************************************************************/
 | |
| void *PlgDBrealloc(PGLOBAL g, void *area, MBLOCK& mp, size_t newsize)
 | |
|   {
 | |
|   MBLOCK m;
 | |
| 
 | |
| #if defined(_DEBUG)
 | |
| //  assert (mp.Memp != NULL);
 | |
| #endif
 | |
| 
 | |
|   if (trace(2))
 | |
|     htrc("PlgDBrealloc: %p size=%zd sub=%d\n", mp.Memp, mp.Size, mp.Sub);
 | |
| 
 | |
|   if (newsize == mp.Size)
 | |
|     return mp.Memp;      // Nothing to do
 | |
|   else
 | |
|     m = mp;
 | |
| 
 | |
|   if (!mp.Sub && mp.Size < BIGMEM && newsize < BIGMEM) {
 | |
|     // Allocation was done by malloc, try to use realloc but
 | |
|     // suballoc if newsize is smaller than one quarter of free mem.
 | |
|     size_t      maxsub;
 | |
|     PPOOLHEADER pph = (PPOOLHEADER)((area) ? area : g->Sarea);
 | |
| 
 | |
|     maxsub = (pph->FreeBlk < 131072) ? 0 : pph->FreeBlk - 131072;
 | |
| 
 | |
|     if ((mp.Sub = (newsize <= (maxsub >> 2)))) {
 | |
|       mp.Memp = PlugSubAlloc(g, area, newsize);
 | |
|       memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
 | |
|       PlgDBfree(m);    // Free the old block
 | |
|     } else {
 | |
| 			if (!(mp.Memp = realloc(mp.Memp, newsize))) {
 | |
| 				mp = m;          // Possible only if newsize > Size
 | |
| 				return NULL;     // Failed
 | |
| 			} else if (trace(8))
 | |
| 				htrc("PlgDBrealloc: realloc(%ld) at %p\n", newsize, mp.Memp);
 | |
| 
 | |
| 		} // endif's
 | |
| 
 | |
|     mp.Size = newsize;
 | |
|   } else if (!mp.Sub || newsize > mp.Size) {
 | |
|     // Was suballocated or Allocation was done by VirtualAlloc
 | |
|     // Make a new allocation and copy the useful part
 | |
|     // Note: DO NOT reset Memp and Sub so we know that this
 | |
|     // is a reallocation in PlgDBalloc
 | |
|     mp.Size = newsize;
 | |
| 
 | |
|     if (PlgDBalloc(g, area, mp)) {
 | |
|       memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
 | |
|       PlgDBfree(m);    // Free the old block
 | |
|     } else {
 | |
|       mp = m;          // No space to realloc, do nothing
 | |
| 
 | |
|       if (newsize > m.Size)
 | |
|         return NULL;   // Failed
 | |
| 
 | |
|     } // endif PlgDBalloc
 | |
| 
 | |
|   } // endif's
 | |
| 
 | |
|   if (trace(8))
 | |
|     htrc(" newsize=%zd newp=%p sub=%d\n", mp.Size, mp.Memp, mp.Sub);
 | |
| 
 | |
|   return mp.Memp;
 | |
|   } // end of PlgDBrealloc
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PlgDBfree: free memory if not suballocated.                        */
 | |
| /***********************************************************************/
 | |
| void PlgDBfree(MBLOCK& mp)
 | |
|   {
 | |
| 	if (!mp.Sub && mp.Memp) {
 | |
| 		const char*v = "free";
 | |
| #if defined(_WIN32)
 | |
| 		if (mp.Size >= BIGMEM) {
 | |
| 			v = "VirtualFree";
 | |
| 			VirtualFree(mp.Memp, 0, MEM_RELEASE);
 | |
| 		} else
 | |
| #endif
 | |
| 			free(mp.Memp);
 | |
| 
 | |
| 		if (trace(8))
 | |
| 			htrc("PlgDBfree: %s(%p) size=%d\n", v, mp.Memp, mp.Size);
 | |
| 
 | |
| 	}	// endif mp
 | |
| 
 | |
|   // Do not reset Next to avoid cutting the Mblock chain
 | |
|   mp.Memp = NULL;
 | |
|   mp.Sub = false;
 | |
|   mp.Size = 0;
 | |
|   } // end of PlgDBfree
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Program for sub-allocating one item in a storage area.             */
 | |
| /*  Note: This function is equivalent to PlugSubAlloc except that in   */
 | |
| /*  case of insufficient memory, it returns NULL instead of doing a    */
 | |
| /*  throw. The caller must test the return value for error.            */
 | |
| /***********************************************************************/
 | |
| void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size)
 | |
|   {
 | |
|   PPOOLHEADER pph;                           // Points on area header.
 | |
| 
 | |
|   if (!memp)
 | |
|     /*******************************************************************/
 | |
|     /*  Allocation is to be done in the Sarea.                         */
 | |
|     /*******************************************************************/
 | |
|     memp = g->Sarea;
 | |
| 
 | |
| //size = ((size + 3) / 4) * 4;       /* Round up size to multiple of 4 */
 | |
|   size = ((size + 7) / 8) * 8;       /* Round up size to multiple of 8 */
 | |
|   pph = (PPOOLHEADER)memp;
 | |
| 
 | |
|   if (trace(16))
 | |
|     htrc("PlgDBSubAlloc: memp=%p size=%zd used=%zd free=%zd\n",
 | |
|          memp, size, pph->To_Free, pph->FreeBlk);
 | |
| 
 | |
|   if (size > pph->FreeBlk) {   /* Not enough memory left in pool */
 | |
|     snprintf(g->Message, sizeof(g->Message),
 | |
|     "Not enough memory in Work area for request of %zd (used=%zd free=%zd)",
 | |
|             size, pph->To_Free, pph->FreeBlk);
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("%s\n", g->Message);
 | |
| 
 | |
|     return NULL;
 | |
|     } // endif size
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Do the suballocation the simplest way.                           */
 | |
|   /*********************************************************************/
 | |
|   memp = MakePtr(memp, pph->To_Free);   // Points to suballocated block
 | |
|   pph->To_Free += size;                 // New offset of pool free block
 | |
|   pph->FreeBlk -= size;                 // New size   of pool free block
 | |
| 
 | |
|   if (trace(16))
 | |
|     htrc("Done memp=%p used=%zd free=%zd\n",
 | |
|          memp, pph->To_Free, pph->FreeBlk);
 | |
| 
 | |
|   return (memp);
 | |
|   } // end of PlgDBSubAlloc
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Program for sub-allocating and copying a string in a storage area. */
 | |
| /***********************************************************************/
 | |
| char *PlgDBDup(PGLOBAL g, const char *str)
 | |
|   {
 | |
|   if (str) {
 | |
|     char *sm = (char*)PlgDBSubAlloc(g, NULL, strlen(str) + 1);
 | |
| 
 | |
|     if (sm)
 | |
|       strcpy(sm, str);
 | |
| 
 | |
|     return sm;
 | |
|   } else
 | |
|     return NULL;
 | |
| 
 | |
|   } // end of PlgDBDup
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  PUTOUT: Plug DB object typing routine.                             */
 | |
| /***********************************************************************/
 | |
| void PlugPutOut(PGLOBAL g, FILE *f, short t, void *v, uint n)
 | |
|   {
 | |
|   char  m[64];
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("PUTOUT: f=%p t=%d v=%p n=%d\n", f, t, v, n);
 | |
| 
 | |
|   if (!v)
 | |
|     return;
 | |
| 
 | |
|   memset(m, ' ', n);                             /* Make margin string */
 | |
|   m[n] = '\0';
 | |
|   n += 2;                                        /* Increase margin    */
 | |
| 
 | |
|   switch (t) {
 | |
|     case TYPE_ERROR:
 | |
|       fprintf(f, "--> %s\n", (PSZ)v);
 | |
|       break;
 | |
| 
 | |
|     case TYPE_STRING:
 | |
|     case TYPE_PSZ:
 | |
|       fprintf(f, "%s%s\n", m, (PSZ)v);
 | |
|       break;
 | |
| 
 | |
|     case TYPE_DOUBLE:
 | |
|       fprintf(f, "%s%lf\n", m, *(double *)v);
 | |
|       break;
 | |
| 
 | |
|     case TYPE_LIST:
 | |
|     case TYPE_COLIST:
 | |
|     case TYPE_COL:
 | |
|      {PPARM p;
 | |
| 
 | |
|       if (t == TYPE_LIST)
 | |
|         fprintf(f, "%s%s\n", m, MSG(LIST));
 | |
|       else
 | |
|         fprintf(f, "%s%s\n", m, "Colist:");
 | |
| 
 | |
|       for (p = (PPARM)v; p; p = p->Next)
 | |
|         PlugPutOut(g, f, p->Type, p->Value, n);
 | |
| 
 | |
|       } break;
 | |
| 
 | |
|     case TYPE_INT:
 | |
|       fprintf(f, "%s%d\n", m, *(int *)v);
 | |
|       break;
 | |
| 
 | |
|     case TYPE_SHORT:
 | |
|       fprintf(f, "%s%hd\n", m, *(short *)v);
 | |
|       break;
 | |
| 
 | |
|     case TYPE_TINY:
 | |
|       fprintf(f, "%s%d\n", m, (int)*(char *)v);
 | |
|       break;
 | |
| 
 | |
|     case TYPE_VOID:
 | |
|       break;
 | |
| 
 | |
|     case TYPE_SQL:
 | |
|     case TYPE_TABLE:
 | |
|     case TYPE_TDB:
 | |
|     case TYPE_XOBJECT:
 | |
|       ((PBLOCK)v)->Printf(g, f, n-2);
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       fprintf(f, "%s%s %d\n", m, MSG(ANSWER_TYPE), t);
 | |
|     } /* endswitch */
 | |
| 
 | |
|   return;
 | |
|   } /* end of PlugPutOut */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  NewPointer: makes a table of pointer values to be changed later.   */
 | |
| /***********************************************************************/
 | |
| DllExport void NewPointer(PTABS t, void *oldv, void *newv)
 | |
|   {
 | |
|   PTABPTR tp;
 | |
| 
 | |
|   if (!oldv)                                       /* error ?????????? */
 | |
|     return;
 | |
| 
 | |
|   if (!t->P1 || t->P1->Num == 50)
 | |
|   {
 | |
|     if (!(tp = new TABPTR)) {
 | |
|       PGLOBAL g = t->G;
 | |
| 
 | |
|       snprintf(g->Message, sizeof(g->Message), "NewPointer: %s", MSG(MEM_ALLOC_ERROR));
 | |
| 			throw 3;
 | |
| 		} else {
 | |
|       tp->Next = t->P1;
 | |
|       tp->Num = 0;
 | |
|       t->P1 = tp;
 | |
|     } /* endif tp */
 | |
|   }
 | |
| 
 | |
|   t->P1->Old[t->P1->Num] = oldv;
 | |
|   t->P1->New[t->P1->Num++] = newv;
 | |
|   } /* end of NewPointer */
 | |
| 
 | |
| #if 0
 | |
| /***********************************************************************/
 | |
| /*  Compare two files and return 0 if they are identical, else 1.      */
 | |
| /***********************************************************************/
 | |
| int FileComp(PGLOBAL g, char *file1, char *file2)
 | |
|   {
 | |
|   char *fn[2], *bp[2], buff1[4096], buff2[4096];
 | |
|   int   i, k, n[2], h[2] = {-1,-1};
 | |
|   int  len[2], rc = -1;
 | |
| 
 | |
|   fn[0] = file1; fn[1] = file2;
 | |
|   bp[0] = buff1; bp[1] = buff2;
 | |
| 
 | |
|   for (i = 0; i < 2; i++) {
 | |
| #if defined(_WIN32)
 | |
|     h[i]= global_open(g, MSGID_NONE, fn[i], _O_RDONLY | _O_BINARY);
 | |
| #else   // !_WIN32
 | |
|     h[i]= global_open(g, MSGOD_NONE, fn[i], O_RDONLY);
 | |
| #endif  // !_WIN32
 | |
| 
 | |
|     if (h[i] == -1) {
 | |
| //      if (errno != ENOENT) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR),
 | |
|                 "rb", (int)errno, fn[i]);
 | |
|         strcat(strcat(g->Message, ": "), strerror(errno));
 | |
| 				throw 666;
 | |
| 				//      } else
 | |
| //        len[i] = 0;          // File does not exist yet
 | |
| 
 | |
|     } else {
 | |
|       if ((len[i] = _filelength(h[i])) < 0) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(FILELEN_ERROR), "_filelength", fn[i]);
 | |
| 				throw 666;
 | |
| 			} // endif len
 | |
| 
 | |
|     } // endif h
 | |
| 
 | |
|     } // endfor i
 | |
| 
 | |
|   if (len[0] != len[1])
 | |
|     rc = 1;
 | |
| 
 | |
|   while (rc == -1) {
 | |
|     for (i = 0; i < 2; i++)
 | |
|       if ((n[i] = read(h[i], bp[i], 4096)) < 0) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), fn[i], strerror(errno));
 | |
|         goto fin;
 | |
|         } // endif n
 | |
| 
 | |
|     if (n[0] != n[1])
 | |
|       rc = 1;
 | |
|     else if (*n == 0)
 | |
|       rc = 0;
 | |
|     else for (k = 0; k < *n; k++)
 | |
|       if (*(bp[0] + k) != *(bp[1] + k)) {
 | |
|         rc = 1;
 | |
|         goto fin;
 | |
|         } // endif bp
 | |
| 
 | |
|     } // endwhile
 | |
| 
 | |
|  fin:
 | |
|   for (i = 0; i < 2; i++)
 | |
|     if (h[i] != -1)
 | |
|       close(h[i]);
 | |
| 
 | |
|   return rc;
 | |
|   } // end of FileComp
 | |
| #endif // 0
 | 
