diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0b71a954585c8ce3a2f1f261862561251f5af4d7..580dd42583d4419063c98fe406698153abd0940e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,5 +1,5 @@
 sbbs-linux:
-  tags: [linux]
+  tags: [Linux]
   image: gcc
   stage: build
   script:
@@ -14,7 +14,7 @@ sbbs-linux:
       - "src/sbbs3/*/*.exe.release/*"
 
 sexpots-linux:
-  tags: [linux]
+  tags: [Linux]
   image: gcc
   stage: build
   script:
@@ -26,7 +26,7 @@ sexpots-linux:
       - "src/sexpots/*.exe.release/*"
 
 syncterm-linux:
-  tags: [linux]
+  tags: [Linux]
   image: gcc
   stage: build
   script:
@@ -37,10 +37,8 @@ syncterm-linux:
     paths:
       - "src/syncterm/*.exe.release/*"
 
-
 sbbs-windows:
-  tags: [windows]
-
+  tags: [Windows]
   stage: build
   script:
     - cd src/xpdev
@@ -62,8 +60,7 @@ sbbs-windows:
       - "src/sbbs3/*/*.exe"
 
 sexpots-windows:
-  tags: [windows]
-
+  tags: [Windows]
   stage: build
   script:
     - cd src/sexpots
@@ -71,4 +68,40 @@ sexpots-windows:
   artifacts:
     name: sexpots-win32
     paths:
-      - "src/sexpots/Release/*.exe"
\ No newline at end of file
+      - "src/sexpots/Release/*.exe"
+
+sbbs-freebsd:
+  tags: [FreeBSD]
+  stage: build
+  script:
+    - cd src/sbbs3
+    - gmake RELEASE=1 all
+  artifacts:
+    name: sbbs-freebsd-x64
+    paths:
+      - "src/sbbs3/*.exe.release/*"
+      - "src/sbbs3/*.lib.release/*"
+      - "src/sbbs3/*/*.exe.release/*"
+
+syncterm-freebsd:
+  tags: [FreeBSD]
+  stage: build
+  script:
+    - cd src/syncterm
+    - gmake RELEASE=1
+  artifacts:
+    name: syncterm-freebsd-x64
+    paths:
+      - "src/syncterm/*.exe.release/*"
+
+syncterm-windows:
+  tags: [FreeBSD]
+  stage: build
+  script:
+    - cd src/syncterm
+    - gmake AR=mingw32-ar CC=mingw32-gcc CXX=mingw32-g++ RANLIB=mingw32-ranlib RELEASE=1 STATIC_CRYPTLIB=yes USE_SDL=1 VERBOSE=please WINDRES=mingw32-windres
+  artifacts:
+    name: syncterm-win32
+    paths:
+      - "src/syncterm/*.exe.release/*"
+ 
\ No newline at end of file
diff --git a/src/syncterm/ripper.c b/src/syncterm/ripper.c
index e908bf9d898ddc67052db551c6e02b4b3157fc6a..a289c3e2938ab19d356b33734798d9515fd77d74 100644
--- a/src/syncterm/ripper.c
+++ b/src/syncterm/ripper.c
@@ -7253,6 +7253,7 @@ get_text_variable(const char * const var)
 	vardef = bsearch(var, builtins, sizeof(builtins) / sizeof(builtins[0]), sizeof(builtins[0]), bicmp);
 	if (vardef == NULL) {
 		puts("TODO: User variables");
+		return(calloc(1, 1));
 	}
 	return vardef->func(var, vardef->data);
 }
@@ -7286,6 +7287,7 @@ rv_date(const char * const var, const void * const data)
 									ctime_r(&now, str);
 									return strdup(str);
 							}
+							break;
 						case 'Y':
 							snprintf(str, sizeof(str), "%02d", nlt.tm_mday);
 							return strdup(str);
@@ -7726,10 +7728,9 @@ parse_string(const char *buf)
 	char *p;
 	int ch;
 
-	char *ret = malloc(strlen(buf));
+	char *ret = malloc(strlen(buf) + 1);
 	if (ret == NULL)
 		return ret;
-	p = ret;
 	for (p = ret; (ch = next_char(buf, &inpos)) > 0; inpos++)
 		*(p++) = ch;
 	*p = 0;
@@ -8722,6 +8723,12 @@ add_button(int x1, int y1, int x2, int y2, int hotkey, int flags, char *text)
 	}
 
 	draw_button(but, false);
+	if (!but->flags.mouse) {
+		free(but->command);
+		free(but->icon);
+		free(but->label);
+		free(but);
+	}
 }
 
 static void append_str(uint8_t **resp, size_t *size, size_t *pos, const char *str)
@@ -8790,6 +8797,8 @@ do_popup(const char * const str)
 	if (soo == NULL)
 		return NULL;
 	question = strndup(p, soo - p);
+	if (question == NULL)
+		return NULL;
 	soo += 2;
 	p = soo;
 	opts = 0;
@@ -8909,6 +8918,7 @@ do_popup(const char * const str)
 	rip.x = x;
 	rip.y = y;
 	write_text(question);
+	free(question);
 
 	for (i = 0; i < opts; i++) {
 		set_pixel(x1 + 13, y1 + 27 + (i * 13), light);
@@ -9019,13 +9029,11 @@ handle_command_str(const char *incmd)
 {
 	const char *p, *p2, *p3, *p4;
 	char str[2];
-	const char *cmd;
 
 	if (incmd == NULL)
 		return;
-	cmd = strdup(incmd);
 
-	for (p = cmd; *p; p++) {
+	for (p = incmd; *p; p++) {
 		// TODO: No way to send a ^ or a $ or a [
 		if (*p == '^' || *p == '`') {	// CTRL char
 			p++;
@@ -9224,7 +9232,7 @@ full_ellipse(int xc, int yc, int a, int b, bool fill)
 	long crit1 = -(a2/4 + a%2 + b2);
 	long crit2 = -(b2/4 + b%2 + a2);
 	long crit3 = -(b2/4 + b%2);
-	long t = -a2*y; /* t = e(x+1/2,y-1/2) - (aˆ2+bˆ2)/4 */
+	long t = -a2*y; /* t = e(x+1/2,y-1/2) - (a²+b²)/4 */
 	long dxt = 2*b2*x, dyt = -2*a2*y;
 	long d2xt = 2*b2, d2yt = 2*a2;
 	int fy;
@@ -10125,6 +10133,7 @@ setpixel(x1, y1, xx);
 								}
 								draw_line(argv[arg1 - 1].x, argv[arg1 - 1].y, argv[0].x, argv[0].y);
 							}
+							free(argv);
 							break;
 						case 's':	// RIP_FILL_PATTERN
 							handled = true;
@@ -10505,7 +10514,7 @@ setpixel(x1, y1, xx);
 									if (fread(planes, row, 4, icn) != 4) {
 										free(planes);
 										free(pix->pixels);
-										free(pix);
+										FREE_AND_NULL(pix);
 										fclose(icn);
 										break;
 									}
@@ -10517,18 +10526,20 @@ setpixel(x1, y1, xx);
 										*(op++) = ega_colours[co];
 									}
 								}
-								fclose(icn);
-								free(planes);
+								if (pix) {
+									fclose(icn);
+									free(planes);
 
-								struct text_info ti;
-								gettextinfo(&ti);
-								setpixels(x1 + rip.viewport.sx, y1 + rip.viewport.sy, x1 + rip.viewport.sx + pix->width - 1, y1 + rip.viewport.sy + pix->height - 1, 0, 0, pix, NULL);
-								if (arg2) {
-									freepixels(rip.clipboard);
-									rip.clipboard = pix;
-								}
-								else {
-									freepixels(pix);
+									struct text_info ti;
+									gettextinfo(&ti);
+									setpixels(x1 + rip.viewport.sx, y1 + rip.viewport.sy, x1 + rip.viewport.sx + pix->width - 1, y1 + rip.viewport.sy + pix->height - 1, 0, 0, pix, NULL);
+									if (arg2) {
+										freepixels(rip.clipboard);
+										rip.clipboard = pix;
+									}
+									else {
+										freepixels(pix);
+									}
 								}
 							}
 							break;
@@ -10706,14 +10717,16 @@ setpixel(x1, y1, xx);
 									if (fwrite(planes, row, 4, icn) != 4) {
 										fclose(icn);
 										unlink(cache_path);
-										free(planes);
+										FREE_AND_NULL(planes);
 										fclose(icn);
 										break;
 									}
 								}
-								fwrite("F", 1, 1, icn);
-								fclose(icn);
-								free(planes);
+								if (planes) {
+									fwrite("F", 1, 1, icn);
+									fclose(icn);
+									free(planes);
+								}
 							}
 							break;
 					}
@@ -11096,6 +11109,11 @@ do_skypix(char *buf, size_t len)
 			}
 			strlwr(fnt->entry[i].std.name);
 			flen = flength(fnt->entry[i].std.name);
+			if (flen < 0) {
+				printf("TODO: Unable to read font %s size\n", fnt->entry[i].std.name);
+				free(fnt);
+				break;
+			}
 			font = fopen(fnt->entry[i].std.name, "rb");
 			if (font == NULL) {
 				printf("TODO: Unable to open font %s\n", fnt->entry[i].std.name);
@@ -11105,6 +11123,7 @@ do_skypix(char *buf, size_t len)
 			free(fnt);
 			amiga_font = malloc(flen);
 			if (amiga_font == NULL) {
+				fclose(font);
 				break;
 			}
 			fread(amiga_font, 1, flen, font);
@@ -11489,10 +11508,10 @@ parse_rip(BYTE *origbuf, unsigned blen, unsigned maxlen)
 						if (buf[pos] == '\b') {
 							if (rip.lchars > 0)
 								rip.lchars--;
-								if (rip.lchars == 0) {
-									rip.state = RIP_STATE_BOL;
-									break;
-								}
+							if (rip.lchars == 0) {
+								rip.state = RIP_STATE_BOL;
+								break;
+							}
 						}
 						else if (buf[pos] < 32)
 							rip.lchars = -1;
diff --git a/src/syncterm/term.c b/src/syncterm/term.c
index 01cc931066de24867a2385a59ebedf9f3e200467..79f00751de455a1c806cf13d2f363085d8b64fa4 100644
--- a/src/syncterm/term.c
+++ b/src/syncterm/term.c
@@ -815,7 +815,7 @@ void begin_upload(struct bbslist *bbs, BOOL autozm, int lastch)
 	int i;
 	FILE*	fp;
 	struct file_pick fpick;
-	char	*opts[6]={
+	char	*opts[7]={
 			 "ZMODEM"
 			,"YMODEM"
 			,"XMODEM-1K"
@@ -912,7 +912,7 @@ void begin_download(struct bbslist *bbs)
 {
 	char	path[MAX_PATH+1];
 	int i;
-	char	*opts[5]={
+	char	*opts[6]={
 			 "ZMODEM"
 			,"YMODEM-g"
 			,"YMODEM"