Repository: AllStarLink/app_rpt
Pull request #544: app_rpt.c: use ast_str for macrobuf
Author: login: mkmer
Labels: name: code quality
description: Improvments around code quality without functional changes
Created: 2025-03-07T13:40:21Z | Merged: 2025-03-19T17:54:22Z

Refactor `myrpt->macrobuf` to use `ast_str*`. 
Create function `macro_append()` for securely adding commands to the buffer.
 


Diff:
diff --git a/apps/app_rpt.c b/apps/app_rpt.c
index c701e036f..d5683bb8c 100644
--- a/apps/app_rpt.c
+++ b/apps/app_rpt.c
@@ -834,17 +834,10 @@ void rpt_event_process(struct rpt *myrpt)
 		if (!cmd) {
 			continue;
 		}
-		if (action == 'F') {	/* execute a function */
-			rpt_mutex_lock(&myrpt->lock);
-			if ((MAXMACRO - strlen(myrpt->macrobuf)) >= strlen(cmd)) {
-				ast_verb(3, "Event on node %s doing macro %s for condition %s\n", myrpt->name, cmd, v->value);
-				myrpt->macrotimer = MACROTIME;
-				strncat(myrpt->macrobuf, cmd, MAXMACRO - 1);
-			} else {
-				ast_log(LOG_WARNING, "Could not execute event %s for %s: Macro buffer overflow\n", cmd, argv[1]);
-			}
-			rpt_mutex_unlock(&myrpt->lock);
-		} else if (action == 'C') {	/* execute a command */
+		if (action == 'F') { /* execute a function */
+			ast_verb(3, "Event on node %s doing macro %s for condition %s\n", myrpt->name, cmd, v->value);
+			macro_append(myrpt, cmd);
+		} else if (action == 'C') { /* execute a command */
 			/* make a local copy of the value of this entry */
 			myval = ast_strdupa(cmd);
 			/* separate out specification into comma-delimited fields */
@@ -886,7 +879,7 @@ void rpt_event_process(struct rpt *myrpt)
 				ast_log(LOG_WARNING, "Could not execute event %s for %s: Command buffer in use\n", cmd, argv[1]);
 			}
 			rpt_mutex_unlock(&myrpt->lock);
-		} else if (action == 'S') {	/* execute a shell command */
+		} else if (action == 'S') { /* execute a shell command */
 			char *cp;
 
 			ast_verb(3, "Event on node %s doing shell command %s for condition %s\n", myrpt->name, cmd, v->value);
@@ -2568,15 +2561,8 @@ static void do_scheduler(struct rpt *myrpt)
 		}
 		if (myrpt->linkactivitytimer >= myrpt->p.lnkacttime) {
 			/* Execute lnkactmacro */
-			if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(myrpt->p.lnkactmacro)) {
-				ast_log(LOG_WARNING, "Link Activity timer could not execute macro %s: Macro buffer full\n", myrpt->p.lnkactmacro);
-			} else {
-				ast_debug(5, "Executing link activity timer macro %s\n", myrpt->p.lnkactmacro);
-				rpt_mutex_lock(&myrpt->lock);
-				myrpt->macrotimer = MACROTIME;
-				strncat(myrpt->macrobuf, myrpt->p.lnkactmacro, MAXMACRO - 1);
-				rpt_mutex_unlock(&myrpt->lock);
-			}
+			ast_debug(5, "Executing link activity timer macro %s\n", myrpt->p.lnkactmacro);
+			macro_append(myrpt, myrpt->p.lnkactmacro);
 			myrpt->linkactivitytimer = 0;
 			myrpt->linkactivityflag = 0;
 		}
@@ -2588,13 +2574,8 @@ static void do_scheduler(struct rpt *myrpt)
 		else {
 			myrpt->rptinacttimer = 0;
 			myrpt->rptinactwaskeyedflag = 0;
-			if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(myrpt->p.rptinactmacro)) {
-				ast_log(LOG_WARNING, "Rpt inactivity timer could not execute macro %s: Macro buffer full\n", myrpt->p.rptinactmacro);
-			} else {
-				ast_debug(5, "Executing rpt inactivity timer macro %s\n", myrpt->p.rptinactmacro);
-				myrpt->macrotimer = MACROTIME;
-				strncat(myrpt->macrobuf, myrpt->p.rptinactmacro, MAXMACRO - 1);
-			}
+			ast_debug(5, "Executing rpt inactivity timer macro %s\n", myrpt->p.rptinactmacro);
+			macro_append(myrpt, myrpt->p.rptinactmacro);
 		}
 	}
 
@@ -2662,14 +2643,9 @@ static void do_scheduler(struct rpt *myrpt)
 			val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.macro, skedlist->name);
 			if (!val) {
 				ast_log(LOG_WARNING, "Scheduler could not find macro %s\n", skedlist->name);
-				return;			/* Macro not found */
+				return; /* Macro not found */
 			}
-			if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(val)) {
-				ast_log(LOG_WARNING, "Scheduler could not execute macro %s: Macro buffer full\n", skedlist->name);
-				return;			/* Macro buffer full */
-			}
-			myrpt->macrotimer = MACROTIME;
-			strncat(myrpt->macrobuf, val, MAXMACRO - 1);
+			macro_append(myrpt, val);
 		} else {
 			ast_log(LOG_WARNING, "Malformed scheduler entry in rpt.conf: %s = %s\n", skedlist->name, skedlist->value);
 		}
@@ -3785,15 +3761,7 @@ static inline int rxchannel_read(struct rpt *myrpt, const int lasttx)
 		x = ast_tvdiff_ms(ast_tvnow(), myrpt->lastdtmftime);
 		if ((myrpt->p.litzcmd) && (x >= myrpt->p.litztime) && strchr(myrpt->p.litzchar, c)) {
 			ast_debug(1, "Doing litz command %s on node %s\n", myrpt->p.litzcmd, myrpt->name);
-			rpt_mutex_lock(&myrpt->lock);
-			if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(myrpt->p.litzcmd)) {
-				rpt_mutex_unlock(&myrpt->lock);
-				return 0;
-			}
-			myrpt->macrotimer = MACROTIME;
-			strncat(myrpt->macrobuf, myrpt->p.litzcmd, MAXMACRO - 1);
-			rpt_mutex_unlock(&myrpt->lock);
-
+			macro_append(myrpt, myrpt->p.litzcmd);
 			return 0;
 		}
 		mute_frame_helper(myrpt);
@@ -3852,7 +3820,7 @@ static inline int rxchannel_read(struct rpt *myrpt, const int lasttx)
 			myrpt->elketimer = 0;
 			myrpt->localoverride = 0;
 			if (f->datalen && f->data.ptr) {
-				char *val, busy = 0;
+				char *val;
 
 				send_link_pl(myrpt, f->data.ptr);
 
@@ -3872,33 +3840,19 @@ static inline int rxchannel_read(struct rpt *myrpt, const int lasttx)
 					char val[16];
 					strcpy(val, "*6");
 					myrpt->macropatch = 1;
+					macro_append(myrpt, val);
 					rpt_mutex_lock(&myrpt->lock);
-					if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(val)) {
-						busy = 1;
-					}
-					if (!busy) {
-						myrpt->macrotimer = MACROTIME;
-						strncat(myrpt->macrobuf, val, MAXMACRO - 1);
-						if (!busy)
-							strcpy(myrpt->lasttone, (char *) f->data.ptr);
-					}
+					ast_copy_string(myrpt->lasttone, (char *) f->data.ptr, sizeof(myrpt->lasttone));
 					rpt_mutex_unlock(&myrpt->lock);
 				} else if (strcmp((char *) f->data.ptr, myrpt->lasttone)) {
 					val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.tonemacro, (char *) f->data.ptr);
 					if (val) {
 						ast_debug(1, "Tone %s doing %s on node %s\n", (char *) f->data.ptr, val, myrpt->name);
-						rpt_mutex_lock(&myrpt->lock);
-						if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(val)) {
-							busy = 1;
-						}
-						if (!busy) {
-							myrpt->macrotimer = MACROTIME;
-							strncat(myrpt->macrobuf, val, MAXMACRO - 1);
-						}
-						rpt_mutex_unlock(&myrpt->lock);
+						macro_append(myrpt, val);
 					}
-					if (!busy)
-						strcpy(myrpt->lasttone, (char *) f->data.ptr);
+					rpt_mutex_lock(&myrpt->lock);
+					ast_copy_string(myrpt->lasttone, (char *) f->data.ptr, sizeof(myrpt->lasttone));
+					rpt_mutex_unlock(&myrpt->lock);
 				}
 			} else {
 				myrpt->lasttone[0] = 0;
@@ -4696,7 +4650,8 @@ static inline void voxtostate_to_voxtotimer(struct rpt *myrpt)
 static void *rpt(void *this)
 {
 	struct rpt *myrpt = this;
-	char *idtalkover, c, myfirst;
+	char *idtalkover, c, myfirst, *str;
+	int len;
 	int ms = MSWAIT, lasttx = 0, lastexttx = 0, lastpatchup = 0, val, identqueued, othertelemqueued;
 	int tailmessagequeued, ctqueued, lastmyrx, localmsgqueued;
 	struct ast_channel *who;
@@ -4711,6 +4666,13 @@ static void *rpt(void *this)
 	sprintf(tmpstr, "%s/%s", myrpt->p.archivedir, myrpt->name);
 	mkdir(tmpstr, 0775);
 	myrpt->ready = 0;
+	if (!myrpt->macrobuf) {
+		myrpt->macrobuf = ast_str_create(MAXMACRO);
+		if (!myrpt->macrobuf) {
+			myrpt->rpt_thread = AST_PTHREADT_STOP;
+			pthread_exit(NULL);
+		}
+	}
 	rpt_mutex_lock(&myrpt->lock);
 	myrpt->remrx = 0;
 	myrpt->remote_webtransceiver = 0;
@@ -4826,6 +4788,7 @@ static void *rpt(void *this)
 	myrpt->vote_counter = 10;
 	myrpt->rptinactwaskeyedflag = 0;
 	myrpt->rptinacttimer = 0;
+
 	if (myrpt->p.rxburstfreq) {
 #ifdef NATIVE_DSP
 		if (!(myrpt->dsp = ast_dsp_new())) {
@@ -4842,7 +4805,7 @@ static void *rpt(void *this)
 #endif
 	}
 	if (myrpt->p.startupmacro) {
-		snprintf(myrpt->macrobuf, MAXMACRO - 1, "PPPP%s", myrpt->p.startupmacro);
+		ast_str_set(&myrpt->macrobuf, 0, "PPPP%s", myrpt->p.startupmacro);
 	}
 	/* @@@@@@@ UNLOCK @@@@@@@ */
 	rpt_mutex_unlock(&myrpt->lock);
@@ -5357,12 +5320,15 @@ static void *rpt(void *this)
 			process_command(myrpt);
 		}
 
-		c = myrpt->macrobuf[0];
+		str = ast_str_buffer(myrpt->macrobuf);
+		len = ast_str_strlen(myrpt->macrobuf);
+		c = str[0];
 		time(&t);
 		if (c && !myrpt->macrotimer && starttime && t > starttime) {
 			char cin = c & 0x7f;
 			myrpt->macrotimer = MACROTIME;
-			memmove(myrpt->macrobuf, myrpt->macrobuf + 1, MAXMACRO - 1);
+			ast_copy_string(str, str + 1, len - 1);
+			ast_str_truncate(myrpt->macrobuf, -1);
 			if ((cin == 'p') || (cin == 'P'))
 				myrpt->macrotimer = MACROPTIME;
 			rpt_mutex_unlock(&myrpt->lock);
@@ -5565,6 +5531,10 @@ static int load_config(int reload)
 			ast_log(LOG_ERROR, "Attempting to add repeater node %s would exceed max. number of repeaters (%d)\n", this, MAXRPTS);
 			continue;
 		}
+		if (rpt_vars[n].macrobuf) {
+			ast_free(rpt_vars[n].macrobuf);
+			rpt_vars[n].macrobuf = NULL;
+		}
 		memset(&rpt_vars[n], 0, sizeof(rpt_vars[n]));
 		val = (char *) ast_variable_retrieve(cfg, this, "rxchannel");
 		if (val) {
@@ -6146,14 +6116,14 @@ static int rpt_exec(struct ast_channel *chan, const char *data)
 	int res = -1, i, rem_totx, rem_rx, remkeyed, n, phone_mode = 0;
 	int iskenwood_pci4, authtold, authreq, setting, notremming, reming;
 	int dtmfed, phone_vox = 0, phone_monitor = 0;
-	char tmp[256], keyed = 0, keyed1 = 0;
-	char *options, *stringp, *callstr, c, *altp, *memp;
+	char tmp[TMP_SIZE], keyed = 0, keyed1 = 0;
+	char *options, *stringp, *callstr, c, *altp, *memp, *str;
 	char sx[320], myfirst, *b, *b1;
 	struct rpt *myrpt;
 	struct ast_channel *who;
 	struct ast_channel *cs[20];
 	struct rpt_link *l;
-	int ms, elap, myrx;
+	int ms, elap, myrx, len;
 	time_t last_timeout_warning;
 	struct rpt_tele *telem;
 	int numlinks;
@@ -6989,7 +6959,7 @@ static int rpt_exec(struct ast_channel *chan, const char *data)
 	myrpt->hfscanmode = 0;
 	myrpt->hfscanstatus = 0;
 	if (myrpt->p.startupmacro) {
-		snprintf(myrpt->macrobuf, MAXMACRO - 1, "PPPP%s", myrpt->p.startupmacro);
+		ast_str_set(&myrpt->macrobuf, 0, "PPPP%s", myrpt->p.startupmacro);
 	}
 	time(&myrpt->start_time);
 	myrpt->last_activity_time = myrpt->start_time;
@@ -7002,6 +6972,16 @@ static int rpt_exec(struct ast_channel *chan, const char *data)
 	myrpt->lastitx = !myrpt->lastitx;
 	myrpt->tunerequest = 0;
 	myrpt->tunetx = 0;
+	if (!myrpt->macrobuf) {
+		myrpt->macrobuf = ast_str_create(MAXMACRO);
+		if (!myrpt->macrobuf) {
+			rpt_mutex_unlock(&myrpt->lock);
+			rpt_hangup_rx_tx(myrpt);
+			rpt_hangup(myrpt, RPT_PCHAN);
+			ao2_ref(cap, -1);
+			pthread_exit(NULL);
+		}
+	}
 	rpt_mutex_unlock(&myrpt->lock);
 	ast_set_write_format(chan, ast_format_slin);
 	ast_set_read_format(chan, ast_format_slin);
@@ -7323,10 +7303,13 @@ static int rpt_exec(struct ast_channel *chan, const char *data)
 			}
 		}
 		rpt_mutex_lock(&myrpt->lock);
-		c = myrpt->macrobuf[0];
+		str = ast_str_buffer(myrpt->macrobuf);
+		len = ast_str_strlen(myrpt->macrobuf);
+		c = str[0];
 		if (c && !myrpt->macrotimer) {
 			myrpt->macrotimer = MACROTIME;
-			memmove(myrpt->macrobuf, myrpt->macrobuf + 1, MAXMACRO - 1);
+			ast_copy_string(str, str + 1, len - 1);
+			ast_str_truncate(myrpt->macrobuf, -1);
 			if ((c == 'p') || (c == 'P'))
 				myrpt->macrotimer = MACROPTIME;
 			rpt_mutex_unlock(&myrpt->lock);
diff --git a/apps/app_rpt/app_rpt.h b/apps/app_rpt/app_rpt.h
index 51983fb1f..7a664eead 100644
--- a/apps/app_rpt/app_rpt.h
+++ b/apps/app_rpt/app_rpt.h
@@ -69,24 +69,24 @@ typedef struct {
 #endif
 
 /* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
-#define	MAXDTMF 32
-#define	MAXMACRO 2048
-#define	MAXLINKLIST 5120
-#define	LINKLISTTIME 10000
-#define	LINKLISTSHORTTIME 200
-#define	LINKPOSTTIME 30000
-#define	LINKPOSTSHORTTIME 200
-#define	KEYPOSTTIME 30000
-#define	KEYPOSTSHORTTIME 200
-#define	KEYTIMERTIME 250
-#define	MACROTIME 100
-#define	MACROPTIME 500
-#define	DTMF_TIMEOUT 3
-#define	KENWOOD_RETRIES 5
-#define	TOPKEYN 32
-#define	TOPKEYWAIT 3
-#define	TOPKEYMAXSTR 30
-#define	NEWKEYTIME 2000
+#define MAXDTMF 32
+#define MAXMACRO 512
+#define MAXLINKLIST 5120
+#define LINKLISTTIME 10000
+#define LINKLISTSHORTTIME 200
+#define LINKPOSTTIME 30000
+#define LINKPOSTSHORTTIME 200
+#define KEYPOSTTIME 30000
+#define KEYPOSTSHORTTIME 200
+#define KEYTIMERTIME 250
+#define MACROTIME 100
+#define MACROPTIME 500
+#define DTMF_TIMEOUT 3
+#define KENWOOD_RETRIES 5
+#define TOPKEYN 32
+#define TOPKEYWAIT 3
+#define TOPKEYMAXSTR 30
+#define NEWKEYTIME 2000
 
 #define	AUTHTELLTIME 7000
 #define	AUTHTXTIME 1000
@@ -327,9 +327,9 @@ struct rpt_chan_stat {
 #define MAX_STAT_LINKS 256
 #define POLITEID 30000
 #define FUNCTDELAY 1500
-
-#define	MAXXLAT 20
-#define	MAXXLATTIME 3
+#define TMP_SIZE 256 /* Size of tmp char* */
+#define MAXXLAT 20
+#define MAXXLATTIME 3
 
 #define MAX_SYSSTATES 10
 
@@ -748,7 +748,7 @@ struct rpt {
 	char tounkeyed;
 	char tonotify;
 	char dtmfbuf[MAXDTMF];
-	char macrobuf[MAXMACRO];
+	struct ast_str *macrobuf;
 	char rem_dtmfbuf[MAXDTMF];
 	char lastdtmfcommand[MAXDTMF];
 	char cmdnode[50];
diff --git a/apps/app_rpt/rpt_cli.c b/apps/app_rpt/rpt_cli.c
index 7b46b119d..5bd4bc333 100644
--- a/apps/app_rpt/rpt_cli.c
+++ b/apps/app_rpt/rpt_cli.c
@@ -734,16 +734,7 @@ static int rpt_do_fun(int fd, int argc, const char *const *argv)
 	for (i = 0; i < nrpts; i++) {
 		if (!strcmp(argv[2], rpt_vars[i].name)) {
 			struct rpt *myrpt = &rpt_vars[i];
-			rpt_mutex_lock(&myrpt->lock);
-			if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(argv[3])) {
-				rpt_mutex_unlock(&myrpt->lock);
-				busy = 1;
-			}
-			if (!busy) {
-				myrpt->macrotimer = MACROTIME;
-				strncat(myrpt->macrobuf, argv[3], MAXMACRO - 1);
-			}
-			rpt_mutex_unlock(&myrpt->lock);
+			macro_append(myrpt, argv[3]);
 		}
 	}
 	if (busy) {
diff --git a/apps/app_rpt/rpt_config.c b/apps/app_rpt/rpt_config.c
index 59ea36980..0eb0ef0dc 100644
--- a/apps/app_rpt/rpt_config.c
+++ b/apps/app_rpt/rpt_config.c
@@ -1317,29 +1317,19 @@ void load_rpt_vars(int n, int init)
 
 int rpt_push_alt_macro(struct rpt *myrpt, char *sptr)
 {
-	int busy = 0;
-
-	rpt_mutex_lock(&myrpt->lock);
-	if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(sptr)) {
-		rpt_mutex_unlock(&myrpt->lock);
-		busy = 1;
-	}
-	if (!busy) {
-		int x;
-		ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
-		myrpt->macrotimer = MACROTIME;
-		for (x = 0; *(sptr + x); x++) {
-			myrpt->macrobuf[x] = *(sptr + x) | 0x80;
-		}
-		*(sptr + x) = 0;
-	}
-	rpt_mutex_unlock(&myrpt->lock);
+	char *altstr, *cp;
 
-	if (busy) {
-		ast_log(LOG_WARNING, "Function decoder busy on app_rpt command macro.\n");
+	ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
+	altstr = ast_strdup(sptr);
+	if (!altstr) {
+		return -1;
 	}
-
-	return busy;
+	for (cp = altstr; *cp; cp++) {
+		*cp |= 0x80;
+	}
+	macro_append(myrpt, altstr);
+	ast_free(altstr);
+	return 0;
 }
 
 void rpt_update_boolean(struct rpt *myrpt, char *varname, int newval)
diff --git a/apps/app_rpt/rpt_functions.c b/apps/app_rpt/rpt_functions.c
index 6800bea91..3cd48a46a 100644
--- a/apps/app_rpt/rpt_functions.c
+++ b/apps/app_rpt/rpt_functions.c
@@ -1113,16 +1113,7 @@ int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_s
 		rpt_telemetry(myrpt, MACRO_NOTFOUND, NULL);
 		return DC_COMPLETE;
 	}
-	rpt_mutex_lock(&myrpt->lock);
-	if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(val)) {
-		rpt_mutex_unlock(&myrpt->lock);
-		rpt_telem_select(myrpt, command_source, mylink);
-		rpt_telemetry(myrpt, MACRO_BUSY, NULL);
-		return DC_ERROR;
-	}
-	myrpt->macrotimer = MACROTIME;
-	strncat(myrpt->macrobuf, val, MAXMACRO - 1);
-	rpt_mutex_unlock(&myrpt->lock);
+	macro_append(myrpt, val);
 	return DC_COMPLETE;
 }
 
diff --git a/apps/app_rpt/rpt_mdc1200.c b/apps/app_rpt/rpt_mdc1200.c
index dcb39df6f..96df09af8 100644
--- a/apps/app_rpt/rpt_mdc1200.c
+++ b/apps/app_rpt/rpt_mdc1200.c
@@ -17,7 +17,7 @@
 #include "app_rpt.h"
 
 #include "rpt_mdc1200.h"
-
+#include "rpt_utils.h"
 #include "rpt_lock.h"
 #include "rpt_config.h"
 #include "rpt_link.h"
@@ -160,10 +160,9 @@ static const char *my_variable_match(const struct ast_config *config, const char
 
 void mdc1200_cmd(struct rpt *myrpt, char *data)
 {
-	char busy, *myval;
+	char *myval;
 	int i;
 
-	busy = 0;
 	if ((data[0] == 'I') && (!strcmp(data, myrpt->lastmdc)))
 		return;
 	myval = (char *) my_variable_match(myrpt->cfg, myrpt->p.mdcmacro, data);
@@ -178,22 +177,15 @@ void mdc1200_cmd(struct rpt *myrpt, char *data)
 		}
 		if (!myrpt->keyed)
 			return;
-		rpt_mutex_lock(&myrpt->lock);
-		if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(myval)) {
-			rpt_mutex_unlock(&myrpt->lock);
-			busy = 1;
-		}
-		if (!busy) {
-			myrpt->macrotimer = MACROTIME;
-			strncat(myrpt->macrobuf, myval, MAXMACRO - 1);
-		}
-		rpt_mutex_unlock(&myrpt->lock);
+		macro_append(myrpt, myval);
 	}
-	if ((data[0] == 'I') && (!busy))
-		strcpy(myrpt->lastmdc, data);
+	if (data[0] == 'I') {
+		ast_copy_string(myrpt->lastmdc, data, sizeof(myrpt->lastmdc));
+	}
+	return;
 }
 
-#ifdef	_MDC_ENCODE_H_
+#ifdef _MDC_ENCODE_H_
 
 void mdc1200_ack_status(struct rpt *myrpt, short UnitID)
 {
diff --git a/apps/app_rpt/rpt_uchameleon.c b/apps/app_rpt/rpt_uchameleon.c
index e40d07bc6..f8c0a3c90 100644
--- a/apps/app_rpt/rpt_uchameleon.c
+++ b/apps/app_rpt/rpt_uchameleon.c
@@ -87,7 +87,7 @@ int uchameleon_connect(struct daq_entry_tag *t)
 void uchameleon_alarm_handler(struct daq_pin_entry_tag *p)
 {
 	char *valuecopy;
-	int i, busy;
+	int i;
 	char *s;
 	char *argv[7];
 	int argc;
@@ -107,31 +107,18 @@ void uchameleon_alarm_handler(struct daq_pin_entry_tag *p)
 	 * high function: argv[5]
 	 *
 	 */
-	i = busy = 0;
+	i = 0;
 	s = (p->value) ? argv[5] : argv[4];
 	if ((argc == 6) && (s[0] != '-')) {
 		for (i = 0; i < nrpts; i++) {
 			if (!strcmp(argv[3], rpt_vars[i].name)) {
-
 				struct rpt *myrpt = &rpt_vars[i];
-				rpt_mutex_lock(&myrpt->lock);
-				if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(s)) {
-					rpt_mutex_unlock(&myrpt->lock);
-					busy = 1;
-				}
-				if (!busy) {
-					myrpt->macrotimer = MACROTIME;
-					strncat(myrpt->macrobuf, s, MAXMACRO - 1);
-				}
-				rpt_mutex_unlock(&myrpt->lock);
-
+				macro_append(myrpt, s);
 			}
 		}
 	}
 	if (argc != 6) {
 		ast_log(LOG_WARNING, "Not enough arguments to process alarm\n");
-	} else if (busy) {
-		ast_log(LOG_WARNING, "Function decoder busy while processing alarm");
 	}
 	ast_free(valuecopy);
 }
diff --git a/apps/app_rpt/rpt_utils.c b/apps/app_rpt/rpt_utils.c
index fde4c0927..02b4bd5ec 100644
--- a/apps/app_rpt/rpt_utils.c
+++ b/apps/app_rpt/rpt_utils.c
@@ -228,3 +228,13 @@ time_t rpt_time_monotonic(void)
 	
 	return ts.tv_sec;
 }
+
+int macro_append(struct rpt *myrpt, const char *cmd)
+{
+	int res;
+	rpt_mutex_lock(&myrpt->lock);
+	myrpt->macrotimer = MACROTIME;
+	res = ast_str_append(&myrpt->macrobuf, 0, "%s", cmd);
+	rpt_mutex_unlock(&myrpt->lock);
+	return res;
+}
diff --git a/apps/app_rpt/rpt_utils.h b/apps/app_rpt/rpt_utils.h
index 1caa0a85f..a8a7d35bf 100644
--- a/apps/app_rpt/rpt_utils.h
+++ b/apps/app_rpt/rpt_utils.h
@@ -73,3 +73,10 @@ time_t rpt_mktime(struct ast_tm *tm, const char *zone);
  * \retval		Monotonic seconds.
  */
 time_t rpt_time_monotonic(void);
+
+/*! \brief Append a command to the macro buffer
+ * \param myrpt Pointer to the rpt structure
+ * \param cmd Command to append
+ * \retval 0 on success, -1 on failure (result of ast_str_append)
+ */
+int macro_append(struct rpt *myrpt, const char *cmd);


Reviews:
user: login: InterLinked1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-07T15:46:09Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-07T18:44:24Z
user: login: InterLinked1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-07T20:28:31Z
user: login: Allan-N
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-07T23:48:11Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T00:08:09Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T00:15:35Z
user: login: Allan-N
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T00:32:56Z
user: login: Allan-N
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T00:37:04Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T13:17:19Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T13:19:18Z
user: login: InterLinked1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-08T13:46:49Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T14:06:57Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T14:11:47Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T14:21:40Z
user: login: Allan-N
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T14:36:41Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T14:48:17Z
user: login: Allan-N
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T14:58:32Z
user: login: Allan-N
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-09T00:32:46Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-09T13:11:18Z
user: login: InterLinked1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-09T14:39:43Z
user: login: mkmer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-09T14:51:50Z

Review comments:
diff_hunk: @@ -4769,6 +4722,12 @@ static void *rpt(void *this)
 	sprintf(tmpstr, "%s/%s", myrpt->p.archivedir, myrpt->name);
 	mkdir(tmpstr, 0775);
 	myrpt->ready = 0;
+	myrpt->macrobuf = ast_str_create(MAXMACRO);
+	if (!myrpt->macrobuf) {
+		ast_log(LOG_ERROR, "Failed to allocate macro buffer\n");
path: apps/app_rpt.c
user: login: InterLinked1
body: no error message needed
created_at: 2025-03-07T15:43:53Z
author_association: MEMBER
diff_hunk: @@ -1318,29 +1318,17 @@ void load_rpt_vars(int n, int init)
 
 int rpt_push_alt_macro(struct rpt *myrpt, char *sptr)
 {
-	int busy = 0;
-
-	rpt_mutex_lock(&myrpt->lock);
-	if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(sptr)) {
-		rpt_mutex_unlock(&myrpt->lock);
-		busy = 1;
-	}
-	if (!busy) {
-		int x;
-		ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
-		myrpt->macrotimer = MACROTIME;
-		for (x = 0; *(sptr + x); x++) {
-			myrpt->macrobuf[x] = *(sptr + x) | 0x80;
-		}
-		*(sptr + x) = 0;
-	}
-	rpt_mutex_unlock(&myrpt->lock);
+	int x;
+	char tmp[TMP_SIZE];
 
-	if (busy) {
-		ast_log(LOG_WARNING, "Function decoder busy on app_rpt command macro.\n");
+	ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
+	for (x = 0; *(sptr + x) && (x < sizeof(tmp) - 1); x++) {
+		tmp[x] = *(sptr + x) | 0x80;
 	}
-
-	return busy;
+	*(sptr + x) = 0; /* Do we really need to do this? it was 0 to end loop*/
path: apps/app_rpt/rpt_config.c
user: login: InterLinked1
body: The condition is `*(sptr + x) && (x < sizeof(tmp) - 1)` so if the latter were not true, I don't think this is necessarily true
created_at: 2025-03-07T15:45:26Z
author_association: MEMBER
diff_hunk: @@ -228,3 +228,12 @@ time_t rpt_time_monotonic(void)
 	
 	return ts.tv_sec;
 }
+
+int macro_append(struct rpt *myrpt, const char *cmd) {
path: apps/app_rpt/rpt_utils.c
user: login: InterLinked1
body: Opening { should be on its line
created_at: 2025-03-07T15:45:44Z
author_association: MEMBER
diff_hunk: @@ -1318,29 +1318,17 @@ void load_rpt_vars(int n, int init)
 
 int rpt_push_alt_macro(struct rpt *myrpt, char *sptr)
 {
-	int busy = 0;
-
-	rpt_mutex_lock(&myrpt->lock);
-	if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(sptr)) {
-		rpt_mutex_unlock(&myrpt->lock);
-		busy = 1;
-	}
-	if (!busy) {
-		int x;
-		ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
-		myrpt->macrotimer = MACROTIME;
-		for (x = 0; *(sptr + x); x++) {
-			myrpt->macrobuf[x] = *(sptr + x) | 0x80;
-		}
-		*(sptr + x) = 0;
-	}
-	rpt_mutex_unlock(&myrpt->lock);
+	int x;
+	char tmp[TMP_SIZE];
 
-	if (busy) {
-		ast_log(LOG_WARNING, "Function decoder busy on app_rpt command macro.\n");
+	ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
+	for (x = 0; *(sptr + x) && (x < sizeof(tmp) - 1); x++) {
+		tmp[x] = *(sptr + x) | 0x80;
 	}
-
-	return busy;
+	*(sptr + x) = 0; /* Do we really need to do this? it was 0 to end loop*/
path: apps/app_rpt/rpt_config.c
user: login: mkmer
body: Well, I added the size verification after I added the comment.  Either way, sptr is the input, tmp is the output.  I don't think we should be changing it at all. 
created_at: 2025-03-07T18:44:23Z
author_association: COLLABORATOR
diff_hunk: @@ -1318,29 +1318,17 @@ void load_rpt_vars(int n, int init)
 
 int rpt_push_alt_macro(struct rpt *myrpt, char *sptr)
 {
-	int busy = 0;
-
-	rpt_mutex_lock(&myrpt->lock);
-	if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(sptr)) {
-		rpt_mutex_unlock(&myrpt->lock);
-		busy = 1;
-	}
-	if (!busy) {
-		int x;
-		ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
-		myrpt->macrotimer = MACROTIME;
-		for (x = 0; *(sptr + x); x++) {
-			myrpt->macrobuf[x] = *(sptr + x) | 0x80;
-		}
-		*(sptr + x) = 0;
-	}
-	rpt_mutex_unlock(&myrpt->lock);
+	int x;
+	char tmp[TMP_SIZE];
 
-	if (busy) {
-		ast_log(LOG_WARNING, "Function decoder busy on app_rpt command macro.\n");
+	ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
+	for (x = 0; *(sptr + x) && (x < sizeof(tmp) - 1); x++) {
+		tmp[x] = *(sptr + x) | 0x80;
 	}
-
-	return busy;
+	*(sptr + x) = 0; /* Do we really need to do this? it was 0 to end loop*/
path: apps/app_rpt/rpt_config.c
user: login: InterLinked1
body: Yeah, it's also a little unclear since that variable is a char* rather than const char*. Probably because we are modifying it here, but if we don't need to for the caller, would make sense to remove this line and make it a const char*
created_at: 2025-03-07T20:28:31Z
author_association: MEMBER
diff_hunk: @@ -1318,29 +1318,17 @@ void load_rpt_vars(int n, int init)
 
 int rpt_push_alt_macro(struct rpt *myrpt, char *sptr)
 {
-	int busy = 0;
-
-	rpt_mutex_lock(&myrpt->lock);
-	if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(sptr)) {
-		rpt_mutex_unlock(&myrpt->lock);
-		busy = 1;
-	}
-	if (!busy) {
-		int x;
-		ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
-		myrpt->macrotimer = MACROTIME;
-		for (x = 0; *(sptr + x); x++) {
-			myrpt->macrobuf[x] = *(sptr + x) | 0x80;
-		}
-		*(sptr + x) = 0;
-	}
-	rpt_mutex_unlock(&myrpt->lock);
+	int x;
+	char tmp[TMP_SIZE];
 
-	if (busy) {
-		ast_log(LOG_WARNING, "Function decoder busy on app_rpt command macro.\n");
+	ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
+	for (x = 0; *(sptr + x) && (x < sizeof(tmp) - 1); x++) {
+		tmp[x] = *(sptr + x) | 0x80;
 	}
-
-	return busy;
+	*(sptr + x) = 0; /* Do we really need to do this? it was 0 to end loop*/
path: apps/app_rpt/rpt_config.c
user: login: Allan-N
body: I think you've made this too complicated.  We have a string passed in, sptr.  We want to take each character of the string and pick the high bit.  Isn't this as simple as :
```
char *altstr, *cp;
altstr = strdup(sptr);
for (cp = altstr; *cp; cp++) {
    *cp |= 0x80;        
}
macro_append(myrpt, altstr);
free(altstr);
```

Alternatively, you can play games with the ast_str buffer by capturing the length before appending the string, append the string, and then pick the high bit for every character appended.  That would eliminate the need to strdup/free.
created_at: 2025-03-07T23:29:33Z
author_association: COLLABORATOR
diff_hunk: @@ -4769,6 +4722,11 @@ static void *rpt(void *this)
 	sprintf(tmpstr, "%s/%s", myrpt->p.archivedir, myrpt->name);
 	mkdir(tmpstr, 0775);
 	myrpt->ready = 0;
+	myrpt->macrobuf = ast_str_create(MAXMACRO);
path: apps/app_rpt.c
user: login: Allan-N
body: Do we need to add a call to `ast_free(myrpt->macrobuf)` in the cleanup code?
created_at: 2025-03-07T23:48:02Z
author_association: COLLABORATOR
diff_hunk: @@ -4769,6 +4722,11 @@ static void *rpt(void *this)
 	sprintf(tmpstr, "%s/%s", myrpt->p.archivedir, myrpt->name);
 	mkdir(tmpstr, 0775);
 	myrpt->ready = 0;
+	myrpt->macrobuf = ast_str_create(MAXMACRO);
path: apps/app_rpt.c
user: login: mkmer
body: Well... I don't see where we free the myrpt-> structure....
created_at: 2025-03-08T00:08:09Z
author_association: COLLABORATOR
diff_hunk: @@ -4769,6 +4722,11 @@ static void *rpt(void *this)
 	sprintf(tmpstr, "%s/%s", myrpt->p.archivedir, myrpt->name);
 	mkdir(tmpstr, 0775);
 	myrpt->ready = 0;
+	myrpt->macrobuf = ast_str_create(MAXMACRO);
path: apps/app_rpt.c
user: login: mkmer
body: It looks like myrpt is static -> so let's just create one if we don't have one.
created_at: 2025-03-08T00:15:35Z
author_association: COLLABORATOR
diff_hunk: @@ -4769,6 +4722,11 @@ static void *rpt(void *this)
 	sprintf(tmpstr, "%s/%s", myrpt->p.archivedir, myrpt->name);
 	mkdir(tmpstr, 0775);
 	myrpt->ready = 0;
+	myrpt->macrobuf = ast_str_create(MAXMACRO);
path: apps/app_rpt.c
user: login: Allan-N
body: Yes, they are statically allocated :
```
struct rpt rpt_vars[MAXRPTS];
```
created_at: 2025-03-08T00:32:56Z
author_association: COLLABORATOR
diff_hunk: @@ -4901,7 +4861,7 @@ static void *rpt(void *this)
 #endif
 	}
 	if (myrpt->p.startupmacro) {
-		snprintf(myrpt->macrobuf, MAXMACRO - 1, "PPPP%s", myrpt->p.startupmacro);
+		ast_str_append(&myrpt->macrobuf, 0, "PPPP%s", myrpt->p.startupmacro);
path: apps/app_rpt.c
user: login: Allan-N
body: OK.  Here, we are initializing macrobuf.  So, instead of ast_str_append() I think we should be using ast_str_set()
created_at: 2025-03-08T00:35:31Z
author_association: COLLABORATOR
diff_hunk: @@ -7068,7 +7031,7 @@ static int rpt_exec(struct ast_channel *chan, const char *data)
 	myrpt->hfscanmode = 0;
 	myrpt->hfscanstatus = 0;
 	if (myrpt->p.startupmacro) {
-		snprintf(myrpt->macrobuf, MAXMACRO - 1, "PPPP%s", myrpt->p.startupmacro);
+		ast_str_append(&myrpt->macrobuf, 0, "PPPP%s", myrpt->p.startupmacro);
path: apps/app_rpt.c
user: login: Allan-N
body: Again, we are initializing macrobuf. So, instead of ast_str_append() I think we should be using ast_str_set()
created_at: 2025-03-08T00:36:47Z
author_association: COLLABORATOR
diff_hunk: @@ -4901,7 +4861,7 @@ static void *rpt(void *this)
 #endif
 	}
 	if (myrpt->p.startupmacro) {
-		snprintf(myrpt->macrobuf, MAXMACRO - 1, "PPPP%s", myrpt->p.startupmacro);
+		ast_str_append(&myrpt->macrobuf, 0, "PPPP%s", myrpt->p.startupmacro);
path: apps/app_rpt.c
user: login: mkmer
body: This is a bit of an interesting "catch" -> Why are we initializing the buffer in two places?
Seems like we should only init `myrpt` in ONE place.
This could also mean I need to create the buffer in two places!
created_at: 2025-03-08T13:17:19Z
author_association: COLLABORATOR
diff_hunk: @@ -4769,6 +4722,11 @@ static void *rpt(void *this)
 	sprintf(tmpstr, "%s/%s", myrpt->p.archivedir, myrpt->name);
 	mkdir(tmpstr, 0775);
 	myrpt->ready = 0;
+	myrpt->macrobuf = ast_str_create(MAXMACRO);
path: apps/app_rpt.c
user: login: mkmer
body: Well... it looks like we clear the whole structure on reload/load:
https://github.com/AllStarLink/app_rpt/blob/95166c29f49344f2fe5afb6220674ea590c857bd/apps/app_rpt.c#L5611

So I need to find a spot to free this where we won't crash while reloading (after the repeater "stops" and before we `memset`).

created_at: 2025-03-08T13:19:18Z
author_association: COLLABORATOR
diff_hunk: @@ -4769,6 +4722,13 @@ static void *rpt(void *this)
 	sprintf(tmpstr, "%s/%s", myrpt->p.archivedir, myrpt->name);
 	mkdir(tmpstr, 0775);
 	myrpt->ready = 0;
+	if (!myrpt->macrobuf) {
+		myrpt->macrobuf = ast_str_create(MAXMACRO);
+	}
+	if (!myrpt->macrobuf) {
path: apps/app_rpt.c
user: login: InterLinked1
body: This branch can be moved inside the previous one, right?
Both are correct but that would be slightly more optimized
created_at: 2025-03-08T13:43:25Z
author_association: MEMBER
diff_hunk: @@ -7081,6 +7049,17 @@ static int rpt_exec(struct ast_channel *chan, const char *data)
 	myrpt->lastitx = !myrpt->lastitx;
 	myrpt->tunerequest = 0;
 	myrpt->tunetx = 0;
+	if (!myrpt->macrobuf) {
+		myrpt->macrobuf = ast_str_create(MAXMACRO);
+	}
+	if (!myrpt->macrobuf) {
path: apps/app_rpt.c
user: login: InterLinked1
body: Same here, this can only be true if the previous branch wasn't
created_at: 2025-03-08T13:44:02Z
author_association: MEMBER
diff_hunk: @@ -7401,10 +7380,13 @@ static int rpt_exec(struct ast_channel *chan, const char *data)
 			}
 		}
 		rpt_mutex_lock(&myrpt->lock);
-		c = myrpt->macrobuf[0];
+		str = ast_str_buffer(myrpt->macrobuf);
+		len = ast_str_strlen(myrpt->macrobuf);
+		c = str[0];
 		if (c && !myrpt->macrotimer) {
 			myrpt->macrotimer = MACROTIME;
-			memmove(myrpt->macrobuf, myrpt->macrobuf + 1, MAXMACRO - 1);
+			ast_copy_string(str, str + 1, len - 1);
path: apps/app_rpt.c
user: login: InterLinked1
body: I'm not sure if ast_copy_string is safe to use for using memory in an overlapping buffer? Or in any case, I'm not sure why memmove wouldn't work here, could probably keep that and truncate on the following line like you are now if that just zeros out the last char
created_at: 2025-03-08T13:46:09Z
author_association: MEMBER
diff_hunk: @@ -1318,29 +1318,16 @@ void load_rpt_vars(int n, int init)
 
 int rpt_push_alt_macro(struct rpt *myrpt, char *sptr)
 {
-	int busy = 0;
-
-	rpt_mutex_lock(&myrpt->lock);
-	if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(sptr)) {
-		rpt_mutex_unlock(&myrpt->lock);
-		busy = 1;
-	}
-	if (!busy) {
-		int x;
-		ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
-		myrpt->macrotimer = MACROTIME;
-		for (x = 0; *(sptr + x); x++) {
-			myrpt->macrobuf[x] = *(sptr + x) | 0x80;
-		}
-		*(sptr + x) = 0;
-	}
-	rpt_mutex_unlock(&myrpt->lock);
+	char *altstr, *cp;
 
-	if (busy) {
-		ast_log(LOG_WARNING, "Function decoder busy on app_rpt command macro.\n");
+	ast_debug(1, "rpt_push_alt_macro %s\n", sptr);
+	altstr = ast_strdup(sptr);
path: apps/app_rpt/rpt_config.c
user: login: InterLinked1
body: This can fail
created_at: 2025-03-08T13:46:20Z
author_association: MEMBER
diff_hunk: @@ -4769,6 +4722,13 @@ static void *rpt(void *this)
 	sprintf(tmpstr, "%s/%s", myrpt->p.archivedir, myrpt->name);
 	mkdir(tmpstr, 0775);
 	myrpt->ready = 0;
+	if (!myrpt->macrobuf) {
+		myrpt->macrobuf = ast_str_create(MAXMACRO);
+	}
+	if (!myrpt->macrobuf) {
path: apps/app_rpt.c
user: login: mkmer
body: true
created_at: 2025-03-08T14:06:57Z
author_association: COLLABORATOR
diff_hunk: @@ -7401,10 +7380,13 @@ static int rpt_exec(struct ast_channel *chan, const char *data)
 			}
 		}
 		rpt_mutex_lock(&myrpt->lock);
-		c = myrpt->macrobuf[0];
+		str = ast_str_buffer(myrpt->macrobuf);
+		len = ast_str_strlen(myrpt->macrobuf);
+		c = str[0];
 		if (c && !myrpt->macrotimer) {
 			myrpt->macrotimer = MACROTIME;
-			memmove(myrpt->macrobuf, myrpt->macrobuf + 1, MAXMACRO - 1);
+			ast_copy_string(str, str + 1, len - 1);
path: apps/app_rpt.c
user: login: mkmer
body: Looks safe as memcopy :
https://github.com/asterisk/asterisk/blob/c7db162844a53be33f8cbc82f0723c3c016860b2/include/asterisk/strings.h#L413-L425
created_at: 2025-03-08T14:11:47Z
author_association: COLLABORATOR
diff_hunk: @@ -5605,6 +5569,10 @@ static int load_config(int reload)
 		if (reload) {
 			for (n = 0; n < nrpts; n++) {
 				if (!strcmp(this, rpt_vars[n].name)) {
+					if (rpt_vars[n].macrobuf) {
path: apps/app_rpt.c
user: login: mkmer
body: I'm not 100% sure this is the right place to free `macrobuf` (more like 99.9%).
It looks like on a reload, we match here then `memset()` the structure to 0 
https://github.com/AllStarLink/app_rpt/blob/9db0ce2f3ca6a6edd73c1b313ec2ddfe3d62d8b4/apps/app_rpt.c#L5626
This should be a safe spot as we should be stopped at this point, no longer accessing the buffer.
It's going to be NULL either way following the `memset()`
created_at: 2025-03-08T14:21:40Z
author_association: COLLABORATOR
diff_hunk: @@ -3895,35 +3863,19 @@ static inline int rxchannel_read(struct rpt *myrpt, const int lasttx)
 					char val[16];
 					strcpy(val, "*6");
 					myrpt->macropatch = 1;
+					macro_append(myrpt, val);
 					rpt_mutex_lock(&myrpt->lock);
-					if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(val)) {
-						rpt_mutex_unlock(&myrpt->lock);
-						busy = 1;
-					}
-					if (!busy) {
-						myrpt->macrotimer = MACROTIME;
-						strncat(myrpt->macrobuf, val, MAXMACRO - 1);
-						if (!busy)
-							strcpy(myrpt->lasttone, (char *) f->data.ptr);
-					}
+					ast_copy_string(myrpt->lasttone, (char *) f->data.ptr, sizeof(myrpt->lasttone));
 					rpt_mutex_unlock(&myrpt->lock);
 				} else if (strcmp((char *) f->data.ptr, myrpt->lasttone)) {
 					val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.tonemacro, (char *) f->data.ptr);
 					if (val) {
 						ast_debug(1, "Tone %s doing %s on node %s\n", (char *) f->data.ptr, val, myrpt->name);
-						rpt_mutex_lock(&myrpt->lock);
-						if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(val)) {
-							rpt_mutex_unlock(&myrpt->lock);
-							busy = 1;
-						}
-						if (!busy) {
-							myrpt->macrotimer = MACROTIME;
-							strncat(myrpt->macrobuf, val, MAXMACRO - 1);
-						}
-						rpt_mutex_unlock(&myrpt->lock);
+						macro_append(myrpt, val);
 					}
-					if (!busy)
-						strcpy(myrpt->lasttone, (char *) f->data.ptr);
+					rpt_mutex_lock(&myrpt->lock);
+					ast_copy_string(myrpt->lasttone, (char *) f->data.ptr, sizeof(myrpt->lasttone) - 1);
path: apps/app_rpt.c
user: login: Allan-N
body: You do not need/want the "- 1".  Just pass the size of the buffer (myrpt->lasttone) and let ast_copy_string ensure the trailing NUL.
created_at: 2025-03-08T14:29:16Z
author_association: COLLABORATOR
diff_hunk: @@ -5605,6 +5569,10 @@ static int load_config(int reload)
 		if (reload) {
 			for (n = 0; n < nrpts; n++) {
 				if (!strcmp(this, rpt_vars[n].name)) {
+					if (rpt_vars[n].macrobuf) {
path: apps/app_rpt.c
user: login: Allan-N
body: Or do you move the check to just before the memset() ?
created_at: 2025-03-08T14:34:55Z
author_association: COLLABORATOR
diff_hunk: @@ -5605,6 +5569,10 @@ static int load_config(int reload)
 		if (reload) {
 			for (n = 0; n < nrpts; n++) {
 				if (!strcmp(this, rpt_vars[n].name)) {
+					if (rpt_vars[n].macrobuf) {
path: apps/app_rpt.c
user: login: mkmer
body: We also get called for "normal" startup, so then the memory is not initialized and we can't be sure !0 is actually a pointer (at least that's my theory).
created_at: 2025-03-08T14:48:17Z
author_association: COLLABORATOR
diff_hunk: @@ -5605,6 +5569,10 @@ static int load_config(int reload)
 		if (reload) {
 			for (n = 0; n < nrpts; n++) {
 				if (!strcmp(this, rpt_vars[n].name)) {
+					if (rpt_vars[n].macrobuf) {
path: apps/app_rpt.c
user: login: Allan-N
body: In app_rpt.c, we have :
```
struct rpt rpt_vars[MAXRPTS]; 
```
This variable, not on the stack, should start off zero'd ... right?
created_at: 2025-03-08T14:58:31Z
author_association: COLLABORATOR
diff_hunk: @@ -5605,6 +5569,10 @@ static int load_config(int reload)
 		if (reload) {
 			for (n = 0; n < nrpts; n++) {
 				if (!strcmp(this, rpt_vars[n].name)) {
+					if (rpt_vars[n].macrobuf) {
path: apps/app_rpt.c
user: login: mkmer
body: I believe this is the code that zeroes it.  I don't know what the compiler does when it allocates static memory.  Is there a definitive operation for compilers? 
created_at: 2025-03-09T13:11:17Z
author_association: COLLABORATOR
diff_hunk: @@ -5605,6 +5569,10 @@ static int load_config(int reload)
 		if (reload) {
 			for (n = 0; n < nrpts; n++) {
 				if (!strcmp(this, rpt_vars[n].name)) {
+					if (rpt_vars[n].macrobuf) {
path: apps/app_rpt.c
user: login: InterLinked1
body: static variables are zero initialized in the C standard, so if the intent is for them to default to 0, it doesn't *need* to be specified. Sometimes it's done just for clarity.

If they are zeroed, it should be done in the initializer, they certainly shouldn't be memset or anything like that
created_at: 2025-03-09T14:39:43Z
author_association: MEMBER
diff_hunk: @@ -5605,6 +5569,10 @@ static int load_config(int reload)
 		if (reload) {
 			for (n = 0; n < nrpts; n++) {
 				if (!strcmp(this, rpt_vars[n].name)) {
+					if (rpt_vars[n].macrobuf) {
path: apps/app_rpt.c
user: login: mkmer
body: So we can move it to just before the `memset()` -> I think that will be more clear.
created_at: 2025-03-09T14:51:50Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-19T13:13:58Z
body: <!-- cpp linter action -->
# Cpp-Linter Report :warning:
Some files did not pass the configured checks!

<details><summary>clang-format (v14.0.6) reports: <strong>1 file(s) not formatted</strong></summary>

- apps/app_rpt/app_rpt.h

</details>

Have any feedback or feature suggestions? [Share it here.](https://github.com/cpp-linter/cpp-linter-action/issues)
author_association: CONTRIBUTOR
user: login: InterLinked1
created_at: 2025-03-19T14:11:19Z
body: > # Cpp-Linter Report ⚠️
> Some files did not pass the configured checks!
> 
> clang-format (v14.0.6) reports: **3 file(s) not formatted**
> * apps/app_rpt.c
> * apps/app_rpt/app_rpt.h
> * apps/app_rpt/rpt_mdc1200.c
> 
> Have any feedback or feature suggestions? [Share it here.](https://github.com/cpp-linter/cpp-linter-action/issues)

@mkmer How does one see the actual lines causing it to fail in the CI logs?
If it just says the file failed, that doesn't seem very useful.
author_association: MEMBER