From 6080737aa131d0b919e46d3a6993e6396d4c818f Mon Sep 17 00:00:00 2001
From: "Rob Swindell (on Windows 11)" <rob@synchro.net>
Date: Sat, 23 Dec 2023 12:58:40 -0800
Subject: [PATCH] Add/use realloc_or_free() instead of realloc() in some places
 Where ever we assign the realloc() result to the same pointer we pass, use
 this new function instead to eliminate the cppcheck error reported by Nelgin:
 Common realloc mistake: 'p' nulled but not freed upon failure 
 [memleakOnRealloc]

This isn't going to actually solve any memory leaks, it's just good practice for critical error (e.g. no memory error) handling.
---
 src/sbbs3/baja.c          | 26 +++++++++++++-------------
 src/sbbs3/dupefind.c      |  2 +-
 src/sbbs3/execmisc.cpp    | 28 ++++++++++++++--------------
 src/sbbs3/fixsmb.c        |  2 +-
 src/sbbs3/jsexec.c        |  2 +-
 src/sbbs3/qwk.cpp         |  4 ++--
 src/sbbs3/qwknodes.c      |  2 +-
 src/sbbs3/rechocfg.c      | 10 +++++-----
 src/sbbs3/sbbsecho.c      | 14 +++++++-------
 src/sbbs3/scfg/scfg.c     |  6 +++---
 src/sbbs3/scfg/scfgchat.c | 10 +++++-----
 src/sbbs3/scfg/scfgnet.c  |  8 ++++----
 src/sbbs3/scfg/scfgxfr1.c | 10 +++++-----
 src/sbbs3/scfg/scfgxfr2.c |  2 +-
 src/sbbs3/scfg/scfgxtrn.c |  4 ++--
 src/sbbs3/smbutil.c       |  2 +-
 src/sbbs3/targets.mk      | 10 +++++++++-
 src/xpdev/genwrap.c       | 14 +++++++++++++-
 src/xpdev/genwrap.h       |  2 ++
 19 files changed, 90 insertions(+), 68 deletions(-)

diff --git a/src/sbbs3/baja.c b/src/sbbs3/baja.c
index d734d1f4f5..7a14d26116 100644
--- a/src/sbbs3/baja.c
+++ b/src/sbbs3/baja.c
@@ -314,7 +314,7 @@ void newvar(char* src, char *in)
 		if(i<vars)
 			return;
 	}
-	if((var_name=(uint32_t *)realloc(var_name,sizeof(int32_t)*(vars+1)))==NULL) {
+	if((var_name=(uint32_t *)realloc_or_free(var_name,sizeof(int32_t)*(vars+1)))==NULL) {
 		printf("Too many (%"PRIu32") variables!\n",vars);
 		bail(1); }
 	var_name[vars]=l;
@@ -522,14 +522,14 @@ void compile(char *src)
 			if(sp)
 				*sp=0;
 			truncsp(arg2);
-			if((define_str=(char **)realloc(define_str,sizeof(char *)*(defines+1)))
+			if((define_str=(char **)realloc_or_free(define_str,sizeof(char *)*(defines+1)))
 				==NULL) {
 				printf("Too many defines.\n");
 				bail(1); }
 			if((define_str[defines]=(char *)malloc(strlen(arg)+1))==NULL) {
 				printf("Too many defines.\n");
 				bail(1); }
-			if((define_val=(char **)realloc(define_val,sizeof(char *)*(defines+1)))
+			if((define_val=(char **)realloc_or_free(define_val,sizeof(char *)*(defines+1)))
 				==NULL) {
 				printf("Too many defines.\n");
 				bail(1); }
@@ -721,11 +721,11 @@ void compile(char *src)
 				printf("!SYNTAX ERROR (duplicate label name):\n");
 				printf(linestr,src,line,p);
 				bail(1); }
-			if((label_name=(char **)realloc(label_name,sizeof(char *)*(labels+1)))
+			if((label_name=(char **)realloc_or_free(label_name,sizeof(char *)*(labels+1)))
 				==NULL) {
 				printf("Too many labels.\n");
 				bail(1); }
-			if((label_indx=(uint *)realloc(label_indx,sizeof(int)*(labels+1)))
+			if((label_indx=(uint *)realloc_or_free(label_indx,sizeof(int)*(labels+1)))
 				==NULL) {
 				printf("Too many labels.\n");
 				bail(1); }
@@ -741,19 +741,19 @@ void compile(char *src)
 			sp=strchr(arg,' ');
 			if(sp)
 				*sp=0;
-			if((goto_label=(char **)realloc(goto_label,sizeof(char *)*(gotos+1)))
+			if((goto_label=(char **)realloc_or_free(goto_label,sizeof(char *)*(gotos+1)))
 				==NULL) {
 				printf("Too many gotos.\n");
 				bail(1); }
-			if((goto_file=(char **)realloc(goto_file,sizeof(char *)*(gotos+1)))
+			if((goto_file=(char **)realloc_or_free(goto_file,sizeof(char *)*(gotos+1)))
 				==NULL) {
 				printf("Too many gotos.\n");
 				bail(1); }
-			if((goto_indx=(uint *)realloc(goto_indx,sizeof(int)*(gotos+1)))
+			if((goto_indx=(uint *)realloc_or_free(goto_indx,sizeof(int)*(gotos+1)))
 				==NULL) {
 				printf("Too many gotos.\n");
 				bail(1); }
-			if((goto_line=(uint *)realloc(goto_line,sizeof(int)*(gotos+1)))
+			if((goto_line=(uint *)realloc_or_free(goto_line,sizeof(int)*(gotos+1)))
 				==NULL) {
 				printf("Too many gotos.\n");
 				bail(1); }
@@ -775,19 +775,19 @@ void compile(char *src)
 			sp=strchr(arg,' ');
 			if(sp)
 				*sp=0;
-			if((call_label=(char **)realloc(call_label,sizeof(char *)*(calls+1)))
+			if((call_label=(char **)realloc_or_free(call_label,sizeof(char *)*(calls+1)))
 				==NULL) {
 				printf("Too many calls.\n");
 				bail(1); }
-			if((call_file=(char **)realloc(call_file,sizeof(char *)*(calls+1)))
+			if((call_file=(char **)realloc_or_free(call_file,sizeof(char *)*(calls+1)))
 				==NULL) {
 				printf("Too many calls.\n");
 				bail(1); }
-			if((call_indx=(uint *)realloc(call_indx,sizeof(int)*(calls+1)))
+			if((call_indx=(uint *)realloc_or_free(call_indx,sizeof(int)*(calls+1)))
 				==NULL) {
 				printf("Too many calls.\n");
 				bail(1); }
-			if((call_line=(uint *)realloc(call_line,sizeof(int)*(calls+1)))
+			if((call_line=(uint *)realloc_or_free(call_line,sizeof(int)*(calls+1)))
 				==NULL) {
 				printf("Too many calls.\n");
 				bail(1); }
diff --git a/src/sbbs3/dupefind.c b/src/sbbs3/dupefind.c
index cc3ab00277..343ba4f907 100644
--- a/src/sbbs3/dupefind.c
+++ b/src/sbbs3/dupefind.c
@@ -196,7 +196,7 @@ int main(int argc,char **argv)
 							}
 							if(g==total_found) {
 								++total_found;
-								if((foundcrc = realloc(foundcrc
+								if((foundcrc = realloc_or_free(foundcrc
 									,total_found*sizeof(uint32_t)))==NULL) {
 									printf("Out of memory reallocating\r\n");
 									return(1); 
diff --git a/src/sbbs3/execmisc.cpp b/src/sbbs3/execmisc.cpp
index bd5883f211..61131f49cb 100644
--- a/src/sbbs3/execmisc.cpp
+++ b/src/sbbs3/execmisc.cpp
@@ -117,9 +117,9 @@ int sbbs_t::exec_misc(csi_t* csi, const char *path)
 						return(0);
 					}
 					csi->str_vars++;
-					csi->str_var=(char **)realloc(csi->str_var
+					csi->str_var=(char **)realloc_or_free(csi->str_var
 						,sizeof(char *)*csi->str_vars);
-					csi->str_var_name=(uint32_t *)realloc(csi->str_var_name
+					csi->str_var_name=(uint32_t *)realloc_or_free(csi->str_var_name
 						,sizeof(int32_t)*csi->str_vars);
 					if(csi->str_var==NULL
 						|| csi->str_var_name==NULL) { /* REALLOC failed */
@@ -147,9 +147,9 @@ int sbbs_t::exec_misc(csi_t* csi, const char *path)
 						return(0);
 					}
 					csi->int_vars++;
-					csi->int_var=(int32_t *)realloc(csi->int_var
+					csi->int_var=(int32_t *)realloc_or_free(csi->int_var
 						,sizeof(char *)*csi->int_vars);
-					csi->int_var_name=(uint32_t *)realloc(csi->int_var_name
+					csi->int_var_name=(uint32_t *)realloc_or_free(csi->int_var_name
 						,sizeof(int32_t)*csi->int_vars);
 					if(csi->int_var==NULL
 						|| csi->int_var_name==NULL) { /* REALLOC failed */
@@ -177,9 +177,9 @@ int sbbs_t::exec_misc(csi_t* csi, const char *path)
 						return(0);
 					}
 					global_str_vars++;
-					global_str_var=(char **)realloc(global_str_var
+					global_str_var=(char **)realloc_or_free(global_str_var
 						,sizeof(char *)*global_str_vars);
-					global_str_var_name=(uint32_t *)realloc(global_str_var_name
+					global_str_var_name=(uint32_t *)realloc_or_free(global_str_var_name
 						,sizeof(int32_t)*global_str_vars);
 					if(global_str_var==NULL
 						|| global_str_var_name==NULL) { /* REALLOC failed */
@@ -208,9 +208,9 @@ int sbbs_t::exec_misc(csi_t* csi, const char *path)
 						return(0);
 					}
 					global_int_vars++;
-					global_int_var=(int32_t *)realloc(global_int_var
+					global_int_var=(int32_t *)realloc_or_free(global_int_var
 						,sizeof(char *)*global_int_vars);
-					global_int_var_name=(uint32_t *)realloc(global_int_var_name
+					global_int_var_name=(uint32_t *)realloc_or_free(global_int_var_name
 						,sizeof(int32_t)*global_int_vars);
 					if(global_int_var==NULL
 						|| global_int_var_name==NULL) { /* REALLOC failed */
@@ -470,9 +470,9 @@ int sbbs_t::exec_misc(csi_t* csi, const char *path)
 								break;
 					if(pp && *pp!=csi->str && i==MAX_SYSVARS) {
 						if(*pp)
-							*pp=(char *)realloc(*pp,strlen(*pp)+strlen(tmp)+1);
+							*pp=(char *)realloc_or_free(*pp,strlen(*pp)+strlen(tmp)+1);
 						else
-							*pp=(char *)realloc(*pp,strlen(tmp)+1);
+							*pp=(char *)realloc_or_free(*pp,strlen(tmp)+1);
 					}
 					if(pp && *pp)
 						strcat(*pp,tmp);
@@ -499,9 +499,9 @@ int sbbs_t::exec_misc(csi_t* csi, const char *path)
 									break;
 						if(pp && *pp!=csi->str && i==MAX_SYSVARS) {
 							if(*pp)
-								*pp=(char *)realloc(*pp,strlen(*pp)+strlen(tmp)+1);
+								*pp=(char *)realloc_or_free(*pp,strlen(*pp)+strlen(tmp)+1);
 							else
-								*pp=(char *)realloc(*pp,strlen(tmp)+1);
+								*pp=(char *)realloc_or_free(*pp,strlen(tmp)+1);
 						}
 						if(pp && *pp)
 							strcat(*pp,tmp);
@@ -519,9 +519,9 @@ int sbbs_t::exec_misc(csi_t* csi, const char *path)
 								break;
 					if(*pp1!=csi->str && (!*pp1 || i==MAX_SYSVARS)) {
 						if(*pp1)
-							*pp1=(char *)realloc(*pp1,strlen(*pp1)+strlen(*pp2)+1);
+							*pp1=(char *)realloc_or_free(*pp1,strlen(*pp1)+strlen(*pp2)+1);
 						else
-							*pp1=(char *)realloc(*pp1,strlen(*pp2)+1);
+							*pp1=(char *)realloc_or_free(*pp1,strlen(*pp2)+1);
 					}
 					if(*pp1 != NULL)
 						strcat(*pp1,*pp2);
diff --git a/src/sbbs3/fixsmb.c b/src/sbbs3/fixsmb.c
index 54e8420cd4..3c1b9c12ff 100644
--- a/src/sbbs3/fixsmb.c
+++ b/src/sbbs3/fixsmb.c
@@ -232,7 +232,7 @@ int fixsmb(char* sub)
 		}
 		if(!dupe_msgnum) {
 			total++;
-			if((numbers = realloc(numbers, total * sizeof(*numbers))) == NULL) {
+			if((numbers = realloc_or_free(numbers, total * sizeof(*numbers))) == NULL) {
 				fprintf(stderr, "realloc failure: %lu\n", total * sizeof(*numbers));
 				return EXIT_FAILURE;
 			}
diff --git a/src/sbbs3/jsexec.c b/src/sbbs3/jsexec.c
index 6ca4fd6a09..800909b442 100644
--- a/src/sbbs3/jsexec.c
+++ b/src/sbbs3/jsexec.c
@@ -1043,7 +1043,7 @@ long js_exec(const char *fname, const char* buf, char** args)
 			if(line_no==1 && strncmp(line,"#!",2)==0)
 				strcpy(line,"\n");	/* To keep line count correct */
 			len=strlen(line);
-			if((js_buf=realloc(js_buf,js_buflen+len))==NULL) {
+			if((js_buf=realloc_or_free(js_buf,js_buflen+len))==NULL) {
 				lprintf(LOG_ERR,"!Error allocating %lu bytes of memory"
 					,(ulong)(js_buflen+len));
 				if(fp!=stdin)
diff --git a/src/sbbs3/qwk.cpp b/src/sbbs3/qwk.cpp
index 336c69d02c..28c9a3b008 100644
--- a/src/sbbs3/qwk.cpp
+++ b/src/sbbs3/qwk.cpp
@@ -217,7 +217,7 @@ void sbbs_t::update_qwkroute(char *via)
 				if(i<total_qwknodes && qwknode[i].time>t)
 					continue;
 				if(i==total_qwknodes) {
-					if((qwknode=(struct qwknode*)realloc(qwknode,sizeof(struct qwknode)*(i+1)))==NULL) {
+					if((qwknode=(struct qwknode*)realloc_or_free(qwknode,sizeof(struct qwknode)*(i+1)))==NULL) {
 						errormsg(WHERE,ERR_ALLOC,via,sizeof(struct qwknode)*(i+1));
 						break;
 					}
@@ -249,7 +249,7 @@ void sbbs_t::update_qwkroute(char *via)
 			if(!stricmp(qwknode[i].id,node))
 				break;
 		if(i==total_qwknodes) {		/* Not in list */
-			if((qwknode=(struct qwknode*)realloc(qwknode,sizeof(struct qwknode)*(total_qwknodes+1)))==NULL) {
+			if((qwknode=(struct qwknode*)realloc_or_free(qwknode,sizeof(struct qwknode)*(total_qwknodes+1)))==NULL) {
 				errormsg(WHERE,ERR_ALLOC,node,sizeof(struct qwknode)*(total_qwknodes+1));
 				break;
 			}
diff --git a/src/sbbs3/qwknodes.c b/src/sbbs3/qwknodes.c
index 904115d189..a20faa60f0 100644
--- a/src/sbbs3/qwknodes.c
+++ b/src/sbbs3/qwknodes.c
@@ -314,7 +314,7 @@ int main(int argc, char **argv)
 						break;
 				if(l==total_crcs) {
 					total_crcs++;
-					if((crc=(uint32_t *)realloc(crc
+					if((crc=(uint32_t *)realloc_or_free(crc
 						,sizeof(uint32_t)*total_crcs))==NULL) {
 						printf("Error allocating %lu bytes\n"
 							,sizeof(uint32_t)*total_crcs);
diff --git a/src/sbbs3/rechocfg.c b/src/sbbs3/rechocfg.c
index d0e03f2b10..b74552379e 100644
--- a/src/sbbs3/rechocfg.c
+++ b/src/sbbs3/rechocfg.c
@@ -324,7 +324,7 @@ bool sbbsecho_read_ini(sbbsecho_cfg_t* cfg)
 	/******************/
 	str_list_t archivelist = iniGetSectionList(ini, "archive:");
 	cfg->arcdefs = strListCount(archivelist);
-	if((cfg->arcdef = realloc(cfg->arcdef, sizeof(arcdef_t)*cfg->arcdefs)) == NULL) {
+	if((cfg->arcdef = realloc_or_free(cfg->arcdef, sizeof(arcdef_t)*cfg->arcdefs)) == NULL) {
 		strListFree(&archivelist);
 		return false;
 	}
@@ -348,7 +348,7 @@ bool sbbsecho_read_ini(sbbsecho_cfg_t* cfg)
 	if(cfg->sort_nodelist)
 		strListSortAlphaCase(nodelist);
 	cfg->nodecfgs = strListCount(nodelist);
-	if((cfg->nodecfg = realloc(cfg->nodecfg, sizeof(nodecfg_t)*cfg->nodecfgs)) == NULL) {
+	if((cfg->nodecfg = realloc_or_free(cfg->nodecfg, sizeof(nodecfg_t)*cfg->nodecfgs)) == NULL) {
 		strListFree(&nodelist);
 		return false;
 	}
@@ -427,7 +427,7 @@ bool sbbsecho_read_ini(sbbsecho_cfg_t* cfg)
 	/**************/
 	str_list_t echolists = iniGetSectionList(ini, "echolist:");
 	cfg->listcfgs = strListCount(echolists);
-	if((cfg->listcfg = realloc(cfg->listcfg, sizeof(echolist_t)*cfg->listcfgs)) == NULL) {
+	if((cfg->listcfg = realloc_or_free(cfg->listcfg, sizeof(echolist_t)*cfg->listcfgs)) == NULL) {
 		strListFree(&echolists);
 		return false;
 	}
@@ -451,7 +451,7 @@ bool sbbsecho_read_ini(sbbsecho_cfg_t* cfg)
 	/***********/
 	str_list_t domains = iniGetSectionList(ini, "domain:");
 	cfg->domain_count = strListCount(domains);
-	if((cfg->domain_list = realloc(cfg->domain_list, sizeof(struct fido_domain)*cfg->domain_count)) == NULL) {
+	if((cfg->domain_list = realloc_or_free(cfg->domain_list, sizeof(struct fido_domain)*cfg->domain_count)) == NULL) {
 		strListFree(&domains);
 		return false;
 	}
@@ -473,7 +473,7 @@ bool sbbsecho_read_ini(sbbsecho_cfg_t* cfg)
 	/**********/
 	str_list_t robots = iniGetSectionList(ini, "robot:");
 	cfg->robot_count = strListCount(robots);
-	if((cfg->robot_list = realloc(cfg->robot_list, sizeof(struct robot)*cfg->robot_count)) == NULL) {
+	if((cfg->robot_list = realloc_or_free(cfg->robot_list, sizeof(struct robot)*cfg->robot_count)) == NULL) {
 		strListFree(&robots);
 		return false;
 	}
diff --git a/src/sbbs3/sbbsecho.c b/src/sbbs3/sbbsecho.c
index 7f79606468..ce36daa55e 100644
--- a/src/sbbs3/sbbsecho.c
+++ b/src/sbbs3/sbbsecho.c
@@ -1661,7 +1661,7 @@ void alter_areas(str_list_t add_area, str_list_t del_area, fidoaddr_t addr, cons
 
 						++cfg.area[u].links;
 						if((cfg.area[u].link=(fidoaddr_t *)
-							realloc(cfg.area[u].link,sizeof(fidoaddr_t)
+							realloc_or_free(cfg.area[u].link,sizeof(fidoaddr_t)
 							*(cfg.area[u].links)))==NULL) {
 							lprintf(LOG_ERR,"ERROR line %d allocating memory for area "
 								"#%u links.",__LINE__,u+1);
@@ -2440,7 +2440,7 @@ int attachment(const char *bundlename, fidoaddr_t dest, enum attachment_mode mod
 				continue;
 			num_mfncrc++;
 			p=getfname(hdr.subj);
-			if((mfncrc=(uint32_t *)realloc(mfncrc,num_mfncrc*sizeof(uint32_t)))==NULL) {
+			if((mfncrc=(uint32_t *)realloc_or_free(mfncrc,num_mfncrc*sizeof(uint32_t)))==NULL) {
 				lprintf(LOG_ERR,"ERROR line %d allocating %lu for bundle name crc"
 					,__LINE__,num_mfncrc*sizeof(uint32_t));
 				continue;
@@ -3931,7 +3931,7 @@ void gen_psb(addrlist_t *seenbys, addrlist_t *paths, const char *inbuf, uint16_t
 					addr.point=atoi(p2+1);
 				if(!addr.zone)
 					addr.zone=zone; 		/* Was 1 */
-				if((seenbys->addr=(fidoaddr_t *)realloc(seenbys->addr
+				if((seenbys->addr=(fidoaddr_t *)realloc_or_free(seenbys->addr
 					,sizeof(fidoaddr_t)*(seenbys->addrs+1)))==NULL) {
 					lprintf(LOG_ERR,"ERROR line %d allocating memory for message "
 						"seenbys.",__LINE__);
@@ -3951,7 +3951,7 @@ void gen_psb(addrlist_t *seenbys, addrlist_t *paths, const char *inbuf, uint16_t
 		}
 	}
 	else {
-		if((seenbys->addr=(fidoaddr_t *)realloc(seenbys->addr
+		if((seenbys->addr=(fidoaddr_t *)realloc_or_free(seenbys->addr
 			,sizeof(fidoaddr_t)))==NULL) {
 			lprintf(LOG_ERR,"ERROR line %d allocating memory for message seenbys."
 				,__LINE__);
@@ -3992,7 +3992,7 @@ void gen_psb(addrlist_t *seenbys, addrlist_t *paths, const char *inbuf, uint16_t
 					addr.point=atoi(p2+1);
 				if(!addr.zone)
 					addr.zone=zone; 		/* Was 1 */
-				if((paths->addr=(fidoaddr_t *)realloc(paths->addr
+				if((paths->addr=(fidoaddr_t *)realloc_or_free(paths->addr
 					,sizeof(fidoaddr_t)*(paths->addrs+1)))==NULL) {
 					lprintf(LOG_ERR,"ERROR line %d allocating memory for message "
 						"paths.",__LINE__);
@@ -4009,7 +4009,7 @@ void gen_psb(addrlist_t *seenbys, addrlist_t *paths, const char *inbuf, uint16_t
 		}
 	}
 	else {
-		if((paths->addr=(fidoaddr_t *)realloc(paths->addr
+		if((paths->addr=(fidoaddr_t *)realloc_or_free(paths->addr
 			,sizeof(fidoaddr_t)))==NULL) {
 			lprintf(LOG_ERR,"ERROR line %d allocating memory for message paths."
 				,__LINE__);
@@ -6454,7 +6454,7 @@ int main(int argc, char **argv)
 					break;
 				}
 				if((cfg.area[areanum].link=(fidoaddr_t *)
-					realloc(cfg.area[areanum].link
+					realloc_or_free(cfg.area[areanum].link
 					,sizeof(fidoaddr_t)*(cfg.area[areanum].links+1)))==NULL) {
 					printf("\n");
 					lprintf(LOG_ERR,"ERROR allocating memory for area #%u links."
diff --git a/src/sbbs3/scfg/scfg.c b/src/sbbs3/scfg/scfg.c
index 3f1085ee3e..0e5c1e1652 100644
--- a/src/sbbs3/scfg/scfg.c
+++ b/src/sbbs3/scfg/scfg.c
@@ -378,7 +378,7 @@ void set_cfg_filename(const char* hostname)
 	if(hostname == NULL)
 		sbbs_get_ini_fname(cfg.filename, cfg.ctrl_dir);
 	else
-		snprintf(cfg.filename, sizeof cfg.filename, "%ssbbs.%s.ini", cfg.ctrl_dir, hostname);
+		snprintf(cfg.filename, sizeof cfg.filename, "%ssbbs%s%s.ini", cfg.ctrl_dir, *hostname ? ".":"", hostname);
 }
 
 int main(int argc, char **argv)
@@ -1122,7 +1122,7 @@ void txt_cfg()
 				uifc.helpbuf=0;
 				continue;
 			}
-			if((cfg.txtsec=(txtsec_t **)realloc(cfg.txtsec
+			if((cfg.txtsec=(txtsec_t **)realloc_or_free(cfg.txtsec
 				,sizeof(txtsec_t *)*(cfg.total_txtsecs+1)))==NULL) {
 				errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_txtsecs+1);
 				cfg.total_txtsecs=0;
@@ -1311,7 +1311,7 @@ void shell_cfg()
 				uifc.helpbuf=0;
 				continue;
 			}
-			if((cfg.shell=(shell_t **)realloc(cfg.shell
+			if((cfg.shell=(shell_t **)realloc_or_free(cfg.shell
 				,sizeof(shell_t *)*(cfg.total_shells+1)))==NULL) {
 				errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_shells+1);
 				cfg.total_shells=0;
diff --git a/src/sbbs3/scfg/scfgchat.c b/src/sbbs3/scfg/scfgchat.c
index adf8681f8e..0fe46efe9e 100644
--- a/src/sbbs3/scfg/scfgchat.c
+++ b/src/sbbs3/scfg/scfgchat.c
@@ -67,7 +67,7 @@ void page_cfg()
 			if(uifc.input(WIN_MID|WIN_SAV,0,0,"Command Line",str,50
 				,K_EDIT)<1)
 				continue;
-			if((cfg.page=(page_t **)realloc(cfg.page,sizeof(page_t *)*(cfg.total_pages+1)))
+			if((cfg.page=(page_t **)realloc_or_free(cfg.page,sizeof(page_t *)*(cfg.total_pages+1)))
 				==NULL) {
 				errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_pages+1);
 				cfg.total_pages=0;
@@ -266,7 +266,7 @@ void chan_cfg()
 				uifc.helpbuf=0;
 				continue; 
 			}
-			if((cfg.chan=(chan_t **)realloc(cfg.chan,sizeof(chan_t *)*(cfg.total_chans+1)))
+			if((cfg.chan=(chan_t **)realloc_or_free(cfg.chan,sizeof(chan_t *)*(cfg.total_chans+1)))
 				==NULL) {
 				errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_chans+1);
 				cfg.total_chans=0;
@@ -542,7 +542,7 @@ void chatact_cfg(uint setnum)
 			if(uifc.input(WIN_MID|WIN_SAV,0,0,"",out,LEN_CHATACTOUT
 				,K_MSG)<1)
 				continue;
-			if((cfg.chatact=(chatact_t **)realloc(cfg.chatact
+			if((cfg.chatact=(chatact_t **)realloc_or_free(cfg.chatact
 				,sizeof(chatact_t *)*(cfg.total_chatacts+1)))==NULL) {
 				errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_chatacts+1);
 				cfg.total_chatacts=0;
@@ -671,7 +671,7 @@ void guru_cfg()
 				uifc.helpbuf=0;
 				continue; 
 			}
-			if((cfg.guru=(guru_t **)realloc(cfg.guru,sizeof(guru_t *)*(cfg.total_gurus+1)))
+			if((cfg.guru=(guru_t **)realloc_or_free(cfg.guru,sizeof(guru_t *)*(cfg.total_gurus+1)))
 				==NULL) {
 				errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_gurus+1);
 				cfg.total_gurus=0;
@@ -827,7 +827,7 @@ void actsets_cfg()
 			if(uifc.input(WIN_MID|WIN_SAV,0,0,"Chat Action Set Name",str,25
 				,0)<1)
 				continue;
-			if((cfg.actset=(actset_t **)realloc(cfg.actset,sizeof(actset_t *)*(cfg.total_actsets+1)))
+			if((cfg.actset=(actset_t **)realloc_or_free(cfg.actset,sizeof(actset_t *)*(cfg.total_actsets+1)))
 				==NULL) {
 				errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_actsets+1);
 				cfg.total_actsets=0;
diff --git a/src/sbbs3/scfg/scfgnet.c b/src/sbbs3/scfg/scfgnet.c
index c7036cf78d..6150d41572 100644
--- a/src/sbbs3/scfg/scfgnet.c
+++ b/src/sbbs3/scfg/scfgnet.c
@@ -53,9 +53,9 @@ bool new_qhub(unsigned new_qhubnum)
 
 bool new_qhub_sub(qhub_t* qhub, int subnum, sub_t* sub, unsigned confnum)
 {
-	if((qhub->sub=realloc(qhub->sub, sizeof(*qhub->sub)*(qhub->subs+1)))==NULL
-		|| (qhub->conf=(ushort *)realloc(qhub->conf, sizeof(*qhub->conf)*(qhub->subs+1)))==NULL
-		|| (qhub->mode=(uchar *)realloc(qhub->mode, sizeof(*qhub->mode)*(qhub->subs+1)))==NULL) {
+	if((qhub->sub=realloc_or_free(qhub->sub, sizeof(*qhub->sub)*(qhub->subs+1)))==NULL
+		|| (qhub->conf=(ushort *)realloc_or_free(qhub->conf, sizeof(*qhub->conf)*(qhub->subs+1)))==NULL
+		|| (qhub->mode=(uchar *)realloc_or_free(qhub->mode, sizeof(*qhub->mode)*(qhub->subs+1)))==NULL) {
 		/* ToDo: report error */
 		return false;
 	}
@@ -628,7 +628,7 @@ void net_cfg()
 								} else
 									newfaddr = savfaddr;
 
-								if((cfg.faddr=(faddr_t *)realloc(cfg.faddr
+								if((cfg.faddr=(faddr_t *)realloc_or_free(cfg.faddr
 									,sizeof(faddr_t)*(cfg.total_faddrs+1)))==NULL) {
 									errormsg(WHERE,ERR_ALLOC,nulstr
 										,sizeof(faddr_t)*cfg.total_faddrs+1);
diff --git a/src/sbbs3/scfg/scfgxfr1.c b/src/sbbs3/scfg/scfgxfr1.c
index b4972dc989..45574f4696 100644
--- a/src/sbbs3/scfg/scfgxfr1.c
+++ b/src/sbbs3/scfg/scfgxfr1.c
@@ -369,7 +369,7 @@ void xfer_opts()
 						continue;
 					}
 					if(msk == MSK_INS) {
-						if((cfg.fview=(fview_t **)realloc(cfg.fview
+						if((cfg.fview=(fview_t **)realloc_or_free(cfg.fview
 							,sizeof(fview_t *)*(cfg.total_fviews+1)))==NULL) {
 							errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_fviews+1);
 							cfg.total_fviews=0;
@@ -499,7 +499,7 @@ void xfer_opts()
 						continue;
 					}
 					if(msk == MSK_INS) {
-						if((cfg.ftest=(ftest_t **)realloc(cfg.ftest
+						if((cfg.ftest=(ftest_t **)realloc_or_free(cfg.ftest
 							,sizeof(ftest_t *)*(cfg.total_ftests+1)))==NULL) {
 							errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_ftests+1);
 							cfg.total_ftests=0;
@@ -653,7 +653,7 @@ void xfer_opts()
 						continue;
 					}
 					if(msk == MSK_INS) {
-						if((cfg.dlevent=(dlevent_t **)realloc(cfg.dlevent
+						if((cfg.dlevent=(dlevent_t **)realloc_or_free(cfg.dlevent
 							,sizeof(dlevent_t *)*(cfg.total_dlevents+1)))==NULL) {
 							errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_dlevents+1);
 							cfg.total_dlevents=0;
@@ -800,7 +800,7 @@ void xfer_opts()
 						continue;
 					}
 					if(msk == MSK_INS) {
-						if((cfg.fextr=(fextr_t **)realloc(cfg.fextr
+						if((cfg.fextr=(fextr_t **)realloc_or_free(cfg.fextr
 							,sizeof(fextr_t *)*(cfg.total_fextrs+1)))==NULL) {
 							errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_fextrs+1);
 							cfg.total_fextrs=0;
@@ -939,7 +939,7 @@ void xfer_opts()
 						continue;
 					}
 					if(msk == MSK_INS) {
-						if((cfg.fcomp=(fcomp_t **)realloc(cfg.fcomp
+						if((cfg.fcomp=(fcomp_t **)realloc_or_free(cfg.fcomp
 							,sizeof(fcomp_t *)*(cfg.total_fcomps+1)))==NULL) {
 							errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_fcomps+1);
 							cfg.total_fcomps=0;
diff --git a/src/sbbs3/scfg/scfgxfr2.c b/src/sbbs3/scfg/scfgxfr2.c
index 7655e610a0..f8d6804f6b 100644
--- a/src/sbbs3/scfg/scfgxfr2.c
+++ b/src/sbbs3/scfg/scfgxfr2.c
@@ -1021,7 +1021,7 @@ void xfer_cfg()
 							break;
 						if(j==cfg.total_dirs) {
 
-							if((cfg.dir=(dir_t **)realloc(cfg.dir
+							if((cfg.dir=(dir_t **)realloc_or_free(cfg.dir
 								,sizeof(dir_t *)*(cfg.total_dirs+1)))==NULL) {
 								errormsg(WHERE,ERR_ALLOC,"dir",cfg.total_dirs+1);
 								cfg.total_dirs=0;
diff --git a/src/sbbs3/scfg/scfgxtrn.c b/src/sbbs3/scfg/scfgxtrn.c
index 02b4755674..fdade3f7b3 100644
--- a/src/sbbs3/scfg/scfgxtrn.c
+++ b/src/sbbs3/scfg/scfgxtrn.c
@@ -2338,7 +2338,7 @@ int natvpgm_cfg()
 			if(uifc.input(WIN_MID|WIN_SAV,0,0,"Native Program Name",str,12
 				,0)<1)
 				continue;
-			if((cfg.natvpgm=(natvpgm_t **)realloc(cfg.natvpgm
+			if((cfg.natvpgm=(natvpgm_t **)realloc_or_free(cfg.natvpgm
 				,sizeof(natvpgm_t *)*(cfg.total_natvpgms+1)))==NULL) {
 				errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_natvpgms+1);
 				cfg.total_natvpgms=0;
@@ -2640,7 +2640,7 @@ void hotkey_cfg(void)
 				,K_UPPER|K_NOSPACE)<1)
 				continue;
 
-			if((cfg.hotkey=(hotkey_t **)realloc(cfg.hotkey
+			if((cfg.hotkey=(hotkey_t **)realloc_or_free(cfg.hotkey
 				,sizeof(hotkey_t *)*(cfg.total_hotkeys+1)))==NULL) {
 				errormsg(WHERE,ERR_ALLOC,nulstr,cfg.total_hotkeys+1);
 				cfg.total_hotkeys=0;
diff --git a/src/sbbs3/smbutil.c b/src/sbbs3/smbutil.c
index 3f8f226abc..a445aeb935 100644
--- a/src/sbbs3/smbutil.c
+++ b/src/sbbs3/smbutil.c
@@ -212,7 +212,7 @@ void postmsg(char type, char* to, char* to_number, char* to_address,
 		i=fread(buf,1,sizeof(buf),fp);
 		if(i<1)
 			break;
-		if((msgtxt = realloc(msgtxt,msgtxtlen+i+1))==NULL) {
+		if((msgtxt = realloc_or_free(msgtxt,msgtxtlen+i+1))==NULL) {
 			fprintf(errfp,"\n%s!realloc(%ld) failure\n",beep,msgtxtlen+i+1);
 			bail(1);
 		}
diff --git a/src/sbbs3/targets.mk b/src/sbbs3/targets.mk
index 07471770bf..8e0f19a6cd 100644
--- a/src/sbbs3/targets.mk
+++ b/src/sbbs3/targets.mk
@@ -10,6 +10,7 @@ WEBSRVR		= $(LIBODIR)/$(LIBPREFIX)websrvr$(SOFILE)
 MAILSRVR	= $(LIBODIR)/$(LIBPREFIX)mailsrvr$(SOFILE)
 SERVICES	= $(LIBODIR)/$(LIBPREFIX)services$(SOFILE)
 SBBSCON		= $(EXEODIR)/sbbs$(EXEFILE)
+SBBSMONO	= $(EXEODIR)/sbbsmono$(EXEFILE)
 JSEXEC		= $(EXEODIR)/jsexec$(EXEFILE)
 JSDOOR		= $(EXEODIR)/jsdoor$(EXEFILE)
 NODE		= $(EXEODIR)/node$(EXEFILE)
@@ -58,7 +59,9 @@ console:	$(JS_DEPS) xpdev-mt smblib \
 		dlls \
 		$(SBBSCON) $(JSEXEC)
 
-utils:		scfg uedit umonitor $(UTILS)
+utils:	smblib xpdev-mt xpdev ciolib-mt uifc-mt \
+		$(LIBODIR) $(OBJODIR) $(MTOBJODIR) $(EXEODIR) \
+		$(UTILS)
 
 gtkutils: gtkmonitor gtkchat gtkuseredit gtkuserlist
 
@@ -66,6 +69,10 @@ dlls:	$(JS_DEPS) smblib xpdev-mt \
 		$(MTOBJODIR) $(LIBODIR) \
 		$(SBBS) $(FTPSRVR) $(MAILSRVR) $(SERVICES)
 
+mono:	xpdev-mt smblib \
+		$(MTOBJODIR) $(EXEODIR) \
+		$(SBBSMONO)
+
 .PHONY: scfg
 scfg:
 	$(MAKE) -C scfg $(MAKEFLAGS)
@@ -142,6 +149,7 @@ $(WEBSRVR):
 $(MAILSRVR):
 $(SERVICES): 
 $(SBBSCON): $(XPDEV-MT_LIB) $(SMBLIB)
+$(SBBSMONO): $(XPDEV-MT_LIB) $(SMBLIB)
 $(JSEXEC): $(XPDEV-MT_LIB) $(SMBLIB)
 $(JSDOOR): $(XPDEV-MT_LIB)
 $(NODE): $(XPDEV_LIB)
diff --git a/src/xpdev/genwrap.c b/src/xpdev/genwrap.c
index bccfadfd8e..b56d1c8a09 100644
--- a/src/xpdev/genwrap.c
+++ b/src/xpdev/genwrap.c
@@ -1117,7 +1117,7 @@ BOOL terminate_pid(pid_t pid)
 
 /****************************************************************************/
 /* Re-entrant (thread-safe) version of strerror()							*/
-/* GNU (not POSIX) inspired API											*/
+/* GNU (not POSIX) inspired API												*/
 /****************************************************************************/
 char* safe_strerror(int errnum, char *buf, size_t buflen)
 {
@@ -1136,3 +1136,15 @@ char* safe_strerror(int errnum, char *buf, size_t buflen)
 #endif
 	return buf;
 }
+
+/****************************************************************************/
+/* Common realloc mistake: 'p' nulled but not freed upon failure			*/
+/* [memleakOnRealloc]														*/
+/****************************************************************************/
+void* realloc_or_free(void* p, size_t size)
+{
+	void* n = realloc(p, size);
+	if(n == NULL)
+		free(p);
+	return n;
+}
diff --git a/src/xpdev/genwrap.h b/src/xpdev/genwrap.h
index ea118bcacf..4556d437a4 100644
--- a/src/xpdev/genwrap.h
+++ b/src/xpdev/genwrap.h
@@ -394,6 +394,8 @@ msclock_t	msclock(void);
 DLLEXPORT BOOL		check_pid(pid_t);
 DLLEXPORT BOOL		terminate_pid(pid_t);
 
+DLLEXPORT void*		realloc_or_free(void* p, size_t size);
+
 #if defined(__cplusplus)
 }
 #endif
-- 
GitLab