533 lines
14 KiB
C
533 lines
14 KiB
C
#define EXT_ID "justify ver 2.02 "##__DATE__##" "##__TIME__
|
||
/*
|
||
** Justify Z extension
|
||
**
|
||
** History:
|
||
** 12-Sep-1988 mz Made WhenLoaded match declaration
|
||
** 01-Sep-1988 Corrected hang when flush-justifying a line with no
|
||
** spaces.
|
||
** 14-Aug-1988 Corrected right-justification on non-column-1 based
|
||
** lines. Corrected justification over multiple
|
||
** paragraphs.
|
||
** 30-Mar-1988 Extracted from "myext".
|
||
**
|
||
*/
|
||
#include <ctype.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#include "ext.h"
|
||
|
||
#ifndef TRUE
|
||
#define TRUE -1
|
||
#define FALSE 0
|
||
#endif
|
||
|
||
void pascal near DumpLine (char far *, PFILE, COL, COL, LINE, char far *, int);
|
||
int pascal near NextLine (char far *, PFILE, LINE, LINE far *, int far *);
|
||
flagType pascal near isterm (char);
|
||
void pascal near _stat (char *);
|
||
|
||
#ifdef DEBUG
|
||
void pascal near debend (flagType);
|
||
void pascal near debhex (long);
|
||
void pascal near debmsg (char far *);
|
||
#else
|
||
#define debend(x)
|
||
#define debhex(x)
|
||
#define debmsg(x)
|
||
#endif
|
||
|
||
flagType just2space = TRUE;
|
||
int justwidth = 79;
|
||
|
||
/*************************************************************************
|
||
**
|
||
** justify
|
||
** justify paragraph(s)
|
||
**
|
||
** NOARG: Justify between columns 0 and 78, from the current line to
|
||
** blank line.
|
||
** NULLARG: Justify between current column and 78, from the current line to
|
||
** blank line.
|
||
** LINEARG: Justify between current column and 78, the specified lines.
|
||
** "STREAMARG": Justify between specified columns from current line to blank.
|
||
** (handled by boxarg)
|
||
** BOXARG: justify between specified columns the specified rows
|
||
** TEXTARG: Justify between columns 0 and 78, from the current line to
|
||
** blank line, prepending each resulting line with the textarg.
|
||
*/
|
||
flagType pascal EXTERNAL
|
||
justify (
|
||
CMDDATA argData,
|
||
ARG far *pArg,
|
||
flagType fMeta
|
||
)
|
||
{
|
||
int cbLine; /* length of line just read */
|
||
char inbuf[512]; /* input buffer */
|
||
PFILE pFile; /* file handle */
|
||
char far *pText; /* pointer to prepending text */
|
||
COL x1; /* justify to left column */
|
||
COL x2; /* justify to right columne */
|
||
LINE y1; /* start line */
|
||
LINE y2; /* end line */
|
||
LINE yOut; /* output line */
|
||
|
||
//
|
||
// Unreferenced parameters
|
||
//
|
||
(void)argData;
|
||
|
||
_stat(EXT_ID);
|
||
switch (pArg->argType) {
|
||
|
||
case NOARG: /* justify paragraph */
|
||
x1 = 0; /* between cols 0... */
|
||
x2 = justwidth; /* ...and 79 */
|
||
y1 = pArg->arg.noarg.y; /* current line... */
|
||
y2 = -1; /* ...to blank line*/
|
||
pText = 0; /* there is no text */
|
||
break;
|
||
|
||
case NULLARG: /* justify indented */
|
||
x1 = pArg->arg.nullarg.x; /* between cur col... */
|
||
x2 = justwidth; /* ...and 79 */
|
||
y1 = pArg->arg.nullarg.y; /* current line... */
|
||
y2 = -1; /* ...to blank line*/
|
||
pText = 0; /* there is no text */
|
||
break;
|
||
|
||
case LINEARG: /* justify line range */
|
||
x1 = 0; /* between cols 0... */
|
||
x2 = justwidth; /* ...and 79 */
|
||
y1 = pArg->arg.linearg.yStart; /* and range of lines */
|
||
y2 = pArg->arg.linearg.yEnd;
|
||
pText = 0; /* there is no text */
|
||
break;
|
||
|
||
case BOXARG: /* justify box */
|
||
x1 = pArg->arg.boxarg.xLeft; /* from left corner... */
|
||
x2 = pArg->arg.boxarg.xRight; /* ...to right */
|
||
y1 = pArg->arg.boxarg.yTop; /* from top... */
|
||
y2 = pArg->arg.boxarg.yBottom; /* ...to bottom */
|
||
pText = 0; /* there is no text */
|
||
break;
|
||
|
||
case TEXTARG: /* justify & prepend */
|
||
x1 = 0; /* between 0... */
|
||
x2 = justwidth; /* ...and 79 */
|
||
y1 = pArg->arg.textarg.y; /* current line... */
|
||
y2 = -1; /* ...to blank line */
|
||
pText = pArg->arg.textarg.pText; /* there IS text */
|
||
break;
|
||
}
|
||
pFile = FileNameToHandle ("", "");
|
||
|
||
if (y1 == y2) /* if same line, then */
|
||
y2 = -1; /* just to blank line */
|
||
if (x1 == x2) /* if same column */
|
||
x2 = justwidth; /* then just to default */
|
||
if (x2 < x1) { /* if bas-ackwards */
|
||
x1 = 0; /* revert to default */
|
||
x2 = justwidth;
|
||
}
|
||
|
||
/*
|
||
** while we can get data within the specified limits, format each new line
|
||
** and output back to the file.
|
||
*/
|
||
inbuf[0] = 0;
|
||
yOut = y1;
|
||
while (NextLine(inbuf,pFile,y1,&y2,&cbLine)) {
|
||
/*
|
||
** if the line was blank, NextLine returned TRUE becase we're formating a
|
||
** range of text. This means we've reached the end of one paragraph. We dump
|
||
** the text collected so far (if any), and then a blank line.
|
||
*/
|
||
if (cbLine == 0) {
|
||
if (inbuf[0]) {
|
||
DumpLine(inbuf,pFile,x1,x2,yOut++,pText,0);
|
||
y1++;
|
||
if (y2 != (LINE)-1)
|
||
y2++;
|
||
}
|
||
DumpLine("",pFile,x1,x2,yOut++,pText,0);/* dump blank line */
|
||
y1++;
|
||
if (y2 != (LINE)-1)
|
||
y2++;
|
||
}
|
||
else
|
||
/*
|
||
** inbuf contains the data collected so far for output. Output one newly
|
||
** formated line at a time until the contents of inbuf are narrower than
|
||
** our output columns.
|
||
*/
|
||
while ((COL)strlen(inbuf) > (x2-x1)) { /* while data to output */
|
||
DumpLine(inbuf,pFile,x1,x2,yOut++,pText,fMeta);
|
||
y1++; /* line moves with insert*/
|
||
if (y2 != (LINE)-1)
|
||
y2++;
|
||
}
|
||
}
|
||
/*
|
||
** Dump any partial last line. Then if we were formatting to a blank line,
|
||
** dump out one of those too.;
|
||
*/
|
||
if (inbuf[0])
|
||
DumpLine (inbuf,pFile,x1,x2,yOut++,pText,0); /* dump last line */
|
||
if (y2 == -1)
|
||
DumpLine (NULL,pFile,x1,x2,yOut++,pText,0); /* dump blank line */
|
||
|
||
return TRUE;
|
||
|
||
/* end justify */}
|
||
|
||
/*** NextLine - Get next line from file
|
||
*
|
||
* Get next line from file, remove leading and trailing spaces, and append
|
||
* it to the input buffer. Each line is deleted from the file as it is
|
||
* read in. This means that the target terminator, (*py2), is decremented
|
||
* by one for each line read in.
|
||
*
|
||
* Input:
|
||
* pBuf = pointer to input buffer
|
||
* pFile = file pointer
|
||
* y1 = line # to read
|
||
* py2 = pointer to line # to stop at (updated)
|
||
* pcbLine = pointer to place to put the count of bytes read
|
||
*
|
||
* Output:
|
||
* Returns TRUE on a line being read & more reformatting should occurr.
|
||
*
|
||
*************************************************************************/
|
||
int pascal near NextLine (
|
||
char far *pBuf, /* input buffer */
|
||
PFILE pFile, /* file pointer */
|
||
LINE y1, /* line # to read */
|
||
LINE far *py2, /* line # to stop at */
|
||
int far *pcbLine /* loc to place bytes read*/
|
||
) {
|
||
flagType fRet = TRUE;
|
||
char far *pT; /* working pointer */
|
||
char workbuf[512]; /* working buffer */
|
||
|
||
|
||
*pcbLine = 0;
|
||
workbuf[0] = 0;
|
||
/*
|
||
** If asked for line that is not in file, we're done.
|
||
*/
|
||
if (y1 >= FileLength(pFile))
|
||
return FALSE;
|
||
/*
|
||
** if current line past range, (and range is not "-1"), then we're done.
|
||
*/
|
||
if ((*py2 != (LINE)-1) && (y1 > *py2))
|
||
return FALSE;
|
||
/*
|
||
** Get the next line in the file & remove it.
|
||
*/
|
||
*pcbLine = GetLine(y1, workbuf, pFile);
|
||
DelLine(pFile, y1, y1);
|
||
if (*py2 == 0)
|
||
fRet = FALSE;
|
||
else if (*py2 != (LINE)-1)
|
||
(*py2)--;
|
||
/*
|
||
** If the line is blank, and the range is "-1", we're done.
|
||
*/
|
||
if (!*pcbLine && (*py2 == -1))
|
||
return FALSE;
|
||
|
||
/*
|
||
** strip leading spaces in newly input line
|
||
*/
|
||
pT = workbuf; /* point into line */
|
||
while (*pT == ' ')
|
||
pT++; /* skip leading spaces */
|
||
/*
|
||
** If existing buffer is non-empty, append a space & set pointer to end
|
||
*/
|
||
if (strlen(pBuf)) { /* if non-null string */
|
||
pBuf += strlen(pBuf); /* point to null */
|
||
*pBuf++ = ' '; /* append space */
|
||
if (isterm(*(pBuf-2))) /* if sentence term... */
|
||
*pBuf++ = ' '; /* append another */
|
||
}
|
||
/*
|
||
** append new line, but compress multiple spaces into one
|
||
*/
|
||
while (*pT) { /* copy line over */
|
||
if (isterm(*pT)) /* if sentence term... */
|
||
if (*(pT+1) == ' ') { /* ...space */
|
||
*pBuf++ = *pT++; /* copy period */
|
||
*pBuf++ = *pT; /* copy space */
|
||
}
|
||
if ((*pBuf++ = *pT++) == ' ' ) /* copy a char */
|
||
while (*pT == ' ') pT++; /* skip multiple spaces */
|
||
}
|
||
if (*(pBuf-1) == ' ') /* if a trailing space */
|
||
pBuf--; /* remove it */
|
||
*pBuf = 0;
|
||
|
||
return fRet;
|
||
/* end NextLine */}
|
||
|
||
/*** DumpLine - Dump one line of text to the file
|
||
*
|
||
* Dump one line of text to the file. Prepend any required text or spaces,
|
||
* and perform word break/cut at right hand column.
|
||
*
|
||
* Input:
|
||
* pBuf = Pointer to the buffer containing data to output. If NULL, pText
|
||
* will not be prepended to output text.
|
||
* pFile
|
||
* x1
|
||
* x2
|
||
* yOut
|
||
* pText
|
||
* fFlush
|
||
*
|
||
* Output:
|
||
* Returns .....
|
||
*
|
||
* Exceptions:
|
||
*
|
||
* Notes:
|
||
*
|
||
*************************************************************************/
|
||
void pascal near DumpLine (
|
||
char far *pBuf, /* data to output */
|
||
PFILE pFile, /* file to output to */
|
||
COL x1, /* left-hand column */
|
||
COL x2, /* right-hand column */
|
||
LINE yOut, /* line to output to */
|
||
char far *pText, /* text to prepend */
|
||
int fFlush /* flush both sides */
|
||
) {
|
||
int i;
|
||
char far *pT;
|
||
char far *pT2;
|
||
char workbuf[512]; /* working buffer */
|
||
char flushbuf[512]; /* working buffer */
|
||
char fSpace; /* space seen flag */
|
||
|
||
/*
|
||
** Start by prepending any text, and then filling out to the left hand column
|
||
** to justify to.
|
||
*/
|
||
workbuf[0] = 0; /* start with null */
|
||
if (pText && pBuf)
|
||
strcpy(workbuf,pText); /* if starting with text*/
|
||
i = strlen(workbuf); /* length of line-so-far*/
|
||
while (i++ < x1)
|
||
strcat(workbuf," "); /* fill out with spaces */
|
||
|
||
/*
|
||
** Append the data to be output, and then starting at the right column, scan
|
||
** back for a space to break at. If one is not found before the left hand
|
||
** column, then break at the right hand column. Copy any line left over back
|
||
** to the passed in buffer
|
||
*/
|
||
if (pBuf) {
|
||
strcat(workbuf,pBuf); /* get total line */
|
||
*pBuf = 0; /* empty input buffer */
|
||
}
|
||
if ((COL)strlen(workbuf) > x2) { /* if we need to cut */
|
||
pT = &workbuf[x2]; /* point at potential cut*/
|
||
while ((pT > (char far *)&workbuf[0]) && (*pT != ' ')) pT--; /* back up to space*/
|
||
if (pT <= (char far *)&workbuf[x1]) { /* if none found in range*/
|
||
if (pBuf)
|
||
strcpy(pBuf,&workbuf[x2]); /* copy remainder of line*/
|
||
workbuf[x2] = 0; /* and terminate this one*/
|
||
}
|
||
else {
|
||
while (*++pT == ' '); /* Skip leading spaces */
|
||
if (pBuf)
|
||
strcpy(pBuf,pT); /* copy remainder of line*/
|
||
*pT = 0; /* and terminate this one*/
|
||
}
|
||
}
|
||
/*
|
||
** This code is invoked when the user wants to justify both right and left
|
||
** sides of his text. We determine how many spaces we need to add, and scan
|
||
** through and add one space to each run of spaces until we've added enough
|
||
*/
|
||
if (fFlush) { /* right & left justify?*/
|
||
if ((LONG_PTR) (pT = workbuf + strlen(workbuf) - 1) > 0)
|
||
while (*pT == ' ')
|
||
*pT-- = 0;
|
||
if (strchr(workbuf,' ')) {
|
||
while ((i = x2 - strlen(workbuf)) > 0) {/* count of spaces to add */
|
||
strcpy(flushbuf,workbuf); /* start with unmodified*/
|
||
pT = workbuf + x1;
|
||
pT2 = flushbuf + x1; /* skip fixed part */
|
||
fSpace = FALSE; /* assume no spaces */
|
||
while (*pT) { /* while data to copy */
|
||
if ((*pT == ' ') && i) { /* time to insert a space*/
|
||
fSpace = TRUE; /* we've seen a space */
|
||
*pT2++ = ' ';
|
||
i--;
|
||
while (*pT == ' ')
|
||
*pT2++ = *pT++; /* copy run of spaces */
|
||
}
|
||
if (*pT)
|
||
*pT2++ = *pT++; /* copy line */
|
||
else if (!fSpace)
|
||
break; /* no embedded spaces */
|
||
}
|
||
*pT2 = 0;
|
||
strcpy(workbuf,flushbuf); /* copy back */
|
||
if (!fSpace)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
CopyLine ((PFILE) NULL, pFile, yOut, yOut, yOut); /* create new line */
|
||
PutLine (yOut, workbuf, pFile); /* output line */
|
||
|
||
/* end DumpLine */}
|
||
|
||
/*************************************************************************
|
||
**
|
||
** isterm
|
||
** returns true/false based on the character being a sentence terminator:
|
||
** one of '.', '?', '!'. Also, always returns false if just2space is off.
|
||
*/
|
||
flagType pascal near isterm(
|
||
char c /* character to test */
|
||
)
|
||
{
|
||
return (flagType)(just2space && ((c == '.') || (c == '!') || (c == '?')));
|
||
/* end isterm */}
|
||
|
||
|
||
/*
|
||
** switch communication table to Z
|
||
*/
|
||
struct swiDesc swiTable[] = {
|
||
{ "just2space", toPIF(just2space), SWI_BOOLEAN },
|
||
{ "justwidth", toPIF(justwidth), SWI_NUMERIC | RADIX10 },
|
||
{0, 0, 0}
|
||
};
|
||
|
||
/*
|
||
** command communication table to Z
|
||
*/
|
||
struct cmdDesc cmdTable[] = {
|
||
{ "justify", justify, 0, MODIFIES | NOARG | NULLARG | LINEARG | BOXARG | TEXTARG },
|
||
{0, 0, 0}
|
||
};
|
||
|
||
/*
|
||
** WhenLoaded
|
||
** Executed when these extensions get loaded. Identify self & assign keys.
|
||
*/
|
||
void EXTERNAL WhenLoaded () {
|
||
PSWI pwidth;
|
||
|
||
_stat(EXT_ID);
|
||
SetKey ("justify","alt+b");
|
||
|
||
if (pwidth = FindSwitch("rmargin"))
|
||
justwidth = *pwidth->act.ival;
|
||
}
|
||
|
||
/*************************************************************************
|
||
**
|
||
** stat - display status line message
|
||
**
|
||
** Purpose:
|
||
** Places extension name and message on the status line
|
||
**
|
||
** Entry:
|
||
** pszFcn - Pointer to string to be prepended.
|
||
**
|
||
** Exit:
|
||
** none
|
||
**
|
||
** Exceptions:
|
||
** none
|
||
**
|
||
*/
|
||
void pascal near _stat (
|
||
char *pszFcn /* function name */
|
||
) {
|
||
buffer buf; /* message buffer */
|
||
|
||
strcpy(buf,"justify: "); /* start with name */
|
||
#ifdef DEBUG
|
||
if (strlen(pszFcn) > 71) {
|
||
pszFcn+= strlen(pszFcn) - 68;
|
||
strcat (buf, "...");
|
||
}
|
||
#endif
|
||
strcat(buf,pszFcn); /* append message */
|
||
DoMessage (buf); /* display */
|
||
/* end stat */}
|
||
|
||
#ifdef DEBUG
|
||
buffer debstring = {0};
|
||
extern int delay; /* message delay */
|
||
|
||
/*** debhex - output long in hex
|
||
*
|
||
* Display the value of a long in hex
|
||
*
|
||
* Input:
|
||
* lval = long value
|
||
*
|
||
* Output:
|
||
* Returns nothing
|
||
*
|
||
*************************************************************************/
|
||
void pascal near debhex (
|
||
long lval
|
||
) {
|
||
char lbuf[10];
|
||
|
||
_ultoa (lval, lbuf, 16);
|
||
debmsg (lbuf);
|
||
/* end debhex */}
|
||
|
||
/*** debmsg - piece together debug message
|
||
*
|
||
* Outputs a the cummulative message formed by successive calls.
|
||
*
|
||
* Input:
|
||
* psz = pointer to message part
|
||
*
|
||
* Output:
|
||
* Returns nothing
|
||
*************************************************************************/
|
||
void pascal near debmsg (
|
||
char far *psz
|
||
) {
|
||
_stat (strcat (debstring, psz));
|
||
/* end debmsg */}
|
||
|
||
/*** debend - terminates message accumulation & pauses
|
||
*
|
||
* Terminates the message accumulation, displays the final message, and
|
||
* pauses, either for the pause time, or for a keystroke.
|
||
*
|
||
* Input:
|
||
* fWait = TRUE => wait for a keystroke
|
||
*
|
||
* Output:
|
||
* Returns nothing
|
||
*
|
||
*************************************************************************/
|
||
void pascal near debend (
|
||
flagType fWait
|
||
) {
|
||
if (fWait) {
|
||
_stat (strcat (debstring, " Press a key..."));
|
||
ReadChar ();
|
||
}
|
||
debstring[0] = 0;
|
||
/* end debend */}
|
||
#endif
|