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 #ifdef NOT_USED
430 /***********************************************************************/
431 /*  Check the occurrence and matching of a pattern against a string.    */
432 /*  Because this function is only used for catalog name checking,      */
433 /*  it must be case insensitive.                                       */
434 /***********************************************************************/
PlugCheckPattern(PGLOBAL g,LPCSTR string,LPCSTR pat)435 static bool PlugCheckPattern(PGLOBAL g, LPCSTR string, LPCSTR pat)
436   {
437   if (pat && strlen(pat)) {
438     // This leaves 2048 bytes (MAX_STR / 2) for each components
439     LPSTR name = g->Message + MAX_STR / 2;
440 
441     strlwr(strcpy(name, string));
442     strlwr(strcpy(g->Message, pat));         // Can be modified by Eval
443     return EvalLikePattern(name, g->Message);
444   } else
445     return true;
446 
447   } // end of PlugCheckPattern
448 #endif /* NOT_USED */
449 
450 /***********************************************************************/
451 /*  PlugEvalLike: evaluates a LIKE clause.                             */
452 /*  Syntaxe: M like P escape C. strg->M, pat->P, C not implemented yet */
453 /***********************************************************************/
PlugEvalLike(PGLOBAL g,LPCSTR strg,LPCSTR pat,bool ci)454 bool PlugEvalLike(PGLOBAL g, LPCSTR strg, LPCSTR pat, bool ci)
455   {
456   char *tp, *sp;
457   bool  b;
458 
459   if (trace(2))
460     htrc("LIKE: strg='%s' pattern='%s'\n", strg, pat);
461 
462   if (ci) {                        /* Case insensitive test             */
463     if (strlen(pat) + strlen(strg) + 1 < MAX_STR)
464       tp = g->Message;
465     else if (!(tp = new char[strlen(pat) + strlen(strg) + 2])) {
466       strcpy(g->Message, MSG(NEW_RETURN_NULL));
467 			throw (int)OP_LIKE;
468 		} /* endif tp */
469 
470     sp = tp + strlen(pat) + 1;
471     strlwr(strcpy(tp, pat));      /* Make a lower case copy of pat     */
472     strlwr(strcpy(sp, strg));     /* Make a lower case copy of strg    */
473   } else {                        /* Case sensitive test               */
474     if (strlen(pat) < MAX_STR)    /* In most of the case for small pat */
475       tp = g->Message;            /* Use this as temporary work space. */
476     else if (!(tp = new char[strlen(pat) + 1])) {
477       strcpy(g->Message, MSG(NEW_RETURN_NULL));
478 			throw (int)OP_LIKE;
479 		} /* endif tp */
480 
481     strcpy(tp, pat);                  /* Make a copy to be worked into */
482     sp = (char*)strg;
483   } /* endif ci */
484 
485   b = EvalLikePattern(sp, tp);
486 
487   if (tp != g->Message)               /* If working space was obtained */
488     delete [] tp;                     /* by the use of new, delete it. */
489 
490   return (b);
491   } /* end of PlugEvalLike */
492 
493 /***********************************************************************/
494 /*  M and P are variable length character string. If M and P are zero  */
495 /*  length strings then the Like predicate is true.                    */
496 /*                                                                     */
497 /*  The Like predicate is true if:                                     */
498 /*                                                                     */
499 /*  1- A subtring of M is a sequence of 0 or more contiguous <CR> of M */
500 /*     and each <CR> of M is part of exactly one substring.            */
501 /*                                                                     */
502 /*  2- If the i-th <subtring-specifyer> of P is an <arbitrary-char-    */
503 /*     specifier>, the i-th subtring of M is any single <CR>.          */
504 /*                                                                     */
505 /*  3- If the i-th <subtring-specifyer> of P is an <arbitrary-string-  */
506 /*     specifier>, then the i-th subtring of M is any sequence of zero */
507 /*     or more <CR>.                                                   */
508 /*                                                                     */
509 /*  4- If the i-th <subtring-specifyer> of P is neither an <arbitrary- */
510 /*     character-specifier> nor an <arbitrary-string-specifier>, then  */
511 /*     the i-th substring of M is equal to that <substring-specifier>  */
512 /*     according to the collating sequence of the <like-predicate>,    */
513 /*     without the appending of <space-character>, and has the same    */
514 /*     length as that <substring-specifier>.                           */
515 /*                                                                     */
516 /*  5- The number of substrings of M is equal to the number of         */
517 /*     <subtring-specifiers> of P.                                     */
518 /*                                                                     */
519 /*  Otherwise M like P is false.                                       */
520 /***********************************************************************/
EvalLikePattern(LPCSTR sp,LPCSTR tp)521 bool EvalLikePattern(LPCSTR sp, LPCSTR tp)
522   {
523   LPSTR p;
524   char  c;
525   ssize_t   n;
526   bool  b, t = false;
527 
528   if (trace(2))
529     htrc("Eval Like: sp=%s tp=%s\n",
530          (sp) ? sp : "Null", (tp) ? tp : "Null");
531 
532   /********************************************************************/
533   /*  If pattern is void, Like is true only if string is also void.   */
534   /********************************************************************/
535   if (!*tp)
536     return (!*sp);
537 
538   /********************************************************************/
539   /*  Analyse eventual arbitrary specifications ahead of pattern.     */
540   /********************************************************************/
541   for (p = (LPSTR)tp; p;)
542     switch (*p) {                     /*   it can contain % and/or _   */
543       case '%':                       /* An % has been found           */
544         t = true;                     /* Note eventual character skip  */
545         p++;
546         break;
547       case '_':                       /* An _ has been found           */
548         if (*sp) {                    /* If more character in string   */
549           sp++;                       /*   skip it                     */
550           p++;
551         } else
552           return false;               /* Like condition is not met     */
553 
554         break;
555       default:
556         tp = p;                       /* Point to rest of template     */
557         p = NULL;                     /* To stop For loop              */
558         break;
559       } /* endswitch */
560 
561   if ((p = (LPSTR)strpbrk(tp, "%_"))) /* Get position of next % or _   */
562     n = p - tp;
563   else
564     n = strlen(tp);                   /* Get length of pattern head    */
565 
566   if (trace(2))
567     htrc(" testing: t=%d sp=%s tp=%s p=%p\n", t, sp, tp, p);
568 
569   if (n > (signed)strlen(sp))         /* If head is longer than strg   */
570     b = false;                        /* Like condition is not met     */
571   else if (n == 0)                    /* If void <substring-specifier> */
572     b = (t || !*sp);                  /*   true if %  or void strg.    */
573   else if (!t) {
574     /*******************************************************************/
575     /*  No character to skip, check occurrence of <subtring-specifier>  */
576     /*  at the very beginning of remaining string.                     */
577     /*******************************************************************/
578     if (p) {
579       if ((b = !strncmp(sp, tp, n)))
580         b = EvalLikePattern(sp + n, p);
581 
582     } else
583       b = !strcmp(sp, tp);            /*   strg and tmp heads match    */
584 
585   } else
586     if (p)
587       /*****************************************************************/
588       /*  Here is the case explaining why we need a recursive routine. */
589       /*  The test must be done not only against the first occurrence   */
590       /*  of the <substring-specifier> in the remaining string,        */
591       /*  but also with all eventual succeeding ones.                  */
592       /*****************************************************************/
593       for (b = false, c = *p; !b && (signed)strlen(sp) >= n; sp++) {
594         *p = '\0';                    /* Separate pattern header       */
595 
596         if ((sp = strstr(sp, tp))) {
597           *p = c;
598           b = EvalLikePattern(sp + n, p);
599         } else {
600           *p = c;
601           b = false;
602           break;
603         } /* endif s */
604 
605         } /* endfor b, sp */
606 
607     else {
608       sp += (strlen(sp) - n);
609       b = !strcmp(sp, tp);
610     } /* endif p */
611 
612   if (trace(2))
613     htrc(" done: b=%d n=%d sp=%s tp=%s\n",
614           b, n, (sp) ? sp : "Null", tp);
615 
616   return (b);
617   } /* end of EvalLikePattern */
618 
619 /***********************************************************************/
620 /*  MakeEscape: Escape some characters in a string.                    */
621 /***********************************************************************/
MakeEscape(PGLOBAL g,char * str,char q)622 char *MakeEscape(PGLOBAL g, char* str, char q)
623   {
624   char *bufp;
625   int i, k, n = 0, len = (int)strlen(str);
626 
627   for (i = 0; i < len; i++)
628     if (str[i] == q || str[i] == '\\')
629       n++;
630 
631   if (!n)
632     return str;
633   else
634     bufp = (char*)PlugSubAlloc(g, NULL, len + n + 1);
635 
636   for (i = k = 0; i < len; i++) {
637     if (str[i] == q || str[i] == '\\')
638       bufp[k++] = '\\';
639 
640     bufp[k++] = str[i];
641     } // endfor i
642 
643   bufp[k] = 0;
644   return bufp;
645   } /* end of MakeEscape */
646 
647 /***********************************************************************/
648 /*  PlugConvertConstant: convert a Plug constant to an Xobject.        */
649 /***********************************************************************/
PlugConvertConstant(PGLOBAL g,void * & value,short & type)650 void PlugConvertConstant(PGLOBAL g, void* & value, short& type)
651   {
652   if (trace(1))
653     htrc("PlugConvertConstant: value=%p type=%hd\n", value, type);
654 
655   if (type != TYPE_XOBJECT) {
656     value = new(g) CONSTANT(g, value, type);
657     type = TYPE_XOBJECT;
658     } // endif type
659 
660   } // end of PlugConvertConstant
661 
662 /***********************************************************************/
663 /*  Call the Flex preparser to convert a date format to a sscanf input */
664 /*  format and a Strftime output format. Flag if not 0 indicates that  */
665 /*  non quoted blanks are not included in the output format.           */
666 /***********************************************************************/
MakeDateFormat(PGLOBAL g,PCSZ dfmt,bool in,bool out,int flag)667 PDTP MakeDateFormat(PGLOBAL g, PCSZ dfmt, bool in, bool out, int flag)
668 {
669 	int  rc;
670   PDTP pdp = (PDTP)PlugSubAlloc(g, NULL, sizeof(DATPAR));
671 
672   if (trace(1))
673     htrc("MakeDateFormat: dfmt=%s\n", dfmt);
674 
675   memset(pdp, 0, sizeof(DATPAR));
676   pdp->Format = pdp->Curp = PlugDup(g, dfmt);
677   pdp->Outsize = 2 * strlen(dfmt) + 1;
678 
679   if (in)
680     pdp->InFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
681 
682   if (out)
683     pdp->OutFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
684 
685   pdp->Flag = flag;
686 
687   /*********************************************************************/
688   /* Call the FLEX generated parser. In multi-threading mode the next  */
689   /* instruction is protected by mutex fmdflex using static variables. */
690   /*********************************************************************/
691   pthread_mutex_lock(&parmut);
692   rc = fmdflex(pdp);
693   pthread_mutex_unlock(&parmut);
694 
695   if (trace(1))
696     htrc("Done: in=%s out=%s rc=%d\n", SVP(pdp->InFmt), SVP(pdp->OutFmt), rc);
697 
698   return pdp;
699 } // end of MakeDateFormat
700 
701 /***********************************************************************/
702 /* Extract the date from a formatted string according to format.       */
703 /***********************************************************************/
ExtractDate(char * dts,PDTP pdp,int defy,int val[6])704 int ExtractDate(char *dts, PDTP pdp, int defy, int val[6])
705   {
706 	PCSZ fmt;
707 	char c, d, e, W[8][12];
708   int  i, k, m, numval;
709   int  n, y = 30;
710   bool b = true;           // true for null dates
711 
712   if (pdp)
713     fmt = pdp->InFmt;
714   else            // assume standard MySQL date format
715     fmt = "%4d-%2d-%2d %2d:%2d:%2d";
716 
717   if (trace(2))
718     htrc("ExtractDate: dts=%s fmt=%s defy=%d\n", dts, fmt, defy);
719 
720   // Set default values for time only use
721   if (defy) {
722     // This may be a default value for year
723     y = defy;
724     val[0] = y;
725     y = (y < 100) ? y : 30;
726   } else
727     val[0] = 70;
728 
729   val[1] = 1;
730   val[2] = 1;
731 
732   for (i = 3; i < 6; i++)
733     val[i] = 0;
734 
735   numval = 0;
736 
737   // Get the date field parse it with derived input format
738   m = sscanf(dts, fmt, W[0], W[1], W[2], W[3], W[4], W[5], W[6], W[7]);
739 
740   if (m > pdp->Num)
741     m = pdp->Num;
742 
743   for (i = 0; i < m; i++) {
744     if ((n = *(int*)W[i]))
745       b = false;
746 
747     switch (k = pdp->Index[i]) {
748       case 0:
749         if (n < y)
750           n += 100;
751 
752         val[0] = n;
753         numval = MY_MAX(numval, 1);
754         break;
755       case 1:
756       case 2:
757       case 3:
758       case 4:
759       case 5:
760         val[k] = n;
761         numval = MY_MAX(numval, k + 1);
762         break;
763       case -1:
764         c = toupper(W[i][0]);
765         d = toupper(W[i][1]);
766         e = toupper(W[i][2]);
767 
768         switch (c) {
769           case 'J':
770             n = (d == 'A') ? 1
771               : (e == 'N') ? 6 : 7; break;
772           case 'F': n =  2; break;
773           case 'M':
774             n = (e == 'R') ? 3 : 5; break;
775           case 'A':
776             n = (d == 'P') ? 4 : 8; break;
777             break;
778           case 'S': n =  9; break;
779           case 'O': n = 10; break;
780           case 'N': n = 11; break;
781           case 'D': n = 12; break;
782           } /* endswitch c */
783 
784         val[1] = n;
785         numval = MY_MAX(numval, 2);
786         break;
787       case -6:
788         c = toupper(W[i][0]);
789         n = val[3] % 12;
790 
791         if (c == 'P')
792           n += 12;
793 
794         val[3] = n;
795         break;
796       } // endswitch Plugpar
797 
798     } // endfor i
799 
800   if (trace(2))
801     htrc("numval=%d val=(%d,%d,%d,%d,%d,%d)\n",
802           numval, val[0], val[1], val[2], val[3], val[4], val[5]);
803 
804   return (b) ? 0 : numval;
805   } // end of ExtractDate
806 
807 /***********************************************************************/
808 /*  Open file routine: the purpose of this routine is to make a list   */
809 /*  of all open file so they can be closed in SQLINIT on error jump.   */
810 /***********************************************************************/
PlugOpenFile(PGLOBAL g,LPCSTR fname,LPCSTR ftype)811 FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype)
812   {
813   FILE     *fop;
814   PFBLOCK   fp;
815   PDBUSER   dbuserp = (PDBUSER)g->Activityp->Aptr;
816 
817   if (trace(1)) {
818     htrc("PlugOpenFile: fname=%s ftype=%s\n", fname, ftype);
819     htrc("dbuserp=%p\n", dbuserp);
820     } // endif trace
821 
822   if ((fop= global_fopen(g, MSGID_OPEN_MODE_STRERROR, fname, ftype)) != NULL) {
823     if (trace(1))
824       htrc(" fop=%p\n", fop);
825 
826     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
827 
828     if (trace(1))
829       htrc(" fp=%p\n", fp);
830 
831     // fname may be in volatile memory such as stack
832     fp->Fname = PlugDup(g, fname);
833     fp->Count = 1;
834     fp->Type = TYPE_FB_FILE;
835     fp->File = fop;
836     fp->Mode = MODE_ANY;                        // ???
837     fp->Next = dbuserp->Openlist;
838     dbuserp->Openlist = fp;
839     } /* endif fop */
840 
841   if (trace(1))
842     htrc(" returning fop=%p\n", fop);
843 
844   return (fop);
845   } // end of PlugOpenFile
846 
847 /***********************************************************************/
848 /*  Close file routine: the purpose of this routine is to avoid        */
849 /*  double closing that freeze the system on some Unix platforms.      */
850 /***********************************************************************/
PlugReopenFile(PGLOBAL g,PFBLOCK fp,LPCSTR md)851 FILE *PlugReopenFile(PGLOBAL g, PFBLOCK fp, LPCSTR md)
852   {
853   FILE *fop;
854 
855   if ((fop = global_fopen(g, MSGID_OPEN_MODE_STRERROR, fp->Fname, md))) {
856     fp->Count = 1;
857     fp->Type = TYPE_FB_FILE;
858     fp->File = fop;
859     } /* endif fop */
860 
861   return (fop);
862   } // end of PlugOpenFile
863 
864 /***********************************************************************/
865 /*  Close file routine: the purpose of this routine is to avoid        */
866 /*  double closing that freeze the system on some Unix platforms.      */
867 /***********************************************************************/
PlugCloseFile(PGLOBAL g,PFBLOCK fp,bool all)868 int PlugCloseFile(PGLOBAL g, PFBLOCK fp, bool all)
869   {
870   int rc = 0;
871 
872   if (trace(1))
873     htrc("PlugCloseFile: fp=%p count=%hd type=%hd\n",
874           fp, ((fp) ? fp->Count : 0), ((fp) ? fp->Type : 0));
875 
876   if (!fp || !fp->Count)
877     return rc;
878 
879   switch (fp->Type) {
880     case TYPE_FB_FILE:
881       if (fclose((FILE *)fp->File) == EOF)
882         rc = errno;
883 
884       fp->File = NULL;
885       fp->Mode = MODE_ANY;
886       fp->Count = 0;
887       break;
888     case TYPE_FB_MAP:
889       if ((fp->Count = (all) ? 0 : fp->Count - 1))
890         break;
891 
892       if (CloseMemMap(fp->Memory, fp->Length))
893         rc = (int)GetLastError();
894 
895       fp->Memory = NULL;
896       fp->Mode = MODE_ANY;
897       // fall through
898     case TYPE_FB_HANDLE:
899       if (fp->Handle && fp->Handle != INVALID_HANDLE_VALUE)
900         if (CloseFileHandle(fp->Handle))
901           rc = (rc) ? rc : (int)GetLastError();
902 
903       fp->Handle = INVALID_HANDLE_VALUE;
904       fp->Mode = MODE_ANY;
905       fp->Count = 0;
906       break;
907 #ifdef DOMDOC_SUPPORT
908     case TYPE_FB_XML:
909       CloseXMLFile(g, fp, all);
910       break;
911 #endif   // DOMDOC_SUPPORT
912 #ifdef LIBXML2_SUPPORT
913     case TYPE_FB_XML2:
914       CloseXML2File(g, fp, all);
915       break;
916 #endif   // LIBXML2_SUPPORT
917 #ifdef ODBC_SUPPORT
918 		case TYPE_FB_ODBC:
919 			OdbcClose(g, fp);
920 			fp->Count = 0;
921 			fp->File = NULL;
922 			break;
923 #endif   // ODBC_SUPPORT
924 #ifdef ZIP_SUPPORT
925 		case TYPE_FB_ZIP:
926 			if (fp->Mode == MODE_INSERT)
927 				((ZIPUTIL*)fp->File)->close();
928 			else
929 				((UNZIPUTL*)fp->File)->close();
930 
931 			fp->Memory = NULL;
932 			fp->Mode = MODE_ANY;
933 			fp->Count = 0;
934 			fp->File = NULL;
935 			break;
936 #endif   // ZIP_SUPPORT
937 #ifdef JAVA_SUPPORT
938 		case TYPE_FB_JAVA:
939 			((JAVAConn*)fp->File)->Close();
940 			fp->Count = 0;
941 			fp->File = NULL;
942 			break;
943 #endif   // JAVA_SUPPORT
944 #ifdef CMGO_SUPPORT
945 		case TYPE_FB_MONGO:
946 			((CMgoConn*)fp->File)->Close();
947 			fp->Count = 0;
948 			fp->File = NULL;
949 			break;
950 #endif   // JAVA_SUPPORT
951 		default:
952       rc = RC_FX;
953     } // endswitch Type
954 
955   return rc;
956   } // end of PlugCloseFile
957 
958 /***********************************************************************/
959 /*  PlugCleanup: Cleanup remaining items of a SQL query.               */
960 /***********************************************************************/
PlugCleanup(PGLOBAL g,bool dofree)961 void PlugCleanup(PGLOBAL g, bool dofree)
962   {
963   PCATLG  cat;
964   PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
965 
966   // The test on Catalog is to avoid a Windows bug that can make
967   // LoadString in PlugGetMessage to fail in some case
968   if (!dbuserp || !(cat = dbuserp->Catalog))
969     return;
970 
971   /*********************************************************************/
972   /*  Close eventually still open/mapped files.                        */
973   /*********************************************************************/
974   for (PFBLOCK fp = dbuserp->Openlist; fp; fp = fp->Next)
975     PlugCloseFile(g, fp, true);
976 
977   dbuserp->Openlist = NULL;
978 
979   if (dofree) {
980     /*******************************************************************/
981     /*  Cleanup any non suballocated memory still not freed.           */
982     /*******************************************************************/
983     for (PMBLOCK mp = dbuserp->Memlist; mp; mp = mp->Next)
984       PlgDBfree(*mp);
985 
986     dbuserp->Memlist = NULL;
987 
988     /*******************************************************************/
989     /*  If not using permanent storage catalog, reset volatile values. */
990     /*******************************************************************/
991     cat->Reset();
992 
993     /*******************************************************************/
994     /*  This is the place to reset the pointer on domains.             */
995     /*******************************************************************/
996     dbuserp->Subcor = false;
997     dbuserp->Step = "New query";     // was STEP(PARSING_QUERY);
998     dbuserp->ProgMax = dbuserp->ProgCur = dbuserp->ProgSav = 0;
999     } // endif dofree
1000 
1001   } // end of PlugCleanup
1002 
1003 #if 0
1004 /***********************************************************************/
1005 /*  That stupid Windows 98 does not provide this function.             */
1006 /***********************************************************************/
1007 bool WritePrivateProfileInt(LPCSTR sec, LPCSTR key, int n, LPCSTR ini)
1008   {
1009   char buf[12];
1010 
1011   sprintf(buf, "%d", n);
1012   return WritePrivateProfileString(sec, key, buf, ini);
1013   } // end of WritePrivateProfileInt
1014 
1015 /***********************************************************************/
1016 /*  Retrieve a size from an INI file with eventual K or M following.   */
1017 /***********************************************************************/
1018 int GetIniSize(char *section, char *key, char *def, char *ini)
1019   {
1020   char c, buff[32];
1021   int  i;
1022   int  n = 0;
1023 
1024   GetPrivateProfileString(section, key, def, buff, sizeof(buff), ini);
1025 
1026   if ((i = sscanf(buff, " %d %c ", &n, &c)) == 2)
1027     switch (toupper(c)) {
1028       case 'M':
1029         n *= 1024;
1030       case 'K':
1031         n *= 1024;
1032       } // endswitch c
1033 
1034   if (trace(1))
1035     htrc("GetIniSize: key=%s buff=%s i=%d n=%d\n", key, buff, i, n);
1036 
1037   return n;
1038   } // end of GetIniSize
1039 
1040 /***********************************************************************/
1041 /* Allocate a string retrieved from an INI file and return its address */
1042 /***********************************************************************/
1043 DllExport PSZ GetIniString(PGLOBAL g, void *mp, LPCSTR sec, LPCSTR key,
1044                                                 LPCSTR def, LPCSTR ini)
1045   {
1046   char  buff[_MAX_PATH];
1047   PSZ   p;
1048   int   n, m = sizeof(buff);
1049   char *buf = buff;
1050 
1051 #if defined(_DEBUG)
1052   assert (sec && key);
1053 #endif
1054 
1055  again:
1056   n = GetPrivateProfileString(sec, key, def, buf, m, ini);
1057 
1058   if (n == m - 1) {
1059     // String may have been truncated, make sure to have all
1060     if (buf != buff)
1061       delete [] buf;
1062 
1063     m *= 2;
1064     buf = new char[m];
1065     goto again;
1066     } // endif n
1067 
1068   p = (PSZ)PlugSubAlloc(g, mp, n + 1);
1069 
1070   if (trace(1))
1071     htrc("GetIniString: sec=%s key=%s buf=%s\n", sec, key, buf);
1072 
1073   strcpy(p, buf);
1074 
1075   if (buf != buff)
1076     delete [] buf;
1077 
1078   return p;
1079   } // end of GetIniString
1080 #endif // 0
1081 
1082 /***********************************************************************/
1083 /*  GetAmName: return the name correponding to an AM code.             */
1084 /***********************************************************************/
GetAmName(PGLOBAL g,AMT am,void * memp)1085 char *GetAmName(PGLOBAL g, AMT am, void *memp)
1086   {
1087   char *amn= (char*)PlugSubAlloc(g, memp, 16);
1088 
1089   switch (am) {
1090     case TYPE_AM_ERROR: strcpy(amn, "ERROR"); break;
1091     case TYPE_AM_ROWID: strcpy(amn, "ROWID"); break;
1092     case TYPE_AM_FILID: strcpy(amn, "FILID"); break;
1093     case TYPE_AM_VIEW:  strcpy(amn, "VIEW");  break;
1094     case TYPE_AM_COUNT: strcpy(amn, "COUNT"); break;
1095     case TYPE_AM_DCD:   strcpy(amn, "DCD");   break;
1096     case TYPE_AM_CMS:   strcpy(amn, "CMS");   break;
1097     case TYPE_AM_MAP:   strcpy(amn, "MAP");   break;
1098     case TYPE_AM_FMT:   strcpy(amn, "FMT");   break;
1099     case TYPE_AM_CSV:   strcpy(amn, "CSV");   break;
1100     case TYPE_AM_MCV:   strcpy(amn, "MCV");   break;
1101     case TYPE_AM_DOS:   strcpy(amn, "DOS");   break;
1102     case TYPE_AM_FIX:   strcpy(amn, "FIX");   break;
1103     case TYPE_AM_BIN:   strcpy(amn, "BIN");   break;
1104     case TYPE_AM_VCT:   strcpy(amn, "VEC");   break;
1105     case TYPE_AM_VMP:   strcpy(amn, "VMP");   break;
1106     case TYPE_AM_DBF:   strcpy(amn, "DBF");   break;
1107     case TYPE_AM_QRY:   strcpy(amn, "QRY");   break;
1108     case TYPE_AM_SQL:   strcpy(amn, "SQL");   break;
1109     case TYPE_AM_PLG:   strcpy(amn, "PLG");   break;
1110     case TYPE_AM_PLM:   strcpy(amn, "PLM");   break;
1111     case TYPE_AM_DOM:   strcpy(amn, "DOM");   break;
1112     case TYPE_AM_DIR:   strcpy(amn, "DIR");   break;
1113     case TYPE_AM_ODBC:  strcpy(amn, "ODBC");  break;
1114 		case TYPE_AM_JDBC:  strcpy(amn, "JDBC");  break;
1115 		case TYPE_AM_MAC:   strcpy(amn, "MAC");   break;
1116     case TYPE_AM_OEM:   strcpy(amn, "OEM");   break;
1117     case TYPE_AM_OUT:   strcpy(amn, "OUT");   break;
1118     default:           sprintf(amn, "OEM(%d)", am);
1119     } // endswitch am
1120 
1121   return amn;
1122   } // end of GetAmName
1123 
1124 #if defined(SE_CATCH)
1125 /***********************************************************************/
1126 /*  GetExceptionDesc: return the description of an exception code.     */
1127 /***********************************************************************/
GetExceptionDesc(PGLOBAL g,unsigned int e)1128 char *GetExceptionDesc(PGLOBAL g, unsigned int e)
1129   {
1130   char *p;
1131 
1132   switch (e) {
1133     case EXCEPTION_GUARD_PAGE:
1134       p = MSG(GUARD_PAGE);
1135       break;
1136     case EXCEPTION_DATATYPE_MISALIGNMENT:
1137       p = MSG(DATA_MISALIGN);
1138       break;
1139     case EXCEPTION_BREAKPOINT:
1140       p = MSG(BREAKPOINT);
1141       break;
1142     case EXCEPTION_SINGLE_STEP:
1143       p = MSG(SINGLE_STEP);
1144       break;
1145     case EXCEPTION_ACCESS_VIOLATION:
1146       p = MSG(ACCESS_VIOLATN);
1147       break;
1148     case EXCEPTION_IN_PAGE_ERROR:
1149       p = MSG(PAGE_ERROR);
1150       break;
1151     case EXCEPTION_INVALID_HANDLE:
1152       p = MSG(INVALID_HANDLE);
1153       break;
1154     case EXCEPTION_ILLEGAL_INSTRUCTION:
1155       p = MSG(ILLEGAL_INSTR);
1156       break;
1157     case EXCEPTION_NONCONTINUABLE_EXCEPTION:
1158       p = MSG(NONCONT_EXCEPT);
1159       break;
1160     case EXCEPTION_INVALID_DISPOSITION:
1161       p = MSG(INVALID_DISP);
1162       break;
1163     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
1164       p = MSG(ARRAY_BNDS_EXCD);
1165       break;
1166     case EXCEPTION_FLT_DENORMAL_OPERAND:
1167       p = MSG(FLT_DENORMAL_OP);
1168       break;
1169     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
1170       p = MSG(FLT_ZERO_DIVIDE);
1171       break;
1172     case EXCEPTION_FLT_INEXACT_RESULT:
1173       p = MSG(FLT_BAD_RESULT);
1174       break;
1175     case EXCEPTION_FLT_INVALID_OPERATION:
1176       p = MSG(FLT_INVALID_OP);
1177       break;
1178     case EXCEPTION_FLT_OVERFLOW:
1179       p = MSG(FLT_OVERFLOW);
1180       break;
1181     case EXCEPTION_FLT_STACK_CHECK:
1182       p = MSG(FLT_STACK_CHECK);
1183       break;
1184     case EXCEPTION_FLT_UNDERFLOW:
1185       p = MSG(FLT_UNDERFLOW);
1186       break;
1187     case EXCEPTION_INT_DIVIDE_BY_ZERO:
1188       p = MSG(INT_ZERO_DIVIDE);
1189       break;
1190     case EXCEPTION_INT_OVERFLOW:
1191       p = MSG(INT_OVERFLOW);
1192       break;
1193     case EXCEPTION_PRIV_INSTRUCTION:
1194       p = MSG(PRIV_INSTR);
1195       break;
1196     case EXCEPTION_STACK_OVERFLOW:
1197       p = MSG(STACK_OVERFLOW);
1198       break;
1199     case CONTROL_C_EXIT:
1200       p = MSG(CONTROL_C_EXIT);
1201       break;
1202     case STATUS_NO_MEMORY:
1203       p = MSG(NO_MEMORY);
1204       break;
1205     default:
1206       p = MSG(UNKNOWN_EXCPT);
1207       break;
1208     } // endswitch nSE
1209 
1210   return p;
1211   } // end of GetExceptionDesc
1212 #endif   // SE_CATCH
1213 
1214 /***********************************************************************/
1215 /*  PlgDBalloc: allocates or suballocates memory conditionally.        */
1216 /*  If mp.Sub is true at entry, this forces suballocation.             */
1217 /*  If the memory is allocated, makes an entry in an allocation list   */
1218 /*  so it can be freed at the normal or error query completion.        */
1219 /***********************************************************************/
PlgDBalloc(PGLOBAL g,void * area,MBLOCK & mp)1220 void *PlgDBalloc(PGLOBAL g, void *area, MBLOCK& mp)
1221 {
1222 //bool        b;
1223   size_t      maxsub, minsub;
1224   void       *arp = (area) ? area : g->Sarea;
1225   PPOOLHEADER pph = (PPOOLHEADER)arp;
1226 
1227   if (mp.Memp) {
1228     // This is a reallocation. If this block is not suballocated, it
1229     // was already placed in the chain of memory blocks and we must
1230     // not do it again as it can trigger a loop when freeing them.
1231     // Note: this works if blocks can be reallocated only once.
1232     // Otherwise a new boolean must be added to the block that
1233     // indicate that it is chained, or a test on the whole chain be
1234     // done to check whether the block is already there.
1235 //  b = mp.Sub;
1236     mp.Sub = false;    // Restrict suballocation to one quarter
1237   } // endif Memp
1238 
1239   // Suballoc when possible if mp.Sub is initially true, but leaving
1240   // a minimum amount of storage for future operations such as the
1241   // optimize recalculation after insert; otherwise
1242   // suballoc only if size is smaller than one quarter of free mem.
1243   minsub = (pph->FreeBlk + pph->To_Free + 524248) >> 2;
1244   maxsub = (pph->FreeBlk < minsub) ? 0 : pph->FreeBlk - minsub;
1245   mp.Sub = mp.Size <= ((mp.Sub) ? maxsub : (maxsub >> 2));
1246 
1247 	if (trace(2))
1248 		htrc("PlgDBalloc: in %p size=%zd used=%zd free=%zd sub=%d\n",
1249 			arp, mp.Size, pph->To_Free, pph->FreeBlk, mp.Sub);
1250 
1251 	if (!mp.Sub) {
1252     // For allocations greater than one fourth of remaining storage
1253     // in the area, do allocate from virtual storage.
1254 		const char*v = "malloc";
1255 #if defined(_WIN32)
1256 		if (mp.Size >= BIGMEM) {
1257 			v = "VirtualAlloc";
1258 			mp.Memp = VirtualAlloc(NULL, mp.Size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
1259 		} else
1260 #endif
1261       mp.Memp = malloc(mp.Size);
1262 
1263 		if (trace(8))
1264 			htrc("PlgDBalloc: %s(%zd) at %p\n", v, mp.Size, mp.Memp);
1265 
1266 		if (!mp.Inlist && mp.Memp) {
1267       // New allocated block, put it in the memory block chain.
1268       PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
1269 
1270       mp.Next = dbuserp->Memlist;
1271       dbuserp->Memlist = &mp;
1272       mp.Inlist = true;
1273     } // endif mp
1274 
1275   } else
1276     // Suballocating is Ok.
1277     mp.Memp = PlugSubAlloc(g, area, mp.Size);
1278 
1279   return mp.Memp;
1280 } // end of PlgDBalloc
1281 
1282 /***********************************************************************/
1283 /*  PlgDBrealloc: reallocates memory conditionally.                    */
1284 /*  Note that this routine can fail only when block size is increased  */
1285 /*  because otherwise we keep the old storage on failure.              */
1286 /***********************************************************************/
PlgDBrealloc(PGLOBAL g,void * area,MBLOCK & mp,size_t newsize)1287 void *PlgDBrealloc(PGLOBAL g, void *area, MBLOCK& mp, size_t newsize)
1288   {
1289   MBLOCK m;
1290 
1291 #if defined(_DEBUG)
1292 //  assert (mp.Memp != NULL);
1293 #endif
1294 
1295   if (trace(2))
1296     htrc("PlgDBrealloc: %p size=%zd sub=%d\n", mp.Memp, mp.Size, mp.Sub);
1297 
1298   if (newsize == mp.Size)
1299     return mp.Memp;      // Nothing to do
1300   else
1301     m = mp;
1302 
1303   if (!mp.Sub && mp.Size < BIGMEM && newsize < BIGMEM) {
1304     // Allocation was done by malloc, try to use realloc but
1305     // suballoc if newsize is smaller than one quarter of free mem.
1306     size_t      maxsub;
1307     PPOOLHEADER pph = (PPOOLHEADER)((area) ? area : g->Sarea);
1308 
1309     maxsub = (pph->FreeBlk < 131072) ? 0 : pph->FreeBlk - 131072;
1310 
1311     if ((mp.Sub = (newsize <= (maxsub >> 2)))) {
1312       mp.Memp = PlugSubAlloc(g, area, newsize);
1313       memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
1314       PlgDBfree(m);    // Free the old block
1315     } else {
1316 			if (!(mp.Memp = realloc(mp.Memp, newsize))) {
1317 				mp = m;          // Possible only if newsize > Size
1318 				return NULL;     // Failed
1319 			} else if (trace(8))
1320 				htrc("PlgDBrealloc: realloc(%ld) at %p\n", newsize, mp.Memp);
1321 
1322 		} // endif's
1323 
1324     mp.Size = newsize;
1325   } else if (!mp.Sub || newsize > mp.Size) {
1326     // Was suballocated or Allocation was done by VirtualAlloc
1327     // Make a new allocation and copy the useful part
1328     // Note: DO NOT reset Memp and Sub so we know that this
1329     // is a reallocation in PlgDBalloc
1330     mp.Size = newsize;
1331 
1332     if (PlgDBalloc(g, area, mp)) {
1333       memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
1334       PlgDBfree(m);    // Free the old block
1335     } else {
1336       mp = m;          // No space to realloc, do nothing
1337 
1338       if (newsize > m.Size)
1339         return NULL;   // Failed
1340 
1341     } // endif PlgDBalloc
1342 
1343   } // endif's
1344 
1345   if (trace(8))
1346     htrc(" newsize=%zd newp=%p sub=%d\n", mp.Size, mp.Memp, mp.Sub);
1347 
1348   return mp.Memp;
1349   } // end of PlgDBrealloc
1350 
1351 /***********************************************************************/
1352 /*  PlgDBfree: free memory if not suballocated.                        */
1353 /***********************************************************************/
PlgDBfree(MBLOCK & mp)1354 void PlgDBfree(MBLOCK& mp)
1355   {
1356 	if (!mp.Sub && mp.Memp) {
1357 		const char*v = "free";
1358 #if defined(_WIN32)
1359 		if (mp.Size >= BIGMEM) {
1360 			v = "VirtualFree";
1361 			VirtualFree(mp.Memp, 0, MEM_RELEASE);
1362 		} else
1363 #endif
1364 			free(mp.Memp);
1365 
1366 		if (trace(8))
1367 			htrc("PlgDBfree: %s(%p) size=%d\n", v, mp.Memp, mp.Size);
1368 
1369 	}	// endif mp
1370 
1371   // Do not reset Next to avoid cutting the Mblock chain
1372   mp.Memp = NULL;
1373   mp.Sub = false;
1374   mp.Size = 0;
1375   } // end of PlgDBfree
1376 
1377 /***********************************************************************/
1378 /*  Program for sub-allocating one item in a storage area.             */
1379 /*  Note: This function is equivalent to PlugSubAlloc except that in   */
1380 /*  case of insufficient memory, it returns NULL instead of doing a    */
1381 /*  throw. The caller must test the return value for error.            */
1382 /***********************************************************************/
PlgDBSubAlloc(PGLOBAL g,void * memp,size_t size)1383 void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size)
1384   {
1385   PPOOLHEADER pph;                           // Points on area header.
1386 
1387   if (!memp)
1388     /*******************************************************************/
1389     /*  Allocation is to be done in the Sarea.                         */
1390     /*******************************************************************/
1391     memp = g->Sarea;
1392 
1393 //size = ((size + 3) / 4) * 4;       /* Round up size to multiple of 4 */
1394   size = ((size + 7) / 8) * 8;       /* Round up size to multiple of 8 */
1395   pph = (PPOOLHEADER)memp;
1396 
1397   if (trace(16))
1398     htrc("PlgDBSubAlloc: memp=%p size=%zd used=%zd free=%zd\n",
1399          memp, size, pph->To_Free, pph->FreeBlk);
1400 
1401   if (size > pph->FreeBlk) {   /* Not enough memory left in pool */
1402     sprintf(g->Message,
1403     "Not enough memory in Work area for request of %zd (used=%zd free=%zd)",
1404             size, pph->To_Free, pph->FreeBlk);
1405 
1406     if (trace(1))
1407       htrc("%s\n", g->Message);
1408 
1409     return NULL;
1410     } // endif size
1411 
1412   /*********************************************************************/
1413   /*  Do the suballocation the simplest way.                           */
1414   /*********************************************************************/
1415   memp = MakePtr(memp, pph->To_Free);   // Points to suballocated block
1416   pph->To_Free += size;                 // New offset of pool free block
1417   pph->FreeBlk -= size;                 // New size   of pool free block
1418 
1419   if (trace(16))
1420     htrc("Done memp=%p used=%zd free=%zd\n",
1421          memp, pph->To_Free, pph->FreeBlk);
1422 
1423   return (memp);
1424   } // end of PlgDBSubAlloc
1425 
1426 /***********************************************************************/
1427 /*  Program for sub-allocating and copying a string in a storage area. */
1428 /***********************************************************************/
PlgDBDup(PGLOBAL g,const char * str)1429 char *PlgDBDup(PGLOBAL g, const char *str)
1430   {
1431   if (str) {
1432     char *sm = (char*)PlgDBSubAlloc(g, NULL, strlen(str) + 1);
1433 
1434     if (sm)
1435       strcpy(sm, str);
1436 
1437     return sm;
1438   } else
1439     return NULL;
1440 
1441   } // end of PlgDBDup
1442 
1443 /***********************************************************************/
1444 /*  PUTOUT: Plug DB object typing routine.                             */
1445 /***********************************************************************/
PlugPutOut(PGLOBAL g,FILE * f,short t,void * v,uint n)1446 void PlugPutOut(PGLOBAL g, FILE *f, short t, void *v, uint n)
1447   {
1448   char  m[64];
1449 
1450   if (trace(1))
1451     htrc("PUTOUT: f=%p t=%d v=%p n=%d\n", f, t, v, n);
1452 
1453   if (!v)
1454     return;
1455 
1456   memset(m, ' ', n);                             /* Make margin string */
1457   m[n] = '\0';
1458   n += 2;                                        /* Increase margin    */
1459 
1460   switch (t) {
1461     case TYPE_ERROR:
1462       fprintf(f, "--> %s\n", (PSZ)v);
1463       break;
1464 
1465     case TYPE_STRING:
1466     case TYPE_PSZ:
1467       fprintf(f, "%s%s\n", m, (PSZ)v);
1468       break;
1469 
1470     case TYPE_DOUBLE:
1471       fprintf(f, "%s%lf\n", m, *(double *)v);
1472       break;
1473 
1474     case TYPE_LIST:
1475     case TYPE_COLIST:
1476     case TYPE_COL:
1477      {PPARM p;
1478 
1479       if (t == TYPE_LIST)
1480         fprintf(f, "%s%s\n", m, MSG(LIST));
1481       else
1482         fprintf(f, "%s%s\n", m, "Colist:");
1483 
1484       for (p = (PPARM)v; p; p = p->Next)
1485         PlugPutOut(g, f, p->Type, p->Value, n);
1486 
1487       } break;
1488 
1489     case TYPE_INT:
1490       fprintf(f, "%s%d\n", m, *(int *)v);
1491       break;
1492 
1493     case TYPE_SHORT:
1494       fprintf(f, "%s%hd\n", m, *(short *)v);
1495       break;
1496 
1497     case TYPE_TINY:
1498       fprintf(f, "%s%d\n", m, (int)*(char *)v);
1499       break;
1500 
1501     case TYPE_VOID:
1502       break;
1503 
1504     case TYPE_SQL:
1505     case TYPE_TABLE:
1506     case TYPE_TDB:
1507     case TYPE_XOBJECT:
1508       ((PBLOCK)v)->Printf(g, f, n-2);
1509       break;
1510 
1511     default:
1512       fprintf(f, "%s%s %d\n", m, MSG(ANSWER_TYPE), t);
1513     } /* endswitch */
1514 
1515   return;
1516   } /* end of PlugPutOut */
1517 
1518 /***********************************************************************/
1519 /*  NewPointer: makes a table of pointer values to be changed later.   */
1520 /***********************************************************************/
NewPointer(PTABS t,void * oldv,void * newv)1521 DllExport void NewPointer(PTABS t, void *oldv, void *newv)
1522   {
1523   PTABPTR tp;
1524 
1525   if (!oldv)                                       /* error ?????????? */
1526     return;
1527 
1528   if (!t->P1 || t->P1->Num == 50)
1529   {
1530     if (!(tp = new TABPTR)) {
1531       PGLOBAL g = t->G;
1532 
1533       sprintf(g->Message, "NewPointer: %s", MSG(MEM_ALLOC_ERROR));
1534 			throw 3;
1535 		} else {
1536       tp->Next = t->P1;
1537       tp->Num = 0;
1538       t->P1 = tp;
1539     } /* endif tp */
1540   }
1541 
1542   t->P1->Old[t->P1->Num] = oldv;
1543   t->P1->New[t->P1->Num++] = newv;
1544   } /* end of NewPointer */
1545 
1546 #if 0
1547 /***********************************************************************/
1548 /*  Compare two files and return 0 if they are identical, else 1.      */
1549 /***********************************************************************/
1550 int FileComp(PGLOBAL g, char *file1, char *file2)
1551   {
1552   char *fn[2], *bp[2], buff1[4096], buff2[4096];
1553   int   i, k, n[2], h[2] = {-1,-1};
1554   int  len[2], rc = -1;
1555 
1556   fn[0] = file1; fn[1] = file2;
1557   bp[0] = buff1; bp[1] = buff2;
1558 
1559   for (i = 0; i < 2; i++) {
1560 #if defined(_WIN32)
1561     h[i]= global_open(g, MSGID_NONE, fn[i], _O_RDONLY | _O_BINARY);
1562 #else   // !_WIN32
1563     h[i]= global_open(g, MSGOD_NONE, fn[i], O_RDONLY);
1564 #endif  // !_WIN32
1565 
1566     if (h[i] == -1) {
1567 //      if (errno != ENOENT) {
1568         sprintf(g->Message, MSG(OPEN_MODE_ERROR),
1569                 "rb", (int)errno, fn[i]);
1570         strcat(strcat(g->Message, ": "), strerror(errno));
1571 				throw 666;
1572 				//      } else
1573 //        len[i] = 0;          // File does not exist yet
1574 
1575     } else {
1576       if ((len[i] = _filelength(h[i])) < 0) {
1577         sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", fn[i]);
1578 				throw 666;
1579 			} // endif len
1580 
1581     } // endif h
1582 
1583     } // endfor i
1584 
1585   if (len[0] != len[1])
1586     rc = 1;
1587 
1588   while (rc == -1) {
1589     for (i = 0; i < 2; i++)
1590       if ((n[i] = read(h[i], bp[i], 4096)) < 0) {
1591         sprintf(g->Message, MSG(READ_ERROR), fn[i], strerror(errno));
1592         goto fin;
1593         } // endif n
1594 
1595     if (n[0] != n[1])
1596       rc = 1;
1597     else if (*n == 0)
1598       rc = 0;
1599     else for (k = 0; k < *n; k++)
1600       if (*(bp[0] + k) != *(bp[1] + k)) {
1601         rc = 1;
1602         goto fin;
1603         } // endif bp
1604 
1605     } // endwhile
1606 
1607  fin:
1608   for (i = 0; i < 2; i++)
1609     if (h[i] != -1)
1610       close(h[i]);
1611 
1612   return rc;
1613   } // end of FileComp
1614 #endif // 0
1615