/* @(#)dterm.c 1.1 (Berkeley) %G%"
* Converts ditroff output to text on a terminal. It is NOT meant to
* produce readable output, but is to show one how one's paper is (in
* general) formatted - what will go where on which page.
* -hn set horizontal resolution to n (in characters per inch;
* -vn set vertical resolution (default is 6.0).
* -ln set maximum output line-length to n (default is 79).
* -olist output page list - as in troff.
* -c continue at end of page. Default is to stop at the end
* of each page, print "dterm:" and wait for a command.
* Type ? to get a list of available commands.
* -w sets h = 20, v = 12, l = 131, also sets -c to allow for
* extra-wide printouts on the printer.
#define hgoto(n) hpos = n
#define vgoto(n) vpos = n
#define hmot(n) hpos += n
#define vmot(n) vpos += n
#define sgn(n) ((n > 0) ? 1 : ((n < 0) ? -1 : 0))
#define abs(n) ((n) >= 0 ? (n) : -(n))
#define max(x,y) ((x) > (y) ? (x) : (y))
#define min(x,y) ((x) < (y) ? (x) : (y))
#define arcmove(x,y) { hgoto(x); vmot(-vpos-(y)); }
#define sqr(x) (long int)(x)*(x)
char SccsId
[] = "@(#)dterm.c 1.1 (Berkeley) %G%";
"em", "-", "hy", "-", "en", "-", "ru", "_", "l.", ".", "L.", ".",
"br", "|", "vr", "|", "fm", "'", "or", "|",
int keepon
= 0; /* flag: Do we not stop at the end of each page? */
int output
= 0; /* do we do output at all? */
int nolist
= 0; /* output page list if > 0 */
int olist
[20]; /* pairs of page numbers */
float hscale
= 10.0; /* characters and lines per inch for output device */
float vscale
= 6.0; /* (defaults are for printer) */
FILE *fp
= stdin
; /* input file pointer */
char pagebuf
[PGHEIGHT
][PGWIDTH
];
int hpos
; /* horizontal position to be next (left = 0) */
int vpos
; /* current vertical position (down positive) */
int np
; /* number of pages seen */
int npmax
; /* high-water mark of np */
int pgnum
[40]; /* their actual numbers */
long pgadr
[40]; /* their seek addresses */
int DP
= 10; /* step size for drawing */
int drawdot
= '.'; /* draw with this character */
int drawsize
= 1; /* shrink by this factor when drawing */
int maxdots
= 32000; /* maximum number of dots in an object */
while (argc
> 1 && **argv
== '-') {
linelen
= atoi(++*argv
) - 1;
if (strcmp(*argv
, "-") == 0)
else if ((fp
= fopen(*argv
, "r")) == NULL
)
error(FATAL
, "can't open %s", *argv
);
outlist(s
) /* process list of page numbers to be printed */
n1
= 10 * n1
+ *s
++ - '0';
n2
= 10 * n2
+ *s
++ - '0';
in_olist(n
) /* is n in olist? */
return(1); /* everything is included */
for (i
= 0; i
< nolist
; i
+= 2)
if (n
>= olist
[i
] && n
<= olist
[i
+1])
while ((c
= getc(fp
)) != EOF
) {
case '\n': /* when input is text */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
/* two motion digits plus a character */
hmot((c
-'0')*10 + getc(fp
)-'0');
case 'c': /* single ascii character */
case 'C': /* funny character */
case 't': /* straight text */
fgets(buf
, sizeof(buf
), fp
);
case 'D': /* draw function */
fgets(buf
, sizeof(buf
), fp
);
case 'l': /* draw a line */
sscanf(buf
+1, "%d %d", &n
, &m
);
sscanf(buf
+1, "%d %d", &m
, &n
);
sscanf(buf
+1, "%d %d %d %d", &n
, &m
, &n1
, &m1
);
case 'g': /* "gremlin" curve */
case '~': /* wiggly line */
case 't': /* thickness - not important */
case 's': /* style - not important */
error(FATAL
,"unknown drawing command %s\n",buf
);
case 's': /* point size - ignored */
case 'f': /* font request - ignored */
case 'H': /* absolute horizontal motion */
case 'h': /* relative horizontal motion */
case 'w': /* word space */
case 'V': /* absolute vertical motion */
case 'v': /* relative vertical motion */
case 'n': /* end of line */
case 'x': /* device control */
error(!FATAL
, "unknown input character %o %c\n", c
, c
);
devcntrl(fp
) /* interpret device control functions */
switch (str
[0]) { /* crude for now */
case 'i': /* initialize */
case 'r': /* resolution assumed when prepared */
hscale
= (float) n
/ hscale
;
vscale
= (float) n
/ vscale
;
case 'f': /* font used */
case 'T': /* device name */
case 'p': /* pause -- can restart */
while (getc(fp
) != '\n') /* skip rest of input line */
/* error printing routine - first argument is a "fatal" flag */
error(f
, s
, a1
, a2
, a3
, a4
, a5
, a6
, a7
) {
fprintf(stderr
, "dterm: ");
fprintf(stderr
, s
, a1
, a2
, a3
, a4
, a5
, a6
, a7
);
t_init(reinit
) /* initialize device */
for (i
= 0; i
< PGHEIGHT
; i
++)
for (j
= 0; j
< PGWIDTH
; j
++)
/* just got "p#" command. print the current page and */
t_page(n
) /* do whatever new page functions */
for (bp
= buf
; (*bp
= readch()); )
fputs("you can't; it's not a file\n", stderr
);
fputs("too far back\n", stderr
);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
for (i
= 0; i
< npmax
; i
++)
if (i
>= npmax
|| fp
== stdin
) {
fputs("you can't\n", stderr
);
fputs("p print this page again\n", stderr
);
fputs("-n go back n pages\n", stderr
);
fputs("n print page n (previously printed)\n", stderr
);
fputs("o... set the -o output list to ...\n", stderr
);
/* print the contents of the current page. puts out */
putpage() /* only the part of the page that's been written on */
for (i
= minv
; i
<= maxv
; i
++) {
for (k
= maxh
; pagebuf
[i
][k
] == ' '; k
--)
for (j
= minh
; j
<= k
; j
++)
t_text(s
) /* print string s as text */
while ((c
= *s
++) != '\n') {
put1s(s
) /* s is a funny char name */
static char prev
[10] = "";
if (strcmp(s
, prev
) != 0) {
for (i
= 0; spectab
[i
] != 0; i
+= 2)
if (strcmp(spectab
[i
], s
) == 0) {
for (p
= spectab
[previ
+1]; *p
; p
++)
put1(c
) /* output char c */
static nbol
; /* 0 if at beginning of a line */
rcf
= fopen ("/dev/tty", "r");
fprintf (stderr
, "dterm: "); /* issue prompt */
if ((c
= getc (rcf
)) == EOF
)
store(c
) /* put 'c' in the page at (hpos, vpos) */
i
= hpos
/ hscale
; /* scale the position to page coordinates */
if (i
>= PGWIDTH
) i
= PGWIDTH
- 1; /* don't go over the edge */
if (j
>= PGHEIGHT
) j
= PGHEIGHT
- 1;
pagebuf
[j
][i
] = c
; /* write the character */
if (i
> maxh
) maxh
= i
; /* update the page bounds */
drawline(dx
, dy
, s
) /* draw line from here to dx, dy using s */
numdots
= min(numdots
, maxdots
);
for (i
= 0; i
< numdots
; i
++) {
for (i
= 0; i
< numdots
; i
++) {
if (abs (xd
) > abs (yd
)) {
val
= slope
= (float) xd
/yd
;
numdots
= min(numdots
, maxdots
);
perpincr
= DP
* sgn (yd
);
val
= slope
= (float) yd
/xd
;
numdots
= min(numdots
, maxdots
);
perpincr
= DP
* sgn (xd
);
incrway
= sgn ((int) slope
);
for (i
= 0; i
< numdots
; i
++) {
drawwig(s
) /* draw wiggly line */
int x
[50], y
[50], xp
, yp
, pxp
, pyp
;
char temp
[50], *p
, *getstr();
for (N
= 2; (p
=getstr(p
,temp
)) != NULL
&& N
< sizeof(x
)/sizeof(x
[0]);) {
for (i
= 1; i
< N
; i
++) {
for (i
= 0; i
< N
-1; i
++) { /* interval */
numdots
= (dist(x
[i
], y
[i
], x
[i
+1], y
[i
+1])
+ dist(x
[i
+1], y
[i
+1], x
[i
+2], y
[i
+2])) / 2;
numdots
= min(numdots
, maxdots
);
for (j
= 0; j
< numdots
; j
++) { /* points within */
xp
= t1
* x
[i
+2] + t2
* x
[i
+1] + t3
* x
[i
] + 0.5;
yp
= t1
* y
[i
+2] + t2
* y
[i
+1] + t3
* y
[i
] + 0.5;
if (xp
!= pxp
|| yp
!= pyp
) {
/* copy next non-blank string from p to temp, update p */
while (*p
== ' ' || *p
== '\t' || *p
== '\n')
while (*p
!= ' ' && *p
!= '\t' && *p
!= '\n' && *p
!= '\0')
conicarc(hpos
+ d
/2, -vpos
, hpos
, -vpos
, hpos
, -vpos
, d
/2, d
/2);
hgoto(xc
+ d
); /* circle goes to right side */
dist(x1
, y1
, x2
, y2
) /* integer distance from x1,y1 to x2,y2 */
return sqrt(dx
*dx
+ dy
*dy
) + 0.5;
drawarc(dx1
, dy1
, dx2
, dy2
)
x0
= hpos
+ dx1
; /* center */
x2
= x0
+ dx2
; /* "to" */
r
= sqrt((float) dx1
* dx1
+ (float) dy1
* dy1
) + 0.5;
conicarc(x0
, -y0
, hpos
, -vpos
, x2
, -y2
, r
, r
);
conicarc(hpos
+ a
/2, -vpos
, hpos
, -vpos
, hpos
, -vpos
, a
/2, b
/2);
conicarc(x
, y
, x0
, y0
, x1
, y1
, a
, b
)
/* based on Bresenham, CACM Feb 77, pp 102-3 by Chris Van Wyk */
/* capitalized vars are an internal reference frame */
int xs
, ys
, xt
, yt
, Xs
, Ys
, qs
, Xt
, Yt
, qt
,
M1x
, M1y
, M2x
, M2y
, M3x
, M3y
,
if (a
!= b
) /* an arc of an ellipse; internally, think of circle */
/* a circular arc; radius computed from center and first point */
radius
= sqrt((float)(sqr(x0
- x
) + sqr(y0
- y
)));
/* now, use start and end point locations to figure out
the angle at which start and end happen; use these
angles with known radius to figure out where start
slope
= atan2((double)(y0
- y
), (double)(x0
- x
) );
if (slope
== 0.0 && x0
< x
)
x0
= x
+ radius
* cos(slope
) + 0.5;
y0
= y
+ radius
* sin(slope
) + 0.5;
slope
= atan2((double)(y1
- y
), (double)(x1
- x
));
if (slope
== 0.0 && x1
< x
)
x1
= x
+ radius
* cos(slope
) + 0.5;
y1
= y
+ radius
* sin(slope
) + 0.5;
/* step 2: translate to zero-centered circle */
/* step 3: normalize to first quadrant */
/* step 4: calculate number of quadrant crossings */
if (((4 + qt
- qs
) % 4 == 0) && (Xt
<= Xs
) && (Yt
>= Ys
))
Q
= (4 + qt
- qs
) % 4 - 1;
/* step 5: calculate initial decision difference */
delta
= sqr(Xs
+ 1) + sqr(Ys
- 1) - sqr(xs
) - sqr(ys
);
/* here begins the work of drawing. */
while ((Q
>= 0) || ((Q
> -2) && ((Xt
> Xc
) && (Yt
< Yc
)))) {
if (dotcount
++ % DP
== 0)
putdot((int)xc
, (int)yc
);
Ys
= Yc
= sqrt((float)(sqr(xs
) + sqr(ys
)));
delta
= sqr(Xs
+ 1) + sqr(Ys
- 1) - sqr(xs
) - sqr(ys
);
if (2 * delta
+ 2 * Yc
- 1 <= 0)
else if (2 * delta
- 2 * Xc
- 1 <= 0)
delta
+= 2 * Xc
- 2 * Yc
+ 2;
drawline((int)xc
-ox1
,(int)yc
-oy1
,".");