view 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 source

/* $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 */