/* Common library. It contains some general-purpose low-level functions useful for other components: - dynamic-link libraries management, - settings file support, - error messages handling. */ #include "common.h" #define KERNEL #include "corefunc.h" #include "inter.h" #undef KERNEL /********************************/ /* Library-management functions */ /********************************/ /*------------------------------------------*/ /* Implementation of OS-dependent functions */ Local(CharPtr) ErrBufPtr; #if _WIN /* !! Declarations of Windows functions must match onse in windows.h !! */ #ifdef WIN32 LibHandle FAR PASCAL LoadLibraryA(CharPtr); #define LoadLibrary LoadLibraryA #else LibHandle FAR PASCAL LoadLibrary(CharPtr); #endif void FAR PASCAL FreeLibrary(LibHandle); void FAR * FAR PASCAL GetProcAddress(LibHandle,CharPtr); /* !! Declarations of Windows functions must match ones in windows.h !! */ Local(CharPtr) _LibraryError(void) { return ErrBufPtr; } Local(LibHandle) _LibraryLock(CharPtr LibName) { LibHandle lh; Char b[sizeof("LIBLOAD")+3]; lh=LoadLibrary(LibName); if ((unsigned)lh<=32) { /* LibName is equal to ParBuf! */ StrCat(LibName," "); GetParamAppend(SFS_KERNELERRORS,"LIBLOAD"); StrCat(LibName," "); sprintf(b,"LIBLOAD%i",(int)lh); ErrBufPtr=GetParamAppend(SFS_KERNELERRORS,b); lh=0; } return lh; } Local(void) _LibraryUnlock(LibHandle LibHndl) { FreeLibrary(LibHndl); } Local(VoidPtr) _LibraryFuncAddress(LibHandle LibHndl, CharPtr FuncName) { VoidPtr fp; fp=GetProcAddress(LibHndl,FuncName); if (!fp) { StrCpy(ParBuf,FuncName); ErrBufPtr=GetParamAppend(SFS_KERNELERRORS,"FUNCADR"); } return fp; } #endif #if _MAC #error Interface to Dynamical Libraries management is not defined (dynlib.h) #endif #if _UNIX #ifdef PROC_HPPA /* HP-UX */ #include #define _LibraryLock(l) shl_load(l,BIND_DEFERRED,0L) #define _LibraryUnlock(h) shl_unload(h) Local(VoidPtr) _LibraryFuncAddress(LibHandle h, CharPtr f) { VoidPtr fp; if (shl_findsym(&h,f,TYPE_PROCEDURE,&fp)) { /* not found */ StrCpy(ParBuf,f); ErrBufPtr=GetParamAppend(SFS_KERNELERRORS,"FUNCADR"); fp=NULL; } return fp; } Local(CharPtr) _LibraryError(void) { return ErrBufPtr; } #else /* PROC_HPPA */ #include #ifdef _UNIX_R6K /* AIX RS 6000 */ #ifndef RTLD_LAZY #define RTLD_LAZY 1 #endif #endif #define _LibraryLock(l) dlopen(l,RTLD_LAZY) #define _LibraryFuncAddress(h,f) dlsym(h,f) #define _LibraryUnlock(h) dlclose(h) #define _LibraryError() dlerror() #endif /* PROC_HPPA */ #endif /* if _UNIX */ /* End of implementation of OS-dependent functions */ /*-------------------------------------------------*/ #if DEB_LIB static ByteStorePtr lbs; static FILE *ldeb=NULL; typedef struct { CharPtr libname; LibHandle handle; Int2 lockcount; } LibBlock; void _InitDebLib(void) { LibBlock blk; MemFill(&blk,0,sizeof(LibBlock)); lbs=BSNew(0); BSWrite(lbs,&blk,sizeof(LibBlock)); /* BSFree(lbs) will crash without this */ ldeb=fopen("deb_lib","wt"); } void _TermDebLib(void) { LibBlock blk; Int4 n; n=BSLen(lbs)/sizeof(LibBlock)-1; BSSeek(lbs,sizeof(LibBlock),SEEK_SET); if (n) { Beep(); Message(MSG_OK,"There are %i unlocked libraries (see deb_lib file)",(int)n); fprintf(ldeb,"\n\n\n==========================\n\n"); fflush(ldeb); } for (; n; n--) { BSRead(lbs,&blk,sizeof(LibBlock)); fprintf(ldeb,"%s with lock count %i\n",blk.libname,(int)blk.lockcount); fflush(ldeb); MemFree(blk.libname); } BSFree(lbs); fclose(ldeb); } static Boolean _DebLibSearch(CharPtr libname, LibHandle handle, LibBlock PNTR blk) { Int4 n; n=BSLen(lbs)/sizeof(LibBlock)-1; BSSeek(lbs,sizeof(LibBlock),SEEK_SET); for (; n; n--) { BSRead(lbs,blk,sizeof(LibBlock)); if (libname ? !StrCmp(blk->libname,libname) : blk->handle==handle) { BSSeek(lbs,-((int)(sizeof(LibBlock))),SEEK_CUR); return TRUE; } } return FALSE; } static void _DebLibLock(CharPtr libname, LibHandle handle) { LibBlock blk; if (_DebLibSearch(libname,0,&blk)) { blk.lockcount++; } else { blk.libname=StringSave(libname); blk.lockcount=1; blk.handle=handle; } BSWrite(lbs,&blk,sizeof(LibBlock)); fprintf(ldeb,"Lock %s, Count %i, Handle %x\n",libname,(int)blk.lockcount, (int)handle); fflush(ldeb); } static void _DebLibUnlock(LibHandle handle) { LibBlock _blk; if (_DebLibSearch(NULL,handle,&_blk)) { _blk.lockcount--; fprintf(ldeb,"Unlock %s, Count %i, Handle %x\n",_blk.libname,(int)_blk.lockcount, (int)handle); fflush(ldeb); if (_blk.lockcount) BSWrite(lbs,&_blk,sizeof(LibBlock)); else { MemFree(_blk.libname); BSDelete(lbs,sizeof(LibBlock)); } } else if (handle && Message(MSG_OKC,"%x is not a handle of a library", (int)handle)==ANS_CANCEL) { Abend(); } } static void _DebLibAddress(LibHandle handle, CharPtr name, VoidPtr addr) { LibBlock _blk; if (_DebLibSearch(NULL,handle,&_blk)) { fprintf(ldeb,"Addr %s(%s)=%p\n",_blk.libname,name,addr); fflush(ldeb); } else if (Message(MSG_OKC,"%x is not a handle of a library", (int)handle)==ANS_CANCEL) { Abend(); } } #endif /* There are three reasons we need to maintain our own list of loaded shared libraries. 1. Thank you very much, HP! shl_load and shl_unload don't employ the usage count mechanism: - load increases the count, - unload decreases the count, - library is removed from the memory only when the count reaches 0. Instead, the first shl_unload(lib) removes the lib and frees its handle. As a result the following does not work: h=shl_load("lib"); h=shl_load("lib"); the same! shl_unload(h); ... there is no "lib" in the memory any more ... 2. Linux's dlopen contributs also: h1=dlopen("lib"); h2=dlopen("lib"); the same! ... h1!=h2 ... 3. UnlockRequested and UnlockAll need true value of usage counts. */ #define LIBMAX 20 typedef struct { CharPtr l; /* lib name */ LibHandle h; /* lib handle */ int c; /* usage count */ } LibDesc, PNTR LibDescPtr; Local(LibDesc) Libs[LIBMAX]; /* it is clean after load (see ANSI C standard) */ Local(LibHandle) my_LibraryLock(CharPtr l) { Int2 i,j; for (i=0, j=-1; itermproc=termproc; urp->lib=hLib; urp->next=UnlockList; UnlockList=urp; Epilog } Global(void) LibraryUnlockRequested(void) { UnlockReqPtr urp; LibHandle lh; Int2 i,j; while (UnlockList) { lh=UnlockList->lib; if (UnlockList->termproc) UnlockList->termproc(); for (i=0; inext; _MemFree(UnlockList); UnlockList=urp; } } Global(void) LibraryUnlockAll(void) { LIB_ITERM_FUNC_TYPE ITermFuncPtr; Int2 i; for (i=0; i0) { while (Libs[i].c>1) LibraryUnlock(Libs[i].h); ITermFuncPtr=(LIB_ITERM_FUNC_TYPE)(LibraryFuncAddressCond(Libs[i].h,LIB_ITERM_FUNC_STR)); if (ITermFuncPtr) ITermFuncPtr(); LibraryUnlock(Libs[i].h); } } /****************************/ /* Settings file management */ /****************************/ Global(Char) ParBuf[PAR_BUF]=""; /* work text buffer */ #if _WIN Local(Char) ConfigFile[PATH_MAX]=""; #define CONFIG_FILE ConfigFile Global(Boolean) GetConfigFilePath(void) { CharPtr p; StrCpy(ConfigFile,p=CurrentDirectory()); FileBuildPath(ConfigFile,NULL,APP_NAME".ini"); _MemFree(p); return access(ConfigFile,0)==0; } #endif /* _WIN */ #if _UNIX #define CONFIG_FILE APP_NAME Global(Boolean) GetConfigFilePath(void) { return access("."CONFIG_FILE"rc",0)==0; } #endif /* _UNIX */ #if _MAC #error Global(Boolean) GetConfigFilePath(void) is not declared #endif /* _MAC */ #define ERR_HEAD "Settings file '%s', section '%s', key '%s': " #define NOT_FOUND "not found" /* STR_EMPTY -> '' */ Local(void) RemoveEmptyChars(CharPtr p) { for (; (p=StrStr(p,STR_EMPTY))!=NULL; ) StrCpy(p,p+1); } /* Symbolic refernce to the value of a Key under a Section has the form $(Section.Key) It may be used in any value string and will be repalced by the value of appropriate Key. Avoid recursion! CHAR_NL are replaced by \n. */ #define SYMREF "$(%n%*[^.].%n%*[^)])%n" Local(Boolean) TranslateRef(CharPtr Buf, CharPtr Section, CharPtr Key) { CharPtr rp; /* start of ref */ int soff; /* offset of section name */ int koff; /* offset of key name */ int eoff; /* end of ref */ Boolean res=TRUE; /* OK */ Char buf[PAR_BUF]; for (rp=Buf; res && (rp=StrChr(rp,*SYMREF))!=NULL; ) { soff=koff=eoff=0; sscanf(rp,SYMREF,&soff,&koff,&eoff); if (eoff) { *(rp+koff-1)='\0'; *(rp+eoff-1)='\0'; if (GetAppParam(CONFIG_FILE,rp+soff,rp+koff,"",buf,sizeof(buf))) { /* STR_EMPTY -> empty */ RemoveEmptyChars(buf); StrCat(buf,rp+eoff); StrCpy(rp,buf); } else { Warning("TRANSLN",APP_NAME,Section,Key); res=FALSE; } } else { /* format incorrect, don't substitute */ rp++; } } return res; } Global(Boolean) Translate(CharPtr Buf, CharPtr Section, CharPtr Key) { CharPtr rp; Boolean res; /* TRUE means OK */ /* Substitute $(section,key) */ res=TranslateRef(Buf,Section,Key); /* CHAR_NL -> \n */ while ((rp=StrChr(Buf,CHAR_NL))!=NULL) *rp='\n'; /* STR_EMPTY -> empty */ RemoveEmptyChars(Buf); return res; } /* Reads a value from the settings file, performs format substitution and put the result into the buffer */ #ifdef VAR_ARGS Global(CharPtr) GetParam(Section, Key, va_alist) CharPtr Section; CharPtr Key; va_dcl #else Global(CharPtr) GetParam(CharPtr Section, CharPtr Key,...) #endif { va_list valist; Char buf[PAR_BUF]; if (GetAppParam(CONFIG_FILE,Section,Key,"",buf,PAR_BUF)) if (Translate(buf,Section,Key)) { #ifdef VAR_ARGS va_start(valist); #else va_start(valist,Key); #endif vsprintf(ParBuf,buf,valist); va_end(valist); return ParBuf; } else return NULL; else { Message(MSG_OK,ERR_HEAD NOT_FOUND,APP_NAME,Section,Key); return NULL; } } /* Reads a string from the settings file and put it to the buffer */ Global(CharPtr) GetParamString(CharPtr Section, CharPtr Key) { if (GetAppParam(CONFIG_FILE,Section,Key,"",ParBuf,PAR_BUF)) return Translate(ParBuf,Section,Key) ? ParBuf : NULL; else { Message(MSG_OK,ERR_HEAD NOT_FOUND,APP_NAME,Section,Key); return NULL; } } /* Reads a value from the settings file and append it to the buffer */ Global(CharPtr) GetParamAppend(CharPtr Section, CharPtr Key) { Int2 len=(Int2)StrLen(ParBuf); if (GetAppParam(CONFIG_FILE,Section,Key,"",ParBuf+len,PAR_BUF-len)) return Translate(ParBuf,Section,Key) ? ParBuf : NULL; else { Message(MSG_OK,ERR_HEAD NOT_FOUND,APP_NAME,Section,Key); return NULL; } } /* Writes a string to the settings file */ Global(void) SetParam(CharPtr Section, CharPtr Key, CharPtr Value) { CharPtr p; for (; (p=StringChr(Value,'\n'))!=NULL; *p=CHAR_NL); SetAppParam(CONFIG_FILE,Section,Key,Value); } /***************************/ /* Error messages handling */ /***************************/ /* Issues a warning message. The text of a message is obtained form the SFS_KERNELERRORS section using the first parameter as a key name. NOTE: This routine must not be available when it is in DLL. So, Global is used instead of Entry. Also it DOES NOT use common input buffer of GetParam family. */ #ifdef VAR_ARGS Global(Int2) Warning(Key, va_alist) CharPtr Key; va_dcl #else Global(Int2) Warning(CharPtr Key,...) #endif { static Char Buf[PAR_BUF],Format[PAR_BUF]; va_list list; if (GetAppParam(CONFIG_FILE,SFS_KERNELERRORS,Key,"",Format,PAR_BUF)) { Translate(Format,SFS_KERNELERRORS,Key); #ifdef VAR_ARGS va_start(list); #else va_start(list,Key); #endif vsprintf(Buf,Format,list); #if DEB if (Message(MSG_OKC,Buf)==ANS_CANCEL) Abend(); #else Message(MSG_OK,Buf); #endif va_end(list); } return 1; } /*************************/ /* Directories and files */ /*************************/ #if _WIN #include #endif #if _MAC #error ChangeDirectory is not defined #endif #if _UNIX #include #endif /* Changes current directory */ Global(void) ChangeDirectory(CharPtr dir) { #if DEB_LIB CharPtr p; int ret; #endif if (dir==NULL) return; #if DEB_LIB p=CurrentDirectory(); ret= #endif #if _WIN chdir(dir); #endif #if _MAC #error ChangeDirectory is not defined #endif #if _UNIX chdir(dir); #endif #if DEB_LIB fprintf(ldeb,"ChangeDirectory(%s)=%i (errno=%i)\n",dir,ret,errno); fprintf(ldeb," from directory=%s\n",p); _MemFree(p); fflush(ldeb); #endif } /* Gets current directory */ Global(CharPtr) CurrentDirectory(void) { CharPtr buf; Int2 len; len=PATH_MAX; buf=_MemNew(len); #if _WIN getcwd(buf,len); #endif #if _MAC #error CurrentDirectory is not defined #endif #if _UNIX getcwd(buf,len); #endif #if DEB_LIB if (ldeb) { /* is NULL for setup */ fprintf(ldeb,"CurrentDirectory is %s\n",buf); fflush(ldeb); } #endif return buf; } /* Truncates a file */ Global(Boolean) TruncateFile(FILE PNTR stream, long size) { #if _WIN return chsize(fileno(stream),size)==0; #endif #if _MAC #error TruncateFile is not defined #endif #if _UNIX return ftruncate(fileno(stream),size)==0; #endif } /* Windows: GetInputFileName&GetOutputFileName change the current directory */ #if _WIN #pragma argsused #endif Global(Boolean) myGetInputFileName(CharPtr fileName, size_t maxsize, CharPtr extType) { #if _WIN && !defined(WIN32) Message(MSG_OK,"Sorry, this feature doesn't work under Windows 3.1"); return FALSE; #else CharPtr dir; Boolean res; dir=CurrentDirectory(); res=GetInputFileName(fileName,maxsize,extType,NULL); ChangeDirectory(dir); _MemFree(dir); return res; #endif } #if _WIN #pragma argsused #endif Global(Boolean) myGetOutputFileName(CharPtr fileName, size_t maxsize, CharPtr dfault) { #if _WIN && !defined(WIN32) Message(MSG_OK,"Sorry, this feature doesn't work under Windows 3.1"); return FALSE; #else CharPtr dir; Boolean res; dir=CurrentDirectory(); res=GetOutputFileName(fileName,maxsize,dfault); ChangeDirectory(dir); _MemFree(dir); return res; #endif } /***************************/ /* Useful string functions */ /***************************/ /* Substitutes all occurences of *what by *with in *source */ Global(CharPtr) StrSubstitute(CharPtr source, CharPtr what, CharPtr with, Boolean wholeid, Int2Ptr num) { CharPtr p; CharPtr n; Int2 lwhat,lwith,lh; if (num) *num=0; lwhat=StrLen(what); lwith=StrLen(with); for (p=source; (p=StringStr(p,what),p); ) { if (wholeid && p!=source && IS_ALPHANUM(*(p-1))) { p+=lwhat; continue; } n=_MemNew(StrLen(source)+1-lwhat+lwith); StringNCpy(n,source,lh=(Int2)(p-source)); n[(Int2)(p-source)]='\0'; StringCat(n,with); StringCat(n,p+lwhat); _MemFree(source); source=n; p=source+lh+lwith; if (num) (*num)++; } return source; } /* Inserts *what before charater number position of *source */ Global(CharPtr) StrInsert(CharPtr source, Int2 position, CharPtr what) { CharPtr p; p=_MemNew(StrLen(source)+StrLen(what)+1); StrCat(StrCpy(p+position,what),source+position); StrNCpy(p,source,position); _MemFree(source); return p; } /* Removes leading and trailing blanks */ Global(void) StrTrim(CharPtr source) { Int2 s,e; #define IGNCHRS " \n\r\t\x1A" s=(Int2)StrSpn(source,IGNCHRS); if (source[s]=='\0') *source='\0'; else { for (e=(Int2)StrLen(source)-1; e>=s && StrChr(IGNCHRS,source[e]); e--); e+=1-s; /* e=e+1-s; e is now len of trimmed text */ MemMove(source,source+s,e); source[e]='\0'; } #undef IGNCHRS } /**************************************/ /* Several file-related aux. routines */ /**************************************/ #define IN 3 Local(FILE PNTR) in[IN]; Local(Int2) inl; Local(CharPtr) descGetName(CharPtr filename) { CharPtr p; if (StrChr(filename,DIRDELIMCHR)) return filename; p=_StringSave(filename); GetParam(SFS_PATHS,"KERNEL"); StrCat(ParBuf,p); _MemFree(p); return ParBuf; } Global(Boolean) DescOpen(CharPtr filename) { in[inl=0]=FileOpen(descGetName(filename),"rt"); return in[inl]!=NULL; } Global(void) DescClose(void) { Int2 i; for (i=0; i<=inl; i++) FileClose(in[i]); } Global(Boolean) DescEof(void) { return inl==0 && feof(in[0]); } Global(void) DescRewind(void) { for ( ; inl; ) FileClose(in[inl--]); rewind(in[0]); } Global(void) DescSeek(DescPtr fp) { Int2 linl; linl=(Int2)(fp>>24); for ( ; inl>linl; ) FileClose(in[inl--]); fseek(in[linl],fp&0x00ffffffL,SEEK_SET); } Local(DescPtr) descTell(void) { long fpos; fpos=ftell(in[inl]); return (inl<<24)|(Int4)fpos; } Local(CharPtr) getLine(CharPtr b) { CharPtr p; CharPtr q; read: p=FileGets(b,BL,in[inl]); /* remove heading and trailing blanks */ if (p) { q=StrChr(p,'!'); if (q) *q='\0'; StrTrim(p); if (*p=='\0') goto read; Translate(p,NULL,NULL); } return p; } Global(CharPtr) GetLine(CharPtr b) { do { if (getLine(b)) if (b[0]==SECPRF[0] && b[1]==SECPRF[0] && inlmlen) mlen=len; } } while (!feof(s)); _MemFree(p); fclose(s); } return mlen; } /**********************/ /* Execute OS command */ /**********************/ /* cmd - commind line. check - (PC, ignored for UNIX) checks whether the command terminated. post - post-processing */ #if _WIN /*--------------------------------------------------------------------------*/ /* !! Declarations of Windows functions must be the same as in windows.h !! */ Uint2 FAR PASCAL WinExec(CharPtr cmd, Uint2 show); Uint2 FAR PASCAL SetTimer(Uint2 hwnd, Uint2 idTimer,Uint2 uTimeout, void (FAR PASCAL *tmprc)()); Int2 FAR PASCAL KillTimer(Uint2 hwnd, Uint2 idTimer); #ifdef WIN32 #define Catch setjmp #define Throw longjmp #else int FAR PASCAL Catch(VoidPtr CatchBuf); void FAR PASCAL Throw(VoidPtr CatchBuf, int RetCode); #endif /* */ /*--------------------------------------------------------------------------*/ static Uint2 idTimer; static Int2 (FAR *_PostProc)(void); static Boolean (FAR *_CheckProc)(void); #ifdef WIN32 static jmp_buf CatchBuf; #else static CatchBuf[50]; /* Must be at least sizeof(CATCHBUF) bytes long */ #endif #pragma argsused static void FAR PASCAL _export WakeupProc(Uint2 hwnd, Uint2 msg, Uint2 idTimer, Uint4 dwTime) { if (_CheckProc()) { KillTimer(NULL,idTimer); Throw(CatchBuf,_PostProc()+1); } } #define MINH 32 /* Handle<=MINH indicates an error */ Global(Int2) ExecuteCommand(CharPtr cmd, Boolean (FAR *CheckProc)(void), Int2 (FAR *PostProc)(void)) { Uint2 h; Int2 r; Char b[sizeof("WINEXEC")+3]; h=(cmd && *cmd) ? WinExec(cmd,0) : MINH+1; if (h>MINH) { /* OK, sheduled */ /* Windows only scheduled the command but did not execute it yet (unlike Unix). So we have to wait checking periodically is it completed. */ _CheckProc=CheckProc; _PostProc=PostProc; idTimer=SetTimer(NULL,0,1000,WakeupProc); if (!idTimer) { Warning("WINEXECT"); return 2; } r=Catch(CatchBuf); if (r) { /* Throw */ return r-1; } else { /* env is copied */ ProcessEvents(); /* !!! */ } } else { /* error, will not be executed */ /* cmd is equal to Buf! */ StrCat(cmd," "); GetParamAppend(SFS_KERNELERRORS,"WINEXEC"); sprintf(b,"LIBLOAD%i",(int)h); if (Message(MSG_OKC,GetParamAppend(SFS_KERNELERRORS,b))==ANS_CANCEL) { /* The answer is 'Cancel', go for SYSABEND dump */ #if DEB Abend(); #endif } } return 1; } #endif #if _MAC #error ExecuteCommand is not defined #endif #if _UNIX Global(Int2) ExecuteCommand(CharPtr cmd, Boolean (*CheckProc)(void), Int2 (*PostProc)(void)) { return (system(cmd), PostProc()); } #endif /************************/ /* Vibrant-related zaps */ /************************/ #if _UNIX #undef Boolean #include extern XtAppContext Nlm_appContext; /* defined in Vibrant */ /* As ProcessAnEvent except it process timer events also */ Global(void) myProcessAnyEvent(void) { XEvent event; if (XtAppPending(Nlm_appContext)&(XtIMXEvent|XtIMTimer)) { XtAppNextEvent(Nlm_appContext,&event); XtDispatchEvent(&event); } } #define Boolean Nlm_Boolean #else Global(void) myProcessAnyEvent(void) { ProcessAnEvent(); } #endif /*************/ /* More zaps */ /*************/ #if defined(COMP_GNU) && defined(OS_UNIX_SUN) /* There are no div_t and div in stdlib.h */ Global(div_t) div (int numer, int denom) { div_t d; d.quot=numer/denom; d.rem=numer%denom; return d; } #endif /*********/ /* Debug */ /*********/ #if DEB void PrintRect(RectPtr pr, CharPtr t) { #if _UNIX printf( #endif #if _WIN Message(MSG_OK, #endif #if MAC Message(MSG_OK, #endif "%s: (%i,%i)-(%i,%i), dx=%i dy=%i\n", t,pr->left,pr->top,pr->right,pr->bottom, pr->right-pr->left,pr->bottom-pr->top); } void Abend(void) { #if _WIN Int1 i=0; i=1/i; #endif #if _UNIX abort(); #endif } #endif /***************************/ /* Debug memory management */ /***************************/ #if DEB_MEM Local(Boolean) MemTrace=TRUE; #if _WIN #pragma argsused #endif Global(void) Debug_MemTrace(Int2 index) { MemTrace^=TRUE; } #if _WIN #pragma argsused #endif Global(Boolean) Debug_MemTrace_Status(Int2 index) { return MemTrace; } #define DEB_MEM_FAST 1 /* use 32 ByteStores, one per each possible last byte */ #define FILES_TAB 50 static CharPtr FileTab[FILES_TAB]; static Int2 FileTabN=1; static Int2 DebInsertFileName(CharPtr filename) { Int2 i; for (i=1; i=FILES_TAB) return 0; FileTab[FileTabN]=StringSave(filename); return FileTabN++; } typedef struct { Pointer ptr; size_t size; Int2 line; Int2 filename; } Block; #if DEB_MEM_FAST static ByteStorePtr Bs[32]; #define bs(p) Bs[(((int)p)&0xff)>>3] #else static ByteStorePtr Bs[1]; #define bs(p) Bs[0] #endif static FILE *deb; static void DoubleFault(int line) { if (Message(MSG_OKC,"Double fault in line %i",line)==ANS_CANCEL) Abend(); } #define Check(cond) if (!(cond)) DoubleFault(__LINE__) void _InitDebMem(void) { Block blk; Int4 l; Int2 i; MemFill(&blk,0,sizeof(Block)); deb=fopen("deb_mem","wt"); Check(deb); FileTab[0]=StringSave("???"); for (i=1; i=0); s=BSSeek(bs(ptr),sizeof(Block),SEEK_SET); Check(s==0); for (; n; n--) { l=BSRead(bs(ptr),&blk,sizeof(blk)); Check(l==sizeof(blk)); if (blk.ptr==ptr) { s=BSSeek(bs(ptr),-((int)(sizeof(blk))),SEEK_CUR); Check(s==0); return TRUE; } } return FALSE; } Pointer __MemNew(size_t size, CharPtr FileName, int Line) { Block _blk; Int4 _l; Int2 _s; if (!size) return NULL; _blk.ptr=MemNew(size); if (!MemTrace) return _blk.ptr; _blk.size=size; _blk.line=Line; _blk.filename=DebInsertFileName(FileName); if (_blk.ptr) { _s=BSSeek(bs(_blk.ptr),0,SEEK_END); Check(_s==0); _l=BSWrite(bs(_blk.ptr),&_blk,sizeof(_blk)); Check(_l==sizeof(_blk)); } else { if (Message(MSG_OKC,"MemNew cannot allocate %i bytes\nFile %s, line %i", (int)size,FileName,Line)==ANS_CANCEL) { Abend(); } } fprintf(deb,"%s(%i): New(%i)=%p\n",FileName,Line,(int)size,_blk.ptr); fflush(deb); return _blk.ptr; } Pointer __MemGet(size_t size, unsigned int clear, CharPtr FileName, int Line) { Block _blk; Int4 _l; Int2 _s; if (!size) return NULL; _blk.ptr=MemGet(size,clear); if (!MemTrace) return _blk.ptr; _blk.size=size; _blk.line=Line; _blk.filename=DebInsertFileName(FileName); if (_blk.ptr) { _s=BSSeek(bs(_blk.ptr),0,SEEK_END); Check(_s==0); _l=BSWrite(bs(_blk.ptr),&_blk,sizeof(_blk)); Check(_l==sizeof(_blk)); } else { if (Message(MSG_OKC,"MemGet cannot allocate %i bytes\nFile %s, line %i", (int)size,FileName,Line)==ANS_CANCEL) { Abend(); } } fprintf(deb,"%s(%i): Get(%i)=%p\n",FileName,Line,(int)size,_blk.ptr); fflush(deb); return _blk.ptr; } Pointer LIBCALL __MemFree(Pointer ptr, CharPtr FileName, int Line) { Int4 _l; if (!ptr) return NULL; if (!MemTrace) { MemFree(ptr); return NULL;} if (Search(ptr)) { _l=BSDelete(bs(ptr),sizeof(Block)); Check(_l==sizeof(Block)); MemFree(ptr); } else { if (Message(MSG_OKC,"MemFree cannot free block %p\nFile %s, line %i", ptr,FileName,Line)==ANS_CANCEL) { Abend(); } _TermDebMem(); } fprintf(deb,"%s(%i): Free(%p)\n",FileName,Line,ptr); fflush(deb); return NULL; } Pointer LIBCALL __MemMore(Pointer ptr, size_t size, CharPtr FileName, int Line) { Block _blk; Int4 _l; Int2 _s; if (!MemTrace) {_blk.ptr=MemMore(ptr,size); return _blk.ptr;} _blk.size=size; _blk.ptr=ptr; _blk.line=Line; _blk.filename=DebInsertFileName(FileName); if (Search(_blk.ptr)) { _l=BSDelete(bs(_blk.ptr),sizeof(Block)); Check(_l==sizeof(Block)); fprintf(deb,"%s(%i): More(%p,%i)=",FileName,Line,_blk.ptr,(int)size); _blk.ptr=MemMore(_blk.ptr,size); fprintf(deb,"%p\n",_blk.ptr); fflush(deb); if (_blk.ptr) { _s=BSSeek(bs(_blk.ptr),0,SEEK_END); Check(_s==0); _l=BSWrite(bs(_blk.ptr),&_blk,sizeof(_blk)); Check(_l==sizeof(_blk)); } else { if (Message(MSG_OKC,"MemMore cannot allocate %i bytes\nFile %s, Line %i", (int)size,FileName,Line)==ANS_CANCEL) { Abend(); } } } else { if (Message(MSG_OKC,"MemMore cannot free block %p\nFile %s, line %i", _blk.ptr,FileName,Line)==ANS_CANCEL) { Abend(); } } return _blk.ptr; } Pointer LIBCALL __MemExtend(Pointer ptr, size_t size, size_t oldsize, CharPtr FileName, int Line) { Block _blk; Int4 _l; Int2 _s; if (!MemTrace) {_blk.ptr=MemExtend(ptr,size,oldsize); return _blk.ptr;} _blk.size=size; _blk.ptr=ptr; _blk.line=Line; _blk.filename=DebInsertFileName(FileName); if (Search(_blk.ptr)) { _l=BSDelete(bs(_blk.ptr),sizeof(Block)); Check(_l==sizeof(Block)); fprintf(deb,"%s(%i): Extend(%p,%i,%i)=",FileName,Line,_blk.ptr,(int)size,(int)oldsize); _blk.ptr=MemExtend(_blk.ptr,size,oldsize); fprintf(deb,"%p\n",_blk.ptr); fflush(deb); if (_blk.ptr) { _s=BSSeek(bs(_blk.ptr),0,SEEK_END); Check(_s==0); _l=BSWrite(bs(_blk.ptr),&_blk,sizeof(_blk)); Check(_l==sizeof(_blk)); } else { if (Message(MSG_OKC,"MemExtend cannot allocate %i bytes\nFile %s, Line %i", (int)size,FileName,Line)==ANS_CANCEL) { Abend(); } } } else { if (Message(MSG_OKC,"MemExtend cannot free block %p\nFile %s, line %i", _blk.ptr,FileName,Line)==ANS_CANCEL) { Abend(); } } return _blk.ptr; } CharPtr LIBCALL __StringSave(const char FAR *ptr, CharPtr FileName, int Line) { Block _blk; Int4 _l; Int2 _s; if (!ptr) return NULL; _blk.ptr=StringSave(ptr); if (!MemTrace) return _blk.ptr; _blk.size=StrLen(ptr)+1; _blk.line=Line; _blk.filename=DebInsertFileName(FileName); if (_blk.ptr) { _s=BSSeek(bs(_blk.ptr),0,SEEK_END); Check(_s==0); _l=BSWrite(bs(_blk.ptr),&_blk,sizeof(_blk)); Check(_l==sizeof(_blk)); } else { if (Message(MSG_OKC,"StringSave cannot save string '%s'\nFile %s, line %i", ptr,FileName,Line)==ANS_CANCEL) { Abend(); } } fprintf(deb,"%s(%i): Save(%s)=%p\n",FileName,Line,ptr,_blk.ptr); fflush(deb); return _blk.ptr; } Pointer LIBCALL __BSMerge(ByteStorePtr bsp, Pointer buffer, CharPtr FileName, int Line) { Int4 _l; Int2 _s; Block _blk; _blk.ptr=BSMerge(bsp,buffer); if (!MemTrace) return _blk.ptr; _blk.line=Line; _blk.filename=DebInsertFileName(FileName); if (_blk.ptr) { if (!buffer) { _blk.size=(size_t)BSLen(bsp); _s=BSSeek(bs(_blk.ptr),0,SEEK_END); Check(_s==0); _l=BSWrite(bs(_blk.ptr),&_blk,sizeof(_blk)); Check(_l==sizeof(_blk)); } } else { if (Message(MSG_OKC,"BSMegre cannot allocate buffer\nFile %s, line %i", FileName,Line)==ANS_CANCEL) { Abend(); } } fprintf(deb,"%s(%i): Merge()=%p\n",FileName,Line,_blk.ptr); fflush(deb); return _blk.ptr; } #ifdef VAR_ARGS Global(void) DebugMsg(format, va_list) { CharPtr format; va_dcl #else Global(void) DebugMsg(CharPtr format,...) { #endif va_list valist; #ifdef VAR_ARGS va_start(valist); #else va_start(valist,format); #endif vfprintf(deb,format,valist); fflush(deb); } #endif