mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
2ff01e763e
Old style C functions `strcpy()`, `strcat()` and `sprintf()` are vulnerable to security issues due to lacking memory boundary checks. Replace these in the Connect storage engine with safe new and/or custom functions such as `snprintf()` `safe_strcpy()` and `safe_strcat()`. With this change FlawFinder and other static security analyzers report 287 fewer findings. All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc.
646 lines
21 KiB
C++
646 lines
21 KiB
C++
/************** PlugUtil C Program Source Code File (.C) ***************/
|
|
/* */
|
|
/* PROGRAM NAME: PLUGUTIL */
|
|
/* ------------- */
|
|
/* Version 3.1 */
|
|
/* */
|
|
/* COPYRIGHT: */
|
|
/* ---------- */
|
|
/* (C) Copyright to the author Olivier BERTRAND 1993-2020 */
|
|
/* */
|
|
/* WHAT THIS PROGRAM DOES: */
|
|
/* ----------------------- */
|
|
/* This program are initialization and utility Plug routines. */
|
|
/* */
|
|
/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
|
|
/* -------------------------------------- */
|
|
/* */
|
|
/* REQUIRED FILES: */
|
|
/* --------------- */
|
|
/* See Readme.C for a list and description of required SYSTEM files. */
|
|
/* */
|
|
/* PLUG.C - Source code */
|
|
/* GLOBAL.H - Global declaration file */
|
|
/* OPTION.H - Option declaration file */
|
|
/* */
|
|
/* REQUIRED LIBRARIES: */
|
|
/* ------------------- */
|
|
/* */
|
|
/* OS2.LIB - OS2 libray */
|
|
/* LLIBCE.LIB - Protect mode/standard combined large model C */
|
|
/* library */
|
|
/* */
|
|
/* REQUIRED PROGRAMS: */
|
|
/* ------------------ */
|
|
/* */
|
|
/* IBM C Compiler */
|
|
/* IBM Linker */
|
|
/* */
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
/* */
|
|
/* Include relevant MariaDB header file. */
|
|
/* */
|
|
/***********************************************************************/
|
|
#include "my_global.h"
|
|
#if defined(_WIN32)
|
|
//#include <windows.h>
|
|
#else
|
|
#if defined(UNIX) || defined(UNIV_LINUX)
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
//#define __stdcall
|
|
#else
|
|
#include <dir.h>
|
|
#endif
|
|
#include <stdarg.h>
|
|
#endif
|
|
|
|
#if defined(WIN)
|
|
#include <alloc.h>
|
|
#endif
|
|
#include <errno.h> /* definitions of ERANGE ENOMEM */
|
|
#if !defined(UNIX) && !defined(UNIV_LINUX)
|
|
#include <direct.h> /* Directory management library */
|
|
#endif
|
|
|
|
/***********************************************************************/
|
|
/* */
|
|
/* Include application header files */
|
|
/* */
|
|
/* global.h is header containing all global declarations. */
|
|
/* */
|
|
/***********************************************************************/
|
|
#define STORAGE /* Initialize global variables */
|
|
|
|
#include "m_string.h"
|
|
#include "osutil.h"
|
|
#include "global.h"
|
|
#include "plgdbsem.h"
|
|
#if defined(NEWMSG)
|
|
#include "rcmsg.h"
|
|
#endif // NEWMSG
|
|
|
|
#if defined(_WIN32)
|
|
extern HINSTANCE s_hModule; /* Saved module handle */
|
|
#endif // _WIN32
|
|
|
|
#if defined(XMSG)
|
|
extern char *msg_path;
|
|
char *msglang(void);
|
|
#endif // XMSG
|
|
|
|
/***********************************************************************/
|
|
/* Local Definitions and static variables */
|
|
/***********************************************************************/
|
|
typedef struct {
|
|
ushort Segsize;
|
|
ushort Size;
|
|
} AREASIZE;
|
|
|
|
ACTIVITY defActivity = { /* Describes activity and language */
|
|
NULL, /* Points to user work area(s) */
|
|
"Unknown"}; /* Application name */
|
|
|
|
#if defined(XMSG) || defined(NEWMSG)
|
|
static char stmsg[200];
|
|
#endif // XMSG || NEWMSG
|
|
|
|
#if defined(UNIX) || defined(UNIV_LINUX)
|
|
#include "rcmsg.h"
|
|
#endif // UNIX
|
|
|
|
/**************************************************************************/
|
|
/* Conditional tracing output function. */
|
|
/**************************************************************************/
|
|
void xtrc(uint x, char const *fmt, ...)
|
|
{
|
|
if (GetTraceValue() & x) {
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
} // endif x
|
|
|
|
} // end of xtrc
|
|
|
|
/**************************************************************************/
|
|
/* Tracing output function. */
|
|
/**************************************************************************/
|
|
void htrc(char const* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
} // end of htrc
|
|
|
|
/***********************************************************************/
|
|
/* Plug initialization routine. */
|
|
/* Language points on initial language name and eventual path. */
|
|
/* Return value is the pointer to the Global structure. */
|
|
/***********************************************************************/
|
|
PGLOBAL PlugInit(LPCSTR Language, size_t worksize)
|
|
{
|
|
PGLOBAL g;
|
|
|
|
if (trace(2))
|
|
htrc("PlugInit: Language='%-.256s'\n",
|
|
((!Language) ? "Null" : (char*)Language));
|
|
|
|
try {
|
|
g = new GLOBAL;
|
|
} catch (...) {
|
|
fprintf(stderr, MSG(GLOBAL_ERROR), (int)sizeof(GLOBAL));
|
|
return NULL;
|
|
} // end try/catch
|
|
|
|
g->Sarea = NULL;
|
|
g->Createas = false;
|
|
g->Alchecked = 0;
|
|
g->Mrr = 0;
|
|
g->Activityp = NULL;
|
|
g->Xchk = NULL;
|
|
g->N = 0;
|
|
g->More = 0;
|
|
g->Saved_Size = 0;
|
|
strcpy(g->Message, "");
|
|
|
|
/*******************************************************************/
|
|
/* Allocate the main work segment. */
|
|
/*******************************************************************/
|
|
if (worksize && AllocSarea(g, worksize)) {
|
|
char errmsg[MAX_STR];
|
|
snprintf(errmsg, sizeof(errmsg) - 1, MSG(WORK_AREA), g->Message);
|
|
strcpy(g->Message, errmsg);
|
|
} // endif Sarea
|
|
|
|
g->jump_level = -1; /* New setting to allow recursive call of Plug */
|
|
return(g);
|
|
} /* end of PlugInit */
|
|
|
|
/***********************************************************************/
|
|
/* PlugExit: Terminate Plug operations. */
|
|
/***********************************************************************/
|
|
PGLOBAL PlugExit(PGLOBAL g)
|
|
{
|
|
if (g) {
|
|
PDBUSER dup = PlgGetUser(g);
|
|
|
|
if (dup)
|
|
free(dup);
|
|
|
|
FreeSarea(g);
|
|
delete g;
|
|
} // endif g
|
|
|
|
return NULL;
|
|
} // end of PlugExit
|
|
|
|
/***********************************************************************/
|
|
/* Remove the file type from a file name. */
|
|
/* Note: this routine is not really implemented for Unix. */
|
|
/***********************************************************************/
|
|
LPSTR PlugRemoveType(LPSTR pBuff, LPCSTR FileName)
|
|
{
|
|
#if defined(_WIN32)
|
|
char drive[_MAX_DRIVE];
|
|
#else
|
|
char *drive = NULL;
|
|
#endif
|
|
char direc[_MAX_DIR];
|
|
char fname[_MAX_FNAME];
|
|
char ftype[_MAX_EXT];
|
|
|
|
_splitpath(FileName, drive, direc, fname, ftype);
|
|
|
|
if (trace(2)) {
|
|
htrc("after _splitpath: FileName=%-.256s\n", FileName);
|
|
htrc("drive=%-.256s dir=%-.256s fname=%-.256s ext=%-.256s\n",
|
|
SVP(drive), direc, fname, ftype);
|
|
} // endif trace
|
|
|
|
_makepath(pBuff, drive, direc, fname, "");
|
|
|
|
if (trace(2))
|
|
htrc("buff='%-.256s'\n", pBuff);
|
|
|
|
return pBuff;
|
|
} // end of PlugRemoveType
|
|
|
|
BOOL PlugIsAbsolutePath(LPCSTR path)
|
|
{
|
|
#if defined(_WIN32)
|
|
return ((path[0] >= 'a' && path[0] <= 'z') ||
|
|
(path[0] >= 'A' && path[0] <= 'Z')) && path[1] == ':';
|
|
#else
|
|
return path[0] == '/';
|
|
#endif
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* Set the full path of a file relatively to a given path. */
|
|
/* Note: this routine is not really implemented for Unix. */
|
|
/***********************************************************************/
|
|
LPCSTR PlugSetPath(LPSTR pBuff, LPCSTR prefix, LPCSTR FileName, LPCSTR defpath)
|
|
{
|
|
char newname[_MAX_PATH];
|
|
char direc[_MAX_DIR], defdir[_MAX_DIR], tmpdir[_MAX_DIR];
|
|
char fname[_MAX_FNAME];
|
|
char ftype[_MAX_EXT];
|
|
#if defined(_WIN32)
|
|
char drive[_MAX_DRIVE], defdrv[_MAX_DRIVE];
|
|
#else
|
|
char *drive = NULL, *defdrv = NULL;
|
|
#endif
|
|
|
|
if (trace(2))
|
|
htrc("prefix=%-.256s fn=%-.256s path=%-.256s\n", prefix, FileName, defpath);
|
|
|
|
if (strlen(FileName) >= _MAX_PATH)
|
|
{
|
|
*pBuff= 0; /* Hope this is treated as error of some kind*/
|
|
return FileName;
|
|
}
|
|
|
|
if (!strncmp(FileName, "//", 2) || !strncmp(FileName, "\\\\", 2)) {
|
|
strcpy(pBuff, FileName); // Remote file
|
|
return pBuff;
|
|
} // endif
|
|
|
|
if (PlugIsAbsolutePath(FileName))
|
|
{
|
|
strcpy(pBuff, FileName); // FileName includes absolute path
|
|
return pBuff;
|
|
} // endif
|
|
|
|
#if !defined(_WIN32)
|
|
if (*FileName == '~') {
|
|
if (_fullpath(pBuff, FileName, _MAX_PATH)) {
|
|
if (trace(2))
|
|
htrc("pbuff='%-.256s'\n", pBuff);
|
|
|
|
return pBuff;
|
|
} else
|
|
return FileName; // Error, return unchanged name
|
|
|
|
} // endif FileName
|
|
#endif // !_WIN32
|
|
|
|
if (prefix && strcmp(prefix, ".") && !PlugIsAbsolutePath(defpath))
|
|
{
|
|
char tmp[_MAX_PATH];
|
|
int len= snprintf(tmp, sizeof(tmp) - 1, "%s%s%s",
|
|
prefix, defpath, FileName);
|
|
memcpy(pBuff, tmp, (size_t) len);
|
|
pBuff[len]= '\0';
|
|
return pBuff;
|
|
}
|
|
|
|
_splitpath(FileName, drive, direc, fname, ftype);
|
|
|
|
if (defpath) {
|
|
char c = defpath[strlen(defpath) - 1];
|
|
|
|
strcpy(tmpdir, defpath);
|
|
|
|
if (c != '/' && c != '\\')
|
|
strcat(tmpdir, "/");
|
|
|
|
} else
|
|
strcpy(tmpdir, "./");
|
|
|
|
_splitpath(tmpdir, defdrv, defdir, NULL, NULL);
|
|
|
|
if (trace(2)) {
|
|
htrc("after _splitpath: FileName=%-.256s\n", FileName);
|
|
#if defined(_WIN32)
|
|
htrc("drive=%-.256s dir=%-.256s fname=%-.256s ext=%-.256s\n", drive, direc, fname, ftype);
|
|
htrc("defdrv=%-.256s defdir=%-.256s\n", defdrv, defdir);
|
|
#else
|
|
htrc("dir=%-.256s fname=%-.256s ext=%-.256s\n", direc, fname, ftype);
|
|
#endif
|
|
} // endif trace
|
|
|
|
if (drive && !*drive)
|
|
strcpy(drive, defdrv);
|
|
|
|
switch (*direc) {
|
|
case '\0':
|
|
snprintf(direc, sizeof(direc), "%s", defdir);
|
|
break;
|
|
case '\\':
|
|
case '/':
|
|
break;
|
|
default:
|
|
// This supposes that defdir ends with a SLASH
|
|
safe_strcat(defdir, sizeof(defdir), direc);
|
|
snprintf(direc, sizeof(direc), "%s", defdir);
|
|
} // endswitch
|
|
|
|
_makepath(newname, drive, direc, fname, ftype);
|
|
|
|
if (trace(2))
|
|
htrc("newname='%-.256s'\n", newname);
|
|
|
|
if (_fullpath(pBuff, newname, _MAX_PATH)) {
|
|
if (trace(2))
|
|
htrc("pbuff='%-.256s'\n", pBuff);
|
|
|
|
return pBuff;
|
|
} else
|
|
return FileName; // Error, return unchanged name
|
|
|
|
} // end of PlugSetPath
|
|
|
|
#if defined(XMSG)
|
|
/***********************************************************************/
|
|
/* PlugGetMessage: get a message from the message file. */
|
|
/***********************************************************************/
|
|
char *PlugReadMessage(PGLOBAL g, int mid, char *m)
|
|
{
|
|
char msgfile[_MAX_PATH], msgid[32], buff[256];
|
|
char *msg;
|
|
FILE *mfile = NULL;
|
|
|
|
//GetPrivateProfileString("Message", msglang, "Message\\english.msg",
|
|
// msgfile, _MAX_PATH, plgini);
|
|
//strcat(strcat(strcpy(msgfile, msg_path), msglang()), ".msg");
|
|
strcat(strcpy(buff, msglang()), ".msg");
|
|
PlugSetPath(msgfile, NULL, buff, msg_path);
|
|
|
|
if (!(mfile = fopen(msgfile, "rt"))) {
|
|
snprintf(stmsg, sizeof(stmsg), "Fail to open message file %s", msgfile);
|
|
goto err;
|
|
} // endif mfile
|
|
|
|
for (;;)
|
|
if (!fgets(buff, 256, mfile)) {
|
|
snprintf(stmsg, sizeof(stmsg), "Cannot get message %d %s", mid, SVP(m));
|
|
goto fin;
|
|
} else
|
|
if (atoi(buff) == mid)
|
|
break;
|
|
|
|
if (sscanf(buff, " %*d %.31s \"%.255[^\"]", msgid, stmsg) < 2) {
|
|
// Old message file
|
|
if (!sscanf(buff, " %*d \"%.255[^\"]", stmsg)) {
|
|
snprintf(stmsg, sizeof(stmsg), "Bad message file for %d %s", mid, SVP(m));
|
|
goto fin;
|
|
} else
|
|
m = NULL;
|
|
|
|
} // endif sscanf
|
|
|
|
if (m && strcmp(m, msgid)) {
|
|
// Message file is out of date
|
|
strcpy(stmsg, m);
|
|
goto fin;
|
|
} // endif m
|
|
|
|
fin:
|
|
fclose(mfile);
|
|
|
|
err:
|
|
if (g) {
|
|
// Called by STEP
|
|
msg = PlugDup(g, stmsg);
|
|
} else // Called by MSG or PlgGetErrorMsg
|
|
msg = stmsg;
|
|
|
|
return msg;
|
|
} // end of PlugReadMessage
|
|
|
|
#elif defined(NEWMSG)
|
|
/***********************************************************************/
|
|
/* PlugGetMessage: get a message from the resource string table. */
|
|
/***********************************************************************/
|
|
char *PlugGetMessage(PGLOBAL g, int mid)
|
|
{
|
|
char *msg;
|
|
|
|
#if 0 // was !defined(UNIX) && !defined(UNIV_LINUX)
|
|
int n = LoadString(s_hModule, (uint)mid, (LPTSTR)stmsg, 200);
|
|
|
|
if (n == 0) {
|
|
DWORD rc = GetLastError();
|
|
const int BUF_SIZE= 512;
|
|
msg = (char*)PlugSubAlloc(g, NULL, BUF_SIZE); // Extend buf allocation
|
|
n = snprintf(msg, BUF_SIZE, "Message %d, rc=%d: ", mid, rc);
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
|
|
(LPTSTR)(msg + n), BUF_SIZE - n, NULL);
|
|
return msg;
|
|
} // endif n
|
|
|
|
#else // ALL
|
|
if (!GetRcString(mid, stmsg, 200))
|
|
snprintf(stmsg, sizeof(stmsg) "Message %d not found", mid);
|
|
#endif // ALL
|
|
|
|
if (g) {
|
|
// Called by STEP
|
|
msg = PlugDup(g, stmsg);
|
|
} else // Called by MSG or PlgGetErrorMsg
|
|
msg = stmsg;
|
|
|
|
return msg;
|
|
} // end of PlugGetMessage
|
|
#endif // NEWMSG
|
|
|
|
#if defined(_WIN32)
|
|
/***********************************************************************/
|
|
/* Return the line length of the console screen buffer. */
|
|
/***********************************************************************/
|
|
short GetLineLength(PGLOBAL g)
|
|
{
|
|
CONSOLE_SCREEN_BUFFER_INFO coninfo;
|
|
HANDLE hcons = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
BOOL b = GetConsoleScreenBufferInfo(hcons, &coninfo);
|
|
|
|
return (b) ? coninfo.dwSize.X : 0;
|
|
} // end of GetLineLength
|
|
#endif // _WIN32
|
|
|
|
/***********************************************************************/
|
|
/* Program for memory allocation of work and language areas. */
|
|
/***********************************************************************/
|
|
bool AllocSarea(PGLOBAL g, size_t size)
|
|
{
|
|
/*********************************************************************/
|
|
/* This is the allocation routine for the WIN32/UNIX/AIX version. */
|
|
/*********************************************************************/
|
|
#if defined(_WIN32)
|
|
if (size >= 1048576) // 1M
|
|
g->Sarea = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
else
|
|
#endif
|
|
g->Sarea = malloc(size);
|
|
|
|
if (!g->Sarea) {
|
|
snprintf(g->Message, sizeof(g->Message), MSG(MALLOC_ERROR), "malloc");
|
|
g->Sarea_Size = 0;
|
|
} else {
|
|
g->Sarea_Size = size;
|
|
PlugSubSet(g->Sarea, size);
|
|
} // endif Sarea
|
|
|
|
#if defined(DEVELOPMENT)
|
|
if (true) {
|
|
#else
|
|
if (trace(8)) {
|
|
#endif
|
|
if (g->Sarea) {
|
|
htrc("Work area of %zd allocated at %p\n", size, g->Sarea);
|
|
} else
|
|
htrc("SareaAlloc: %-.256s\n", g->Message);
|
|
|
|
} // endif trace
|
|
|
|
return (!g->Sarea);
|
|
} // end of AllocSarea
|
|
|
|
/***********************************************************************/
|
|
/* Program for memory freeing the work area. */
|
|
/***********************************************************************/
|
|
void FreeSarea(PGLOBAL g)
|
|
{
|
|
if (g->Sarea) {
|
|
#if defined(_WIN32)
|
|
if (g->Sarea_Size >= 1048576) // 1M
|
|
VirtualFree(g->Sarea, 0, MEM_RELEASE);
|
|
else
|
|
#endif
|
|
free(g->Sarea);
|
|
|
|
#if defined(DEVELOPMENT)
|
|
if (true)
|
|
#else
|
|
if (trace(8))
|
|
#endif
|
|
htrc("Freeing Sarea at %p size = %zd\n", g->Sarea, g->Sarea_Size);
|
|
|
|
g->Sarea = NULL;
|
|
g->Sarea_Size = 0;
|
|
} // endif Sarea
|
|
|
|
return;
|
|
} // end of FreeSarea
|
|
|
|
/***********************************************************************/
|
|
/* Program for SubSet initialization of memory pools. */
|
|
/* Here there should be some verification done such as validity of */
|
|
/* the address and size not larger than memory size. */
|
|
/***********************************************************************/
|
|
BOOL PlugSubSet(void *memp, size_t size)
|
|
{
|
|
PPOOLHEADER pph = (PPOOLHEADER)memp;
|
|
|
|
pph->To_Free = (size_t)sizeof(POOLHEADER);
|
|
pph->FreeBlk = size - pph->To_Free;
|
|
return FALSE;
|
|
} /* end of PlugSubSet */
|
|
|
|
/***********************************************************************/
|
|
/* Use it to export a function that do throwing. */
|
|
/***********************************************************************/
|
|
static void *DoThrow(int n)
|
|
{
|
|
throw n;
|
|
} /* end of DoThrow */
|
|
|
|
/***********************************************************************/
|
|
/* Program for sub-allocating one item in a storage area. */
|
|
/* The simple way things are done here is based on the fact */
|
|
/* that no freeing of suballocated blocks is permitted in CONNECT. */
|
|
/***********************************************************************/
|
|
void *PlugSubAlloc(PGLOBAL g, void *memp, size_t size)
|
|
{
|
|
PPOOLHEADER pph; /* Points on area header. */
|
|
|
|
if (!memp)
|
|
/*******************************************************************/
|
|
/* Allocation is to be done in the Sarea. */
|
|
/*******************************************************************/
|
|
memp = g->Sarea;
|
|
|
|
size = ROUNDUP_TO_8(size); /* Round up size to multiple of 8 */
|
|
pph = (PPOOLHEADER)memp;
|
|
|
|
if (trace(16))
|
|
htrc("SubAlloc in %p size=%zd used=%zd free=%zd\n",
|
|
memp, size, pph->To_Free, pph->FreeBlk);
|
|
|
|
if (size > pph->FreeBlk) { /* Not enough memory left in pool */
|
|
PCSZ pname = "Work";
|
|
|
|
snprintf(g->Message, sizeof(g->Message),
|
|
"Not enough memory in %-.256s area for request of %zu (used=%zu free=%zu)",
|
|
pname, size, pph->To_Free, pph->FreeBlk);
|
|
|
|
if (trace(1))
|
|
htrc("PlugSubAlloc: %-.256s\n", g->Message);
|
|
|
|
DoThrow(1234);
|
|
} /* endif size OS32 code */
|
|
|
|
/*********************************************************************/
|
|
/* Do the suballocation the simplest way. */
|
|
/*********************************************************************/
|
|
memp = MakePtr(memp, pph->To_Free); /* Points to suballocated block */
|
|
pph->To_Free += size; /* New offset of pool free block */
|
|
pph->FreeBlk -= size; /* New size of pool free block */
|
|
|
|
if (trace(16))
|
|
htrc("Done memp=%p used=%zd free=%zd\n",
|
|
memp, pph->To_Free, pph->FreeBlk);
|
|
|
|
return (memp);
|
|
} /* end of PlugSubAlloc */
|
|
|
|
/***********************************************************************/
|
|
/* Program for sub-allocating and copying a string in a storage area. */
|
|
/***********************************************************************/
|
|
char *PlugDup(PGLOBAL g, const char *str)
|
|
{
|
|
if (str) {
|
|
char *sm = (char*)PlugSubAlloc(g, NULL, strlen(str) + 1);
|
|
|
|
snprintf(sm, strlen(str) + 1, "%s", str);
|
|
return sm;
|
|
} else
|
|
return NULL;
|
|
|
|
} // end of PlugDup
|
|
|
|
/*************************************************************************/
|
|
/* This routine makes a pointer from an offset to a memory pointer. */
|
|
/*************************************************************************/
|
|
void* MakePtr(void* memp, size_t offset)
|
|
{
|
|
// return ((offset == 0) ? NULL : &((char*)memp)[offset]);
|
|
return (!offset) ? NULL : (char *)memp + offset;
|
|
} /* end of MakePtr */
|
|
|
|
/*************************************************************************/
|
|
/* This routine makes an offset from a pointer new format. */
|
|
/*************************************************************************/
|
|
size_t MakeOff(void* memp, void* ptr)
|
|
{
|
|
if (ptr) {
|
|
#if defined(_DEBUG) || defined(DEVELOPMENT)
|
|
if (ptr <= memp) {
|
|
fprintf(stderr, "ptr %p <= memp %p", ptr, memp);
|
|
DoThrow(999);
|
|
} // endif ptr
|
|
#endif // _DEBUG || DEVELOPMENT
|
|
return (size_t)(((char*)ptr) - ((char*)memp));
|
|
} else
|
|
return 0;
|
|
|
|
} /* end of MakeOff */
|
|
|
|
/*---------------------- End of PLUGUTIL program ------------------------*/
|