summaryrefslogtreecommitdiff
path: root/dviware/ln03/rose/specials.c
blob: 1cfb0bb471475365804a8e344747ea93e1ff7816 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
/* This file contains the code that implements the handling of \special's
in Dvi2ln3. 

Copyright (c) 1985 by Digital Equipment Corporation. Author: Flavio Rose,
(617) 467-7545, ...ucbvax!decwrl!dec-rhea!dec-dvinci!rose. 

Right now, the following \special's are implemented: 

ln03:defpoint fixnum ( [dimension] , [dimension] )
ln03:connect fixnum [/ fixnum] fixnum [/ fixnum]
ln03:plotfile filename
ln03:resetpoints fixnum fixnum

A dimension is a number with optional decimal point, followed by one of pt,
in, pc, cm, mm, bp, dd, cc, and mi. 

We try to mimic the functionality of DVIAPS, a program that drives the
Autologic Micro-5 typesetter, written by Textset Inc. of Ann Arbor,
Michigan. This has been done on the basis of user-level documentation
supplied by Textset. This code does not contain any Textset-proprietary
information. 

\special's are parsed case-insensitively.

Revision history:

10/8/85		Created file, wrote, tested code for ln03:plotfile

10/16/85	ln03:resetpoints, ln03:defpoint, ln03:connect

12/12/85	Corrected bug: ln03:plotfile does not update LN03's
    		position to the latest from the DVI file.

*/

#ifdef vms
#include stdio
#include ctype
#include math
#include file
#else
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#endif

/* In VMS we declare external variables to be globalref. 
This is not really necessary, just an old habit. */

#ifdef vms
#define GLOBAL globaldef
#define EXTERN globalref
#else
#define GLOBAL 
#define EXTERN extern
#endif

#define NUMSPECIALS 4

GLOBAL char *specialnames[NUMSPECIALS] = { "defpoint",
    "connect", "plotfile", "resetpoints" } ;

EXTERN FILE *dvifile,*outfile;

/* There are two top-level routines for handling specials, one to be
invoked during pass1, the other to be invoked during pass2. The parameter
that is passed is the size of the \special string. */ 

int do_special_pass1 (p) 
int p;
{
    for (; p != 0; p--) getc(dvifile);
    return(0);
}

/* Specials get put in a buffer for easier parsing. The buffer is a little
longer than needed to make some things simpler. [[This ought to be
cleaner.]] */ 

#define MAXSPECIAL 1000

GLOBAL char sb[MAXSPECIAL+7];
GLOBAL int sbp,cstart;

/* Do_special_pass2 gets called from the main program to perform special
processing in pass2, the pass in which stuff actually gets written out to
the LN3 output file. The parameter p is the length of the special. */

int do_special_pass2 (p)
int p;
{
    char c;
    int i,j;

/* Skip whitespace in input */
        
    sbp = 0;
    while (p != 0) {
    	c = getc(dvifile);
    	p--;
    	if (!isspace(c)) {
	    sb[sbp] = c;
	    sbp = 1;
	    break;
	}
    }
    if (sbp == 0) return(1);

/* Now we have non-whitespace, read the rest of the special, or as much as
will fit in the buffer. Lowercase the stuff as it is read in. */ 

    for (; p != 0; p--) {
    	sb[sbp] = tolower(getc(dvifile));
    	sbp++;
    	if (sbp > MAXSPECIAL) break;
    }

/* Put a null as sentinel at the end of the special. */

    sb[sbp] = 0;

/* Determine whether the special pertains to the ln03, in which case sbpf
will contain "ln03" followed by whitespace or a colon. */ 

    if (strncmp(sb,"ln03",sizeof("ln03")-1) != 0) return(1);
    sbp = sizeof("ln03")-1;

/* Skip whitespace and one colon */

    while (isspace(sb[sbp])) sbp++;
    if (sb[sbp] != ':') return(1);
    sbp++;

/* Now, it might be that the special was too long for the special buffer,
so check that out and issue an error message. */ 

    if (p != 0) {
    	printf("\n \special too long (over %d bytes).",MAXSPECIAL);
    	for (; p != 0; p--) getc(dvifile);
    	return(1);
    }

/* Now determine if the special command is one of those which the driver is
supposed to recognize. As in the DVIAPS program, only the first six bytes
of the command are significant, so plotfile could be written plotfi and
defpoint could be written defpoi. */ 

    while (isspace(sb[sbp])) sbp++;
    cstart = sbp;
    while (!isspace(sb[sbp]) && sb[sbp] != '\0') sbp++;
    sb[sbp] = 0;
    sbp++;

    for (i = 0; i<NUMSPECIALS; i++)
    	if (strncmp(&sb[cstart],specialnames[i],6) == 0) break;

    if (i == NUMSPECIALS) {
    	printf("\n Unrecognized option '%s' ignored in \special command",
    		&sb[cstart]);
    	return(1);
    } else {
    	switch (i) {
    	case 0:
	    j = do_defpoint_pass2();
    	    break;
    	case 1:
    	    j = do_connect_pass2();
    	    break;
    	case 2:
    	    j = do_plotfile_pass2();
    	case 3:
    	    j = do_resetpoints();
    	}
    }
}

/* In certain \specials, dimensions may be specified in any of nine
different units of measure. */ 

#define MAXUNITS 9

GLOBAL char *unit_array[MAXUNITS] = { "pt", "in", "pc", "cm", "mm", "bp", 
    "dd", "cc", "mi" };

/* Unit_convert gives a floating point conversion factor from units to
pixels. [[The factor for micas is doubtful.]] */ 

#define PIXELS_PER_INCH (300.0)

GLOBAL float unit_convert[MAXUNITS] = { PIXELS_PER_INCH/72.27, 
    PIXELS_PER_INCH, 12.0*PIXELS_PER_INCH/72.27,
    PIXELS_PER_INCH/2.54, PIXELS_PER_INCH/25.4, 
    PIXELS_PER_INCH/72.0, (1238.0/1157.0)*PIXELS_PER_INCH/72.27,
    12.0*(1238.0/1157.0)*PIXELS_PER_INCH/72.27, PIXELS_PER_INCH/2540.0 };

/* There are 255 point variables, numbered 1 through 255. */

#define MAXPOINTS 255

GLOBAL int point_hh[MAXPOINTS], point_vv[MAXPOINTS];
GLOBAL int point_present[MAXPOINTS];

GLOBAL char *special_mess = "\n Error in \special{ln03:%s...} parameters";

/* For the purposes of defpoint, we need to know the current horizontal and
vertical positions in pixels. */ 

EXTERN int hh,vv,hoff,voff;

/* ln03:defpoint fixnum ( [dimension] , [dimension] ) */

int do_defpoint_pass2()
{
    int i,which,hh_val,vv_val;
    float xx_p,yy_p;

    which = 0;
    if (scan_fixnum(&which) != 0 || which < 1 || which > MAXPOINTS) {
    	printf(special_mess,"defpoint");
    	printf("\n Invalid point number (%d)",which);
    	return(1);
    }
    while (isspace(sb[sbp]) || sb[sbp] == ',') sbp++;
    hh_val = hh;
    vv_val = vv;
    if (sb[sbp] != '(') goto record_point;
    sbp++;
    while (isspace(sb[sbp])) sbp++;
    if (sb[sbp] == '\0') goto record_point;
    if (sb[sbp] == ',') sbp++;
    else {
    	scan_dimension(&hh_val);
        while (isspace(sb[sbp])) sbp++;
    	if (sb[sbp] == ',') sbp++;
    	else goto record_point;
    } 
    while(isspace(sb[sbp])) sbp++;
    if (sb[sbp] != ')' && sb[sbp] != '\0') scan_dimension(&vv_val);
record_point:
    point_hh[which-1] = hh_val;
    point_vv[which-1] = vv_val;
    point_present[which-1] = 1;
    return(0);
}

EXTERN long first_counter;

int do_connect_pass2()
{
    int a1,b1,a2,b2,w;
    
    scan_xpoint(&a1,&b1);
    while (isspace(sb[sbp]) || sb[sbp] == ',') sbp++;
    scan_xpoint(&a2,&b2);
    while (isspace(sb[sbp]) || sb[sbp] == ',') sbp++;
    w = 2; /* default width is two pixels ~ 0.4 points */
    if (sb[sbp] != '\0') scan_dimension(&w);

    if (first_counter%2 == 0) connect_points(b1,b2,w);
    else connect_points(a1,a2,w);

    return(0);
}

EXTERN int ln3p,vpset,hh_old;

int do_plotfile_pass2()
{
    char buf[512];
    int i,j;
    char c;

/* Read filename into array. */ 

    while (isspace(sb[sbp])) sbp++;
    i = sbp;
    while (!isspace(sb[sbp]) && sb[sbp] != '\0') sbp++;
    sb[sbp] = 0;

/* Try to open the file. [[What is the status of O_RDONLY under Unix? Is
that a 4.2bsd hack that it would be best to leave out?]] */ 

#ifdef vms
    j = open(&sb[i],O_RDONLY,0);
#else
    j = open(&sb[i],0,0);
#endif
    if (j == -1) {
    	printf("\n Unable to open plotfile %s",&sb[i]);
    	return(1);
    }

/* In executing the plotfile special, always emit escape sequences to place
the LN03 at the current position of the DVI file, even if it would seem
that the LN03 is already there. We do this in order to be able to emit a
newline at this point. Emitting a newline at this point keeps the line
length of the .ln3 file to <= 16 characters more than the line length of
the plotfile. */ 

    fprintf(outfile,"\n\033[%dd\033[%d`",vv+voff,hh+hoff);

/* Read stuff from the plotfile, write it to the output file. [[Could this
cause problem with one record being split into two?]] */ 

    while((i = read(j,buf,512)) > 0) 
    	fwrite(buf,i,1,outfile);
    close(j);

/* Now, set the variables ln3p, vpset and hh_old to indicate to the caller
that its recorded value of the current position of the LN03 is no longer
correct. This will make the caller issue absolute positioning commands
before doing any further output to the dvifile. */ 

    ln3p = 0;
    vpset = 0;
    hh_old = 30000;
    return(0);
}


/* Scan_fixnum reads an integer off sb[], starting at sbp and advancing sbp
to the first character past a valid integer. The integer is returned in i.
*/ 

int scan_fixnum(i)
long *i;
{
    int sbp_save;

    sbp_save = sbp;
    *i = atol(&sb[sbp]);
    while(isspace(sb[sbp])) sbp++;
    if (sb[sbp] == '+' || sb[sbp] == '-') sbp++;
    while (isdigit(sb[sbp])) sbp++;
    return(sbp == sbp_save);
}


/* Scan_flonum reads a flonum (without exponential notation) off sb[],
starting at sbp and advancing sbp to the first character past a valid
flonum. The flonum is returned in x. [[Unfortunately, it is impossible to
use atof or sscanf to implement this function, because they don't allow
flonums like ".3" which don't have any digits before the decimal point.]]
*/ 

int scan_flonum(x)
float *x;
{
    float j,frac;
    char negative;
    int sbp_save;

    sbp_save = sbp;
    negative = 0;
    j = 0.0;
    while (isspace(sb[sbp])) sbp++;
    if (sb[sbp] == '-') { negative = 1; sbp++; }
    else if (sb[sbp] == '+') sbp++;
    while (isdigit(sb[sbp])) { 
    	j = 10.0*j + ((float)(sb[sbp] - '0'));
    	sbp++;
    }
    if (sb[sbp] == '.') {
    	sbp++;
    	frac = 0.1;
    	while (isdigit(sb[sbp])) {
	    j += frac*((float)(sb[sbp] - '0'));
	    frac *= 0.1;
	    sbp++;
	}
    }
    if (sbp != sbp_save) {
    	*x = negative ? (-j) : j;
        return(0);
    } else return(1);
    
}


/* Scan_dimension tries to parse a dimension off sb[], advancing sbp over
what it can parse. The dimension is converted to pixels and returned in
val. If the unit isn't recognizable, scan_dimension prints an error
message, returns 1 and leaves val unchanged. */ 

int scan_dimension(val)
int *val;
{
    float x;
    int i;

    x = 0.0;
    scan_flonum(&x);
    while (isspace(sb[sbp])) sbp++;
    for (i = 0; i<MAXUNITS; i++)
    	if (strncmp(&sb[sbp],unit_array[i],2) == 0) break;
    if (i == MAXUNITS) {
    	printf(special_mess,&sb[cstart]);
    	printf("\n No such unit of measure: %.2s",&sb[sbp]);
    	return(1);
    }
    sbp += 2;
    x *= unit_convert[i];
#ifdef vms
    *val = (x < 0) ? floor(x-0.5) : floor(x+0.5);
#else
    *val = (x < 0) ? (x-0.5) : (x+0.5);
#endif
    return(0);
}

/* Scan_xpoint picks an xpoint off sb[], advancing sbp as usual. If both
parts are present they are returned in a,b; if only one is, it's returned
in both a and b. */ 

int scan_xpoint(a,b)
int *a,*b;
{

    *a = 0; *b = 0;
    scan_fixnum(a);
    while (isspace(sb[sbp])) sbp++;
    if (sb[sbp] == '/') {
    	sbp++;
    	scan_fixnum(b);
    } else *b = *a;
    return(0);
}

/* int do_rule(xx0,yy0,xx1,yy1) */

int connect_points(b1,b2,w) 
int b1,b2;
int w;
{
    int halfw;


    if (b1 < 1 || b1 > MAXPOINTS || !point_present[b1-1]) {
    	printf(special_mess,&sb[cstart]);
    	printf("\n Invalid point number (%d)",b1);
    	return(1);
    }
    if (b2 < 1 || b2 > MAXPOINTS || !point_present[b2-1]) {
    	printf(special_mess,&sb[cstart]);
    	printf("\n Invalid point number (%d)",b2);
    	return(1);
    }

    halfw = w/2;
    if (point_hh[b1-1] == point_hh[b2-1]) 
    	do_rule(point_hh[b1-1]-halfw,
    		point_vv[b1-1],point_hh[b1-1]+w-1-halfw,
    		point_vv[b2-1]);
    else if (point_vv[b1-1] == point_vv[b2-1])
    	do_rule(point_hh[b1-1],
    		point_vv[b1-1]-halfw,point_hh[b2-1],
    		point_vv[b1-1]+w-1-halfw);
    else {
    	printf(special_mess,&sb[cstart]);
    	printf("\n Can't connect along a diagonal.");
    	return(1);
    }        
    return(0);
}

int do_resetpoints ()
{
    int a,b;

    a = 0;
    scan_fixnum(&a);
    b = 0;
    scan_fixnum(&b);

    if (0 < a && a <= b && b <= MAXPOINTS) 
    	for (; a <= b; a++) point_present[a] = 0;
    else if (0 < a && a <= MAXPOINTS)
    	point_present[a] = 0;
    return(0);
}