fbmuck-6.01/contrib/jresolver/
fbmuck-6.01/contrib/jresolver/org/
fbmuck-6.01/contrib/jresolver/org/fuzzball/
fbmuck-6.01/docs/devel/
fbmuck-6.01/game/
fbmuck-6.01/game/logs/
fbmuck-6.01/game/muf/
fbmuck-6.01/scripts/
fbmuck-6.01/src_docs/
/* Primitives Package */

#include "copyright.h"
#include "config.h"

#include <sys/types.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include "db.h"
#include "tune.h"
#include "inst.h"
#include "externs.h"
#include "match.h"
#include "interface.h"
#include "params.h"
#include "fbstrings.h"
#include "interp.h"

static struct inst *oper1, *oper2, *oper3, *oper4;
static struct inst temp1, temp2, temp3;
static int tmp, result;
static dbref ref;
static char buf[BUFFER_LEN];
static char *pname;

/* FMTTOKEN defines the start of a variable formatting string insertion */
#define FMTTOKEN '%'

void
prim_fmtstring(PRIM_PROTOTYPE)
{
	int slen, scnt, tstop, tlen, tnum, i;
	int slrj, spad1, spad2, slen1, slen2, temp;
	char sstr[BUFFER_LEN], sfmt[255], hold[256], tbuf[BUFFER_LEN];
	char *ptr, *begptr;

	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Top argument must be a string.");
	if (!oper1->data.string) {
		CLEAR(oper1);
		PushNullStr;
		return;
	}
	/* We now have the non-null format string, parse it */
	result = 0;					/* End of current string, must be smaller than BUFFER_LEN */
	tmp = 0;					/* Number of props to search for/found */
	slen = strlen(oper1->data.string->data);
	scnt = 0;
	tstop = 0;
	strcpy(sstr, oper1->data.string->data);
	CLEAR(oper1);
	while ((scnt < slen) && (result < BUFFER_LEN)) {
		CHECKOP(0);
		if (sstr[scnt] == FMTTOKEN) {
			if (sstr[scnt + 1] == FMTTOKEN) {
				buf[result++] = FMTTOKEN;
				scnt += 2;
			} else {
				scnt++;
				if ((sstr[scnt] == '-') || (sstr[scnt] == '|')) {
					if (sstr[scnt] == '-')
						slrj = 1;
					else
						slrj = 2;
					scnt++;
				} else {
					slrj = 0;
				}
				if ((sstr[scnt] == '+') || (sstr[scnt] == ' ')) {
					if (sstr[scnt] == '+')
						spad1 = 1;
					else
						spad1 = 2;
					scnt++;
				} else {
					spad1 = 0;
				}
				if (sstr[scnt] == '0') {
					scnt++;
					spad2 = 1;
				} else {
					spad2 = 0;
				}
				slen1 = atoi(&sstr[scnt]);
				if ((sstr[scnt] >= '0') && (sstr[scnt] <= '9')) {
					while ((sstr[scnt] >= '0') && (sstr[scnt] <= '9'))
						scnt++;
				} else {
					if (sstr[scnt] == '*') {
						scnt++;
						CHECKOP(1);
						oper2 = POP();
						if (oper2->type != PROG_INTEGER)
							abort_interp("Format specified integer argument not found.");
						slen1 = oper2->data.number;
						CLEAR(oper2);
					} else {
						slen1 = 0;
					}
				}
				if (sstr[scnt] == '.') {
					scnt++;
					slen2 = atoi(&sstr[scnt]);
					if ((sstr[scnt] >= '0') && (sstr[scnt] <= '9')) {
						while ((sstr[scnt] >= '0') && (sstr[scnt] <= '9'))
							scnt++;
					} else {
						if (sstr[scnt] == '*') {
							scnt++;
							CHECKOP(1);
							oper2 = POP();
							if (oper2->type != PROG_INTEGER)
								abort_interp("Format specified integer argument not found.");
							if (oper2->data.number < 0)
								abort_interp("Dynamic precision value must be a positive integer.");
							slen2 = oper2->data.number;
							CLEAR(oper2);
						} else {
							abort_interp("Invalid format string.");
						}
					}
				} else {
					slen2 = -1;
				}

				/* If s is the format and oper2 is really a string, repair the lengths to account for ansi codes. */
				CHECKOP(1);
				oper2 = POP();
				if(('s' == sstr[scnt]) && (PROG_STRING == oper2->type) && (oper2->data.string)) {
					ptr = oper2->data.string->data;

					i = 0;
					while ((-1 == slen2 || i < slen2) && *ptr) {  /* adapted from prim_ansi_strlen */
						begptr = ptr;
						if (*ptr++ == ESCAPE_CHAR) {
							if (*ptr == '\0') {;
							} else if (*ptr != '[') {
								ptr++;
							} else {
								ptr++;
								while (isdigit(*ptr) || *ptr == ';')
									ptr++;
								if (*ptr == 'm')
									ptr++;
							}
							i += (int) (ptr - begptr);
							slen1 += (int) (ptr - begptr);
							if(-1 != slen2) slen2 += (int) (ptr - begptr);
						} else { i++; };
					}
				}

				if (slen1 && ((abs(slen1) + result) > BUFFER_LEN))
					abort_interp("Specified format field width too large.");
				sfmt[0] = '%';
				sfmt[1] = '\0';
				if (slrj == 1)
					strcatn(sfmt, sizeof(sfmt), "-");
				if (spad1) {
					if (spad1 == 1)
						strcatn(sfmt, sizeof(sfmt), "+");
					else
						strcatn(sfmt, sizeof(sfmt), " ");
				}
				if (spad2)
					strcatn(sfmt, sizeof(sfmt), "0");
				if (slen1 != 0) {
					snprintf(tbuf, sizeof(tbuf), "%d", slen1);
					strcatn(sfmt, sizeof(sfmt), tbuf);
				}
				if (slen2 != -1) {
					snprintf(tbuf, sizeof(tbuf), ".%d", slen2);
					strcatn(sfmt, sizeof(sfmt), tbuf);
				}

				if (sstr[scnt] == '~') {
					switch (oper2->type) {
					case PROG_OBJECT:
						sstr[scnt] = 'D';
						break;
					case PROG_FLOAT:
						sstr[scnt] = 'g';
						break;
					case PROG_INTEGER:
						sstr[scnt] = 'i';
						break;
					case PROG_LOCK:
						sstr[scnt] = 'l';
						break;
					case PROG_STRING:
						sstr[scnt] = 's';
						break;
					default:
						sstr[scnt] = '?';
						break;
					}
				}
				switch (sstr[scnt]) {
				case 'i':
					strcatn(sfmt, sizeof(sfmt), "d");
					if (oper2->type != PROG_INTEGER)
						abort_interp("Format specified integer argument not found.");
					snprintf(tbuf, sizeof(tbuf), sfmt, oper2->data.number);
					tlen = strlen(tbuf);
					if (slrj == 2) {
						tnum = 0;
						while ((tbuf[tnum] == ' ') && (tnum < tlen))
							tnum++;
						if ((tnum) && (tnum < tlen)) {
							temp = tnum / 2;
							for (i = tnum; i < tlen; i++)
								tbuf[i - temp] = tbuf[i];
							for (i = tlen - temp; i < tlen; i++)
								tbuf[i] = ' ';
						}
					}
					if (tlen + result > BUFFER_LEN)
						abort_interp("Resultant string would overflow buffer.");
					buf[result] = '\0';
					strcatn(buf, sizeof(buf), tbuf);
					result += tlen;
					CLEAR(oper2);
					break;
				case 'S':
				case 's':
					strcatn(sfmt, sizeof(sfmt), "s");
					if (oper2->type != PROG_STRING)
						abort_interp("Format specified string argument not found.");
					snprintf(tbuf, sizeof(tbuf), sfmt,
							((oper2->data.string) ? oper2->data.string->data : ""));
					tlen = strlen(tbuf);
					if (slrj == 2) {
						tnum = 0;
						while ((tbuf[tnum] == ' ') && (tnum < tlen))
							tnum++;
						if ((tnum) && (tnum < tlen)) {
							temp = tnum / 2;
							for (i = tnum; i < tlen; i++)
								tbuf[i - temp] = tbuf[i];
							for (i = tlen - temp; i < tlen; i++)
								tbuf[i] = ' ';
						}
					}
					if (strlen(tbuf) + result > BUFFER_LEN)
						abort_interp("Resultant string would overflow buffer.");
					buf[result] = '\0';
					strcatn(buf, sizeof(buf), tbuf);
					result += strlen(tbuf);
					CLEAR(oper2);
					break;
				case '?':
					strcatn(sfmt, sizeof(sfmt), "s");
					switch (oper2->type) {
					case PROG_OBJECT:
						strcpy(hold, "OBJECT");
						break;
					case PROG_FLOAT:
						strcpy(hold, "FLOAT");
						break;
					case PROG_INTEGER:
						strcpy(hold, "INTEGER");
						break;
					case PROG_LOCK:
						strcpy(hold, "LOCK");
						break;
					case PROG_STRING:
						strcpy(hold, "STRING");
						break;
					case PROG_VAR:
						strcpy(hold, "VARIABLE");
						break;
					case PROG_LVAR:
						strcpy(hold, "LOCAL-VARIABLE");
						break;
					case PROG_SVAR:
						strcpy(hold, "SCOPED-VARIABLE");
						break;
					case PROG_ADD:
						strcpy(hold, "ADDRESS");
						break;
					case PROG_ARRAY:
						strcpy(hold, "ARRAY");
						break;
					case PROG_FUNCTION:
						strcpy(hold, "FUNCTION-NAME");
						break;
					case PROG_IF:
						strcpy(hold, "IF-STATEMENT");
						break;
					case PROG_EXEC:
						strcpy(hold, "EXECUTE");
						break;
					case PROG_JMP:
						strcpy(hold, "JUMP");
						break;
					case PROG_PRIMITIVE:
						strcpy(hold, "PRIMITIVE");
						break;
					default:
						strcpy(hold, "UNKNOWN");
					}
					snprintf(tbuf, sizeof(tbuf), sfmt, hold);
					tlen = strlen(tbuf);
					if (slrj == 2) {
						tnum = 0;
						while ((tbuf[tnum] == ' ') && (tnum < tlen))
							tnum++;
						if ((tnum) && (tnum < tlen)) {
							temp = tnum / 2;
							for (i = tnum; i < tlen; i++)
								tbuf[i - temp] = tbuf[i];
							for (i = tlen - temp; i < tlen; i++)
								tbuf[i] = ' ';
						}
					}
					if (strlen(tbuf) + result > BUFFER_LEN)
						abort_interp("Resultant string would overflow buffer.");
					buf[result] = '\0';
					strcatn(buf, sizeof(buf), tbuf);
					result += strlen(tbuf);
					CLEAR(oper2);
					break;
				case 'd':
					strcatn(sfmt, sizeof(sfmt), "s");
					if (oper2->type != PROG_OBJECT)
						abort_interp("Format specified object not found.");
					snprintf(hold, sizeof(hold), "#%d", oper2->data.objref);
					snprintf(tbuf, sizeof(tbuf), sfmt, hold);
					tlen = strlen(tbuf);
					if (slrj == 2) {
						tnum = 0;
						while ((tbuf[tnum] == ' ') && (tnum < tlen))
							tnum++;
						if ((tnum) && (tnum < tlen)) {
							temp = tnum / 2;
							for (i = tnum; i < tlen; i++)
								tbuf[i - temp] = tbuf[i];
							for (i = tlen - temp; i < tlen; i++)
								tbuf[i] = ' ';
						}
					}
					if (strlen(tbuf) + result > BUFFER_LEN)
						abort_interp("Resultant string would overflow buffer.");
					buf[result] = '\0';
					strcatn(buf, sizeof(buf), tbuf);
					result += strlen(tbuf);
					CLEAR(oper2);
					break;
				case 'D':
					strcatn(sfmt, sizeof(sfmt), "s");
					if (oper2->type != PROG_OBJECT)
						abort_interp("Format specified object not found.");
					if (!valid_object(oper2))
						abort_interp("Format specified object not valid.");
					ref = oper2->data.objref;
					CHECKREMOTE(ref);
					/* if ((Typeof(ref) != TYPE_PLAYER) && (Typeof(ref) != TYPE_PROGRAM))
					   ts_lastuseobject(ref); */
					if (NAME(ref)) {
						strcpy(hold, PNAME(ref));
					} else {
						hold[0] = '\0';
					}
					snprintf(tbuf, sizeof(tbuf), sfmt, hold);
					tlen = strlen(tbuf);
					if (slrj == 2) {
						tnum = 0;
						while ((tbuf[tnum] == ' ') && (tnum < tlen))
							tnum++;
						if ((tnum) && (tnum < tlen)) {
							temp = tnum / 2;
							for (i = tnum; i < tlen; i++)
								tbuf[i - temp] = tbuf[i];
							for (i = tlen - temp; i < tlen; i++)
								tbuf[i] = ' ';
						}
					}
					if (strlen(tbuf) + result > BUFFER_LEN)
						abort_interp("Resultant string would overflow buffer.");
					buf[result] = '\0';
					strcatn(buf, sizeof(buf), tbuf);
					result += strlen(tbuf);
					CLEAR(oper2);
					break;
				case 'l':
					strcatn(sfmt, sizeof(sfmt), "s");
					if (oper2->type != PROG_LOCK)
						abort_interp("Format specified lock not found.");
					strcpy(hold, unparse_boolexp(ProgUID, oper2->data.lock, 1));
					snprintf(tbuf, sizeof(tbuf), sfmt, hold);
					tlen = strlen(tbuf);
					if (slrj == 2) {
						tnum = 0;
						while ((tbuf[tnum] == ' ') && (tnum < tlen))
							tnum++;
						if ((tnum) && (tnum < tlen)) {
							temp = tnum / 2;
							for (i = tnum; i < tlen; i++)
								tbuf[i - temp] = tbuf[i];
							for (i = tlen - temp; i < tlen; i++)
								tbuf[i] = ' ';
						}
					}
					if (strlen(tbuf) + result > BUFFER_LEN)
						abort_interp("Resultant string would overflow buffer.");
					buf[result] = '\0';
					strcatn(buf, sizeof(buf), tbuf);
					result += strlen(tbuf);
					CLEAR(oper2);
					break;
				case 'f':
				case 'e':
				case 'g':
					strcatn(sfmt, sizeof(sfmt), "l");
					snprintf(hold, sizeof(hold), "%c", sstr[scnt]);
					strcatn(sfmt, sizeof(sfmt), hold);
					if (oper2->type != PROG_FLOAT)
						abort_interp("Format specified float not found.");
					snprintf(tbuf, sizeof(tbuf), sfmt, oper2->data.fnumber);
					tlen = strlen(tbuf);
					if (slrj == 2) {
						tnum = 0;
						while ((tbuf[tnum] == ' ') && (tnum < tlen))
							tnum++;
						if ((tnum) && (tnum < tlen)) {
							temp = tnum / 2;
							for (i = tnum; i < tlen; i++)
								tbuf[i - temp] = tbuf[i];
							for (i = tlen - temp; i < tlen; i++)
								tbuf[i] = ' ';
						}
					}
					if (strlen(tbuf) + result > BUFFER_LEN)
						abort_interp("Resultant string would overflow buffer.");
					buf[result] = '\0';
					strcatn(buf, sizeof(buf), tbuf);
					result += strlen(tbuf);
					CLEAR(oper2);
					break;
				default:
					abort_interp("Invalid format string.");
					break;
				}
				scnt++;
				tstop += strlen(tbuf);
			}
		} else {
			if ((sstr[scnt] == '\\') && (sstr[scnt + 1] == 't')) {
				if ((result - (tstop % 8) + 1) >= BUFFER_LEN)
					abort_interp("Resultant string would overflow buffer.");
				if ((tstop % 8) == 0) {
					buf[result++] = ' ';
					tstop++;
				}
				while ((tstop % 8) != 0) {
					buf[result++] = ' ';
					tstop++;
				}
				scnt += 2;
				tstop = 0;
			} else {
				if (sstr[scnt] == '\r') {
					tstop = 0;
					scnt++;
					buf[result++] = '\r';
				} else {
					buf[result++] = sstr[scnt++];
					tstop++;
				}
			}
		}
	}
	CHECKOP(0);
	if (result >= BUFFER_LEN)
		abort_interp("Resultant string would overflow buffer.");
	buf[result] = '\0';
	if (result)
		PushString(buf);
	else
		PushNullStr;
}


void
prim_array_fmtstrings(PRIM_PROTOTYPE)
{
	int slen, scnt, tstop, tlen, tnum, i;
	int slrj, spad1, spad2, slen1, slen2, temp;
	char sstr[BUFFER_LEN], sfmt[255], hold[256], tbuf[BUFFER_LEN];
	char *ptr, *begptr;
	char fieldbuf[BUFFER_LEN];
	char *fieldname = fieldbuf;
	char *fmtstr = NULL;
	stk_array *arr = NULL;
	stk_array *arr2 = NULL;
	stk_array *nu = NULL;

	CHECKOP(2);
	oper2 = POP();
	oper1 = POP();

	if (oper1->type != PROG_ARRAY)
		abort_interp("Argument not an array of arrays. (1)");
	if (!array_is_homogenous(oper1->data.array, PROG_ARRAY))
		abort_interp("Argument not a homogenous array of arrays. (1)");
	arr = oper1->data.array;

	if (oper2->type != PROG_STRING)
		abort_interp("Expected string argument. (2)");
	fmtstr = DoNullInd(oper2->data.string);
	slen = strlen(fmtstr);

	nu = new_array_packed(0);
	if (array_first(arr, &temp1)) {
		do {
			strcpy(sstr, fmtstr);
			result = 0;		/* End of current string, must be smaller than BUFFER_LEN */
			tmp = 0;		/* Number of props to search for/found */
			scnt = 0;
			tstop = 0;
			/*   "%-20.19[name]s %6[dbref]d"   */
			while ((scnt < slen) && (result < BUFFER_LEN)) {
				if (sstr[scnt] == FMTTOKEN) {
					if (sstr[scnt + 1] == FMTTOKEN) {
						buf[result++] = FMTTOKEN;
						scnt += 2;
					} else {
						scnt++;
						if ((sstr[scnt] == '-') || (sstr[scnt] == '|')) {
							if (sstr[scnt] == '-')
								slrj = 1;
							else
								slrj = 2;
							scnt++;
						} else {
							slrj = 0;
						}
						if ((sstr[scnt] == '+') || (sstr[scnt] == ' ')) {
							if (sstr[scnt] == '+')
								spad1 = 1;
							else
								spad1 = 2;
							scnt++;
						} else {
							spad1 = 0;
						}
						if (sstr[scnt] == '0') {
							scnt++;
							spad2 = 1;
						} else {
							spad2 = 0;
						}
						slen1 = atoi(&sstr[scnt]);
						if ((sstr[scnt] >= '0') && (sstr[scnt] <= '9')) {
							while ((sstr[scnt] >= '0') && (sstr[scnt] <= '9'))
								scnt++;
						} else {
							slen1 = -1;
						}
						if (sstr[scnt] == '.') {
							scnt++;
							slen2 = atoi(&sstr[scnt]);
							if ((sstr[scnt] >= '0') && (sstr[scnt] <= '9')) {
								while ((sstr[scnt] >= '0') && (sstr[scnt] <= '9'))
									scnt++;
							} else {
								abort_interp("Invalid format string.");
							}
						} else {
							slen2 = -1;
						}

						if (sstr[scnt] == '[') {
							scnt++;
							fieldname = fieldbuf;
							while(sstr[scnt] && sstr[scnt] != ']') {
								*fieldname++ = sstr[scnt++];
							}
							if (sstr[scnt] != ']') {
								abort_interp("Specified format field didn't have an array index terminator ']'.");
							}
							scnt++;
							*fieldname++ = '\0';

							oper3 = array_getitem(arr, &temp1);
							arr2 = oper3->data.array;
							oper3 = NULL;
							if (number(fieldbuf)) {
								temp2.type = PROG_INTEGER;
								temp2.data.number = atoi(fieldbuf);
								oper3 = array_getitem(arr2, &temp2);
							}
							if (!oper3) {
								temp2.type = PROG_STRING;
								temp2.data.string = alloc_prog_string(fieldbuf);
								oper3 = array_getitem(arr2, &temp2);
								CLEAR(&temp2);
							}
							if (!oper3) {
								temp3.type = PROG_STRING;
								temp3.data.string = NULL;
								oper3 = &temp3;
							}
							nargs = 2;
						} else {
							abort_interp("Specified format field didn't have an array index.");
						}

						/* If s is the format and oper3 is really a string, repair the lengths to account for ansi codes. */
						if(('s' == sstr[scnt]) && (PROG_STRING == oper3->type) && (oper3->data.string)) {
							ptr = oper3->data.string->data;

							i = 0;
							while ((-1 == slen2 || i < slen2) && *ptr) {  /* adapted from prim_ansi_strlen */
								begptr = ptr;
								if (*ptr++ == ESCAPE_CHAR) {
									if (*ptr == '\0') {;
									} else if (*ptr != '[') {
										ptr++;
									} else {
										ptr++;
										while (isdigit(*ptr) || *ptr == ';')
											ptr++;
										if (*ptr == 'm')
											ptr++;
									}
									i += (int) (ptr - begptr);
									slen1 += (int) (ptr - begptr);
									if(-1 != slen2) slen2 += (int) (ptr - begptr);
								} else { i++; };
							}
						}
						if ((slen1 > 0) && ((slen1 + result) > BUFFER_LEN))
							abort_interp("Specified format field width too large.");

						sfmt[0] = '%';
						sfmt[1] = '\0';
						if (slrj == 1)
							strcatn(sfmt, sizeof(sfmt), "-");
						if (spad1) {
							if (spad1 == 1)
								strcatn(sfmt, sizeof(sfmt), "+");
							else
								strcatn(sfmt, sizeof(sfmt), " ");
						}
						if (spad2)
							strcatn(sfmt, sizeof(sfmt), "0");
						if (slen1 != -1) {
							snprintf(tbuf, sizeof(tbuf), "%d", slen1);
							strcatn(sfmt, sizeof(sfmt), tbuf);
						}
						if (slen2 != -1) {
							snprintf(tbuf, sizeof(tbuf), ".%d", slen2);
							strcatn(sfmt, sizeof(sfmt), tbuf);
						}
						if (sstr[scnt] == '~') {
							switch (oper3->type) {
							case PROG_OBJECT:
								sstr[scnt] = 'D';
								break;
							case PROG_FLOAT:
								sstr[scnt++] = 'l';
								sstr[scnt] = 'g';
								break;
							case PROG_INTEGER:
								sstr[scnt] = 'i';
								break;
							case PROG_LOCK:
								sstr[scnt] = 'l';
								break;
							case PROG_STRING:
								sstr[scnt] = 's';
								break;
							default:
								sstr[scnt] = '?';
								break;
							}
						}
						switch (sstr[scnt]) {
						case 'i':
							strcatn(sfmt, sizeof(sfmt), "d");
							if (oper3->type != PROG_INTEGER)
								abort_interp("Format specified integer argument not found.");
							snprintf(tbuf, sizeof(tbuf), sfmt, oper3->data.number);
							tlen = strlen(tbuf);
							if (slrj == 2) {
								tnum = 0;
								while ((tbuf[tnum] == ' ') && (tnum < tlen))
									tnum++;
								if ((tnum) && (tnum < tlen)) {
									temp = tnum / 2;
									for (i = tnum; i < tlen; i++)
										tbuf[i - temp] = tbuf[i];
									for (i = tlen - temp; i < tlen; i++)
										tbuf[i] = ' ';
								}
							}
							if (tlen + result > BUFFER_LEN)
								abort_interp("Resultant string would overflow buffer.");
							buf[result] = '\0';
							strcatn(buf, sizeof(buf), tbuf);
							result += tlen;
							break;
						case 'S':
						case 's':
							strcatn(sfmt, sizeof(sfmt), "s");
							if (oper3->type != PROG_STRING)
								abort_interp("Format specified string argument not found.");
							snprintf(tbuf, sizeof(tbuf), sfmt, DoNullInd(oper3->data.string));
							tlen = strlen(tbuf);
							if (slrj == 2) {
								tnum = 0;
								while ((tbuf[tnum] == ' ') && (tnum < tlen))
									tnum++;
								if ((tnum) && (tnum < tlen)) {
									temp = tnum / 2;
									for (i = tnum; i < tlen; i++)
										tbuf[i - temp] = tbuf[i];
									for (i = tlen - temp; i < tlen; i++)
										tbuf[i] = ' ';
								}
							}
							if (strlen(tbuf) + result > BUFFER_LEN)
								abort_interp("Resultant string would overflow buffer.");
							buf[result] = '\0';
							strcatn(buf, sizeof(buf), tbuf);
							result += strlen(tbuf);
							break;
						case '?':
							strcatn(sfmt, sizeof(sfmt), "s");
							switch (oper3->type) {
							case PROG_OBJECT:
								strcpy(hold, "OBJECT");
								break;
							case PROG_FLOAT:
								strcpy(hold, "FLOAT");
								break;
							case PROG_INTEGER:
								strcpy(hold, "INTEGER");
								break;
							case PROG_LOCK:
								strcpy(hold, "LOCK");
								break;
							case PROG_STRING:
								strcpy(hold, "STRING");
								break;
							case PROG_VAR:
								strcpy(hold, "VARIABLE");
								break;
							case PROG_LVAR:
								strcpy(hold, "LOCAL-VARIABLE");
								break;
							case PROG_SVAR:
								strcpy(hold, "SCOPED-VARIABLE");
								break;
							case PROG_ADD:
								strcpy(hold, "ADDRESS");
								break;
							case PROG_ARRAY:
								strcpy(hold, "ARRAY");
								break;
							case PROG_FUNCTION:
								strcpy(hold, "FUNCTION-NAME");
								break;
							case PROG_IF:
								strcpy(hold, "IF-STATEMENT");
								break;
							case PROG_EXEC:
								strcpy(hold, "EXECUTE");
								break;
							case PROG_JMP:
								strcpy(hold, "JUMP");
								break;
							case PROG_PRIMITIVE:
								strcpy(hold, "PRIMITIVE");
								break;
							default:
								strcpy(hold, "UNKNOWN");
							}
							snprintf(tbuf, sizeof(tbuf), sfmt, hold);
							tlen = strlen(tbuf);
							if (slrj == 2) {
								tnum = 0;
								while ((tbuf[tnum] == ' ') && (tnum < tlen))
									tnum++;
								if ((tnum) && (tnum < tlen)) {
									temp = tnum / 2;
									for (i = tnum; i < tlen; i++)
										tbuf[i - temp] = tbuf[i];
									for (i = tlen - temp; i < tlen; i++)
										tbuf[i] = ' ';
								}
							}
							if (strlen(tbuf) + result > BUFFER_LEN)
								abort_interp("Resultant string would overflow buffer.");
							buf[result] = '\0';
							strcatn(buf, sizeof(buf), tbuf);
							result += strlen(tbuf);
							break;
						case 'd':
							strcatn(sfmt, sizeof(sfmt), "s");
							if (oper3->type != PROG_OBJECT)
								abort_interp("Format specified object not found.");
							snprintf(hold, sizeof(hold), "#%d", oper3->data.objref);
							snprintf(tbuf, sizeof(tbuf), sfmt, hold);
							tlen = strlen(tbuf);
							if (slrj == 2) {
								tnum = 0;
								while ((tbuf[tnum] == ' ') && (tnum < tlen))
									tnum++;
								if ((tnum) && (tnum < tlen)) {
									temp = tnum / 2;
									for (i = tnum; i < tlen; i++)
										tbuf[i - temp] = tbuf[i];
									for (i = tlen - temp; i < tlen; i++)
										tbuf[i] = ' ';
								}
							}
							if (strlen(tbuf) + result > BUFFER_LEN)
								abort_interp("Resultant string would overflow buffer.");
							buf[result] = '\0';
							strcatn(buf, sizeof(buf), tbuf);
							result += strlen(tbuf);
							break;
						case 'D':
							strcatn(sfmt, sizeof(sfmt), "s");
							if (oper3->type != PROG_OBJECT)
								abort_interp("Format specified object not found.");
							if (!valid_object(oper3))
								abort_interp("Format specified object not valid.");
							ref = oper3->data.objref;
							CHECKREMOTE(ref);
							if (NAME(ref)) {
								strcpy(hold, PNAME(ref));
							} else {
								hold[0] = '\0';
							}
							snprintf(tbuf, sizeof(tbuf), sfmt, hold);
							tlen = strlen(tbuf);
							if (slrj == 2) {
								tnum = 0;
								while ((tbuf[tnum] == ' ') && (tnum < tlen))
									tnum++;
								if ((tnum) && (tnum < tlen)) {
									temp = tnum / 2;
									for (i = tnum; i < tlen; i++)
										tbuf[i - temp] = tbuf[i];
									for (i = tlen - temp; i < tlen; i++)
										tbuf[i] = ' ';
								}
							}
							if (strlen(tbuf) + result > BUFFER_LEN)
								abort_interp("Resultant string would overflow buffer.");
							buf[result] = '\0';
							strcatn(buf, sizeof(buf), tbuf);
							result += strlen(tbuf);
							break;
						case 'l':
							strcatn(sfmt, sizeof(sfmt), "s");
							if (oper3->type != PROG_LOCK)
								abort_interp("Format specified lock not found.");
							strcpy(hold, unparse_boolexp(ProgUID, oper3->data.lock, 1));
							snprintf(tbuf, sizeof(tbuf), sfmt, hold);
							tlen = strlen(tbuf);
							if (slrj == 2) {
								tnum = 0;
								while ((tbuf[tnum] == ' ') && (tnum < tlen))
									tnum++;
								if ((tnum) && (tnum < tlen)) {
									temp = tnum / 2;
									for (i = tnum; i < tlen; i++)
										tbuf[i - temp] = tbuf[i];
									for (i = tlen - temp; i < tlen; i++)
										tbuf[i] = ' ';
								}
							}
							if (strlen(tbuf) + result > BUFFER_LEN)
								abort_interp("Resultant string would overflow buffer.");
							buf[result] = '\0';
							strcatn(buf, sizeof(buf), tbuf);
							result += strlen(tbuf);
							break;
						case 'f':
						case 'e':
						case 'g':
							strcatn(sfmt, sizeof(sfmt), "l");
							snprintf(hold, sizeof(hold), "%c", sstr[scnt]);
							strcatn(sfmt, sizeof(sfmt), hold);
							if (oper3->type != PROG_FLOAT)
								abort_interp("Format specified float not found.");
							snprintf(tbuf, sizeof(tbuf), sfmt, oper3->data.fnumber);
							tlen = strlen(tbuf);
							if (slrj == 2) {
								tnum = 0;
								while ((tbuf[tnum] == ' ') && (tnum < tlen))
									tnum++;
								if ((tnum) && (tnum < tlen)) {
									temp = tnum / 2;
									for (i = tnum; i < tlen; i++)
										tbuf[i - temp] = tbuf[i];
									for (i = tlen - temp; i < tlen; i++)
										tbuf[i] = ' ';
								}
							}
							if (strlen(tbuf) + result > BUFFER_LEN)
								abort_interp("Resultant string would overflow buffer.");
							buf[result] = '\0';
							strcatn(buf, sizeof(buf), tbuf);
							result += strlen(tbuf);
							break;
						default:
							abort_interp("Invalid format string.");
							break;
						}
						nargs = 2;
						scnt++;
						tstop += strlen(tbuf);
					}
				} else {
					if ((sstr[scnt] == '\\') && (sstr[scnt + 1] == 't')) {
						if ((result - (tstop % 8) + 1) >= BUFFER_LEN)
							abort_interp("Resultant string would overflow buffer.");
						if ((tstop % 8) == 0) {
							buf[result++] = ' ';
							tstop++;
						}
						while ((tstop % 8) != 0) {
							buf[result++] = ' ';
							tstop++;
						}
						scnt += 2;
						tstop = 0;
					} else {
						if (sstr[scnt] == '\r') {
							tstop = 0;
							scnt++;
							buf[result++] = '\r';
						} else {
							buf[result++] = sstr[scnt++];
							tstop++;
						}
					}
				}
			}
			if (result >= BUFFER_LEN)
				abort_interp("Resultant string would overflow buffer.");
			buf[result] = '\0';

			temp2.type = PROG_STRING;
			temp2.data.string = alloc_prog_string(buf);
			array_appenditem(&nu, &temp2);
			CLEAR(&temp2);
		} while (array_next(arr, &temp1));
	}

	CLEAR(oper1);
	CLEAR(oper2);
	PushArrayRaw(nu);
}



void
prim_split(PRIM_PROTOTYPE)
{
	char *temp=NULL;

	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Non-string argument. (2)");
	if (!oper1->data.string)
		abort_interp("Null string split argument. (2)");
	if (oper2->type != PROG_STRING)
		abort_interp("Non-string argument. (1)");

	*buf = '\0';
	if (!oper2->data.string) {
		result = 0;
	} else {
		strcpy(buf, oper2->data.string->data);
		pname = strstr(buf, oper1->data.string->data);
		if (!pname) {
			result = -1;
		} else {
			temp = pname + oper1->data.string->length;
			*pname = '\0';
			result = 1;
		}
	}
	CLEAR(oper1);
	CLEAR(oper2);
	if (result > 0) {
		if (result == 1) {
			if (buf[0] == '\0') {
				PushNullStr;
			} else {
				PushString(buf);
			}
			if (temp[0] == '\0') {
				PushNullStr;
			} else {
				PushString(temp);
			}
		} else {
			PushString(buf);
			PushNullStr;
		}
	} else {
		PushString(buf);
		PushNullStr;
	}
}

void
prim_rsplit(PRIM_PROTOTYPE)
{
	char *temp=NULL, *hold=NULL;

	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Non-string argument. (2)");
	if (!oper1->data.string)
		abort_interp("Null split argument. (2)");
	if (oper2->type != PROG_STRING)
		abort_interp("Non-string argument. (1)");

	*buf = '\0';
	if (!oper2->data.string) {
		result = 0;
	} else {
		if (oper1->data.string->length > oper2->data.string->length) {
			result = -1;
		} else {
			strcpy(buf, oper2->data.string->data);
			temp = buf + (oper2->data.string->length - oper1->data.string->length);
			hold = NULL;
			while ((temp != (buf - 1)) && (!hold)) {
				if (*temp == *(oper1->data.string->data))
					if (!strncmp(temp, oper1->data.string->data, oper1->data.string->length))
						hold = temp;
				temp--;
			}
			if (!hold) {
				result = -1;
			} else {
				*hold = '\0';
				hold += oper1->data.string->length;
				result = 1;
			}
		}
	}
	CLEAR(oper1);
	CLEAR(oper2);
	if (result) {
		if (result == 1) {
			if (buf[0] == '\0') {
				PushNullStr;
			} else {
				PushString(buf);
			}
			if (hold && hold[0] == '\0') {
				PushNullStr;
			} else {
				PushString(hold);
			}
		} else {
			PushString(buf);
			PushNullStr;
		}
	} else {
		PushString(buf);
		PushNullStr;
	}
}

void
prim_ctoi(PRIM_PROTOTYPE)
{
	char c;

	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Non-string argument. (1)");
	if (!oper1->data.string) {
		c = '\0';
	} else {
		c = oper1->data.string->data[0];
	}
	result = c;
	CLEAR(oper1);
	PushInt(result);
}

void
prim_itoc(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if ((oper1->type != PROG_INTEGER) || (oper1->data.number < 0))
		abort_interp("Argument must be a positive integer. (1)");
	if (oper1->data.number > 127 || (!isprint((char) oper1->data.number) &&
									 ((char) oper1->data.number != '\r') &&
									 ((char) oper1->data.number != ESCAPE_CHAR))) {
		result = 0;
	} else {
		result = 1;
		buf[0] = (char) oper1->data.number;
		buf[1] = '\0';
	}
	CLEAR(oper1);
	if (result) {
		PushString(buf);
	} else {
		PushNullStr;
	}
}

void
prim_stod(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();

	if (oper1->type != PROG_STRING)
		abort_interp("Non-string argument. (1)");

	if (!oper1->data.string) {
		ref = NOTHING;
	} else {
		const char *ptr = oper1->data.string->data;
		const char *nptr = NULL;

		while (isspace(*ptr)) ptr++;
		if (*ptr == '#') ptr++;
		if (*ptr == '+') ptr++;
		nptr = ptr;
		if (*nptr == '-') nptr++;
		while (*nptr && !isspace(*nptr) &&
		       (*nptr >= '0' || *nptr <= '9')) {
		        nptr++;
		} /* while */
		if (*nptr && !isspace(*nptr)) {
		        ref = NOTHING;
		} else {
		        ref = (dbref) atoi(ptr);
		} /* if */
	}
	CLEAR(oper1);
	PushObject(ref);
}

void
prim_midstr(PRIM_PROTOTYPE)
{
	int start, range;

	CHECKOP(3);
	oper1 = POP();
	oper2 = POP();
	oper3 = POP();
	result = 0;
	if (oper3->type != PROG_STRING)
		abort_interp("Non-string argument. (1)");
	if (oper2->type != PROG_INTEGER)
		abort_interp("Non-integer argument. (2)");
	if (oper2->data.number < 1)
		abort_interp("Data must be a positive integer. (2)");
	if (oper1->type != PROG_INTEGER)
		abort_interp("Non-integer argument. (3)");
	if (oper1->data.number < 0)
		abort_interp("Data must be a positive integer. (3)");
	if (!oper3->data.string) {
		result = 1;
	} else {
		if (oper2->data.number > oper3->data.string->length) {
			result = 1;
		} else {
			start = oper2->data.number - 1;
			if ((oper1->data.number + start) > oper3->data.string->length) {
				range = oper3->data.string->length - start;
			} else {
				range = oper1->data.number;
			}
			bcopy(oper3->data.string->data + start, buf, range);
			buf[range] = '\0';
		}
	}
	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
	if (result) {
		PushNullStr;
	} else {
		PushString(buf);
	}
}

void
prim_numberp(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING || !oper1->data.string)
		result = 0;
	else
		result = number(oper1->data.string->data);
	CLEAR(oper1);
	PushInt(result);
}

void
prim_stringcmp(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_STRING || oper2->type != PROG_STRING)
		abort_interp("Non-string argument.");
	if (oper1->data.string == oper2->data.string)
		result = 0;
	else if (!(oper2->data.string && oper1->data.string))
		result = oper1->data.string ? -1 : 1;
	else {
#if defined(ANONYMITY)
		char pad[16384];

		strcpy(pad, unmangle(player, oper2->data.string->data));
		result = string_compare(pad, unmangle(player, oper1->data.string->data));
#else
		result = string_compare(oper2->data.string->data, oper1->data.string->data);
#endif
	}
	CLEAR(oper1);
	CLEAR(oper2);
	PushInt(result);
}

void
prim_strcmp(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_STRING || oper2->type != PROG_STRING)
		abort_interp("Non-string argument.");
	if (oper1->data.string == oper2->data.string)
		result = 0;
	else if (!(oper2->data.string && oper1->data.string))
		result = oper1->data.string ? -1 : 1;
	else {
#if defined(ANONYMITY)
		char pad[16384];

		strcpy(pad, unmangle(player, oper2->data.string->data));
		result = strcmp(pad, unmangle(player, oper1->data.string->data));
#else
		result = strcmp(oper2->data.string->data, oper1->data.string->data);
#endif
	}
	CLEAR(oper1);
	CLEAR(oper2);
	PushInt(result);
}

void
prim_strncmp(PRIM_PROTOTYPE)
{
	CHECKOP(3);
	oper1 = POP();
	oper2 = POP();
	oper3 = POP();
	if (oper1->type != PROG_INTEGER)
		abort_interp("Non-integer argument.");
	if (oper2->type != PROG_STRING || oper3->type != PROG_STRING)
		abort_interp("Non-string argument.");
	if (oper2->data.string == oper3->data.string)
		result = 0;
	else
		result = strncmp(DoNullInd(oper3->data.string),
		                 DoNullInd(oper2->data.string),
						 oper1->data.number);
	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
	PushInt(result);
}

void
prim_strcut(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	temp1 = *(oper1 = POP());
	temp2 = *(oper2 = POP());
	if (temp1.type != PROG_INTEGER)
		abort_interp("Non-integer argument (2)");
	if (temp1.data.number < 0)
		abort_interp("Argument must be a positive integer.");
	if (temp2.type != PROG_STRING)
		abort_interp("Non-string argument (1)");
	if (!temp2.data.string) {
		PushNullStr;
		PushNullStr;
	} else {
		if (temp1.data.number > temp2.data.string->length) {
			temp2.data.string->links++;
			PushStrRaw(temp2.data.string);
			PushNullStr;
		} else {
			bcopy(temp2.data.string->data, buf, temp1.data.number);
			buf[temp1.data.number] = '\0';
			PushString(buf);
			if (temp2.data.string->length > temp1.data.number) {
				bcopy(temp2.data.string->data + temp1.data.number, buf,
					  temp2.data.string->length - temp1.data.number + 1);
				PushString(buf);
			} else {
				PushNullStr;
			}
		}
	}
	CLEAR(&temp1);
	CLEAR(&temp2);
}

void
prim_strlen(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Non-string argument.");
	if (!oper1->data.string)
		result = 0;
	else
#if defined(ANONYMITY)
		result = strlen(unmangle(player, oper1->data.string->data));
#else
		result = oper1->data.string->length;
#endif
	CLEAR(oper1);
	PushInt(result);
}

void
prim_strcat(PRIM_PROTOTYPE)
{
	struct shared_string *string;

	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_STRING || oper2->type != PROG_STRING)
		abort_interp("Non-string argument.");
	if (!oper1->data.string && !oper2->data.string)
		string = NULL;
	else if (!oper2->data.string) {
		oper1->data.string->links++;
		string = oper1->data.string;
	} else if (!oper1->data.string) {
		oper2->data.string->links++;
		string = oper2->data.string;
	} else if (oper1->data.string->length + oper2->data.string->length > (BUFFER_LEN) - 1) {
		abort_interp("Operation would result in overflow.");
	} else {
		bcopy(oper2->data.string->data, buf, oper2->data.string->length);
		bcopy(oper1->data.string->data, buf + oper2->data.string->length,
			  oper1->data.string->length + 1);
		string = alloc_prog_string(buf);
	}
	CLEAR(oper1);
	CLEAR(oper2);
	PushStrRaw(string);
}

void
prim_atoi(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING || !oper1->data.string)
		result = 0;
	else
		result = atoi(oper1->data.string->data);
	CLEAR(oper1);
	PushInt(result);
}

void
prim_notify(PRIM_PROTOTYPE)
{
	struct inst *oper1 = NULL; /* prevents re-entrancy issues! */
	struct inst *oper2 = NULL; /* prevents re-entrancy issues! */

	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Non-string argument (2)");
	if (!valid_object(oper2))
		abort_interp("Invalid object argument (1)");
	CHECKREMOTE(oper2->data.objref);

	if (oper1->data.string) {
		if (tp_force_mlev1_name_notify && mlev < 2 && player != oper2->data.objref) {
			prefix_message(buf, oper1->data.string->data, PNAME(player), BUFFER_LEN, 1);
		}
		else
		{
			/* TODO: The original code made this copy, is it really necessary? */
			strcpy(buf, oper1->data.string->data);
		}

		notify_listeners(player, program, oper2->data.objref,
						 getloc(oper2->data.objref), buf, 1);
	}
	CLEAR(oper1);
	CLEAR(oper2);
}


void
prim_notify_exclude(PRIM_PROTOTYPE)
{
	/* roomD excludeDn ... excludeD1 nI messageS  -- */
	struct inst *oper1 = NULL; /* prevents re-entrancy issues! */
	struct inst *oper2 = NULL; /* prevents re-entrancy issues! */

	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper2->type != PROG_INTEGER)
		abort_interp("non-integer count argument (top-1)");
	if (oper1->type != PROG_STRING)
		abort_interp("Non-string message argument (top)");

	if (tp_force_mlev1_name_notify && mlev < 2) {
		prefix_message(buf, DoNullInd(oper1->data.string), PNAME(player), BUFFER_LEN, 1);
	}
	else
		strcpy(buf, DoNullInd(oper1->data.string));

	result = oper2->data.number;
	CLEAR(oper1);
	CLEAR(oper2);
	{
		dbref what, where, excluded[STACK_SIZE];
		int count, i;

		CHECKOP(0);
		count = i = result;
		if (i >= STACK_SIZE || i < 0)
			abort_interp("Count argument is out of range.");
		while (i > 0) {
			CHECKOP(1);
			oper1 = POP();
			if (oper1->type != PROG_OBJECT)
				abort_interp("Invalid object argument.");
			excluded[--i] = oper1->data.objref;
			CLEAR(oper1);
		}
		CHECKOP(1);
		oper1 = POP();
		if (!valid_object(oper1))
			abort_interp("Non-object argument (1)");
		where = oper1->data.objref;
		if (Typeof(where) != TYPE_ROOM && Typeof(where) != TYPE_THING &&
			Typeof(where) != TYPE_PLAYER) abort_interp("Invalid location argument (1)");
		CHECKREMOTE(where);
		what = DBFETCH(where)->contents;
		CLEAR(oper1);
		if (*buf) {
			while (what != NOTHING) {
				if (Typeof(what) != TYPE_ROOM) {
					for (tmp = 0, i = count; i-- > 0;) {
						if (excluded[i] == what)
							tmp = 1;
					}
				} else {
					tmp = 1;
				}
				if (!tmp)
					notify_listeners(player, program, what, where, buf, 0);
				what = DBFETCH(what)->next;
			}
		}

		if (tp_listeners) {
			for (tmp = 0, i = count; i-- > 0;) {
				if (excluded[i] == where)
					tmp = 1;
			}
			if (!tmp)
				notify_listeners(player, program, where, where, buf, 0);
			if (tp_listeners_env && !tmp) {
				what = DBFETCH(where)->location;
				for (; what != NOTHING; what = DBFETCH(what)->location)
					notify_listeners(player, program, what, where, buf, 0);
			}
		}
	}
}

void
prim_intostr(PRIM_PROTOTYPE)
{
	char *ptr=NULL;
	int val;
	int negflag;

	CHECKOP(1);
	oper1 = POP();
	if (oper1->type == PROG_STRING)
		abort_interp("Invalid argument.");
	if (oper1->type == PROG_FLOAT) {
		snprintf(buf, sizeof(buf), "%.15lg", oper1->data.fnumber);
		if (!strchr(buf, '.') && !strchr(buf, 'n') && !strchr(buf, 'e')) {
			strcatn(buf, sizeof(buf), ".0");
		}
		ptr=buf;
	} else {
		val = oper1->data.number;
		ptr = &buf[BUFFER_LEN];
		negflag = (val < 0) ? 1 : 0;
		val = abs(val);

		*(--ptr) = '\0';
		if (!val) {
			*(--ptr) = '0';
		} else {
			while (val) {
				*(--ptr) = '0' + (val % 10);
				val /= 10;
			}
		}
		if (negflag) {
			*(--ptr) = '-';
		}
		/* snprintf(buf, sizeof(buf), "%d", oper1->data.number); */
	}
	CLEAR(oper1);
	PushString(ptr);
}

void
prim_explode(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	temp1 = *(oper1 = POP());
	temp2 = *(oper2 = POP());
	oper1 = &temp1;
	oper2 = &temp2;
	if (temp1.type != PROG_STRING)
		abort_interp("Non-string argument (2)");
	if (temp2.type != PROG_STRING)
		abort_interp("Non-string argument (1)");
	if (!temp1.data.string)
		abort_interp("Empty string argument (2)");
	{
		int i;
		const char *delimit = temp1.data.string->data;

		if (!temp2.data.string) {
			result = 1;
			CLEAR(&temp1);
			CLEAR(&temp2);
			PushNullStr;
			PushInt(result);
			return;
		} else {
			result = 0;
			bcopy(temp2.data.string->data, buf, temp2.data.string->length + 1);
			for (i = temp2.data.string->length - 1; i >= 0; i--) {
				if (!strncmp(buf + i, delimit, temp1.data.string->length)) {
					buf[i] = '\0';
					CHECKOFLOW(1);
					PushString((buf + i + temp1.data.string->length));
					result++;
				}
			}
			CHECKOFLOW(1);
			PushString(buf);
			result++;
		}
	}
	CHECKOFLOW(1);
	CLEAR(&temp1);
	CLEAR(&temp2);
	PushInt(result);
}


void
prim_explode_array(PRIM_PROTOTYPE)
{
	stk_array *nu;
	char *tempPtr;
	char *lastPtr;
	CHECKOP(2);
	temp1 = *(oper1 = POP());
	temp2 = *(oper2 = POP());
	oper1 = &temp1;
	oper2 = &temp2;
	if (temp1.type != PROG_STRING)
		abort_interp("Non-string argument (2)");
	if (temp2.type != PROG_STRING)
		abort_interp("Non-string argument (1)");
	if (!temp1.data.string)
		abort_interp("Empty string argument (2)");
	CHECKOFLOW(1);

	{
		const char *delimit = temp1.data.string->data;
		int delimlen = temp1.data.string->length;

		nu = new_array_packed(0);
		if (!temp2.data.string) {
			lastPtr = "";
		} else {
			strcpy(buf, temp2.data.string->data);
			tempPtr = lastPtr = buf;
			while (*tempPtr) {
				if (!strncmp(tempPtr, delimit, delimlen)) {
					*tempPtr = '\0';
					tempPtr += delimlen;

					temp3.type = PROG_STRING;
					temp3.data.string = alloc_prog_string(lastPtr);
					array_appenditem(&nu, &temp3);
					CLEAR(&temp3);

					lastPtr = tempPtr;
				} else {
					tempPtr++;
				}
			}
		}
	}

	temp3.type = PROG_STRING;
	temp3.data.string = alloc_prog_string(lastPtr);
	array_appenditem(&nu, &temp3);

	CLEAR(&temp1);
	CLEAR(&temp2);
	CLEAR(&temp3);

	PushArrayRaw(nu);
}


void
prim_subst(PRIM_PROTOTYPE)
{
	CHECKOP(3);
	oper1 = POP();
	oper2 = POP();
	oper3 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Non-string argument (3)");
	if (oper2->type != PROG_STRING)
		abort_interp("Non-string argument (2)");
	if (oper3->type != PROG_STRING)
		abort_interp("Non-string argument (1)");
	if (!oper1->data.string)
		abort_interp("Empty string argument (3)");
	{
		int i = 0, j = 0, k = 0;
		const char *match;
		const char *replacement;
		char xbuf[BUFFER_LEN];

		buf[0] = '\0';
		if (oper3->data.string) {
			bcopy(oper3->data.string->data, xbuf, oper3->data.string->length + 1);
			match = oper1->data.string->data;
			replacement = DoNullInd(oper2->data.string);
			k = *replacement ? oper2->data.string->length : 0;
			while (xbuf[i]) {
				if (!strncmp(xbuf + i, match, oper1->data.string->length)) {
					if ((j + k + 1) > BUFFER_LEN)
						abort_interp("Operation would result in overflow.");
					strcatn(buf, sizeof(buf), replacement);
					i += oper1->data.string->length;
					j += k;
				} else {
					if ((j + 1) > BUFFER_LEN)
						abort_interp("Operation would result in overflow.");
					buf[j++] = xbuf[i++];
					buf[j] = '\0';
				}
			}
		}
	}
	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
	PushString(buf);
}

void
prim_instr(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Invalid argument type (2)");
	if (!(oper1->data.string))
		abort_interp("Empty string argument (2)");
	if (oper2->type != PROG_STRING)
		abort_interp("Non-string argument (1)");
	if (!oper2->data.string) {
		result = 0;
	} else {

		const char *remaining = oper2->data.string->data;
		const char *match = oper1->data.string->data;
		int step = 1;

		result = 0;
		do {
			if (!strncmp(remaining, match, oper1->data.string->length)) {
				result = remaining - oper2->data.string->data + 1;
				break;
			}
			remaining += step;
		} while (remaining >= oper2->data.string->data && *remaining);
	}
	CLEAR(oper1);
	CLEAR(oper2);
	PushInt(result);
}

void
prim_rinstr(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Invalid argument type (2)");
	if (!(oper1->data.string))
		abort_interp("Empty string argument (2)");
	if (oper2->type != PROG_STRING)
		abort_interp("Non-string argument (1)");
	if (!oper2->data.string) {
		result = 0;
	} else {

		const char *remaining = oper2->data.string->data;
		const char *match = oper1->data.string->data;
		int step = -1;

		remaining += oper2->data.string->length - 1;

		result = 0;
		do {
			if (!strncmp(remaining, match, oper1->data.string->length)) {
				result = remaining - oper2->data.string->data + 1;
				break;
			}
			remaining += step;
		} while (remaining >= oper2->data.string->data && *remaining);
	}
	CLEAR(oper1);
	CLEAR(oper2);
	PushInt(result);
}

void
prim_pronoun_sub(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();				/* oper1 is a string, oper2 a dbref */
	if (!valid_object(oper2))
		abort_interp("Invalid argument (1)");
	if (oper1->type != PROG_STRING)
		abort_interp("Invalid argument (2)");
	if (oper1->data.string) {
		strcpy(buf, pronoun_substitute(fr->descr, oper2->data.objref,
									   oper1->data.string->data));
	} else {
		buf[0] = '\0';
	}
	CLEAR(oper1);
	CLEAR(oper2);
	PushString(buf);
}

void
prim_toupper(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Non-string argument.");
	if (oper1->data.string) {
		strcpy(buf, oper1->data.string->data);
	} else {
		buf[0] = '\0';
	}
	for (ref = 0; buf[ref]; ref++)
		buf[ref] = UPCASE(buf[ref]);
	CLEAR(oper1);
	PushString(buf);
}

void
prim_tolower(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Non-string argument.");
	if (oper1->data.string) {
		strcpy(buf, oper1->data.string->data);
	} else {
		buf[0] = '\0';
	}
	for (ref = 0; buf[ref]; ref++)
		buf[ref] = DOWNCASE(buf[ref]);
	CLEAR(oper1);
	PushString(buf);
}

void
prim_unparseobj(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_OBJECT)
		abort_interp("Non-object argument.");
	{
		result = oper1->data.objref;
		switch (result) {
		case NOTHING:
			snprintf(buf, sizeof(buf), "*NOTHING*");
			break;
		case HOME:
			snprintf(buf, sizeof(buf), "*HOME*");
			break;
		default:
			if (result < 0 || result > db_top)
				snprintf(buf, sizeof(buf), "*INVALID*");
			else
				snprintf(buf, sizeof(buf), "%s(#%d%s)", RNAME(result), result, unparse_flags(result));
		}
		CLEAR(oper1);
		PushString(buf);
	}
}

void
prim_smatch(PRIM_PROTOTYPE)
{
	char xbuf[BUFFER_LEN];

	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_STRING || oper2->type != PROG_STRING)
		abort_interp("Non-string argument.");

#if defined(ANONYMITY)
	strcpy(buf, unmangle(player, DoNullInd(oper1->data.string)));
	strcpy(xbuf, unmangle(player, DoNullInd(oper2->data.string)));
#else
	strcpy(buf, DoNullInd(oper1->data.string));
	strcpy(xbuf, DoNullInd(oper2->data.string));
#endif
	result = equalstr(buf, xbuf);

	CLEAR(oper1);
	CLEAR(oper2);
	PushInt(result);
}

void
prim_striplead(PRIM_PROTOTYPE)
{								/* string -- string' */
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Not a string argument.");
	strcpy(buf, DoNullInd(oper1->data.string));
	for (pname = buf; *pname && isspace(*pname); pname++) ;
	CLEAR(oper1);
	PushString(pname);
}

void
prim_striptail(PRIM_PROTOTYPE)
{								/* string -- string' */
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Not a string argument.");
	strcpy(buf, DoNullInd(oper1->data.string));
	result = strlen(buf);
	while ((result-- > 0) && isspace(buf[result]))
		buf[result] = '\0';
	CLEAR(oper1);
	PushString(buf);
}

void
prim_stringpfx(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_STRING || oper2->type != PROG_STRING)
		abort_interp("Non-string argument.");
	if (oper1->data.string == oper2->data.string)
		result = 0;
	else {
		result = string_prefix(DoNullInd(oper2->data.string), DoNullInd(oper1->data.string));
	}
	CLEAR(oper1);
	CLEAR(oper2);
	PushInt(result);
}


void
prim_strencrypt(PRIM_PROTOTYPE)
{
	const char *ptr;

	CHECKOP(2);
	oper2 = POP();
	oper1 = POP();
	if (oper1->type != PROG_STRING || oper2->type != PROG_STRING) {
		abort_interp("Non-string argument.");
	}
	if (!oper2->data.string || !*(oper2->data.string->data)) {
		abort_interp("Key cannot be a null string. (2)");
	}
#if defined(ANONYMITY)
	{
		char pad[BUFFER_LEN * 2];
		char pad2[BUFFER_LEN * 2];

		strcpy(pad, unmangle(player, DoNullInd(oper1->data.string)));
		strcpy(pad2, unmangle(player, DoNullInd(oper2->data.string)));
		ptr = strencrypt(pad, pad2);
	}
#else
	ptr = strencrypt(DoNullInd(oper1->data.string), oper2->data.string->data);
#endif
	CLEAR(oper1);
	CLEAR(oper2);
	PushString(ptr);
}


void
prim_strdecrypt(PRIM_PROTOTYPE)
{
	const char *ptr;

	CHECKOP(2);
	oper2 = POP();
	oper1 = POP();
	if (oper1->type != PROG_STRING || oper2->type != PROG_STRING) {
		abort_interp("Non-string argument.");
	}
	if (!oper2->data.string || !*(oper2->data.string->data)) {
		abort_interp("Key cannot be a null string. (2)");
	}
#if defined(ANONYMITY)
	{
		char pad[BUFFER_LEN * 2];
		char pad2[BUFFER_LEN * 2];

		strcpy(pad, unmangle(player, DoNullInd(oper1->data.string)));
		strcpy(pad2, unmangle(player, DoNullInd(oper2->data.string)));
		ptr = strdecrypt(pad, pad2);
	}
#else
	ptr = strdecrypt(DoNullInd(oper1->data.string), oper2->data.string->data);
#endif
	CLEAR(oper1);
	CLEAR(oper2);
	PushString(ptr);
}


void
prim_textattr(PRIM_PROTOTYPE)
{
	int totallen;
	int done = 0;
	const char *ptr;
	char *ptr2;
	char buf[BUFFER_LEN];
	char attr[BUFFER_LEN];

	CHECKOP(2);
	oper2 = POP();
	oper1 = POP();
	if (oper1->type != PROG_STRING) {
		abort_interp("Non-string argument. (1)");
	}
	if (oper2->type != PROG_STRING) {
		abort_interp("Non-string argument. (2)");
	}

	if (!oper1->data.string || !oper2->data.string) {
		strcpy(buf, DoNullInd(oper1->data.string));
	} else {
		*buf = '\0';
		ptr = oper2->data.string->data;
		ptr2 = attr;
		while (!done) {
			switch (*ptr) {
			case ' ':{
					ptr++;
					break;
				}

			case '\0':
			case ',':{
					*ptr2++ = '\0';
					if (!string_compare(attr, "reset")) {
						strcatn(buf, sizeof(buf), ANSI_RESET);
					} else if (!string_compare(attr, "normal")) {
						strcatn(buf, sizeof(buf), ANSI_RESET);
					} else if (!string_compare(attr, "bold")) {
						strcatn(buf, sizeof(buf), ANSI_BOLD);
					} else if (!string_compare(attr, "dim")) {
						strcatn(buf, sizeof(buf), ANSI_DIM);
					} else if (!string_compare(attr, "italic")) {
						strcatn(buf, sizeof(buf), ANSI_ITALIC);
					} else if (!string_compare(attr, "uline") ||
							   !string_compare(attr, "underline")) {
						strcatn(buf, sizeof(buf), ANSI_UNDERLINE);
					} else if (!string_compare(attr, "flash")) {
						strcatn(buf, sizeof(buf), ANSI_FLASH);
					} else if (!string_compare(attr, "reverse")) {
						strcatn(buf, sizeof(buf), ANSI_REVERSE);
					} else if (!string_compare(attr, "ostrike")) {
						strcatn(buf, sizeof(buf), ANSI_OSTRIKE);
					} else if (!string_compare(attr, "overstrike")) {
						strcatn(buf, sizeof(buf), ANSI_OSTRIKE);

					} else if (!string_compare(attr, "black")) {
						strcatn(buf, sizeof(buf), ANSI_FG_BLACK);
					} else if (!string_compare(attr, "red")) {
						strcatn(buf, sizeof(buf), ANSI_FG_RED);
					} else if (!string_compare(attr, "yellow")) {
						strcatn(buf, sizeof(buf), ANSI_FG_YELLOW);
					} else if (!string_compare(attr, "green")) {
						strcatn(buf, sizeof(buf), ANSI_FG_GREEN);
					} else if (!string_compare(attr, "cyan")) {
						strcatn(buf, sizeof(buf), ANSI_FG_CYAN);
					} else if (!string_compare(attr, "blue")) {
						strcatn(buf, sizeof(buf), ANSI_FG_BLUE);
					} else if (!string_compare(attr, "magenta")) {
						strcatn(buf, sizeof(buf), ANSI_FG_MAGENTA);
					} else if (!string_compare(attr, "white")) {
						strcatn(buf, sizeof(buf), ANSI_FG_WHITE);

					} else if (!string_compare(attr, "bg_black")) {
						strcatn(buf, sizeof(buf), ANSI_BG_BLACK);
					} else if (!string_compare(attr, "bg_red")) {
						strcatn(buf, sizeof(buf), ANSI_BG_RED);
					} else if (!string_compare(attr, "bg_yellow")) {
						strcatn(buf, sizeof(buf), ANSI_BG_YELLOW);
					} else if (!string_compare(attr, "bg_green")) {
						strcatn(buf, sizeof(buf), ANSI_BG_GREEN);
					} else if (!string_compare(attr, "bg_cyan")) {
						strcatn(buf, sizeof(buf), ANSI_BG_CYAN);
					} else if (!string_compare(attr, "bg_blue")) {
						strcatn(buf, sizeof(buf), ANSI_BG_BLUE);
					} else if (!string_compare(attr, "bg_magenta")) {
						strcatn(buf, sizeof(buf), ANSI_BG_MAGENTA);
					} else if (!string_compare(attr, "bg_white")) {
						strcatn(buf, sizeof(buf), ANSI_BG_WHITE);
					} else {
						abort_interp
								("Unrecognized attribute tag.  Try one of reset, bold, dim, underline, reverse, black, red, yellow, green, cyan, blue, magenta, white, bg_black, bg_red, bg_yellow, bg_green, bg_cyan, bg_blue, bg_magenta, or bg_white.");
					}

					ptr2 = attr;
					if (!*ptr) {
						done++;
					} else {
						ptr++;
					}
					break;
				}

			default:{
					*ptr2++ = *ptr++;
				}
			}
		}
		totallen = strlen(buf);
		totallen += oper1->data.string->length;
		totallen += strlen(ANSI_RESET);
		if (totallen >= BUFFER_LEN) {
			abort_interp("Operation would result in too long of a string.");
		}
		strcatn(buf, sizeof(buf), oper1->data.string->data);
	}
	strcatn(buf, sizeof(buf), ANSI_RESET);

	CLEAR(oper1);
	CLEAR(oper2);
	PushString(buf);
}

void
prim_tokensplit(PRIM_PROTOTYPE)
{								/* string delims escapechar -- prestr poststr charstr */
	char *ptr, *delim, *out;
	char esc;
	char escisdel;
	char outbuf[BUFFER_LEN];

	CHECKOP(3);
	oper3 = POP();
	oper2 = POP();
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Not a string argument. (1)");
	if (oper2->type != PROG_STRING)
		abort_interp("Not a string argument. (2)");
	if (oper3->type != PROG_STRING)
		abort_interp("Not a string argument. (3)");
	if (!oper2->data.string || oper2->data.string->length < 1)
		abort_interp("Invalid null delimiter string. (2)");
	if (oper3->data.string) {
		esc = oper3->data.string->data[0];
	} else {
		esc = '\0';
	}
	escisdel = index(oper2->data.string->data, esc) != 0;
	strcpy(buf, DoNullInd(oper1->data.string));
	ptr = buf;
	out = outbuf;
	while (*ptr) {
		if ((*ptr == esc) && (!escisdel || (ptr[1] == esc))) {
			ptr++;
			if (!*ptr)
				break;
		} else {
			delim = oper2->data.string->data;
			while (*delim) {
				if (*delim == *ptr)
					break;
				delim++;
			}
			if (*delim == *ptr)
				break;
		}
		*out++ = *ptr++;
	}
	*out = '\0';
	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
	if (ptr && *ptr) {
		char charbuf[2];

		charbuf[0] = *ptr;
		charbuf[1] = '\0';
		*ptr++ = '\0';
		PushString(outbuf);
		PushString(ptr);
		PushString(charbuf);
	} else {
		PushString(outbuf);
		PushNullStr;
		PushNullStr;
	}
}

void
prim_ansi_strlen(PRIM_PROTOTYPE)
{
	char *ptr;
	int i;

	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Not a string argument.");

	if (!oper1->data.string) {
		CLEAR(oper1);
		i = 0;
		PushInt(i);
		/* Weird PushInt() #define requires that. */
		return;
	}

	i = 0;

	ptr = oper1->data.string->data;

	while (*ptr) {
		if (*ptr++ == ESCAPE_CHAR) {
			if (*ptr == '\0') {;
			} else if (*ptr != '[') {
				ptr++;
			} else {
				ptr++;
				while (isdigit(*ptr) || *ptr == ';')
					ptr++;
				if (*ptr == 'm')
					ptr++;
			}
		} else {
			i++;
		}
	}
	CLEAR(oper1);
	PushInt(i);
}

void
prim_ansi_strcut(PRIM_PROTOTYPE)
{
	char *ptr;
	char *op;
	char outbuf1[BUFFER_LEN];
	char outbuf2[BUFFER_LEN];
	int loc;

	CHECKOP(2);
	oper2 = POP();
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Not a string argument. (1)");
	if (oper2->type != PROG_INTEGER)
		abort_interp("Not an integer argument. (2)");
	if (!oper1->data.string) {
		CLEAR(oper1);
		CLEAR(oper2);
		PushNullStr;
		PushNullStr;
		return;
	}

	loc = 0;

	if (oper2->data.number >= oper1->data.string->length) {
		strcpy(buf, oper1->data.string->data);
		CLEAR(oper1);
		CLEAR(oper2);
		PushString(buf);
		PushNullStr;
		return;
	} else if (oper2->data.number <= 0) {
		strcpy(buf, oper1->data.string->data);
		CLEAR(oper1);
		CLEAR(oper2);
		PushNullStr;
		PushString(buf);
		return;
	}

	ptr = oper1->data.string->data;

	*outbuf2 = '\0';
	op = outbuf1;
	while (*ptr) {
		if ((*op++ = *ptr++) == ESCAPE_CHAR) {
			if (*ptr == '\0') {
				break;
			} else if (*ptr != '[') {
				*op++ = *ptr++;
			} else {
				*op++ = *ptr++;
				while (isdigit(*ptr) || *ptr == ';')
					*op++ = *ptr++;
				if (*ptr == 'm')
					*op++ = *ptr++;
			}
		} else {
			loc++;
			if (loc == oper2->data.number) {
				break;
			}
		}
	}
	*op = '\0';
	memcpy((void *) outbuf2, (const void *) ptr,
		   oper1->data.string->length - (ptr - oper1->data.string->data) + 1);

	CLEAR(oper1);
	CLEAR(oper2);
	PushString(outbuf1);
	if (!*outbuf2) {
		PushNullStr;
	} else {
		PushString(outbuf2);
	}
}

void
prim_ansi_strip(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_STRING)
		abort_interp("Non-string argument.");

	if (!oper1->data.string) {
		CLEAR(oper1);
		PushNullStr;
		return;
	}

	strip_ansi(buf, oper1->data.string->data);
	CLEAR(oper1);
	PushString(buf);
}

void
prim_ansi_midstr(PRIM_PROTOTYPE)
{
	int loc, start, range;
	const char* ptr;
	char* op;

	CHECKOP(3);
	oper1 = POP(); /* length */
	oper2 = POP(); /* begin */
	oper3 = POP(); /* string */

	if (oper3->type != PROG_STRING)
		abort_interp("Non-string argument! (3)");
	if (oper2->type != PROG_INTEGER)
		abort_interp("Non-integer argument! (2)");
	if (oper1->type != PROG_INTEGER)
		abort_interp("Non-integer argument! (1)");
	if (oper2->data.number < 1)
		abort_interp("Data must be a positve integer. (2)");
	if (oper1->data.number < 0)
		abort_interp("Data must be a postive integer. (1)");

	start = oper2->data.number - 1;
	range = oper1->data.number;

	if (!oper3->data.string || start > oper3->data.string->length ||
			range == 0) {
		CLEAR(oper1);
		CLEAR(oper2);
		CLEAR(oper3);
		PushNullStr;
		return;
	}

	ptr = oper3->data.string->data;
	op = buf;
	loc = 0;

	if (start != 0) {
		/* First, loop till the beginning of the section to copy... */
		while (*ptr && loc < start) {
			if ((*ptr++) == ESCAPE_CHAR) {
				if (*ptr == '\0') {
					break;
				} else if (*ptr != '[') {
					ptr++;
				} else {
					ptr++;
					while (isdigit(*ptr) || *ptr == ';')
						ptr++;
					if (*ptr == 'm')
						ptr++;
				}
			} else {
				loc++;
			}
		}
	}

	loc = 0;
	/* Then, start copying, and counting while we do... */
	while (*ptr) {
		if ((*op++ = *ptr++) == ESCAPE_CHAR) {
			if (*ptr == '\0') {
				break;
			} else if (*ptr != '[') {
				*op++ = *ptr++;
			} else {
				*op++ = *ptr++;
				while (isdigit(*ptr) || *ptr == ';')
					*op++ = *ptr++;
				if (*ptr == 'm')
					*op++ = *ptr++;
			}
		} else {
			loc++;
			if (loc == range) {
				break;
			}
		}
	}
	*op = '\0';

	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
	PushString(buf);
}