diff macros.c @ 3:5a977ccbc7a9 default tip

Empty changelog
author darius
date Sat, 06 Dec 1997 05:41:29 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/macros.c	Sat Dec 06 05:41:29 1997 +0000
@@ -0,0 +1,1032 @@
+/* $Id: macros.c,v 1.1.1.1 1997/12/06 05:41:29 darius Exp $ */
+
+/* here's pretty much all the macro code.   */
+/* This bears little resemblance to the     */
+/* BRM code, i.e. it's somewhat organized :)*/
+/* Bill Dyess  10/05/93	            [BDyess]*/
+
+#ifdef MACROS
+#include"copyright.h"
+#include<stdio.h>
+#if !defined(SVR4) && !defined(sparc)
+#include<strings.h>
+#else
+#include<string.h>
+#endif				/* !SVR4 && !sparc */
+#include<ctype.h>
+#include<fcntl.h>
+#include<sys/types.h>
+#include"Wlib.h"
+#include"data.h"
+#include"defs.h"
+#include"struct.h"
+#include"gameconf.h"
+#include"proto.h"
+
+#define MAXMACRO 4096
+#if defined(__STDC__) || defined(RS6K) || defined(sgi)
+typedef signed char s_char;
+#else
+typedef char s_char;
+#endif
+ 
+/* prototypes */
+void doMacro2 P((struct macro * m, W_Event * data));
+void handle_dollar P((char **locpntr, char **destpntr, W_Event * data));
+void handle_special P((char **locpntr, char **destpntr, W_Event * data));
+char   *strtoupper P((char *buf));
+char   *strtolower P((char *buf));
+void handle_test P((char **locpntr, char **destpntr, W_Event * data));
+void handle_conditional P((char **locpntr, char **destpntr, W_Event * data));
+void getTestString P((char *buf, char **locpntr, char **destpntr, W_Event * data));
+void getConditionalString P((char **locpntr, char **destpntr, W_Event * data));
+void ignoreConditionalString P((char **locpntr));
+
+int     abortflag = 0;
+
+void
+initMacros()
+{
+    struct stringlist *s;
+    char   *loc;
+    unsigned char ch;
+    struct macro *m;
+    int     i;
+    struct dmacro_list *dm;
+    struct dmacro_list *dm_def;
+    int     notdone;
+    unsigned char	c;
+    char  *str;
+
+    /* initialize macro lookup tables */
+    bzero(macrotable, sizeof(struct macro *) * 256);
+
+#ifdef RC_DISTRESS
+    /* sizeof doesn't work if it isn't in the same source file, shoot me */
+    MCOPY(dist_defaults, dist_prefered, sizedist);
+#endif
+
+    for (s = defaults; s; s = s->next) {
+#ifdef RC_DISTRESS
+	if (strncmpi(s->string, "dist.", 5) == 0) {
+	    str = (s->string) + 5;
+	    if (*str == '^') {
+		str++;
+		if (*str == '^')
+		    c = '^';
+		else
+		    c = *str + 128;
+	    } else
+		c = *str;
+	    str++;
+	    if (*str != '.') {
+		str = (s->string) + 4;
+		c = '\0';
+	    }
+	    str++;
+
+	    notdone = 1;
+	    for (dm = &dist_prefered[take], dm_def = &dist_defaults[take], i = take;
+		 dm->name && notdone; dm++, dm_def++, i++) {
+		if (strcmpi(str, dm->name) == 0) {
+		    dm->macro = strdup(s->value);
+		    if (c) {
+			if (!macrotable[c]) {
+			    macrotable[c] = (struct macro *) malloc(sizeof(struct macro));
+			    bzero(macrotable[c], sizeof(struct macro));
+			}
+			macrotable[c]->flags |= MACRCD;
+			macrotable[c]->to = i;
+/*                        printf("dist.%c.%s: %s\n",c,dm->name,dm->macro);*/
+			dm->c = c;
+			dm_def->c = c;
+		    }
+		    notdone = 0;
+		}
+	    }
+	}
+#endif				/* RC_DISTRESS */
+#ifdef BEEPLITE
+
+	else if (strncmpi(s->string, "lite.", 5) == 0) {
+	    int     offset = 5;
+	    char  **lt;
+
+	    if (s->string[6] == '.')
+		offset = 7;
+
+	    notdone = 1;
+
+	    for (lt = &distlite[take], dm = &dist_prefered[take];
+		 dm->name && notdone; dm++, lt++) {
+		if (strcmpi(s->string + offset, dm->name) == 0) {
+		    *lt = strdup(s->value);
+/*                    printf("lite.%s: %s\n",dm->name,*lt);*/
+
+		    notdone = 0;
+		}
+	    }
+	    if (notdone)
+		fprintf(stderr, "Unknown lite %s\n", s->string + offset);
+	}
+#endif				/* BEEPLITE */
+	if (!strncmpi("mac", s->string, 3)) {
+	    if (s->string[3] == '.')
+		loc = s->string + 4;
+	    else if (strncmpi("ro.", s->string + 3, 3))
+		continue;
+	    else
+		loc = s->string + 6;
+	    if (*loc == '^') {	/* possible control char */
+		loc++;
+		if (*loc == '^' && *loc)
+		    ch = '^';
+		else
+		    ch = *loc + 128;
+	    } else
+		ch = *loc;
+	    loc++;
+	    if (!macrotable[ch]) {
+		/*
+		   make sure it doesn't already exist.  I've allowed people
+		   to have singlemacro: before the macro.*.* statements, so
+		   it is possible
+		*/
+		/*
+		   modified to allow multline macros by creating a linked
+		   list of macro structures. -JR
+		*/
+		if (ch == '?') {
+		    printf("Can't use '?' as a macro.  It is reserved for the macro window.  Ignoring.\n");
+		    continue;
+		}
+		macrotable[ch] = m = (struct macro *) malloc(sizeof(struct macro));
+		bzero(m, sizeof(struct macro));
+	    } else {
+#ifdef RC_DISTRESS
+		if (macrotable[ch]->flags & MACRCD) {
+		    m = macrotable[ch];
+		    m->flags &= ~(MACRCD);	/* in case singleMacro was
+						   set */
+		    m->next = 0;
+		} else
+#endif
+		{
+		    m = (struct macro *) malloc(sizeof(struct macro));
+		    m->next = macrotable[ch];
+		    macrotable[ch] = m;
+		    m->next->flags |= MACMULTI;
+		    m->flags = m->next->flags;
+		}
+	    }
+	    if (*(loc++) != '.')
+		m->to = -2;	/* no destination given */
+	    else {
+		ch = *loc;
+		if (ch == '%') {
+		    m->specialto = toupper(*(loc + 1));
+		    m->to = -1;
+		} else {
+		    m->to = ch;
+		}
+	    }
+	    m->string = strdup(s->value);
+	} else if (!strncmpi("singlemacro", s->string, 11)) {
+	    loc = s->value;
+	    while (*loc) {
+		ch = *(loc++);
+		if (ch == '^') {/* for control chars */
+		    if (*loc != '^' && *loc)
+			ch = *loc + 128;
+		    loc++;
+		}
+		if (!macrotable[ch]) {
+		    m = macrotable[ch] = (struct macro *) malloc(sizeof(struct macro));
+		    bzero(m, sizeof(struct macro));
+		    m->flags = MACSINGLE;
+		} else {
+		    for (m = macrotable[ch]; m; m = m->next)
+			m->flags |= MACSINGLE;
+		}
+	    }
+	}
+    }
+    for (i = 0; i < 256; i++) {
+	/* eliminate any macros that have (null) macro strings */
+	if (macrotable[i] && !(macrotable[i]->flags & MACRCD)) {
+	    struct macro *tmp, **scan;
+
+	    scan=&macrotable[i];
+	    while (*scan) {
+		if ( (*scan)->string ) {
+		    scan = &(*scan)->next;
+		} else {
+		    tmp = (*scan);
+		    *scan = tmp->next;
+		    free(tmp);
+		}
+	    }
+	    }
+	}
+#ifdef RC_DISTRESS
+    /*
+       make macro entries for the default RCD keys, if those keys don't have
+       macros defined
+    */
+    for (dm = &dist_prefered[take], i = take; dm->name; dm++, i++) {
+	if (!macrotable[dm->c]) {
+	    macrotable[dm->c] = (struct macro *) malloc(sizeof(struct macro));
+	    bzero(macrotable[dm->c], sizeof(struct macro));
+	    macrotable[dm->c]->flags = MACRCD;
+	    macrotable[dm->c]->to = i;
+	}
+    }
+#endif
+}
+
+void
+doMacro(data)
+    W_Event *data;
+/* takes a key as input and creates a string that is then sent to smessage*/
+{
+    static struct macro *m;
+    int     key = data->key;
+
+    if (key == '?') {
+	showMacroWin();
+	macroState = 0;
+	return;
+    }
+    if (macroState != 2)
+	m = macrotable[key];
+    if (!m) {
+	W_Beep();
+	warning("No such macro");
+	macroState = 0;
+	return;			/* no macro */
+    }
+#ifdef RC_DISTRESS
+    if (m->flags & MACRCD) {
+	rcd(m->to, data);
+	macroState = 0;
+	return;
+    }
+#endif
+    while (m) {
+	if (macroState == 2) {
+	    m->to = key;
+	    doMacro2(m, data);
+	    m->to = -2;
+	} else {
+	    doMacro2(m, data);
+	    if (macroState == 2)
+		return;
+	}
+	m = m->next;
+    }
+    macroState = 0;
+    warning("              ");
+}
+
+void
+doMacro2(m, data)
+    struct macro *m;
+    W_Event *data;
+{
+    int     group = -1, recip = 0;
+    char    buf[MAXMACRO], sourcebuf[MAXMACRO];
+    char   *loc, *dest;
+    struct obtype *target;
+
+    /* first figure out who I'm going to send it to */
+    if ((s_char)m->to == -1) {		/* special recipient */
+	switch (m->specialto) {
+	case 'I':		/* send a message to myself */
+	case 'C':
+	    group = MINDIV;
+	    recip = me->p_no;
+	    break;
+	case 'U':		/* send message to player nearest mouse */
+	case 'P':
+	    group = MINDIV;
+	    target = gettarget(data->Window, data->x, data->y, TARG_PLAYER);
+	    recip = target->o_num;
+	    break;
+	case 'T':		/* send message to team of the player nearest
+				   mouse */
+	case 'Z':
+	    group = MTEAM;
+	    target = gettarget(data->Window, data->x, data->y, TARG_PLAYER);
+	    recip = idx_to_mask(players[target->o_num].p_teami);
+	    break;
+	case 'G':		/* send message to nearest friendly player to
+				   my ship */
+	    group = MINDIV;
+	    target = gettarget((W_Window) 0, me->p_x, me->p_y,
+			       TARG_PLAYER | TARG_FRIENDLY);
+	    recip = target->o_num;
+	    break;
+	case 'H':		/* send message to nearest enemy player to my
+				   ship */
+	    group = MINDIV;
+	    target = gettarget((W_Window) 0, me->p_x, me->p_y,
+			       TARG_PLAYER | TARG_ENEMY);
+	    recip = target->o_num;
+	    break;
+	default:
+	    warning("Bad macro - incorrect 'to' field");
+	    break;
+	}
+    } else if ((s_char)m->to == -2) {	/* get recipient not provided, so change
+				   state to get one */
+	macroState = 2;
+	warning("Send macro to who?");
+	return;
+    } else
+	recip = m->to;
+    /* now parse the macro itself. */
+    strcpy(sourcebuf, m->string);
+    loc = sourcebuf;
+    dest = buf;
+    while (*loc) {
+	if (*loc == '$') {
+	    loc++;
+	    handle_dollar(&loc, &dest, data);	/* handle the special escape */
+	} else if (*loc == '%') {
+	    loc++;
+	    if (*loc == '*')
+		return;		/* %* means exit macro NOW */
+	    handle_special(&loc, &dest, data);	/* handle the special escape */
+	} else {
+	    *(dest++) = *(loc++);
+	}
+    }
+    *dest = 0;
+    if (buf[0] == 0 || abortflag) {	/* abortflag means somewhere there
+					   was a %* */
+	abortflag = 0;
+	macroState = 0;
+	return;			/* null message.  If you *really* want to
+				   print a null message, use <space> */
+    }
+    if (group == -1)
+	group = getgroup(recip, &recip);
+    if (group <= 0)
+	return;
+    if ((m->flags & MACMULTI) && (F_multiline_enabled || paradise))
+	group |= MMACRO;
+    pmessage(buf, recip, group);
+}
+
+void
+handle_special(locpntr, destpntr, data)
+    char  **locpntr, **destpntr;
+    W_Event *data;
+{
+    char    ch = **locpntr;
+    char   *buf = *destpntr;
+    struct obtype *target;
+    struct macro *m;
+    int     targettype = 0;
+    struct id *id;
+    /* for pingstats */
+#if 0
+    extern int ping_iloss_sc;	/* inc % loss 0--100, server to client */
+    extern int ping_iloss_cs;	/* inc % loss 0--100, client to server */
+#endif				/* 0 */
+    extern int ping_tloss_sc;	/* total % loss 0--100, server to client */
+    extern int ping_tloss_cs;	/* total % loss 0--100, client to server */
+#if 0
+    extern int ping_lag;	/* delay in ms of last ping */
+#endif				/* 0 */
+    extern int ping_av;		/* average rt */
+    extern int ping_sd;		/* standard deviation */
+
+    switch (ch) {
+    case 'a':			/* armies carried by sender */
+	sprintf(buf, "%d", me->p_armies);
+	break;
+    case 'd':			/* sender damage percentage */
+	sprintf(buf, "%d", 100 * me->p_damage / me->p_ship->s_maxdamage);
+	break;
+    case 's':			/* sender shield percentage */
+	sprintf(buf, "%d", 100 * me->p_shield / me->p_ship->s_maxshield);
+	break;
+    case 'f':			/* sender fuel percentage */
+	sprintf(buf, "%d", 100 * me->p_fuel / me->p_ship->s_maxfuel);
+	break;
+    case 'w':			/* sender wtemp percentage */
+	sprintf(buf, "%d", 100 * me->p_wtemp / me->p_ship->s_maxwpntemp);
+	break;
+    case 'e':			/* sender etemp percentage */
+	sprintf(buf, "%d", 100 * me->p_etemp / me->p_ship->s_maxegntemp);
+	break;
+    case 'r':			/* team id character of target player */
+	target = gettarget(data->Window, data->x, data->y, TARG_PLAYER);
+	buf[0] = teaminfo[players[target->o_num].p_teami].letter;
+	buf[1] = 0;
+	break;
+    case 't':			/* team id character of target planet */
+	target = gettarget(data->Window, data->x, data->y, TARG_PLANET);
+	buf[0] = teaminfo[mask_to_idx(planets[target->o_num].pl_owner)].letter;
+	buf[1] = 0;
+	break;
+    case 'p':			/* id character of target player */
+	targettype = TARG_PLAYER;
+    case 'g':			/* id character of target friendly player */
+	if (!targettype)
+	    targettype = TARG_PLAYER | TARG_FRIENDLY;
+    case 'h':			/* id char of target enemy player */
+	if (!targettype)
+	    targettype = TARG_PLAYER | TARG_ENEMY;
+	id = getTargetID(data->Window, data->x, data->y, targettype);
+	buf[0] = id->mapstring[1];
+	buf[1] = 0;
+	break;
+    case 'P':			/* id character of player nearest sender */
+	id = getTargetID((W_Window) 0, me->p_x, me->p_y, TARG_PLAYER);
+	buf[0] = id->mapstring[1];
+	buf[1] = 0;
+	break;
+    case 'T':			/* team id character of sender team */
+	buf[0] = me->p_mapchars[0];
+	buf[1] = 0;
+	break;
+    case 'c':			/* sender id character */
+	buf[0] = me->p_mapchars[1];
+	buf[1] = 0;
+	break;
+    case 'C':			/* 1 if cloaked, 0 if not [BDyess] */
+	if (me->p_flags & PFCLOAK)
+	    buf[0] = '1';
+	else
+	    buf[0] = '0';
+	buf[1] = 0;
+	break;
+    case 'n':			/* armies on target planet */
+	target = gettarget(data->Window, data->x, data->y, TARG_PLANET);
+	sprintf(buf, "%d", planets[target->o_num].pl_info ?
+		planets[target->o_num].pl_armies :
+		-1);
+	break;
+    case 'E':			/* 1 if etemped, 0 if not */
+	if (me->p_flags & PFENG)
+	    buf[0] = '1';
+	else
+	    buf[0] = '0';
+	buf[1] = 0;
+	break;
+    case 'W':			/* 1 if wtemped, 0 if not */
+	if (me->p_flags & PFWEP)
+	    buf[0] = '1';
+	else
+	    buf[0] = '0';
+	buf[1] = 0;
+	break;
+    case 'S':			/* sender two character ship type */
+	strncpy(buf, me->p_ship->s_desig, 2);
+	buf[2] = 0;
+	break;
+    case 'G':			/* id char of friendly player nearest sender */
+	targettype = TARG_FRIENDLY;
+    case 'H':			/* id char of enemy player nearest sender */
+	if (!targettype)
+	    targettype = TARG_ENEMY;
+	id = getTargetID((W_Window) 0, me->p_x, me->p_y,
+			 TARG_PLAYER | targettype);
+	buf[0] = id->mapstring[1];
+	buf[1] = 0;
+	break;
+    case 'l':			/* three character name of target planet */
+    case 'L':
+	id = getTargetID(data->Window, data->x, data->y, TARG_PLANET);
+	strcpy(buf, id->mapstring);
+	buf[0] = tolower(buf[0]);
+	break;
+    case 'i':			/* sender full player name (16 character max) */
+    case 'I':
+	strncpy(buf, me->p_name, 16);
+	buf[16] = 0;
+	break;
+    case 'u':			/* full name of target player (16 character
+				   max) */
+    case 'U':
+	id = getTargetID(data->Window, data->x, data->y, TARG_PLAYER);
+	strncpy(buf, id->name, 16);
+	buf[16] = 0;
+	break;
+    case 'z':			/* 3 letter team id of target planet */
+    case 'Z':
+	id = getTargetID(data->Window, data->x, data->y, TARG_PLANET);
+	strcpy(buf, teaminfo[id->team].shortname);
+	strtolower(buf);
+	break;
+    case 'b':			/* nearest planet to sender */
+    case 'B':
+	id = getTargetID((W_Window) 0, me->p_x, me->p_y, TARG_PLANET);
+	strcpy(buf, id->mapstring);
+	buf[0] = tolower(buf[0]);
+	break;
+    case 'v':			/* average ping round trip time */
+	sprintf(buf, "%d", ping_av);
+	break;
+    case 'V':			/* ping stdev */
+	sprintf(buf, "%d", ping_sd);
+	break;
+    case 'y':			/* packet loss */
+	sprintf(buf, "%d", (2 * ping_tloss_sc + ping_tloss_cs) / 3);
+	break;
+    case 'm':			/* last message */
+    case 'M':
+	strcpy(buf, lastMessage);
+	break;
+    case 'o':			/* insert three letter team name */
+    case 'O':
+	strcpy(buf, teaminfo[me->p_teami].shortname);
+	break;
+    case ' ':			/* nothing.  This is so you can start a macro
+				   with spaces */
+	buf[0] = ' ';
+	buf[1] = 0;
+	break;
+    case '%':			/* insert % */
+	buf[0] = '%';
+	buf[1] = 0;
+	break;
+    case '?':			/* start test */
+	(*locpntr)++;
+	handle_test(locpntr, destpntr, data);
+	return;
+    case '{':			/* conditional */
+	handle_conditional(locpntr, destpntr, data);
+	return;
+    case '*':			/* abort! */
+	abortflag = 1;
+	return;
+    case '2':			/* is paradise?  sorry, ran out of good
+				   letters. '2' means, 'is Netrek II?'. */
+	buf[0] = paradise + '0';
+	buf[1] = 0;
+	break;
+    case '_':			/* call another macro. Added 1/24/94 [BDyess] */
+	(*locpntr)++;
+	if (**locpntr == '^') {	/* control char */
+	    (*locpntr)++;
+	    m = macrotable[**locpntr + (**locpntr == '^') ? 0 : 128];
+	} else {
+	    m = macrotable[(int) **locpntr];
+	}
+	if (m) {		/* does the macro exist? */
+	    char    temp[MAXMACRO];
+	    strcpy(temp, m->string);
+	    strcat(temp, *locpntr + 1);
+	    strcpy(*locpntr + 1, temp);
+	} else {		/* somebody screwed up */
+	    printf("Error: called macro ");
+	    if (&m - macrotable >= 128)
+		putchar('^');
+	    printf("%c doesn't exist.\n", **locpntr);
+	}
+	buf[0] = 0;
+	break;
+    default:
+	sprintf(buf, "Unknown %% escape: %%%c", ch);
+	warning(buf);
+	buf[0] = 0;
+	break;
+    }
+    if (isupper(ch))
+	strtoupper(buf);
+    (*locpntr)++;
+    while (**destpntr)
+	(*destpntr)++;
+    return;
+}
+
+void
+handle_test(locpntr, destpntr, data)
+    char  **locpntr, **destpntr;
+    W_Event *data;
+{
+    char    l[MAXMACRO], r[MAXMACRO], condition = 0;
+    short   trueflag = 0;
+
+    getTestString(l, locpntr, destpntr, data);
+    if (**locpntr != '%') {
+	condition = *((*locpntr)++);
+	getTestString(r, locpntr, destpntr, data);
+    }
+    switch (condition) {
+    case '=':
+	if (!strcmp(l, r))
+	    trueflag = 1;
+	break;
+    case '>':
+	if (atoi(l) > atoi(r))
+	    trueflag = 1;
+	break;
+    case '<':
+	if (atoi(l) < atoi(r))
+	    trueflag = 1;
+	break;
+    default:
+	if (atoi(l))
+	    trueflag = 1;
+    }
+    **destpntr = '0' + trueflag;
+    *(*destpntr + 1) = 0;
+    (*destpntr)++;
+    return;
+}
+
+void
+handle_conditional(locpntr, destpntr, data)
+    char  **locpntr, **destpntr;
+    W_Event *data;
+{
+    (*locpntr)++;
+    **destpntr = 0;
+    (*destpntr)--;
+    if (**destpntr == '0') {
+	ignoreConditionalString(locpntr);
+	getConditionalString(locpntr, destpntr, data);
+    } else {
+	getConditionalString(locpntr, destpntr, data);
+	ignoreConditionalString(locpntr);
+    }
+    (*locpntr) += 2;
+    while (**destpntr)
+	(*destpntr)++;
+    return;
+}
+
+void
+ignoreConditionalString(locpntr)
+    char  **locpntr;
+{
+    int     depth = 0, breakflag = 0;
+
+    while (**locpntr) {
+	if (**locpntr == '%') {
+	    switch (*(*locpntr + 1)) {
+	    case '!':
+		if (!depth)
+		    breakflag = 1;
+		(*locpntr) += 2;
+		break;
+	    case '}':
+		if (depth) {
+		    depth--;
+		    (*locpntr) += 2;
+		} else
+		    breakflag = 1;
+		break;
+	    case '{':
+		depth++;
+		(*locpntr) += 2;
+		break;
+	    case '*':
+		abortflag = 1;
+		return;
+	    default:
+		(*locpntr)++;
+	    }
+	    if (breakflag)
+		break;
+	} else
+	    (*locpntr)++;
+    }
+}
+
+void
+getConditionalString(locpntr, destpntr, data)
+    char  **locpntr, **destpntr;
+    W_Event *data;
+{
+    char   *dest = *destpntr;
+
+    while (**locpntr) {
+	if (**locpntr != '%' && **locpntr != '$' && **locpntr)
+	    *(dest++) = *((*locpntr)++);
+	else if (*(*locpntr + 1) == '!') {
+	    (*locpntr) += 2;
+	    break;
+	} else if (*(*locpntr + 1) == '}')
+	    break;
+	else if (**locpntr == '%') {
+	    (*locpntr)++;
+	    handle_special(locpntr, &dest, data);
+	    while (*(dest++));
+	    dest--;
+	} else {		/* **locpntr must equal '$' */
+	    (*locpntr)++;
+	    handle_dollar(locpntr, &dest, data);
+	    while (*(dest++));
+	    dest--;
+	}
+    }
+    *dest = 0;
+    return;
+}
+
+void
+getTestString(buf, locpntr, destpntr, data)
+    char   *buf, **locpntr, **destpntr;
+    W_Event *data;
+{
+    char   *dest = buf;
+
+    if (**locpntr == '%') {
+	(*locpntr)++;
+	handle_special(locpntr, &buf, data);
+    } else if (**locpntr == '$') {
+	(*locpntr)++;
+	handle_dollar(locpntr, &buf, data);
+    } else {
+	while (**locpntr != '%' && **locpntr != '$' && **locpntr != '<' &&
+	       **locpntr != '>' && **locpntr != '=' &&
+	       **locpntr)
+	    *(dest++) = *((*locpntr)++);
+	*dest = 0;
+    }
+    return;
+}
+
+char   *
+strtoupper(buf)
+    char   *buf;
+{
+    char   *s;
+    for (s = buf; *s; s++)
+	*s = toupper(*s);
+    return buf;
+}
+
+char   *
+strtolower(buf)
+    char   *buf;
+{
+    char   *s;
+    for (s = buf; *s; s++)
+	*s = tolower(*s);
+    return buf;
+}
+
+/**********************************************************************/
+
+/*
+   start with a $
+
+   field 1:
+   (n)earest
+   (t)arget
+   (s)elf	(doesn't have fields 2 and 3)
+   (_) ego	(has no other fields)
+
+   field 2:
+   (a)ny
+   (t)eammate
+   (f)riendly
+   (h)ostile
+
+   field 3:
+   (a)ny
+   (u)ser
+   (p)lanet (includes asteroids)
+   (s)tar
+   (n)ebula
+   (b)lack hole
+   (^) non-planet
+   (*) any stellar object
+
+   field 4: (optional)		NYI
+   (U)ppercase
+   (C)apitalize
+   (L)owercase
+
+   field 5:
+   full (n)ame (Hammor, Thought)
+   (i)dentifier (e.g. R5, Ka, Can, Sco)
+   (#) number (0-9a-z for players, %d for planets)
+   (t)eam name (Romulan)
+   (s)hort team id (ROM)
+   (l)etter of team (R)
+   (a)rmies
+   (@) sector
+   (A)rable, 0=not arable, 1=arable but not AGRI, 2=AGRI
+   (M)etal, 0, 1, 2(repair), or 3(sy)
+   (D)ilithium, 0, 1 or 2(fuel)
+
+   Any implementation of the paradise $ codes (subset or superset)
+   must implement and document the $_ code.      -- Robert Forsman
+*/
+
+void
+handle_dollar(locpntr, destpntr, data)
+    char  **locpntr, **destpntr;
+    W_Event *data;
+{
+    char   *buf = *destpntr;
+    struct id *target;
+    char    ch = *((*locpntr)++);
+    W_Window w;
+    int     x, y;
+    int     flags;
+    int     capitalize = 0;
+
+    buf[0] = 0;
+
+    if (ch == '_') {
+	strcpy(buf, "Paradise netrek $ codes are orthogonal and make sense.");
+	while (**destpntr)
+	    (*destpntr)++;
+	return;
+    } if (ch == 's') {
+	target = getTargetID((W_Window) 0, me->p_x, me->p_y, TARG_PLAYER | TARG_SELF);
+    } else {
+	switch (tolower(ch)) {
+	case 'n':
+	    w = 0;
+	    x = me->p_x;
+	    y = me->p_y;
+	    break;
+	case 't':
+	    w = data->Window;
+	    x = data->x;
+	    y = data->y;
+	    break;
+	default:
+	    printf("Invalid $ code field 1 : `%c'\n", ch);
+	    return;
+	}
+
+	ch = *((*locpntr)++);
+	switch (tolower(ch)) {
+	case 'a':
+	    flags = 0;
+	    break;
+	case 't':
+	    flags = TARG_TEAM;
+	    break;
+	case 'f':
+	    flags = TARG_FRIENDLY;
+	    break;
+	case 'h':
+	    flags = TARG_ENEMY;
+	    break;
+	default:
+	    printf("Invalid $ code field 2 : `%c'\n", ch);
+	    return;
+	}
+
+	ch = *((*locpntr)++);
+	switch (tolower(ch)) {
+	case 'a':
+	    flags |= TARG_PLAYER | TARG_ASTRAL;
+	    break;
+	case 'u':
+	    flags |= TARG_PLAYER;
+	    break;
+	case 'p':
+	    flags |= TARG_PLANET;
+	    break;
+	case 's':
+	    flags |= TARG_STAR;
+	    break;
+	case 'n':
+	    flags |= TARG_NEBULA;
+	    break;
+	case 'b':
+	    flags |= TARG_BLACKHOLE;
+	    break;
+	case '^':
+	    flags |= (TARG_ASTRAL & ~TARG_PLANET);
+	    /* fall through */
+	case '*':
+	    flags |= TARG_PLANET;
+	    break;
+	default:
+	    printf("Invalid $ code field 3 : `%c'\n", ch);
+	    return;
+	}
+
+	target = getTargetID(w, x, y, flags);
+    }
+
+    ch = tolower(*((*locpntr)++));
+    if (ch == 'l')
+	capitalize = -1;
+    else if (ch == 'c')
+	capitalize = 1;
+    else if (ch == 'u')
+	capitalize = 2;
+    else
+	(*locpntr)--;		/* oops, back up and try again */
+
+    ch = *((*locpntr)++);
+
+    switch (ch) {
+    case 'n':
+	strcpy(buf, target->name);
+	break;
+    case 'i':
+/*    if (target->type == PLANETTYPE) {*/
+	strcpy(buf, target->mapstring);
+/*
+    } else {
+      buf[0] = target->mapstring[1];
+      buf[1] = 0;
+    }*/
+	break;
+    case '#':
+	if (target->type == PLANETTYPE) {
+	    sprintf(buf, "%d", target->number);
+	} else {
+	    buf[0] = target->mapstring[1];
+	    buf[1] = 0;
+	}
+	break;
+    case 't':
+	strcpy(buf, teaminfo[target->team].name);
+	break;
+    case 's':
+	strcpy(buf, teaminfo[target->team].shortname);
+	break;
+    case 'l':
+	buf[0] = teaminfo[target->team].letter;
+	buf[1] = 0;
+	break;
+    case 'a':
+	sprintf(buf, "%d", (target->type == PLANETTYPE) ?
+		planets[target->number].pl_armies :
+		players[target->number].p_armies);
+	break;
+    case '@':
+	if (!paradise)
+	    break;
+	if (target->type == PLANETTYPE) {
+	    x = planets[target->number].pl_x;
+	    y = planets[target->number].pl_y;
+	} else {
+	    x = players[target->number].p_x;
+	    y = players[target->number].p_y;
+	}
+	sprintf(buf, "%d-%d", x / GRIDSIZE + 1, y / GRIDSIZE + 1);
+	break;
+    case 'A':			/* Arable or AGRI */
+	buf[0] = '0';
+	if (target->type == PLANETTYPE) {
+	    if (planets[target->number].pl_flags & PLARABLE)
+		buf[0] = '1';
+	    if (planets[target->number].pl_flags & PLAGRI)
+		buf[0] = '2';
+	}
+	buf[1] = 0;
+	break;
+    case 'M':			/* Metal, Repair, or SY */
+	buf[0] = '0';
+	if (target->type == PLANETTYPE) {
+	    if (planets[target->number].pl_flags & PLMETAL)
+		buf[0] = '1';
+	    if (planets[target->number].pl_flags & PLREPAIR)
+		buf[0] = '2';
+	    if (planets[target->number].pl_flags & PLSHIPYARD)
+		buf[0] = '3';
+	}
+	buf[1] = 0;
+	break;
+    case 'D':			/* Dilythium or Fuel */
+	buf[0] = '0';
+	if (target->type == PLANETTYPE) {
+	    if (planets[target->number].pl_flags & PLDILYTH)
+		buf[0] = '1';
+	    if (planets[target->number].pl_flags & PLFUEL)
+		buf[0] = '2';
+	}
+	buf[1] = 0;
+	break;
+    default:
+	printf("Invalid $ code field 4 : `%c'\n", ch);
+	return;
+    }
+
+    if (capitalize < 0) {
+	char   *s;
+	for (s = buf; *s; s++)
+	    *s = tolower(*s);
+    } else if (capitalize > 1) {
+	char   *s;
+	for (s = buf; *s; s++)
+	    *s = toupper(*s);
+    } else if (capitalize) {
+	char   *s;
+	s = buf;
+	*s = toupper(*s);
+	for (s++; *s; s++)
+	    *s = tolower(*s);
+    }
+    while (**destpntr)
+	(*destpntr)++;
+    return;
+}
+#endif				/* MACROS */