1 /********** PlgDBUtl Fpe C++ Program Source Code File (.CPP) ***********/
2 /* PROGRAM NAME: PLGDBUTL                                              */
3 /* -------------                                                       */
4 /*  Version 4.1                                                        */
5 /*                                                                     */
6 /* COPYRIGHT:                                                          */
7 /* ----------                                                          */
8 /*  (C) Copyright to the author Olivier BERTRAND          1998-2020    */
9 /*                                                                     */
10 /* WHAT THIS PROGRAM DOES:                                             */
11 /* -----------------------                                             */
12 /*  Utility functions used by DB semantic routines.                    */
13 /*                                                                     */
14 /* WHAT YOU NEED TO COMPILE THIS PROGRAM:                              */
15 /* --------------------------------------                              */
16 /*                                                                     */
17 /*  REQUIRED FILES:                                                    */
18 /*  ---------------                                                    */
19 /*  See Readme.C for a list and description of required SYSTEM files.  */
20 /*                                                                     */
21 /*    PLGDBUTL.C     - Source code                                     */
22 /*    GLOBAL.H       - Global declaration file                         */
23 /*    PLGDBSEM.H     - DB application declaration file                 */
24 /*                                                                     */
25 /*  REQUIRED LIBRARIES:                                                */
26 /*  -------------------                                                */
27 /*    OS2.LIB        - OS2 libray                                      */
28 /*    LLIBCE.LIB     - Protect mode/standard combined large model C    */
29 /*                     library                                         */
30 /*                                                                     */
31 /*  REQUIRED PROGRAMS:                                                 */
32 /*  ------------------                                                 */
33 /*    IBM, MS, Borland or GNU C++ Compiler                             */
34 /*    IBM, MS, Borland or GNU Linker                                   */
35 /***********************************************************************/
36 
37 /***********************************************************************/
38 /*  Include relevant MariaDB header file.                              */
39 /***********************************************************************/
40 #include "my_global.h"
41 #include "my_pthread.h"
42 #if defined(_WIN32)
43 #include <io.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 #define BIGMEM         1048576            // 1 Megabyte
47 #else     // !_WIN32
48 #include <unistd.h>
49 #include <fcntl.h>
50 //#if defined(THREAD)
51 #include <pthread.h>
52 //#endif   // THREAD
53 #include <stdarg.h>
54 #define BIGMEM      2147483647            // Max int value
55 #endif    // !_WIN32
56 #include <locale.h>
57 
58 /***********************************************************************/
59 /*  Include application header files                                   */
60 /***********************************************************************/
61 #include "global.h"    // header containing all global declarations.
62 #include "plgdbsem.h"  // header containing the DB applic. declarations.
63 #include "preparse.h"  // For DATPAR
64 #include "osutil.h"
65 #include "maputil.h"
66 #include "catalog.h"
67 #include "colblk.h"
68 #include "xtable.h"    // header of TBX, TDB and TDBASE classes
69 #include "tabcol.h"    // header of XTAB and COLUMN classes
70 #include "valblk.h"
71 #include "rcmsg.h"
72 #ifdef ZIP_SUPPORT
73 #include "filamzip.h"
74 #endif // ZIP_SUPPORT
75 #ifdef JAVA_SUPPORT
76 #include "javaconn.h"
77 #endif // JAVA_SUPPORT
78 #ifdef CMGO_SUPPORT
79 #include "cmgoconn.h"
80 #endif // JAVA_SUPPORT
81 
82 /***********************************************************************/
83 /*  DB static variables.                                               */
84 /***********************************************************************/
85 bool  Initdone = false;
86 bool  plugin = false;  // True when called by the XDB plugin handler
87 
88 extern "C" {
89 extern char version[];
90 } // extern "C"
91 
92 //#if defined(_WIN32)
93 //extern CRITICAL_SECTION parsec;      // Used calling the Flex parser
94 //#else   // !_WIN32
95 extern pthread_mutex_t parmut;
96 //#endif  // !_WIN32
97 
98 // The debug trace used by the main thread
99 FILE *pfile = NULL;
100 
101 MBLOCK Nmblk = {NULL, false, 0, false, NULL};   // Used to init MBLOCK's
102 
103 /***********************************************************************/
104 /*  Routines called externally and internally by utility routines.     */
105 /***********************************************************************/
106 bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
107 bool EvalLikePattern(LPCSTR, LPCSTR);
108 void PlugConvertConstant(PGLOBAL, void* &, short&);
109 
110 #ifdef DOMDOC_SUPPORT
111 void CloseXMLFile(PGLOBAL, PFBLOCK, bool);
112 #endif   // DOMDOC_SUPPORT
113 
114 #ifdef LIBXML2_SUPPORT
115 #include "libdoc.h"
116 #endif   // LIBXML2_SUPPORT
117 
118 #ifdef ODBC_SUPPORT
119 void OdbcClose(PGLOBAL g, PFBLOCK fp);
120 #endif   // ODBC_SUPPORT
121 
122 /***********************************************************************/
123 /* Routines for file IO with error reporting to g->Message             */
124 /* Note: errno and strerror must be called before the message file     */
125 /* is read in the case of XMSG compile.                                */
126 /***********************************************************************/
global_open_error_msg(GLOBAL * g,int msgid,const char * path,const char * mode)127 static void global_open_error_msg(GLOBAL *g, int msgid, const char *path,
128                                                         const char *mode)
129 {
130   int  len, rno= (int)errno;
131   char errmsg[256]= "";
132 
133   strncat(errmsg, strerror(errno), 255);
134 
135   switch (msgid)
136   {
137     case MSGID_CANNOT_OPEN:
138       len= snprintf(g->Message, sizeof(g->Message) - 1,
139                     MSG(CANNOT_OPEN), // Cannot open %s
140                     path);
141       break;
142 
143     case MSGID_OPEN_MODE_ERROR:
144       len= snprintf(g->Message, sizeof(g->Message) - 1,
145                     MSG(OPEN_MODE_ERROR), // "Open(%s) error %d on %s"
146                     mode, rno, path);
147       break;
148 
149     case MSGID_OPEN_MODE_STRERROR:
150       {char fmt[256];
151       strcat(strcpy(fmt, MSG(OPEN_MODE_ERROR)), ": %s");
152       len= snprintf(g->Message, sizeof(g->Message) - 1,
153                     fmt, // Open(%s) error %d on %s: %s
154                     mode, rno, path, errmsg);
155       }break;
156 
157     case MSGID_OPEN_STRERROR:
158       len= snprintf(g->Message, sizeof(g->Message) - 1,
159                     MSG(OPEN_STRERROR), // "open error: %s"
160                     errmsg);
161       break;
162 
163     case MSGID_OPEN_ERROR_AND_STRERROR:
164       len= snprintf(g->Message, sizeof(g->Message) - 1,
165                     //OPEN_ERROR does not work, as it wants mode %d (not %s)
166                     //MSG(OPEN_ERROR) "%s",// "Open error %d in mode %d on %s: %s"
167                     "Open error %d in mode %s on %s: %s",
168                     rno, mode, path, errmsg);
169       break;
170 
171     case MSGID_OPEN_EMPTY_FILE:
172       len= snprintf(g->Message, sizeof(g->Message) - 1,
173                     MSG(OPEN_EMPTY_FILE), // "Opening empty file %s: %s"
174                     path, errmsg);
175       break;
176 
177     default:
178       DBUG_ASSERT(0);
179       /* Fall through*/
180     case 0:
181       len= 0;
182   }
183   g->Message[len]= '\0';
184 }
185 
186 
global_fopen(GLOBAL * g,int msgid,const char * path,const char * mode)187 FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode)
188 {
189   FILE *f;
190   if (!(f= fopen(path, mode)))
191     global_open_error_msg(g, msgid, path, mode);
192   return f;
193 }
194 
195 
global_open(GLOBAL * g,int msgid,const char * path,int flags)196 int global_open(GLOBAL *g, int msgid, const char *path, int flags)
197 {
198   int h;
199   if ((h= open(path, flags)) <= 0)
200     global_open_error_msg(g, msgid, path, "");
201   return h;
202 }
203 
204 
global_open(GLOBAL * g,int msgid,const char * path,int flags,int mode)205 int global_open(GLOBAL *g, int msgid, const char *path, int flags, int mode)
206 {
207   int h;
208   if ((h= open(path, flags, mode)) <= 0)
209   {
210     char modestr[64];
211     snprintf(modestr, sizeof(modestr), "%d", mode);
212     global_open_error_msg(g, msgid, path, modestr);
213   }
214   return h;
215 }
216 
SetTrc(void)217 DllExport void SetTrc(void)
218 {
219   // If tracing is on, debug must be initialized.
220   debug = pfile;
221 } // end of SetTrc
222 
223 /**************************************************************************/
224 /*  SubAllocate the result structure that will contain result data.       */
225 /**************************************************************************/
PlgAllocResult(PGLOBAL g,int ncol,int maxres,int ids,int * buftyp,XFLD * fldtyp,unsigned int * length,bool blank,bool nonull)226 PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
227                        int *buftyp, XFLD *fldtyp,
228                        unsigned int *length, bool blank, bool nonull)
229 {
230   char     cname[NAM_LEN+1];
231   int      i;
232   PCOLRES *pcrp, crp;
233   PQRYRES  qrp;
234 
235 	try {
236 		/**********************************************************************/
237 		/*  Allocate the structure used to contain the result set.            */
238 		/**********************************************************************/
239 		qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
240 		pcrp = &qrp->Colresp;
241 		qrp->Continued = false;
242 		qrp->Truncated = false;
243 		qrp->Info = false;
244 		qrp->Suball = true;
245 		qrp->Maxres = maxres;
246 		qrp->Maxsize = 0;
247 		qrp->Nblin = 0;
248 		qrp->Nbcol = 0;                                     // will be ncol
249 		qrp->Cursor = 0;
250 		qrp->BadLines = 0;
251 
252 		for (i = 0; i < ncol; i++) {
253 			*pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
254 			crp = *pcrp;
255 			pcrp = &crp->Next;
256 			memset(crp, 0, sizeof(COLRES));
257 			crp->Colp = NULL;
258 			crp->Ncol = ++qrp->Nbcol;
259 			crp->Type = buftyp[i];
260 			crp->Length = length[i];
261 			crp->Clen = GetTypeSize(crp->Type, length[i]);
262 			crp->Prec = 0;
263 
264 			if (ids > 0) {
265 #if defined(XMSG)
266 				// Get header from message file
267 				strncpy(cname, PlugReadMessage(g, ids + crp->Ncol, NULL), NAM_LEN);
268 				cname[NAM_LEN] = 0;					// for truncated long names
269 #else   // !XMSG
270 				GetRcString(ids + crp->Ncol, cname, sizeof(cname));
271 #endif  // !XMSG
272 				crp->Name = (PSZ)PlugDup(g, cname);
273 			} else
274 				crp->Name = NULL;           // Will be set by caller
275 
276 			if (fldtyp)
277 				crp->Fld = fldtyp[i];
278 			else
279 				crp->Fld = FLD_NO;
280 
281 			// Allocate the Value Block that will contain data
282 			if (crp->Length || nonull)
283 				crp->Kdata = AllocValBlock(g, NULL, crp->Type, maxres,
284 					crp->Length, 0, true, blank, false);
285 			else
286 				crp->Kdata = NULL;
287 
288 			if (trace(1))
289 				htrc("Column(%d) %s type=%d len=%d value=%p\n",
290 					crp->Ncol, crp->Name, crp->Type, crp->Length, crp->Kdata);
291 
292 		} // endfor i
293 
294 		*pcrp = NULL;
295 
296 	} catch (int n) {
297   	htrc("Exception %d: %s\n", n, g->Message);
298 	  qrp = NULL;
299   } catch (const char *msg) {
300   	strcpy(g->Message, msg);
301 	  htrc("%s\n", g->Message);
302 	  qrp = NULL;
303   } // end catch
304 
305 	return qrp;
306 } // end of PlgAllocResult
307 
308 /***********************************************************************/
309 /*  Allocate and initialize the new DB User Block.                     */
310 /***********************************************************************/
PlgMakeUser(PGLOBAL g)311 PDBUSER PlgMakeUser(PGLOBAL g)
312   {
313   PDBUSER dbuserp;
314 
315   if (!(dbuserp = (PDBUSER)malloc(sizeof(DBUSERBLK)))) {
316     sprintf(g->Message, MSG(MALLOC_ERROR), "PlgMakeUser");
317     return NULL;
318     } // endif dbuserp
319 
320   memset(dbuserp, 0, sizeof(DBUSERBLK));
321   dbuserp->Maxbmp = MAXBMP;
322 //dbuserp->UseTemp = TMP_AUTO;
323   dbuserp->Check = CHK_ALL;
324   strcpy(dbuserp->Server, "CONNECT");
325   return dbuserp;
326   } // end of PlgMakeUser
327 
328 /***********************************************************************/
329 /*  PlgGetUser: returns DBUSER block pointer.                          */
330 /***********************************************************************/
PlgGetUser(PGLOBAL g)331 PDBUSER PlgGetUser(PGLOBAL g)
332   {
333   PDBUSER dup = (PDBUSER)((g->Activityp) ? g->Activityp->Aptr : NULL);
334 
335   if (!dup)
336     strcpy(g->Message, MSG(APPL_NOT_INIT));
337 
338   return dup;
339   } // end of PlgGetUser
340 
341 /***********************************************************************/
342 /*  PlgGetCatalog: returns CATALOG class pointer.                      */
343 /***********************************************************************/
PlgGetCatalog(PGLOBAL g,bool jump)344 PCATLG PlgGetCatalog(PGLOBAL g, bool jump)
345   {
346   PDBUSER dbuserp = PlgGetUser(g);
347   PCATLG  cat = (dbuserp) ? dbuserp->Catalog : NULL;
348 
349   if (!cat && jump) {
350     // Raise exception so caller doesn't have to check return value
351     strcpy(g->Message, MSG(NO_ACTIVE_DB));
352 		throw 1;
353 	} // endif cat
354 
355   return cat;
356   } // end of PlgGetCatalog
357 
358 #if 0
359 /***********************************************************************/
360 /*  PlgGetDataPath: returns the default data path.                     */
361 /***********************************************************************/
362 char *PlgGetDataPath(PGLOBAL g)
363   {
364   PCATLG cat = PlgGetCatalog(g, false);
365 
366   return (cat) ? cat->GetDataPath() : NULL;
367   } // end of PlgGetDataPath
368 #endif // 0
369 
370 /***********************************************************************/
371 /*  This function returns a database path.                             */
372 /***********************************************************************/
SetPath(PGLOBAL g,const char * path)373 char *SetPath(PGLOBAL g, const char *path)
374 {
375   char *buf= NULL;
376 
377 	if (path) {
378 		size_t len = strlen(path) + (*path != '.' ? 4 : 1);
379 
380 		if (!(buf = (char*)PlgDBSubAlloc(g, NULL, len)))
381 			return NULL;
382 
383 		if (PlugIsAbsolutePath(path)) {
384 			strcpy(buf, path);
385 			return buf;
386 		} // endif path
387 
388 		if (*path != '.') {
389 #if defined(_WIN32)
390 			const char *s = "\\";
391 #else   // !_WIN32
392 			const char *s = "/";
393 #endif  // !_WIN32
394 			strcat(strcat(strcat(strcpy(buf, "."), s), path), s);
395 		} else
396 			strcpy(buf, path);
397 
398 	} // endif path
399 
400   return buf;
401 } // end of SetPath
402 
403 /***********************************************************************/
404 /*  Extract from a path name the required component.                   */
405 /*  This function assumes there is enough space in the buffer.         */
406 /***********************************************************************/
ExtractFromPath(PGLOBAL g,char * pBuff,char * FileName,OPVAL op)407 char *ExtractFromPath(PGLOBAL g, char *pBuff, char *FileName, OPVAL op)
408   {
409   char *drive = NULL, *direc = NULL, *fname = NULL, *ftype = NULL;
410 
411   switch (op) {           // Determine which part to extract
412 #if defined(_WIN32)
413     case OP_FDISK: drive = pBuff; break;
414 #endif   // !UNIX
415     case OP_FPATH: direc = pBuff; break;
416     case OP_FNAME: fname = pBuff; break;
417     case OP_FTYPE: ftype = pBuff; break;
418     default:
419       sprintf(g->Message, MSG(INVALID_OPER), op, "ExtractFromPath");
420       return NULL;
421     } // endswitch op
422 
423   // Now do the extraction
424   _splitpath(FileName, drive, direc, fname, ftype);
425   return pBuff;
426   } // end of PlgExtractFromPath
427 
428 /***********************************************************************/
429 /*  Check the occurence and matching of a pattern against a string.    */
430 /*  Because this function is only used for catalog name checking,      */
431 /*  it must be case insensitive.                                       */
432 /***********************************************************************/
433 #ifdef NOT_USED
PlugCheckPattern(PGLOBAL g,LPCSTR string,LPCSTR pat)434 static bool PlugCheckPattern(PGLOBAL g, LPCSTR string, LPCSTR pat)
435   {
436   if (pat && strlen(pat)) {
437     // This leaves 2048 bytes (MAX_STR / 2) for each components
438     LPSTR name = g->Message + MAX_STR / 2;
439 
440     strlwr(strcpy(name, string));
441     strlwr(strcpy(g->Message, pat));         // Can be modified by Eval
442     return EvalLikePattern(name, g->Message);
443   } else
444     return true;
445 
446   } // end of PlugCheckPattern
447 #endif
448 
449 /***********************************************************************/
450 /*  PlugEvalLike: evaluates a LIKE clause.                             */
451 /*  Syntaxe: M like P escape C. strg->M, pat->P, C not implemented yet */
452 /***********************************************************************/
PlugEvalLike(PGLOBAL g,LPCSTR strg,LPCSTR pat,bool ci)453 bool PlugEvalLike(PGLOBAL g, LPCSTR strg, LPCSTR pat, bool ci)
454   {
455   char *tp, *sp;
456   bool  b;
457 
458   if (trace(2))
459     htrc("LIKE: strg='%s' pattern='%s'\n", strg, pat);
460 
461   if (ci) {                        /* Case insensitive test             */
462     if (strlen(pat) + strlen(strg) + 1 < MAX_STR)
463       tp = g->Message;
464     else if (!(tp = new char[strlen(pat) + strlen(strg) + 2])) {
465       strcpy(g->Message, MSG(NEW_RETURN_NULL));
466 			throw (int)OP_LIKE;
467 		} /* endif tp */
468 
469     sp = tp + strlen(pat) + 1;
470     strlwr(strcpy(tp, pat));      /* Make a lower case copy of pat     */
471     strlwr(strcpy(sp, strg));     /* Make a lower case copy of strg    */
472   } else {                        /* Case sensitive test               */
473     if (strlen(pat) < MAX_STR)    /* In most of the case for small pat */
474       tp = g->Message;            /* Use this as temporary work space. */
475     else if (!(tp = new char[strlen(pat) + 1])) {
476       strcpy(g->Message, MSG(NEW_RETURN_NULL));
477 			throw (int)OP_LIKE;
478 		} /* endif tp */
479 
480     strcpy(tp, pat);                  /* Make a copy to be worked into */
481     sp = (char*)strg;
482   } /* endif ci */
483 
484   b = EvalLikePattern(sp, tp);
485 
486   if (tp != g->Message)               /* If working space was obtained */
487     delete [] tp;                     /* by the use of new, delete it. */
488 
489   return (b);
490   } /* end of PlugEvalLike */
491 
492 /***********************************************************************/
493 /*  M and P are variable length character string. If M and P are zero  */
494 /*  length strings then the Like predicate is true.                    */
495 /*                                                                     */
496 /*  The Like predicate is true if:                                     */
497 /*                                                                     */
498 /*  1- A subtring of M is a sequence of 0 or more contiguous <CR> of M */
499 /*     and each <CR> of M is part of exactly one substring.            */
500 /*                                                                     */
501 /*  2- If the i-th <subtring-specifyer> of P is an <arbitrary-char-    */
502 /*     specifier>, the i-th subtring of M is any single <CR>.          */
503 /*                                                                     */
504 /*  3- If the i-th <subtring-specifyer> of P is an <arbitrary-string-  */
505 /*     specifier>, then the i-th subtring of M is any sequence of zero */
506 /*     or more <CR>.                                                   */
507 /*                                                                     */
508 /*  4- If the i-th <subtring-specifyer> of P is neither an <arbitrary- */
509 /*     character-specifier> nor an <arbitrary-string-specifier>, then  */
510 /*     the i-th substring of M is equal to that <substring-specifier>  */
511 /*     according to the collating sequence of the <like-predicate>,    */
512 /*     without the appending of <space-character>, and has the same    */
513 /*     length as that <substring-specifier>.                           */
514 /*                                                                     */
515 /*  5- The number of substrings of M is equal to the number of         */
516 /*     <subtring-specifiers> of P.                                     */
517 /*                                                                     */
518 /*  Otherwise M like P is false.                                       */
519 /***********************************************************************/
EvalLikePattern(LPCSTR sp,LPCSTR tp)520 bool EvalLikePattern(LPCSTR sp, LPCSTR tp)
521   {
522   LPSTR p;
523   char  c;
524   ssize_t   n;
525   bool  b, t = false;
526 
527   if (trace(2))
528     htrc("Eval Like: sp=%s tp=%s\n",
529          (sp) ? sp : "Null", (tp) ? tp : "Null");
530 
531   /********************************************************************/
532   /*  If pattern is void, Like is true only if string is also void.   */
533   /********************************************************************/
534   if (!*tp)
535     return (!*sp);
536 
537   /********************************************************************/
538   /*  Analyse eventual arbitrary specifications ahead of pattern.     */
539   /********************************************************************/
540   for (p = (LPSTR)tp; p;)
541     switch (*p) {                     /*   it can contain % and/or _   */
542       case '%':                       /* An % has been found           */
543         t = true;                     /* Note eventual character skip  */
544         p++;
545         break;
546       case '_':                       /* An _ has been found           */
547         if (*sp) {                    /* If more character in string   */
548           sp++;                       /*   skip it                     */
549           p++;
550         } else
551           return false;               /* Like condition is not met     */
552 
553         break;
554       default:
555         tp = p;                       /* Point to rest of template     */
556         p = NULL;                     /* To stop For loop              */
557         break;
558       } /* endswitch */
559 
560   if ((p = (LPSTR)strpbrk(tp, "%_"))) /* Get position of next % or _   */
561     n = p - tp;
562   else
563     n = strlen(tp);                   /* Get length of pattern head    */
564 
565   if (trace(2))
566     htrc(" testing: t=%d sp=%s tp=%s p=%p\n", t, sp, tp, p);
567 
568   if (n > (signed)strlen(sp))         /* If head is longer than strg   */
569     b = false;                        /* Like condition is not met     */
570   else if (n == 0)                    /* If void <substring-specifier> */
571     b = (t || !*sp);                  /*   true if %  or void strg.    */
572   else if (!t) {
573     /*******************************************************************/
574     /*  No character to skip, check occurence of <subtring-specifier>  */
575     /*  at the very beginning of remaining string.                     */
576     /*******************************************************************/
577     if (p) {
578       if ((b = !strncmp(sp, tp, n)))
579         b = EvalLikePattern(sp + n, p);
580 
581     } else
582       b = !strcmp(sp, tp);            /*   strg and tmp heads match    */
583 
584   } else
585     if (p)
586       /*****************************************************************/
587       /*  Here is the case explaining why we need a recursive routine. */
588       /*  The test must be done not only against the first occurence   */
589       /*  of the <substring-specifier> in the remaining string,        */
590       /*  but also with all eventual succeeding ones.                  */
591       /*****************************************************************/
592       for (b = false, c = *p; !b && (signed)strlen(sp) >= n; sp++) {
593         *p = '\0';                    /* Separate pattern header       */
594 
595         if ((sp = strstr(sp, tp))) {
596           *p = c;
597           b = EvalLikePattern(sp + n, p);
598         } else {
599           *p = c;
600           b = false;
601           break;
602         } /* endif s */
603 
604         } /* endfor b, sp */
605 
606     else {
607       sp += (strlen(sp) - n);
608       b = !strcmp(sp, tp);
609     } /* endif p */
610 
611   if (trace(2))
612     htrc(" done: b=%d n=%d sp=%s tp=%s\n",
613           b, n, (sp) ? sp : "Null", tp);
614 
615   return (b);
616   } /* end of EvalLikePattern */
617 
618 /***********************************************************************/
619 /*  MakeEscape: Escape some characters in a string.                    */
620 /***********************************************************************/
MakeEscape(PGLOBAL g,char * str,char q)621 char *MakeEscape(PGLOBAL g, char* str, char q)
622   {
623   char *bufp;
624   int i, k, n = 0, len = (int)strlen(str);
625 
626   for (i = 0; i < len; i++)
627     if (str[i] == q || str[i] == '\\')
628       n++;
629 
630   if (!n)
631     return str;
632   else
633     bufp = (char*)PlugSubAlloc(g, NULL, len + n + 1);
634 
635   for (i = k = 0; i < len; i++) {
636     if (str[i] == q || str[i] == '\\')
637       bufp[k++] = '\\';
638 
639     bufp[k++] = str[i];
640     } // endfor i
641 
642   bufp[k] = 0;
643   return bufp;
644   } /* end of MakeEscape */
645 
646 /***********************************************************************/
647 /*  PlugConvertConstant: convert a Plug constant to an Xobject.        */
648 /***********************************************************************/
PlugConvertConstant(PGLOBAL g,void * & value,short & type)649 void PlugConvertConstant(PGLOBAL g, void* & value, short& type)
650   {
651   if (trace(1))
652     htrc("PlugConvertConstant: value=%p type=%hd\n", value, type);
653 
654   if (type != TYPE_XOBJECT) {
655     value = new(g) CONSTANT(g, value, type);
656     type = TYPE_XOBJECT;
657     } // endif type
658 
659   } // end of PlugConvertConstant
660 
661 /***********************************************************************/
662 /*  Call the Flex preparser to convert a date format to a sscanf input */
663 /*  format and a Strftime output format. Flag if not 0 indicates that  */
664 /*  non quoted blanks are not included in the output format.           */
665 /***********************************************************************/
MakeDateFormat(PGLOBAL g,PCSZ dfmt,bool in,bool out,int flag)666 PDTP MakeDateFormat(PGLOBAL g, PCSZ dfmt, bool in, bool out, int flag)
667 {
668 	int  rc;
669   PDTP pdp = (PDTP)PlugSubAlloc(g, NULL, sizeof(DATPAR));
670 
671   if (trace(1))
672     htrc("MakeDateFormat: dfmt=%s\n", dfmt);
673 
674   memset(pdp, 0, sizeof(DATPAR));
675   pdp->Format = pdp->Curp = PlugDup(g, dfmt);
676   pdp->Outsize = 2 * strlen(dfmt) + 1;
677 
678   if (in)
679     pdp->InFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
680 
681   if (out)
682     pdp->OutFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
683 
684   pdp->Flag = flag;
685 
686   /*********************************************************************/
687   /* Call the FLEX generated parser. In multi-threading mode the next  */
688   /* instruction is protected by mutex fmdflex using static variables. */
689   /*********************************************************************/
690   pthread_mutex_lock(&parmut);
691   rc = fmdflex(pdp);
692   pthread_mutex_unlock(&parmut);
693 
694   if (trace(1))
695     htrc("Done: in=%s out=%s rc=%d\n", SVP(pdp->InFmt), SVP(pdp->OutFmt), rc);
696 
697   return pdp;
698 } // end of MakeDateFormat
699 
700 /***********************************************************************/
701 /* Extract the date from a formatted string according to format.       */
702 /***********************************************************************/
ExtractDate(char * dts,PDTP pdp,int defy,int val[6])703 int ExtractDate(char *dts, PDTP pdp, int defy, int val[6])
704   {
705 	PCSZ fmt;
706 	char c, d, e, W[8][12];
707   int  i, k, m, numval;
708   int  n, y = 30;
709   bool b = true;           // true for null dates
710 
711   if (pdp)
712     fmt = pdp->InFmt;
713   else            // assume standard MySQL date format
714     fmt = "%4d-%2d-%2d %2d:%2d:%2d";
715 
716   if (trace(2))
717     htrc("ExtractDate: dts=%s fmt=%s defy=%d\n", dts, fmt, defy);
718 
719   // Set default values for time only use
720   if (defy) {
721     // This may be a default value for year
722     y = defy;
723     val[0] = y;
724     y = (y < 100) ? y : 30;
725   } else
726     val[0] = 70;
727 
728   val[1] = 1;
729   val[2] = 1;
730 
731   for (i = 3; i < 6; i++)
732     val[i] = 0;
733 
734   numval = 0;
735 
736   // Get the date field parse it with derived input format
737   m = sscanf(dts, fmt, W[0], W[1], W[2], W[3], W[4], W[5], W[6], W[7]);
738 
739   if (m > pdp->Num)
740     m = pdp->Num;
741 
742   for (i = 0; i < m; i++) {
743     if ((n = *(int*)W[i]))
744       b = false;
745 
746     switch (k = pdp->Index[i]) {
747       case 0:
748         if (n < y)
749           n += 100;
750 
751         val[0] = n;
752         numval = MY_MAX(numval, 1);
753         break;
754       case 1:
755       case 2:
756       case 3:
757       case 4:
758       case 5:
759         val[k] = n;
760         numval = MY_MAX(numval, k + 1);
761         break;
762       case -1:
763         c = toupper(W[i][0]);
764         d = toupper(W[i][1]);
765         e = toupper(W[i][2]);
766 
767         switch (c) {
768           case 'J':
769             n = (d == 'A') ? 1
770               : (e == 'N') ? 6 : 7; break;
771           case 'F': n =  2; break;
772           case 'M':
773             n = (e == 'R') ? 3 : 5; break;
774           case 'A':
775             n = (d == 'P') ? 4 : 8; break;
776             break;
777           case 'S': n =  9; break;
778           case 'O': n = 10; break;
779           case 'N': n = 11; break;
780           case 'D': n = 12; break;
781           } /* endswitch c */
782 
783         val[1] = n;
784         numval = MY_MAX(numval, 2);
785         break;
786       case -6:
787         c = toupper(W[i][0]);
788         n = val[3] % 12;
789 
790         if (c == 'P')
791           n += 12;
792 
793         val[3] = n;
794         break;
795       } // endswitch Plugpar
796 
797     } // endfor i
798 
799   if (trace(2))
800     htrc("numval=%d val=(%d,%d,%d,%d,%d,%d)\n",
801           numval, val[0], val[1], val[2], val[3], val[4], val[5]);
802 
803   return (b) ? 0 : numval;
804   } // end of ExtractDate
805 
806 /***********************************************************************/
807 /*  Open file routine: the purpose of this routine is to make a list   */
808 /*  of all open file so they can be closed in SQLINIT on error jump.   */
809 /***********************************************************************/
PlugOpenFile(PGLOBAL g,LPCSTR fname,LPCSTR ftype)810 FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype)
811   {
812   FILE     *fop;
813   PFBLOCK   fp;
814   PDBUSER   dbuserp = (PDBUSER)g->Activityp->Aptr;
815 
816   if (trace(1)) {
817     htrc("PlugOpenFile: fname=%s ftype=%s\n", fname, ftype);
818     htrc("dbuserp=%p\n", dbuserp);
819     } // endif trace
820 
821   if ((fop= global_fopen(g, MSGID_OPEN_MODE_STRERROR, fname, ftype)) != NULL) {
822     if (trace(1))
823       htrc(" fop=%p\n", fop);
824 
825     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
826 
827     if (trace(1))
828       htrc(" fp=%p\n", fp);
829 
830     // fname may be in volatile memory such as stack
831     fp->Fname = PlugDup(g, fname);
832     fp->Count = 1;
833     fp->Type = TYPE_FB_FILE;
834     fp->File = fop;
835     fp->Mode = MODE_ANY;                        // ???
836     fp->Next = dbuserp->Openlist;
837     dbuserp->Openlist = fp;
838     } /* endif fop */
839 
840   if (trace(1))
841     htrc(" returning fop=%p\n", fop);
842 
843   return (fop);
844   } // end of PlugOpenFile
845 
846 /***********************************************************************/
847 /*  Close file routine: the purpose of this routine is to avoid        */
848 /*  double closing that freeze the system on some Unix platforms.      */
849 /***********************************************************************/
PlugReopenFile(PGLOBAL g,PFBLOCK fp,LPCSTR md)850 FILE *PlugReopenFile(PGLOBAL g, PFBLOCK fp, LPCSTR md)
851   {
852   FILE *fop;
853 
854   if ((fop = global_fopen(g, MSGID_OPEN_MODE_STRERROR, fp->Fname, md))) {
855     fp->Count = 1;
856     fp->Type = TYPE_FB_FILE;
857     fp->File = fop;
858     } /* endif fop */
859 
860   return (fop);
861   } // end of PlugOpenFile
862 
863 /***********************************************************************/
864 /*  Close file routine: the purpose of this routine is to avoid        */
865 /*  double closing that freeze the system on some Unix platforms.      */
866 /***********************************************************************/
PlugCloseFile(PGLOBAL g,PFBLOCK fp,bool all)867 int PlugCloseFile(PGLOBAL g, PFBLOCK fp, bool all)
868   {
869   int rc = 0;
870 
871   if (trace(1))
872     htrc("PlugCloseFile: fp=%p count=%hd type=%hd\n",
873           fp, ((fp) ? fp->Count : 0), ((fp) ? fp->Type : 0));
874 
875   if (!fp || !fp->Count)
876     return rc;
877 
878   switch (fp->Type) {
879     case TYPE_FB_FILE:
880       if (fclose((FILE *)fp->File) == EOF)
881         rc = errno;
882 
883       fp->File = NULL;
884       fp->Mode = MODE_ANY;
885       fp->Count = 0;
886       break;
887     case TYPE_FB_MAP:
888       if ((fp->Count = (all) ? 0 : fp->Count - 1))
889         break;
890 
891       if (CloseMemMap(fp->Memory, fp->Length))
892         rc = (int)GetLastError();
893 
894       fp->Memory = NULL;
895       fp->Mode = MODE_ANY;
896       // fall through
897     case TYPE_FB_HANDLE:
898       if (fp->Handle && fp->Handle != INVALID_HANDLE_VALUE)
899         if (CloseFileHandle(fp->Handle))
900           rc = (rc) ? rc : (int)GetLastError();
901 
902       fp->Handle = INVALID_HANDLE_VALUE;
903       fp->Mode = MODE_ANY;
904       fp->Count = 0;
905       break;
906 #ifdef DOMDOC_SUPPORT
907     case TYPE_FB_XML:
908       CloseXMLFile(g, fp, all);
909       break;
910 #endif   // DOMDOC_SUPPORT
911 #ifdef LIBXML2_SUPPORT
912     case TYPE_FB_XML2:
913       CloseXML2File(g, fp, all);
914       break;
915 #endif   // LIBXML2_SUPPORT
916 #ifdef ODBC_SUPPORT
917 		case TYPE_FB_ODBC:
918 			OdbcClose(g, fp);
919 			fp->Count = 0;
920 			fp->File = NULL;
921 			break;
922 #endif   // ODBC_SUPPORT
923 #ifdef ZIP_SUPPORT
924 		case TYPE_FB_ZIP:
925 			if (fp->Mode == MODE_INSERT)
926 				((ZIPUTIL*)fp->File)->close();
927 			else
928 				((UNZIPUTL*)fp->File)->close();
929 
930 			fp->Memory = NULL;
931 			fp->Mode = MODE_ANY;
932 			fp->Count = 0;
933 			fp->File = NULL;
934 			break;
935 #endif   // ZIP_SUPPORT
936 #ifdef JAVA_SUPPORT
937 		case TYPE_FB_JAVA:
938 			((JAVAConn*)fp->File)->Close();
939 			fp->Count = 0;
940 			fp->File = NULL;
941 			break;
942 #endif   // JAVA_SUPPORT
943 #ifdef CMGO_SUPPORT
944 		case TYPE_FB_MONGO:
945 			((CMgoConn*)fp->File)->Close();
946 			fp->Count = 0;
947 			fp->File = NULL;
948 			break;
949 #endif   // JAVA_SUPPORT
950 		default:
951       rc = RC_FX;
952     } // endswitch Type
953 
954   return rc;
955   } // end of PlugCloseFile
956 
957 /***********************************************************************/
958 /*  PlugCleanup: Cleanup remaining items of a SQL query.               */
959 /***********************************************************************/
PlugCleanup(PGLOBAL g,bool dofree)960 void PlugCleanup(PGLOBAL g, bool dofree)
961   {
962   PCATLG  cat;
963   PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
964 
965   // The test on Catalog is to avoid a Windows bug that can make
966   // LoadString in PlugGetMessage to fail in some case
967   if (!dbuserp || !(cat = dbuserp->Catalog))
968     return;
969 
970   /*********************************************************************/
971   /*  Close eventually still open/mapped files.                        */
972   /*********************************************************************/
973   for (PFBLOCK fp = dbuserp->Openlist; fp; fp = fp->Next)
974     PlugCloseFile(g, fp, true);
975 
976   dbuserp->Openlist = NULL;
977 
978   if (dofree) {
979     /*******************************************************************/
980     /*  Cleanup any non suballocated memory still not freed.           */
981     /*******************************************************************/
982     for (PMBLOCK mp = dbuserp->Memlist; mp; mp = mp->Next)
983       PlgDBfree(*mp);
984 
985     dbuserp->Memlist = NULL;
986 
987     /*******************************************************************/
988     /*  If not using permanent storage catalog, reset volatile values. */
989     /*******************************************************************/
990     cat->Reset();
991 
992     /*******************************************************************/
993     /*  This is the place to reset the pointer on domains.             */
994     /*******************************************************************/
995     dbuserp->Subcor = false;
996     dbuserp->Step = "New query";     // was STEP(PARSING_QUERY);
997     dbuserp->ProgMax = dbuserp->ProgCur = dbuserp->ProgSav = 0;
998     } // endif dofree
999 
1000   } // end of PlugCleanup
1001 
1002 #if 0
1003 /***********************************************************************/
1004 /*  That stupid Windows 98 does not provide this function.             */
1005 /***********************************************************************/
1006 bool WritePrivateProfileInt(LPCSTR sec, LPCSTR key, int n, LPCSTR ini)
1007   {
1008   char buf[12];
1009 
1010   sprintf(buf, "%d", n);
1011   return WritePrivateProfileString(sec, key, buf, ini);
1012   } // end of WritePrivateProfileInt
1013 
1014 /***********************************************************************/
1015 /*  Retrieve a size from an INI file with eventual K or M following.   */
1016 /***********************************************************************/
1017 int GetIniSize(char *section, char *key, char *def, char *ini)
1018   {
1019   char c, buff[32];
1020   int  i;
1021   int  n = 0;
1022 
1023   GetPrivateProfileString(section, key, def, buff, sizeof(buff), ini);
1024 
1025   if ((i = sscanf(buff, " %d %c ", &n, &c)) == 2)
1026     switch (toupper(c)) {
1027       case 'M':
1028         n *= 1024;
1029       case 'K':
1030         n *= 1024;
1031       } // endswitch c
1032 
1033   if (trace(1))
1034     htrc("GetIniSize: key=%s buff=%s i=%d n=%d\n", key, buff, i, n);
1035 
1036   return n;
1037   } // end of GetIniSize
1038 
1039 /***********************************************************************/
1040 /* Allocate a string retrieved from an INI file and return its address */
1041 /***********************************************************************/
1042 DllExport PSZ GetIniString(PGLOBAL g, void *mp, LPCSTR sec, LPCSTR key,
1043                                                 LPCSTR def, LPCSTR ini)
1044   {
1045   char  buff[_MAX_PATH];
1046   PSZ   p;
1047   int   n, m = sizeof(buff);
1048   char *buf = buff;
1049 
1050 #if defined(_DEBUG)
1051   assert (sec && key);
1052 #endif
1053 
1054  again:
1055   n = GetPrivateProfileString(sec, key, def, buf, m, ini);
1056 
1057   if (n == m - 1) {
1058     // String may have been truncated, make sure to have all
1059     if (buf != buff)
1060       delete [] buf;
1061 
1062     m *= 2;
1063     buf = new char[m];
1064     goto again;
1065     } // endif n
1066 
1067   p = (PSZ)PlugSubAlloc(g, mp, n + 1);
1068 
1069   if (trace(1))
1070     htrc("GetIniString: sec=%s key=%s buf=%s\n", sec, key, buf);
1071 
1072   strcpy(p, buf);
1073 
1074   if (buf != buff)
1075     delete [] buf;
1076 
1077   return p;
1078   } // end of GetIniString
1079 #endif // 0
1080 
1081 /***********************************************************************/
1082 /*  GetAmName: return the name correponding to an AM code.             */
1083 /***********************************************************************/
GetAmName(PGLOBAL g,AMT am,void * memp)1084 char *GetAmName(PGLOBAL g, AMT am, void *memp)
1085   {
1086   char *amn= (char*)PlugSubAlloc(g, memp, 16);
1087 
1088   switch (am) {
1089     case TYPE_AM_ERROR: strcpy(amn, "ERROR"); break;
1090     case TYPE_AM_ROWID: strcpy(amn, "ROWID"); break;
1091     case TYPE_AM_FILID: strcpy(amn, "FILID"); break;
1092     case TYPE_AM_VIEW:  strcpy(amn, "VIEW");  break;
1093     case TYPE_AM_COUNT: strcpy(amn, "COUNT"); break;
1094     case TYPE_AM_DCD:   strcpy(amn, "DCD");   break;
1095     case TYPE_AM_CMS:   strcpy(amn, "CMS");   break;
1096     case TYPE_AM_MAP:   strcpy(amn, "MAP");   break;
1097     case TYPE_AM_FMT:   strcpy(amn, "FMT");   break;
1098     case TYPE_AM_CSV:   strcpy(amn, "CSV");   break;
1099     case TYPE_AM_MCV:   strcpy(amn, "MCV");   break;
1100     case TYPE_AM_DOS:   strcpy(amn, "DOS");   break;
1101     case TYPE_AM_FIX:   strcpy(amn, "FIX");   break;
1102     case TYPE_AM_BIN:   strcpy(amn, "BIN");   break;
1103     case TYPE_AM_VCT:   strcpy(amn, "VEC");   break;
1104     case TYPE_AM_VMP:   strcpy(amn, "VMP");   break;
1105     case TYPE_AM_DBF:   strcpy(amn, "DBF");   break;
1106     case TYPE_AM_QRY:   strcpy(amn, "QRY");   break;
1107     case TYPE_AM_SQL:   strcpy(amn, "SQL");   break;
1108     case TYPE_AM_PLG:   strcpy(amn, "PLG");   break;
1109     case TYPE_AM_PLM:   strcpy(amn, "PLM");   break;
1110     case TYPE_AM_DOM:   strcpy(amn, "DOM");   break;
1111     case TYPE_AM_DIR:   strcpy(amn, "DIR");   break;
1112     case TYPE_AM_ODBC:  strcpy(amn, "ODBC");  break;
1113 		case TYPE_AM_JDBC:  strcpy(amn, "JDBC");  break;
1114 		case TYPE_AM_MAC:   strcpy(amn, "MAC");   break;
1115     case TYPE_AM_OEM:   strcpy(amn, "OEM");   break;
1116     case TYPE_AM_OUT:   strcpy(amn, "OUT");   break;
1117     default:           sprintf(amn, "OEM(%d)", am);
1118     } // endswitch am
1119 
1120   return amn;
1121   } // end of GetAmName
1122 
1123 #if defined(SE_CATCH)
1124 /***********************************************************************/
1125 /*  GetExceptionDesc: return the description of an exception code.     */
1126 /***********************************************************************/
GetExceptionDesc(PGLOBAL g,unsigned int e)1127 char *GetExceptionDesc(PGLOBAL g, unsigned int e)
1128   {
1129   char *p;
1130 
1131   switch (e) {
1132     case EXCEPTION_GUARD_PAGE:
1133       p = MSG(GUARD_PAGE);
1134       break;
1135     case EXCEPTION_DATATYPE_MISALIGNMENT:
1136       p = MSG(DATA_MISALIGN);
1137       break;
1138     case EXCEPTION_BREAKPOINT:
1139       p = MSG(BREAKPOINT);
1140       break;
1141     case EXCEPTION_SINGLE_STEP:
1142       p = MSG(SINGLE_STEP);
1143       break;
1144     case EXCEPTION_ACCESS_VIOLATION:
1145       p = MSG(ACCESS_VIOLATN);
1146       break;
1147     case EXCEPTION_IN_PAGE_ERROR:
1148       p = MSG(PAGE_ERROR);
1149       break;
1150     case EXCEPTION_INVALID_HANDLE:
1151       p = MSG(INVALID_HANDLE);
1152       break;
1153     case EXCEPTION_ILLEGAL_INSTRUCTION:
1154       p = MSG(ILLEGAL_INSTR);
1155       break;
1156     case EXCEPTION_NONCONTINUABLE_EXCEPTION:
1157       p = MSG(NONCONT_EXCEPT);
1158       break;
1159     case EXCEPTION_INVALID_DISPOSITION:
1160       p = MSG(INVALID_DISP);
1161       break;
1162     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
1163       p = MSG(ARRAY_BNDS_EXCD);
1164       break;
1165     case EXCEPTION_FLT_DENORMAL_OPERAND:
1166       p = MSG(FLT_DENORMAL_OP);
1167       break;
1168     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
1169       p = MSG(FLT_ZERO_DIVIDE);
1170       break;
1171     case EXCEPTION_FLT_INEXACT_RESULT:
1172       p = MSG(FLT_BAD_RESULT);
1173       break;
1174     case EXCEPTION_FLT_INVALID_OPERATION:
1175       p = MSG(FLT_INVALID_OP);
1176       break;
1177     case EXCEPTION_FLT_OVERFLOW:
1178       p = MSG(FLT_OVERFLOW);
1179       break;
1180     case EXCEPTION_FLT_STACK_CHECK:
1181       p = MSG(FLT_STACK_CHECK);
1182       break;
1183     case EXCEPTION_FLT_UNDERFLOW:
1184       p = MSG(FLT_UNDERFLOW);
1185       break;
1186     case EXCEPTION_INT_DIVIDE_BY_ZERO:
1187       p = MSG(INT_ZERO_DIVIDE);
1188       break;
1189     case EXCEPTION_INT_OVERFLOW:
1190       p = MSG(INT_OVERFLOW);
1191       break;
1192     case EXCEPTION_PRIV_INSTRUCTION:
1193       p = MSG(PRIV_INSTR);
1194       break;
1195     case EXCEPTION_STACK_OVERFLOW:
1196       p = MSG(STACK_OVERFLOW);
1197       break;
1198     case CONTROL_C_EXIT:
1199       p = MSG(CONTROL_C_EXIT);
1200       break;
1201     case STATUS_NO_MEMORY:
1202       p = MSG(NO_MEMORY);
1203       break;
1204     default:
1205       p = MSG(UNKNOWN_EXCPT);
1206       break;
1207     } // endswitch nSE
1208 
1209   return p;
1210   } // end of GetExceptionDesc
1211 #endif   // SE_CATCH
1212 
1213 /***********************************************************************/
1214 /*  PlgDBalloc: allocates or suballocates memory conditionally.        */
1215 /*  If mp.Sub is true at entry, this forces suballocation.             */
1216 /*  If the memory is allocated, makes an entry in an allocation list   */
1217 /*  so it can be freed at the normal or error query completion.        */
1218 /***********************************************************************/
PlgDBalloc(PGLOBAL g,void * area,MBLOCK & mp)1219 void *PlgDBalloc(PGLOBAL g, void *area, MBLOCK& mp)
1220 {
1221 //bool        b;
1222   size_t      maxsub, minsub;
1223   void       *arp = (area) ? area : g->Sarea;
1224   PPOOLHEADER pph = (PPOOLHEADER)arp;
1225 
1226   if (mp.Memp) {
1227     // This is a reallocation. If this block is not suballocated, it
1228     // was already placed in the chain of memory blocks and we must
1229     // not do it again as it can trigger a loop when freeing them.
1230     // Note: this works if blocks can be reallocated only once.
1231     // Otherwise a new boolean must be added to the block that
1232     // indicate that it is chained, or a test on the whole chain be
1233     // done to check whether the block is already there.
1234 //  b = mp.Sub;
1235     mp.Sub = false;    // Restrict suballocation to one quarter
1236   } // endif Memp
1237 
1238   // Suballoc when possible if mp.Sub is initially true, but leaving
1239   // a minimum amount of storage for future operations such as the
1240   // optimize recalculation after insert; otherwise
1241   // suballoc only if size is smaller than one quarter of free mem.
1242   minsub = (pph->FreeBlk + pph->To_Free + 524248) >> 2;
1243   maxsub = (pph->FreeBlk < minsub) ? 0 : pph->FreeBlk - minsub;
1244   mp.Sub = mp.Size <= ((mp.Sub) ? maxsub : (maxsub >> 2));
1245 
1246 	if (trace(2))
1247 		htrc("PlgDBalloc: in %p size=%zd used=%zd free=%zd sub=%d\n",
1248 			arp, mp.Size, pph->To_Free, pph->FreeBlk, mp.Sub);
1249 
1250 	if (!mp.Sub) {
1251     // For allocations greater than one fourth of remaining storage
1252     // in the area, do allocate from virtual storage.
1253 		const char*v = "malloc";
1254 #if defined(_WIN32)
1255 		if (mp.Size >= BIGMEM) {
1256 			v = "VirtualAlloc";
1257 			mp.Memp = VirtualAlloc(NULL, mp.Size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
1258 		} else
1259 #endif
1260       mp.Memp = malloc(mp.Size);
1261 
1262 		if (trace(8))
1263 			htrc("PlgDBalloc: %s(%zd) at %p\n", v, mp.Size, mp.Memp);
1264 
1265 		if (!mp.Inlist && mp.Memp) {
1266       // New allocated block, put it in the memory block chain.
1267       PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
1268 
1269       mp.Next = dbuserp->Memlist;
1270       dbuserp->Memlist = &mp;
1271       mp.Inlist = true;
1272     } // endif mp
1273 
1274   } else
1275     // Suballocating is Ok.
1276     mp.Memp = PlugSubAlloc(g, area, mp.Size);
1277 
1278   return mp.Memp;
1279 } // end of PlgDBalloc
1280 
1281 /***********************************************************************/
1282 /*  PlgDBrealloc: reallocates memory conditionally.                    */
1283 /*  Note that this routine can fail only when block size is increased  */
1284 /*  because otherwise we keep the old storage on failure.              */
1285 /***********************************************************************/
PlgDBrealloc(PGLOBAL g,void * area,MBLOCK & mp,size_t newsize)1286 void *PlgDBrealloc(PGLOBAL g, void *area, MBLOCK& mp, size_t newsize)
1287   {
1288   MBLOCK m;
1289 
1290 #if defined(_DEBUG)
1291 //  assert (mp.Memp != NULL);
1292 #endif
1293 
1294   if (trace(2))
1295     htrc("PlgDBrealloc: %p size=%zd sub=%d\n", mp.Memp, mp.Size, mp.Sub);
1296 
1297   if (newsize == mp.Size)
1298     return mp.Memp;      // Nothing to do
1299   else
1300     m = mp;
1301 
1302   if (!mp.Sub && mp.Size < BIGMEM && newsize < BIGMEM) {
1303     // Allocation was done by malloc, try to use realloc but
1304     // suballoc if newsize is smaller than one quarter of free mem.
1305     size_t      maxsub;
1306     PPOOLHEADER pph = (PPOOLHEADER)((area) ? area : g->Sarea);
1307 
1308     maxsub = (pph->FreeBlk < 131072) ? 0 : pph->FreeBlk - 131072;
1309 
1310     if ((mp.Sub = (newsize <= (maxsub >> 2)))) {
1311       mp.Memp = PlugSubAlloc(g, area, newsize);
1312       memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
1313       PlgDBfree(m);    // Free the old block
1314     } else {
1315 			if (!(mp.Memp = realloc(mp.Memp, newsize))) {
1316 				mp = m;          // Possible only if newsize > Size
1317 				return NULL;     // Failed
1318 			} else if (trace(8))
1319 				htrc("PlgDBrealloc: realloc(%ld) at %p\n", newsize, mp.Memp);
1320 
1321 		} // endif's
1322 
1323     mp.Size = newsize;
1324   } else if (!mp.Sub || newsize > mp.Size) {
1325     // Was suballocated or Allocation was done by VirtualAlloc
1326     // Make a new allocation and copy the useful part
1327     // Note: DO NOT reset Memp and Sub so we know that this
1328     // is a reallocation in PlgDBalloc
1329     mp.Size = newsize;
1330 
1331     if (PlgDBalloc(g, area, mp)) {
1332       memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
1333       PlgDBfree(m);    // Free the old block
1334     } else {
1335       mp = m;          // No space to realloc, do nothing
1336 
1337       if (newsize > m.Size)
1338         return NULL;   // Failed
1339 
1340     } // endif PlgDBalloc
1341 
1342   } // endif's
1343 
1344   if (trace(8))
1345     htrc(" newsize=%zd newp=%p sub=%d\n", mp.Size, mp.Memp, mp.Sub);
1346 
1347   return mp.Memp;
1348   } // end of PlgDBrealloc
1349 
1350 /***********************************************************************/
1351 /*  PlgDBfree: free memory if not suballocated.                        */
1352 /***********************************************************************/
PlgDBfree(MBLOCK & mp)1353 void PlgDBfree(MBLOCK& mp)
1354   {
1355 	if (!mp.Sub && mp.Memp) {
1356 		const char*v = "free";
1357 #if defined(_WIN32)
1358 		if (mp.Size >= BIGMEM) {
1359 			v = "VirtualFree";
1360 			VirtualFree(mp.Memp, 0, MEM_RELEASE);
1361 		} else
1362 #endif
1363 			free(mp.Memp);
1364 
1365 		if (trace(8))
1366 			htrc("PlgDBfree: %s(%p) size=%d\n", v, mp.Memp, mp.Size);
1367 
1368 	}	// endif mp
1369 
1370   // Do not reset Next to avoid cutting the Mblock chain
1371   mp.Memp = NULL;
1372   mp.Sub = false;
1373   mp.Size = 0;
1374   } // end of PlgDBfree
1375 
1376 /***********************************************************************/
1377 /*  Program for sub-allocating one item in a storage area.             */
1378 /*  Note: This function is equivalent to PlugSubAlloc except that in   */
1379 /*  case of insufficient memory, it returns NULL instead of doing a    */
1380 /*  throw. The caller must test the return value for error.            */
1381 /***********************************************************************/
PlgDBSubAlloc(PGLOBAL g,void * memp,size_t size)1382 void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size)
1383   {
1384   PPOOLHEADER pph;                           // Points on area header.
1385 
1386   if (!memp)
1387     /*******************************************************************/
1388     /*  Allocation is to be done in the Sarea.                         */
1389     /*******************************************************************/
1390     memp = g->Sarea;
1391 
1392 //size = ((size + 3) / 4) * 4;       /* Round up size to multiple of 4 */
1393   size = ((size + 7) / 8) * 8;       /* Round up size to multiple of 8 */
1394   pph = (PPOOLHEADER)memp;
1395 
1396   if (trace(16))
1397     htrc("PlgDBSubAlloc: memp=%p size=%zd used=%zd free=%zd\n",
1398          memp, size, pph->To_Free, pph->FreeBlk);
1399 
1400   if (size > pph->FreeBlk) {   /* Not enough memory left in pool */
1401     sprintf(g->Message,
1402     "Not enough memory in Work area for request of %zd (used=%zd free=%zd)",
1403             size, pph->To_Free, pph->FreeBlk);
1404 
1405     if (trace(1))
1406       htrc("%s\n", g->Message);
1407 
1408     return NULL;
1409     } // endif size
1410 
1411   /*********************************************************************/
1412   /*  Do the suballocation the simplest way.                           */
1413   /*********************************************************************/
1414   memp = MakePtr(memp, pph->To_Free);   // Points to suballocated block
1415   pph->To_Free += size;                 // New offset of pool free block
1416   pph->FreeBlk -= size;                 // New size   of pool free block
1417 
1418   if (trace(16))
1419     htrc("Done memp=%p used=%zd free=%zd\n",
1420          memp, pph->To_Free, pph->FreeBlk);
1421 
1422   return (memp);
1423   } // end of PlgDBSubAlloc
1424 
1425 /***********************************************************************/
1426 /*  Program for sub-allocating and copying a string in a storage area. */
1427 /***********************************************************************/
PlgDBDup(PGLOBAL g,const char * str)1428 char *PlgDBDup(PGLOBAL g, const char *str)
1429   {
1430   if (str) {
1431     char *sm = (char*)PlgDBSubAlloc(g, NULL, strlen(str) + 1);
1432 
1433     if (sm)
1434       strcpy(sm, str);
1435 
1436     return sm;
1437   } else
1438     return NULL;
1439 
1440   } // end of PlgDBDup
1441 
1442 /***********************************************************************/
1443 /*  PUTOUT: Plug DB object typing routine.                             */
1444 /***********************************************************************/
PlugPutOut(PGLOBAL g,FILE * f,short t,void * v,uint n)1445 void PlugPutOut(PGLOBAL g, FILE *f, short t, void *v, uint n)
1446   {
1447   char  m[64];
1448 
1449   if (trace(1))
1450     htrc("PUTOUT: f=%p t=%d v=%p n=%d\n", f, t, v, n);
1451 
1452   if (!v)
1453     return;
1454 
1455   memset(m, ' ', n);                             /* Make margin string */
1456   m[n] = '\0';
1457   n += 2;                                        /* Increase margin    */
1458 
1459   switch (t) {
1460     case TYPE_ERROR:
1461       fprintf(f, "--> %s\n", (PSZ)v);
1462       break;
1463 
1464     case TYPE_STRING:
1465     case TYPE_PSZ:
1466       fprintf(f, "%s%s\n", m, (PSZ)v);
1467       break;
1468 
1469     case TYPE_DOUBLE:
1470       fprintf(f, "%s%lf\n", m, *(double *)v);
1471       break;
1472 
1473     case TYPE_LIST:
1474     case TYPE_COLIST:
1475     case TYPE_COL:
1476      {PPARM p;
1477 
1478       if (t == TYPE_LIST)
1479         fprintf(f, "%s%s\n", m, MSG(LIST));
1480       else
1481         fprintf(f, "%s%s\n", m, "Colist:");
1482 
1483       for (p = (PPARM)v; p; p = p->Next)
1484         PlugPutOut(g, f, p->Type, p->Value, n);
1485 
1486       } break;
1487 
1488     case TYPE_INT:
1489       fprintf(f, "%s%d\n", m, *(int *)v);
1490       break;
1491 
1492     case TYPE_SHORT:
1493       fprintf(f, "%s%hd\n", m, *(short *)v);
1494       break;
1495 
1496     case TYPE_TINY:
1497       fprintf(f, "%s%d\n", m, (int)*(char *)v);
1498       break;
1499 
1500     case TYPE_VOID:
1501       break;
1502 
1503     case TYPE_SQL:
1504     case TYPE_TABLE:
1505     case TYPE_TDB:
1506     case TYPE_XOBJECT:
1507       ((PBLOCK)v)->Printf(g, f, n-2);
1508       break;
1509 
1510     default:
1511       fprintf(f, "%s%s %d\n", m, MSG(ANSWER_TYPE), t);
1512     } /* endswitch */
1513 
1514   return;
1515   } /* end of PlugPutOut */
1516 
1517 /***********************************************************************/
1518 /*  NewPointer: makes a table of pointer values to be changed later.   */
1519 /***********************************************************************/
NewPointer(PTABS t,void * oldv,void * newv)1520 DllExport void NewPointer(PTABS t, void *oldv, void *newv)
1521   {
1522   PTABPTR tp;
1523 
1524   if (!oldv)                                       /* error ?????????? */
1525     return;
1526 
1527   if (!t->P1 || t->P1->Num == 50)
1528   {
1529     if (!(tp = new TABPTR)) {
1530       PGLOBAL g = t->G;
1531 
1532       sprintf(g->Message, "NewPointer: %s", MSG(MEM_ALLOC_ERROR));
1533 			throw 3;
1534 		} else {
1535       tp->Next = t->P1;
1536       tp->Num = 0;
1537       t->P1 = tp;
1538     } /* endif tp */
1539   }
1540 
1541   t->P1->Old[t->P1->Num] = oldv;
1542   t->P1->New[t->P1->Num++] = newv;
1543   } /* end of NewPointer */
1544 
1545 #if 0
1546 /***********************************************************************/
1547 /*  Compare two files and return 0 if they are identical, else 1.      */
1548 /***********************************************************************/
1549 int FileComp(PGLOBAL g, char *file1, char *file2)
1550   {
1551   char *fn[2], *bp[2], buff1[4096], buff2[4096];
1552   int   i, k, n[2], h[2] = {-1,-1};
1553   int  len[2], rc = -1;
1554 
1555   fn[0] = file1; fn[1] = file2;
1556   bp[0] = buff1; bp[1] = buff2;
1557 
1558   for (i = 0; i < 2; i++) {
1559 #if defined(_WIN32)
1560     h[i]= global_open(g, MSGID_NONE, fn[i], _O_RDONLY | _O_BINARY);
1561 #else   // !_WIN32
1562     h[i]= global_open(g, MSGOD_NONE, fn[i], O_RDONLY);
1563 #endif  // !_WIN32
1564 
1565     if (h[i] == -1) {
1566 //      if (errno != ENOENT) {
1567         sprintf(g->Message, MSG(OPEN_MODE_ERROR),
1568                 "rb", (int)errno, fn[i]);
1569         strcat(strcat(g->Message, ": "), strerror(errno));
1570 				throw 666;
1571 				//      } else
1572 //        len[i] = 0;          // File does not exist yet
1573 
1574     } else {
1575       if ((len[i] = _filelength(h[i])) < 0) {
1576         sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", fn[i]);
1577 				throw 666;
1578 			} // endif len
1579 
1580     } // endif h
1581 
1582     } // endfor i
1583 
1584   if (len[0] != len[1])
1585     rc = 1;
1586 
1587   while (rc == -1) {
1588     for (i = 0; i < 2; i++)
1589       if ((n[i] = read(h[i], bp[i], 4096)) < 0) {
1590         sprintf(g->Message, MSG(READ_ERROR), fn[i], strerror(errno));
1591         goto fin;
1592         } // endif n
1593 
1594     if (n[0] != n[1])
1595       rc = 1;
1596     else if (*n == 0)
1597       rc = 0;
1598     else for (k = 0; k < *n; k++)
1599       if (*(bp[0] + k) != *(bp[1] + k)) {
1600         rc = 1;
1601         goto fin;
1602         } // endif bp
1603 
1604     } // endwhile
1605 
1606  fin:
1607   for (i = 0; i < 2; i++)
1608     if (h[i] != -1)
1609       close(h[i]);
1610 
1611   return rc;
1612   } // end of FileComp
1613 #endif // 0
1614