/* D I A L O G 3 . C */
/* Rare or special purpose dialog functions */


#include "word.h"
DEBUGASSERTSZ            /* WIN - bogus macro for assert string */
#include "heap.h"
#include "debug.h"
#include "error.h"
#include "doc.h"
#include "disp.h"
#include "ch.h"
#include "resource.h"
#include "wininfo.h"
#include "opuscmd.h"
#include "cmdtbl.h"
#include "menu2.h"
#include "version.h"
#include "screen.h"
#include "idd.h"
#include "prompt.h"
#include "message.h"
#include "doslib.h"
#include "keys.h"
#include "rareflag.h"
#include "props.h"
#include "format.h"
#include "sel.h"
#include "inter.h"
#include "style.h"

#include "sdmdefs.h"
#include "sdmver.h"
#include "sdm.h"
#include "sdmtmpl.h"
#include "sdmparse.h"



#define chPath ('\\')

#define dttmNinch (0)
#define dttmError (1)

extern struct SCI  vsci;
extern struct SEL  selCur;
extern int vfShowAllStd;
extern char (**vhmpiststcp)[];
extern int vstcBackup;
extern int vdocStsh;
extern int vistLbMac;
extern struct MERR  vmerr;
extern BOOL fElActive;
extern struct MERR  vmerr;
extern struct ITR       vitr;
extern struct PREF vpref;
extern int         wwCur;
extern BOOL fElActive;


/* For dialogs where the grayness of a button depends on more than one
	edit control not being blank */
/*  %%Function:  GrayRgtmcOnBlank  %%Owner:  bobz       */

VOID GrayRgtmcOnBlank(rgtmc, itmcMac, tmcButton)
TMC rgtmc[];
int itmcMac;
TMC tmcButton;
{
	char sz [ichMaxBufDlg];
	int itmc;
	BOOL fEnable = fTrue;

	for (itmc = 0; itmc < itmcMac; itmc++)
		{
		GetTmcText(rgtmc[itmc], sz, ichMaxBufDlg);
		fEnable = fEnable && (CchStripString(sz, CchSz(sz) - 1) != 0);
		}
	EnableTmc(tmcButton, fEnable);
}



/*  table of unit names. Order must be same as ut* order
	(utInch = 0, utPt = 1, etc.) in disp.h */

extern CHAR    *mputsz[utMax];

/*  table of unit values. Order must be same as ut* order (utInch = 0,
		utPt = 1, etc.) in disp.h */

unsigned mputczaUt[utMax] =
	{
	czaInch, czaPoint, czaCm, czaPicas, czaP10, czaP12, czaLine

#ifdef INTL	 /* FUTURE make sure wordwin.h, statline tables updated */
		, czaInch
#endif /* INTL */

	};




#ifndef INTL
   /* eventually use the INTL form for all. At ship time did not
	  want to risk using the rewrite bobz

	  This is the current US form of the function, with hard coded strings
   */

/* ****
*  Description:  Return za in *pza from string representation in ss.
*     (ss can be sz or st since cch is given). Returns true if valid za
*      This is the general all-purpose unit-terminated string to integer
*      routine.
** **** */
/*  %%Function:  FZaFromSs  %%Owner:  bobz       */

int FZaFromSs(pza, ss, cch, ut, pfOverflow)
int     *pza;
CHAR    ss[];
int     cch, ut;
BOOL	*pfOverflow;
{
	long    lza      = 0;
	register CHAR    *pch    = ss;
	register CHAR  *pchMac = &ss[cch];
	int     ch;
	unsigned czaUt;
	int     fNeg;

	*pfOverflow = fFalse;
	if (cch <= 0)
		return fFalse;

	switch (ChLower((*--pchMac)))
		{ /* Check for units */
	case 'n': /* inch */
		if (ChLower(*--pchMac) != 'i')
			goto NoUnits;
	case '"': /* inch */
		ut = utInch;
		break;
	case '0': /* p10 */
		if (*--pchMac != '1' || ChLower(*--pchMac) != 'p')
			goto NoUnits;
		ut = utP10;
		break;
	case '2': /* p12 */
		if (*--pchMac != '1' || ChLower(*--pchMac) != 'p')
			goto NoUnits;
		ut = utP12;
		break;
	case 'i': /* line or pica */
		if (ChLower(*--pchMac) == 'l')
			ut = utLine;
		else  if (ChLower(*pchMac) == 'p')
			ut = utPica;
		else
			goto NoUnits;
		break;
	case 't': /* pt */
		if (ChLower(*--pchMac) != 'p')
			goto NoUnits;
		ut = utPt;
		break;
	case 'm': /* cm */
		if (ChLower(*--pchMac) != 'c')
			goto NoUnits;
		ut = utCm;
		break;
	default:
		++pchMac;
		break;
NoUnits:
		pchMac = &ss[cch];
		}

	while (pch < pchMac && *(pchMac - 1) == chSpace)
		--pchMac;

	czaUt = mputczaUt[ut];

/* extract leading blanks */
	while (*pch == ' ')
		pch++;

	fNeg = *pch == '-';
	if (fNeg) ++pch;        /* skip past minus sign */
	while ((ch = *pch++) != vitr.chDecimal)
		{
		if (ch < '0' || ch > '9')
			return fFalse;
		lza = lza * 10 + (ch - '0') * czaUt;
		if (lza >= (long) czaMax)
			goto NumOvfl;
		if (pch >= pchMac)
			goto GotNum;
		}

	while (pch < pchMac)
		{
		ch = *pch++;
		if (ch < '0' || ch > '9')
			return fFalse;
		lza += ((ch - '0') * czaUt + 5) / 10;
		if (lza >= (long) czaMax)
			goto NumOvfl;
		czaUt = (czaUt + 5) / 10;
		}

GotNum:
	if (lza >= (long) czaMax)
		{
NumOvfl:
		*pfOverflow = fTrue;
		return fFalse;
		}

	*pza = fNeg ? (int) -lza : (int) lza;
	return fTrue;
}

#else  /* INTL form. uses table rather than hard coded strings */

/* ****
*  Description:  Return za in *pza from string representation in ss.
*     (ss must be sz or even though cch is given).
*      FUTURE: rename FzaFromSz. All current callers pass in sz's.
*      Returns true if valid za
*      This is the general all-purpose unit-terminated string to integer
*      routine.
** **** */
/*  %%Function:  FZaFromSs  %%Owner:  bobz       */

int FZaFromSs(pza, sz, cch, ut, pfOverflow)
int     *pza;
CHAR    sz[];
int     cch, ut;
BOOL	*pfOverflow;
{
	long    lza      = 0;
	register CHAR    *pch;
	register CHAR  *pchMac;
	int     ch;
	unsigned czaUt;
	int     fNeg;
	int utT;
	BOOL fRet = fFalse;
	int cchStrip;


#ifdef USA	/* old one only work with us strings */
#ifdef DEBUG
     /* call the old one and compare later */
	int zaT;
    BOOL fOverflowT;
	BOOL fT = FZaFromSsVfy(&zaT, sz, cch, ut, &fOverflowT);

#endif /* DEBUG */
#endif /* USA */



	*pfOverflow = fFalse;

	/* extract units string if any. Set pchMac to last digit in string.
	   set ut based on units string */

     /* strip trailing spaces */
	pchMac = &sz[cch];
	Assert (*pchMac == 0);  /* must be an sz, cch must point to end */
	while (sz < pchMac && *(pchMac - 1) == chSpace)
		--pchMac;
	cchStrip = pchMac - sz;
	if (cchStrip <= 0)
		goto LRet;
	if (cchStrip < cch)	  /* need an sz; restore space later */
		sz[cchStrip] = 0;

#ifdef BZ
		CommSzNum(SzShared("FZaFromSz pchMac "),pchMac);
		CommSzRgCch(SzShared("FZaFromSz sznumber 1st strip "), sz, pchMac - sz);
#endif /* BZ */


	   /* set ut if string matches table entry. Must back up length
		  of unit string for compare
	   */
	for (utT = 0; utT < utMax; utT++)
		{
		int cchUt;

		/* FUTURE (bobz)make mputsz into mputstz so we can get length easier */
		/* we are requiring that ss is an sz to avoid bltting each time */
		cchUt = CchSz(mputsz[utT]) - 1;
		if (cchStrip < cchUt)
			continue;
		pch = pchMac - cchUt;

#ifdef BZ
		CommSzSz(SzShared("FZaFromSz szUnit "), pch);
		CommSzSz(SzShared("FZaFromSz mputsz "), mputsz[utT]);
#endif /* BZ */


		if (!WCompSzSrt(pch,mputsz[utT],fFalse /* fCase */))
			{
#ifdef BZ
			CommSzSz(SzShared("FZaFromSz match "), "");
#endif /* BZ */
			ut = utT;
			pchMac = pch;  /* units found, back up before string */
     			/* strip spaces between number and unit string */
			while (sz < pchMac && *(pchMac - 1) == chSpace)
				--pchMac;

			break;
			}
		}

	czaUt = mputczaUt[ut];

#ifdef BZ
		CommSzRgCch(SzShared("FZaFromSz sznumber after 2nd strip "), sz, pchMac - sz);
#endif /* BZ */

	pch = sz;
/* extract leading blanks */
	while (*pch == ' ')
		pch++;

	fNeg = *pch == '-';
	if (fNeg)
		++pch;        /* skip past minus sign */
	while ((ch = *pch++) != vitr.chDecimal)
		{
		if (ch < '0' || ch > '9')
			goto LRet;
		lza = lza * 10 + (ch - '0') * czaUt;
		if (lza >= (long) czaMax)
			goto NumOvfl;
		if (pch >= pchMac)
			goto GotNum;
		}

	while (pch < pchMac)
		{
		ch = *pch++;
		if (ch < '0' || ch > '9')
			goto LRet;
		lza += ((ch - '0') * czaUt + 5) / 10;
		if (lza >= (long) czaMax)
			goto NumOvfl;
		czaUt = (czaUt + 5) / 10;
		}

GotNum:
	if (lza >= (long) czaMax)
		{
NumOvfl:
		*pfOverflow = fTrue;
		goto LRet;
		}

	*pza = fNeg ? (int) -lza : (int) lza;
	fRet = fTrue;
LRet:


#ifdef USA
#ifdef DEBUG
      /* compare with previous results */
	if (fRet != fT)
		{
#ifdef BZ 
		CommSzNum(SzShared("FZaFromSz Old ret: "), fT);
		CommSzNum(SzShared("FZaFromSz New ret: "), fRet);
#endif /* BZ */
		Assert (fFalse);
		}
	else if (fRet)
		{
		if (zaT != *pza || fOverflowT != *pfOverflow)
			{
#ifdef BZ 
			CommSzNum(SzShared("FZaFromSz Old za: "), zaT);
			CommSzNum(SzShared("FZaFromSz New za: "), *pza);
			CommSzNum(SzShared("FZaFromSz Old fOverflow: "), fOverflowT);
			CommSzNum(SzShared("FZaFromSz New fOverflow: "), *pfOverflow);
#endif /* BZ */
			Assert (fFalse);
			}
		}
#endif /* DEBUG */
#endif /* USA */



	if (cchStrip < cch)	  /* restore old sting value */
		sz[cchStrip] = chSpace;

	return fRet;
}





#ifdef USA
#ifdef DEBUG
  /* compare values from intl and us versions. Should be same */
int FZaFromSsVfy(pza, ss, cch, ut, pfOverflow)
int     *pza;
CHAR    ss[];
int     cch, ut;
BOOL	*pfOverflow;
{
	long    lza      = 0;
	register CHAR    *pch    = ss;
	register CHAR  *pchMac = &ss[cch];
	int     ch;
	unsigned czaUt;
	int     fNeg;

	*pfOverflow = fFalse;

     /* strip trailing spaces ADDED TO VFY ONLY. not in original; should be */
	while (ss < pchMac && *(pchMac - 1) == chSpace)
		--pchMac;
	cch = pchMac - ss;
	if (cch <= 0)
		return fFalse;

	switch (ChLower((*--pchMac)))
		{ /* Check for units */
	case 'n': /* inch */
		if (ChLower(*--pchMac) != 'i')
			goto NoUnits;
	case '"': /* inch */
		ut = utInch;
		break;
	case '0': /* p10 */
		if (*--pchMac != '1' || ChLower(*--pchMac) != 'p')
			goto NoUnits;
		ut = utP10;
		break;
	case '2': /* p12 */
		if (*--pchMac != '1' || ChLower(*--pchMac) != 'p')
			goto NoUnits;
		ut = utP12;
		break;
	case 'i': /* line or pica */
		if (ChLower(*--pchMac) == 'l')
			ut = utLine;
		else  if (ChLower(*pchMac) == 'p')
			ut = utPica;
		else
			goto NoUnits;
		break;
	case 't': /* pt */
		if (ChLower(*--pchMac) != 'p')
			goto NoUnits;
		ut = utPt;
		break;
	case 'm': /* cm */
		if (ChLower(*--pchMac) != 'c')
			goto NoUnits;
		ut = utCm;
		break;
	default:
		++pchMac;
		break;
NoUnits:
		pchMac = &ss[cch];
		}

	while (pch < pchMac && *(pchMac - 1) == chSpace)
		--pchMac;

	czaUt = mputczaUt[ut];

/* extract leading blanks */
	while (*pch == ' ')
		pch++;

	fNeg = *pch == '-';
	if (fNeg) ++pch;        /* skip past minus sign */
	while ((ch = *pch++) != vitr.chDecimal)
		{
		if (ch < '0' || ch > '9')
			return fFalse;
		lza = lza * 10 + (ch - '0') * czaUt;
		if (lza >= (long) czaMax)
			goto NumOvfl;
		if (pch >= pchMac)
			goto GotNum;
		}

	while (pch < pchMac)
		{
		ch = *pch++;
		if (ch < '0' || ch > '9')
			return fFalse;
		lza += ((ch - '0') * czaUt + 5) / 10;
		if (lza >= (long) czaMax)
			goto NumOvfl;
		czaUt = (czaUt + 5) / 10;
		}

GotNum:
	if (lza >= (long) czaMax)
		{
NumOvfl:
		*pfOverflow = fTrue;
		return fFalse;
		}

	*pza = fNeg ? (int) -lza : (int) lza;
	return fTrue;
}

#endif /* DEBUG */
#endif /* USA */


#endif /* INTL */


/* ****
*  Description: Stuff the expansion of linear measure za in unit ut into pch.
		Return # of chars stuffed.  Don't exceed cchMax. This is the general
*       all-purpose integer to unit-terminated string routine...but doesn't
*       put the units on if !fUnit
*       WARNING: this does not null terminate strings unless fUnit is
*         true and cchMax is at least ichMax + 1 characters long!

** **** */

/*  %%Function:  CchExpZaRnd  %%Owner:  bobz       */

int CchExpZaRnd(ppch, za, ut, cchMax, fUnit, grpfRnd)
CHAR **ppch;
int ut, cchMax;
int za;
int fUnit, grpfRnd;
{
	register int cch = 0;
	unsigned czaUt;
	int zu;

	/* If not in point mode and even half line, display as half lines v. points */
	if (ut == utPt && vpref.ut != utPt &&
			(za / (czaLine / 2) * (czaLine / 2)) == za)
		ut = utLine;

	czaUt = mputczaUt[ut];
	if (cchMax < ichMaxNum)  /* need room for # plus term */
		{
		if (cchMax > 0)
			**ppch = 0; /* just to guarantee null term in case it is expected */

		Assert (fFalse);  /* we should stop any of these if we can bz */
		return 0;
		}
	if (za < 0)
		{ /* Output minus sign and make positive */
		*(*ppch)++ = '-';
		za = -za;
		cch++;
		/* invert meaning of hi/lo rounding for neg #'s */
		if (grpfRnd == grpfRndHigh)
			grpfRnd = grpfRndLow;
		else  if (grpfRnd == grpfRndLow)
			grpfRnd = grpfRndHigh;

		}

		{
		long lza;

		/* note: to avoid losing decimals on 1/8" units, we multiply
			by 100 before the first division, then scale down by 10
			Otherwise, dividing czaInch by 200 gives 7.2 -> 7 and we
			round to .12 instead of .13 bz
		*/

		lza = ((long)za * 100);	/* To avoid overflow in subsequent operations. */
		/* if rounding, add .005; rounding high add ~.0099 ((czaUt - 1) /100),
			rounding low add nothing */
		if (grpfRnd == grpfRndRnd)
			lza += czaUt / 2;      /* round off to two decimal places */
			/* -1 lets x.0 not round to x.01 */
		else  if (grpfRnd == grpfRndHigh)
			lza += czaUt - 1;

		zu = (int) (lza / ((long)czaUt * 100));	/* Get integral part */
		cch += CchIntToPpch(zu, ppch);	/* Expand integral part */

		/* Long to avoid overflow in subsequent operations. */
		lza -=  ((long)zu * czaUt * 100); 	/* Retain fraction part */
		za = (int)(lza / 10);
		}

	/* note: za now 10 * original za */

	Assert (za < czaUt * 10);

	if (za >= czaUt || za * 10 >= czaUt)
		{ /* Check *10 first because of possible overflow */
		*(*ppch)++ = vitr.chDecimal;
		cch++;
		zu = za / czaUt;
		*(*ppch)++ = '0' + zu;
		cch++;
		zu = ((za - zu * czaUt) * 10) / czaUt;
		if (zu != 0)
			{
			*(*ppch)++ = '0' + zu;
			cch++;
			}
		}

	if (fUnit)
		cch += CchStuff(ppch, mputsz[ut], cchMax - cch);  /* will null term */
	else
		**ppch = 0; /* just to guarantee null term in case it is expected */
	return cch;
}


csconst CHAR szElipses[] = SzKey("...",Elipses);

/* ****
*  Description: Copy sz into ppch. Copy no more than cchMax characters.
*    If all of sz cannot fit, replace the last 4 chars in ppch with "...\0"
*    Since code is counting on this routine to null-terminate the string
*    this will guarantee that a null terminator will be loaded into the string
*    It does require the buffer to be at least 4 characters, even if less than that
*    is available (so the ellipses won't trash anything.
** **** */
/*  %%Function:  CchStuff  %%Owner:  bobz       */

int CchStuff(ppch, sz, cchMax)
CHAR **ppch, sz[];
int cchMax;
{
	register int cch = 0;
	register CHAR *pch = *ppch;

	/* will copy in null term if room */
	while (cchMax-- > 0 && (*pch = *sz++) != 0)
		{
		cch++;
		pch++;
		}

	/* !cchMax case: if we could copy the string but not the null terminator */
	if (cchMax < 0 || (!cchMax && *pch != 0))
		bltbx(szElipses, (pch - (sizeof(szElipses))), sizeof(szElipses));

	*ppch = pch;
	return cch;
}


/*  %%Function:  WParseOpt  %%Owner:  bobz       */

WORD WParseOpt(tmm, sz, ppv, bArg, tmc, opt)
TMM tmm;
char * sz;
void ** ppv;
WORD bArg;
TMC tmc;
WORD opt; /* wParam */
{
	int wMin;
	int wMax;
	DPV dpv;

	/* only care about min, max for parse */
	if (tmm == tmmParse)
		{
		if (FUnitOpt(opt))
			{
			wMin = !FNonNegOpt(opt) ? -czaMax : (FNZPosOpt(opt) ? 1:0);
			wMax = czaMax;
			}
		else
			{
			wMin = !FNonNegOpt(opt) ? -32765 : (FNZPosOpt(opt) ? 1:0);
			wMax = 32767;
			}
		}
	else
		{
		wMin = wMax = 0;
		}

	return (WParseOptRange(tmm, sz, ppv, bArg, tmc, opt, wMin, wMax));
}


#ifdef OPUS_X64
static csconst CHAR stAuto[] = { 4, 'A', 'u', 't', 'o' };
#else
static csconst CHAR stAuto[] = StKey("Auto",AutoDef);
#endif


/*  %%Function:  WParseOptRange  %%Owner:  bobz       */

WORD WParseOptRange(tmm, sz, ppv, bArg, tmc, opt, wMin, wMax)
TMM tmm;
char * sz;
void ** ppv;
WORD bArg;
TMC tmc;
WORD opt; /* wParam */
int wMin, wMax;
{
	int w;
	DPV dpv;
	WORD ninch;
	int ut;

	ninch = FNonNegOpt(opt) ? uNinch : wNinch;

	Assert(opt != optNil);

	switch (tmm)
		{
#ifdef DEBUG
	default:
		Assert(fFalse);
		break;
#endif

	case tmmParse:
		if (FAutoOpt(opt) && FCheckForAuto(sz))
			{
			SetPpvBToW(ppv, bArg, 0);
			return fTrue;
			}

		if (FUnitOpt(opt))
			{
			dpv = DpvParseFdxa(&w, tmc, sz, wMin, wMax,
					dpvBlank | dpvSpaces, eidOutOfRange, 
					fTrue, FLineOpt(opt) ? utLine : vpref.ut);
			}
		else
			{
			Assert(FIntOpt(opt));
			dpv = DpvParseFdxa(&w, tmc, sz, wMin, wMax,
					dpvBlank, eidOutOfRange, fFalse, vpref.ut);
			}

		if (dpv == dpvNormal)
			{
			SetPpvBToW(ppv, bArg, w);
			return fTrue;
			}
		else  if (dpv == dpvError)
			/* error reporting already handled */
			/* **ppv is set to something different than the 
				gray value since GetTmcVal pays no attention 
				to the return value */
			{
			SetPpvBToW(ppv, bArg, wError);
			return fFalse;
			}
		else  /* blank or null string - treat as unchanged value */			
			{
			SetPpvBToW(ppv, bArg, ninch);
			return fTrue;
			}
		/* NOT REACHED */

	case tmmFormat:
		w = WFromPpvB(ppv, bArg);

		Assert(w >= 0 || !FNonNegOpt(opt) || w == ninch);

		if (w == ninch)
			{
			*sz = 0;
			}
		else  if (FAutoOpt(opt) && w == 0)
			{
			bltbx((CHAR FAR *) stAuto + 1, (LPSTR) sz, *stAuto);
			sz[*stAuto] = '\0';
			}
		else
			{
			if (FUnitOpt(opt))
				{
				if (FLineOpt(opt))
					/* show whole or half lines as utLine,
						else vpref.ut
					*/
					{
					int wRem = abs(w) % czaLine;
					if (!wRem || wRem == czaLine >> 1)
						ut = utLine;
					else
						ut = vpref.ut;
					}
				else
					ut = vpref.ut;

				CchExpZa(&sz, w, ut, ichMaxNum, fTrue);
				}
			else
				{
				char * pch;

				Assert(FIntOpt(opt));
				pch = sz;
				sz[CchIntToPpch(w, &pch)] = '\0';
				}
			}
		break;

#ifdef tmmCwVal 
	case tmmCwVal:
		return 1;
#endif
		}

	return 0;
}


/*  %%Function:  FCheckForAuto  %%Owner:  bobz       */

BOOL FCheckForAuto(sz)
CHAR sz [];
{
	CHAR * pch;
	CHAR * pchEnd;
	int cch;
	CHAR szCopy [ichMaxNum];
	CHAR szAutoCopy [ichMaxNum];


	/* Strip leading white spaces. */
	pch = &sz[0];
	while ((*pch == chSpace || *pch == chTab) && *pch != '\0')
		pch++;

	/* Strip trailing white spaces. */
	pchEnd = pch + CchSz(pch) - 2;
	while (pch <= pchEnd && (*pchEnd == chSpace || *pchEnd == chTab))
		pchEnd--;

	/* pchEnd is pointing one past the desired end. */
	pchEnd++;
	if ((cch = pchEnd - pch) >= ichMaxNum)
		{
		/* Input string too long.  Wimp out and let our authoritative
			parse function handle the situation. */
		return fFalse;
		}

	bltb(pch, szCopy, cch);
	szCopy[cch] = '\0';
	bltbx((char far *) stAuto + 1, (char far *) szAutoCopy, *stAuto);
	szAutoCopy[*stAuto] = '\0';
	return FEqNcSz(szCopy, szAutoCopy);
}


/* NOTE: this function is pretty bogus!  spaces are legal in the middle
of a number! This functionality is also duplicated elsewhere! */
/*-------------------------------------------------------------------
		Purpose:    Parse a string in the given buffer as a number.
		Method:        Scan for digits and ignore white space.
		Returns:    True if parsed as a valid number, else false.
					Character pointer past last one read in *ppch.
					Number parsed is returned in w.  Note that if only a
					prefix is a valid number we return false, with *ppch
					set to the first offending character.
--------------------------------------------------------------mck--*/

#define smInit 0
#define smDig 1
#define smBody 2
#define intPosLast 0x7FFFL

/*  %%Function:  FPwParsePpchPch  %%Owner:  bobz       */

FPwParsePpchPch(pw, ppch, pchMax, pfOverflow)
int *pw;
CHAR **ppch;
CHAR *pchMax;
int *pfOverflow;
{
	char * pch = *ppch;      /* Local buffer pointer */
	unsigned int ch;        /* Character being examined */
	int fNeg = fFalse;
	long lNum = 0;
	BOOL fError = fFalse;
	int sm = smInit;

	*pfOverflow = fFalse;

	while (!fError && !(*pfOverflow) && pch < pchMax)
		{
		ch = *pch;
		if (ch == chSpace)
			pch++;
		else
			switch (sm)
				{
			case smInit:
				if (ch == '-')
					{
					fNeg = fTrue;
					pch++;
					}
				sm = smDig;
				break;

			case smDig:
				if (FDigit(ch))
					sm = smBody;
				else
					fError = fTrue;
				break;

			case smBody:
				if (FDigit(ch))
					{
					/* Overflow? */
					if ((lNum = 10 * lNum + (ch - '0')) > intPosLast)
						*pfOverflow = fTrue;
					else
						pch++;
					}
				else
					fError = fTrue;
				/* break; */
				}
		}

	*ppch = pch;
	*pw = (int) (fNeg ? -lNum : lNum);

	return !fError;
}


/*  %%Function:  DpvParseFdxa  %%Owner:  bobz       */


DPV DpvParseFdxa(pw, tmc, szItem, wLow, wHigh, dpvMask, eid, fDxa, ut)
int * pw;   /* Return value */
TMC tmc;       /* sdm Item number (ignored if tmcNil) */
char * szItem;  /* string to be parsed */
int wLow;      /* Smallest and largest allowed values */
int wHigh;
DPV dpvMask;     /* Bit mask for allowed variations */
int eid;        /* Id of error string if out of range */
int fDxa;      /* Parse as dxa (otherwise int) */
int ut;        /* Units to use as default if fDxa */
{
	char * pch;     /* Parse pointer */
	char * pchEnd;  /* End of buffer */
	char * pchError;/* Position of parse error */
	int fParsed;    /* Parses as number/dxa */
	int fOverflow;	/* True if the number is parsed but it overflow */
	DPV dpvGood;	/* return after good range check */
	int cchItem;     /* length of item string */

	fOverflow = fFalse;
	dpvGood = dpvNormal;

	/* Get the size of the text */
	cchItem = CchSz(szItem) - 1;

	/* See if blank (null line) */
	if ((dpvMask & dpvBlank) && cchItem == 0)
		{
		*pw = valNil;
		return dpvBlank;
		}

	pch = szItem;

	/* See if all spaces  */
	if ((dpvMask & dpvBlank) && (dpvMask & dpvSpaces))
		{
		int ch;
		int fAllSpaces = fTrue;

		while ((ch = *pch++) != 0)
			{
			if (ch != ' ')
				{
				fAllSpaces = fFalse;
				break;
				}
			}

		if (fAllSpaces)
			{
			*pw = valNil;
			return dpvSpaces;
			}
		}

	pch = szItem;
	pchEnd = pch + cchItem;

	/* It parses as a number ... */
	fParsed = fDxa ? FZaFromSs(pw, pch, cchItem, ut, &fOverflow)
			: FPwParsePpchPch(pw, &pch, pchEnd, &fOverflow);

	if (!fDxa && (dpvMask & dpvDouble))
		{
		(*pw) *= 2;
		wLow *= 2;
		wHigh *= 2;
		if (!fParsed)
			{
			/* Check if ".5" was reason for bad parse. */
			if (pch != pchEnd && *pch == vitr.chDecimal)
				{
				pch++;
				/* Allow "ddddd.0*" */
				pchError = pch;
				if (FAllZeroPpchPch(&pchError, pchEnd))
					fParsed = fTrue;
				/* Allow "ddddd.50*" */
				else  if (pch != pchEnd && *pch == '5' &&
						(pch++, FAllZeroPpchPch(&pch, pchEnd)))
					{
					(*pw)++;
					fParsed = fTrue;
					dpvGood = dpvDouble;
					}

				/* Mark furthest error condition */
				else  if (pchError > pch)
					pch = pchError;
				}
			}
		}


	if (fParsed && !fOverflow && *pw >= wLow && *pw <= wHigh)
		return dpvGood;


/* All attempts failed - show user where they went wrong vis the
	attempted number parse. */

	if (tmc != tmcNull && !vmerr.fErrorAlert && !vmerr.fInhibit && !fElActive)
		{
		SetTmcTxs(tmc, TxsOfFirstLim(fParsed ? 0 : pch - szItem, 
				ichLimLast));
		}

	/* overflow will also give a range message */
	if ((fParsed || fOverflow) && (eid == eidOutOfRange || eid == eidDxaOutOfRange))
		{
		if (!fDxa && (dpvMask & dpvDouble))
			{        /* restore original range for error reporting */
			wLow >>= 1;
			wHigh >>= 1;
			}
		RangeError(wLow, wHigh, fDxa, ut);
		}
	else
		ErrorEid(fDxa ? eidNOTDXA : eidNOTNUM, " DpvParseFdxa");

	return dpvError;
}



/*-------------------------------------------------------------------
		Purpose:    Make sure all characters in buffer are spaces or 0's.
		Returns:    *ppch contains first bad character if fFalse returned.
		History:
			10/ 9/84:    Created.
--------------------------------------------------------------mck--*/
/*  %%Function:  FAllZeroPpchPch  %%Owner:  bobz       */

FAllZeroPpchPch(ppch, pchMax)
char ** ppch;        /* Bound of character buffer */
char * pchMax;
{
	char * pch = *ppch;

	while (pch < pchMax)
		{
		if (*pch == '0' || *pch == ' ')
			pch++;
		else
			{
			*ppch = pch;
			return fFalse;
			}
		}

	return fTrue;
}



/**  F  D L G  D I R  A D D  S R C H **/
/* ****
* Description:  Given filename or search spec or partial search spec,
*  add appropriate search string. Used for directory combos and listboxes
*  only.
*
*  Returns false if there was not room to hold the string + search string.
*
*  One assumption is made: that the szEdit is exactly ichMaxText long.
*  We are making the assumption
*  that this is a directory name, adding the search string when possible, and
*  then we will try to open the string as a directory in dlgdirlist. If that
*  fails, we know we have a file name, not a directory. There does not
*  seem to be a more direct way to prove this.
*
*  So, if it is obviously a directory, or may be a directory or file name
*  (or drive name) we set it up as a directory and add the search string.
*
*  We want to add search string to the string - i.e., we are seeing if string
*  szEdit is a directory. If the string is .. or ends in: or \, we know we
*  have a directory name, not a file name, and so add search str to the
*  string. Otherwise, if szEdit has no wildcard characters, add string
*  to the string, even if the string contains a period (directories can have
*  extensions).
*
*  Note the implicit assumption here that \ and not / will be used as the
*  path character. It is held in the defined variable chPath, but we
*  don't handle DOS setups where the / is path and - is the switch character.
* ****  */
/*  %%Function:  FDlgDirAddSrch  %%Owner:  bobz       */

FDlgDirAddSrch(szEdit, szSearch, cchMax)
char *szEdit;
char *szSearch;
int cchMax;
{
	char * pchLast;
	int cbEdit;
	int fAddPathChar;
	char sz [ichMaxBufDlg];

	cbEdit = CchSz(szEdit) - 1;
	Assert(cbEdit < cchMax);

	pchLast = szEdit + cbEdit - 1;

	/* if not ending in \ or : we may have to add a \ */

	fAddPathChar = (*pchLast != chPath && *pchLast != ':');

	if (fAddPathChar && FWildSpec(szEdit))/* leave if already wild card */
		return fTrue;

	/* be sure there is room for search string */
	/* note assumption with fAddPathChar that true == 1, false == 0 */
	Assert(fAddPathChar ? fAddPathChar == 1 : fAddPathChar == 0);

	if ((cbEdit + fAddPathChar + CchSz(szSearch)) >= cchMax)
		return (fFalse);

	if (fAddPathChar)
		*++pchLast = chPath;

	CchCopySz(szSearch, pchLast + 1);
	return fTrue;
}


/*  %%Function:  FWildSpec  %%Owner:  bobz       */

BOOL FWildSpec(sz)
char * sz;
{
	int ichErr;

	return (FntSz(sz, CchSz(sz) - 1, &ichErr, nfoPathWildOK)
			== fntValidWild);
}


#define	lbcEq		((LBC)0)
#define	lbcPrefix	((LBC)1)
#define	lbcLt		((LBC)2)
#define	lbcGt		((LBC)3)

typedef struct
	{
	TMC tmc;
	int cbHeap;
	} EML;


/*  %%Function:  EfMacroList  %%Owner:  bradch */

EfMacroList(lpsy, bsy, stName, peml)
SY FAR *lpsy;
unsigned bsy;
char * stName;
EML * peml;
{
	int iEntry;
	int cb;
	LBC lbc;
	
	cb = stName[0] + 25; /* 25 is a "random" estimate of the overhead */

	if (peml->cbHeap < cb && (peml->cbHeap = CbAvailHeap(sbDds)-1024) < cb)
		{
		ErrorEid(eidLowMemLBox, "EfMacroList");
		return fFalse;
		}

	StToSzInPlace(stName);
	lbc = lbcLt;
	iEntry = IEntryFindListBox(peml->tmc, stName, &lbc);
	if (lbc != lbcEq)
		{
		InsertListBoxEntry(peml->tmc, stName, iEntry);
		peml->cbHeap -= cb;
		}
	
	return fTrue;
}


/*  %%Function:  FillMacroListTmc  %%Owner:  bradch       */

FillMacroListTmc(tmc, mct)
TMC tmc;
int mct;
{
	EML eml;
	
	eml.tmc = tmc;
	eml.cbHeap = 0;
	StartListBoxUpdate(tmc);
	FEnumMacros(EfMacroList, &eml, mct);
	EndListBoxUpdate(tmc);
}


/* B C M  C U R  S E L */
/*  %%Function:  BcmCurSelTmc  %%Owner:  bobz       */

BcmCurSelTmc(tmc)
TMC tmc;
{
	char st [cchMaxSyName + 2];

	st[0] = CchGetTmcText(tmc, st + 1, cchMaxSyName);

	return BcmOfSt(st);
}


/*  %%Function:  FGetDirListEntry  %%Owner:  bobz       */

BOOL FGetDirListEntry(tmc, szBuf, ichMax)
TMC tmc;
char * szBuf;
int ichMax;
{
	char * pchStart;
	int cch;

	cch = CchGetTmcText(tmc, szBuf, ichMax);
	/* we assume that this is a list box of only directories and drives */
	/* strip off brackets and - */
	if (cch == 0 || szBuf[0] != '[')
		{
#ifdef BZ
		CommSzSz(SzShared("FGetDirListEntry fail string: "), szBuf);
		CommSzNum(SzShared("FGetDirListEntry fail cch: "), cch);
#endif /* BZ */

		return fFalse;
		}
	/* Selection is drive */
	if (szBuf[1] == '-')
		{
		pchStart = szBuf + 2;  /* skip over leading [- */
		szBuf[3] = ':';
		szBuf[4] = 0;
		cch -= 1;
		}
	else  /* directory */		
		{
		Assert(cch > 2);
		pchStart = szBuf + 1;  /* skip over leading [ */
		szBuf[cch - 1] = '\\';
		}

	bltbyte(pchStart, szBuf, cch);
	return fTrue;
}


/* F T R U N C A T E  T M C  PATH P S Z  */
/* If a filename placed in tmc (generally static text) would be too
	long, truncate it and add elipises to front. Updates pch to point
	to new start of string.    Returns TRUE if truncated.
*/
/*  %%Function:  FTruncateTmcPathPsz  %%Owner:  bobz       */

BOOL FTruncateTmcPathPsz(tmc, ppch)
TMC tmc;
CHAR **ppch;
{
	int cchTmc;
	struct RC rc;

	/* get rect for tmc in windows coords */
	if (FIsDlgDying())	/* gettmcrec will return 0 then */
		return fFalse;

	GetTmcRec(tmc, &rc);

	if (vsci.dxpTmWidth)
		cchTmc = (rc.xpRight - rc.xpLeft) / vsci.dxpTmWidth;
	else
		{
		cchTmc = (rc.xpRight - rc.xpLeft) / 8; /* TOTALLY abritrary guess bz */
		Assert (fFalse);
		}

	if (cchTmc <= 6)
		return (fFalse);

	return (FTruncatePathCbPsz(cchTmc, ppch));
}


/* F T R U N C A T E  P A T H  C B  P S Z  */
/* If a filename would be too
	long, truncate it and add elipises to front. Updates pch to point
	to new start of string.    Returns TRUE if truncated.
*/
/*  %%Function:  FTruncatePathCbPsz  %%Owner:  bobz       */

BOOL FTruncatePathCbPsz(cbBuf, ppch)
int cbBuf;
CHAR **ppch;
{
	int cchTmc, cch;
	struct RC rc;
	CHAR chDrive;
	CHAR *pch = *ppch;

	if ((cch = (CchSz(pch)-1)) > cbBuf)
		{
		chDrive = *pch;
		Assert(cbBuf > 6);
		cbBuf -= 6;  /* to leave room for ellipses, etc */
		while (cbBuf < cch)
			{
			while (cch-- > 0 && *pch++ != '\\')
				;
			}
		/* prepend <drive>:\... */
		if (cch >= 0)
			pch--; /* backup over backslash if found */
		*--pch = '.';
		*--pch = '.';
		*--pch = '.';
		*--pch = '\\';
		*--pch = ':';
		*--pch = chDrive;
		*ppch = pch;
		return fTrue;
		}

	return fFalse;
}



/*  %%Function:  FGtStyleNames  %%Owner:  bobz       */

FGtStyleNames(stcp1, stcp2)
int stcp1, stcp2;
{
	struct STSH stsh;
	char stName1[cchMaxStyle];
	char stName2[cchMaxStyle];

	Assert(vdocStsh != docNil);
	stsh = (PdodDoc(vdocStsh))->stsh;
	GenStyleNameForStcp(stName1, stsh.hsttbName, stsh.cstcStd, stcp1);
	GenStyleNameForStcp(stName2, stsh.hsttbName, stsh.cstcStd, stcp2);
	return(WCompSt(stName1, stName2) > 0 ? TRUE : FALSE);
}


/* fAll: list all standard styles */
/* fFillMap: vhmpiststcp is valid, fill map with style names.   If this
*  is fFalse, only vistLbMac will be updated. */
/*  %%Function:  GenLbStyMapForDoc  %%Owner:  bobz       */

GenLbStyMapForDoc(doc, fAll, fFillMap)
{
	char *mpiststcp;
	struct DOD *pdod;
	struct STTB **hsttb;
	int ibstMac;
	int stcp, stcpStart, cstcStd, stcpLev3, stcpLev1;

	if (fFillMap)
		{
		Assert(vhmpiststcp != hNil);
		mpiststcp = *vhmpiststcp;
		}
	vistLbMac = 0;
	pdod = PdodDoc(doc);
	cstcStd = pdod->stsh.cstcStd;
	hsttb = pdod->stsh.hsttbName;
	ibstMac = (*hsttb)->ibstMac;
	/* don't use StcpFromStc...need to use negative number if
		that's the way it works out! */
	stcpLev3 = cstcStd - (cstcMax - stcLev3);
	stcpLev1 = stcpLev3 + 2;
	stcpStart = fAll ? (stcStdMin - (cstcMax - 1) + cstcStd) :
			(stcpLev3 < 0 ? stcpLev3 : 0);
	for (stcp = stcpStart; stcp < ibstMac; stcp++)
		{
		if ((vstcBackup == stcNil || stcp != StcpFromStc(vstcBackup,cstcStd)) &&
				((fAll && stcp < cstcStd) ||
				(stcp >= stcpLev3 && stcp <= stcpLev1) ||
				(stcp >=0 && !FStcpEntryIsNull(hsttb, stcp))))
			{
			if (fFillMap)
				mpiststcp[vistLbMac++] = stcp;
			else
				vistLbMac++;
			}
		}
	if (fFillMap)
		SortRgb(mpiststcp, vistLbMac, FGtStyleNames);
}


/*  %%Function:  GenLbStyleMapping  %%Owner:  bobz       */

GenLbStyleMapping()
{
	GenLbStyMapForDoc(vdocStsh, vfShowAllStd, fTrue);
}


/* F E N U M  M A C R O S */
/* return false in OOM problems */

/* %%Function:FEnumMacros %%Owner:krishnam */
FEnumMacros(pfn, wParam, mct)
int (*pfn)();
int wParam;
MCT mct;
{
	extern HPSYT vhpsyt;
	extern HWND	 vhwndCBT;
	extern int	 docGlobalDot;
	BOOL fWantCmds, fWantGlobalMacros, fWantTemplateMacros;
	uns bsy, bsyMac;
	int docDot;
	SY FAR *lpsy;
	SYT FAR *lpsyt;
	char FAR * lpgrpsy;
	char stName [cchMaxSyName];

	docDot = selCur.doc == docNil ? docNil : DocDotMother(selCur.doc);

	lpsyt = LpLockHp(vhpsyt);

	fWantCmds = fWantGlobalMacros = fWantTemplateMacros = fFalse;
	switch (mct)
		{
#ifdef DEBUG
	default:
		Assert(fFalse);
#endif

	case mctAll:		/* GLOBAL, TEMPLATE, COMMANDS */
		fWantCmds = fWantGlobalMacros = fWantTemplateMacros = fTrue;
		break;

	case mctMacros:		/* GLOBAL, TEMPLATE */
		fWantGlobalMacros = fTrue;
		/* FALL THROUGH */

	case mctMacro:		/* TEMPLATE */
		fWantTemplateMacros = fTrue;
		break;

	case mctGlobal:		/* GLOBAL, COMMANDS */
		fWantCmds = fTrue;
		/* FALL THROUGH */

	case mctGlobalMacro:	/* GLOBAL */
		fWantGlobalMacros = fTrue;
		break;
		}

		/* BLOCK */
		{
		lpgrpsy = lpsyt->grpsy;
		bsyMac = lpsyt->bsy;
		bsy = (mct == mctMacros) ? bcmMacStd : 0;
		for ( ; bsy < bsyMac; bsy += CbLpsy(lpsy))
			{
			MCT mctT;
			lpsy = (SY FAR *) (lpgrpsy + bsy);
			mctT = lpsy->mct;

			switch (mctT)
				{
#ifdef DEBUG
			default:
				Assert(fFalse);
#endif

			case mctNil:
			case mctEl:
				continue;

			case mctCmd:
			case mctSdm:
				if (!fWantCmds)
					continue;
				break;

			case mctGlobalMacro:
				if (!fWantGlobalMacros ||
		/* below case can happen with a macro that was in normal.dot while we
   are in tutorial; we switch to cbtnorm.dot for global template, but
   old macros (which we don't want to display) are still in the
		   command table */
				vhwndCBT && lpsy->docDot != docGlobalDot)
					{
					continue;
					}
				break;

			case mctMacro: /* Template macro */
				if (!fWantTemplateMacros || 
						lpsy->docDot != docDot)
					continue;
				break;
				}

			if (lpsy->stName[0] == 0)
				{
				uns bcm;
				char FAR * lpst;

				bcm = *((int FAR *) (lpsy->stName + 1));
				lpst = ((SY FAR *)
						(lpsyt->grpsy + bcm))->stName;
				bltbx(lpst, (char FAR *) stName, *lpst + 1);
				}
			else
				{
				bltbx(lpsy->stName, (char FAR *) stName,
						lpsy->stName[0] + 1);
				}

			if (!(*pfn)(lpsy, bsy, stName, wParam))
				break;
			if (vmerr.fMemFail)  /* if in dialog, dialog probably destroyed */
				break;
			}
		}

	UnlockHp(vhpsyt);
	return (!vmerr.fMemFail);
}


/*  %%Function:  WParseDttm  %%Owner:  bobz       */

WParseDttm(tmm, sz, ppv, bArg, tmc, wParam)
TMM tmm;
char * sz;
void ** ppv;
WORD bArg;
TMC tmc;
WORD wParam;
{
	int cch;
	DPV dpv;
	struct DTTM * pdttm;

	pdttm = ((struct DTTM *) PvParseArg(ppv, bArg));

	switch (tmm)
		{
	case tmmParse:
		if ((cch = CchStripString(sz, CchSz(sz) - 1)) == 0)
			{
			*(long *) pdttm = dttmNinch;
			return fTrue;
			}

		if (!FDttmFromSzPl(sz, pdttm, (long *) NULL))
			{
			ErrorEid(eidInvalidDate, "WParseDttm");

			if (!fElActive)
				SetTmcTxs(tmc, TxsOfFirstLim(0, cch));

			*(long *) pdttm = dttmError;
			return fFalse;
			}

		return fTrue;

	case tmmFormat:
		/* no error reporting for this case */
		if (*(long *) pdttm == dttmNinch)
			*sz = 0;
		else
			{
			PdttmToSt(pdttm, sz, ichMaxBufDlg);
			StToSzInPlace(sz);
			}
		break;

#ifdef tmmCwVal 
	case tmmCwVal:
		Assert(sizeof (struct DTTM) == sizeof (long));
		return (CwFromCch(sizeof (struct DTTM)));
#endif
		}

	return 0;
}


/*  %%Function: FDisableTemplate  %%Owner: peterj  */

extern int docGlobalDot;

FDisableTemplate(doc)
int doc;
{
	int docDot;

	return (doc == docNil || doc == docGlobalDot || 
			PdodDoc(doc)->fLockForEdit ||
			(docDot = DocDotMother(doc)) == docNil ||
			docDot == docGlobalDot ||
			PdodDoc(docDot)->fLockForEdit);
}


