Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

open-axiom repository from github

24005 views
1
/* Released under the Modified BSD license attached to Axiom sources.
2
* TeX Display Math Mode Line Breaking Program
3
*
4
* Author: Robert S. Sutor
5
*
6
* Date: 1991
7
*
8
* Change History:
9
*
10
* 01/19/92 RSS Change to use \[ \] instead of $$ $$
11
*
12
* 09/01/92 RSS Format and fix =-.
13
*
14
* Operation:
15
*
16
* This program reads standard input and writes to standard output. Display math
17
* mode starts with \[ at the beginning of a line and ends with \]. All lines
18
* not in display math mode are simply printed on standard output. The
19
* expressions in display math mode are broken so that they fit on a page
20
* better (line breaking).
21
*
22
* The array stuff is being converted to use the array node type.
23
*
24
* Restrictions:
25
*
26
* 1. Assume \[ and \] start in column 1 and are the only things on the lines.
27
*
28
* 2. Comments in display math mode are not preserved.
29
*
30
* 3. This is meant to deal with output from the AXIOM computer algebra system.
31
* Unpredictable results may occur if used with hand-generated TeX code or
32
* TeX code generated by other programs.
33
*/
34
35
/*
36
* Include files and #defines.
37
*/
38
#include "useproto.h"
39
#include <ctype.h>
40
#include <stdio.h>
41
#include <stdlib.h>
42
#include <string.h>
43
44
#define MATHBUFLEN 8*8192
45
#define MAXMATHTOKEN 80
46
#define MAXCHARSINLINE 60
47
#define FATDELIMMULT 2
48
49
#ifndef max
50
#define max(a,b) (((a) > (b)) ? (a) : (b))
51
#endif
52
#ifndef min
53
#define min(a,b) (((a) < (b)) ? (a) : (b))
54
#endif
55
56
#define TRUE 1
57
#define FALSE 0
58
59
#define STRCHREQ(str,char) (str[0] == char)
60
61
/*
62
* Type declarations.
63
*/
64
65
enum nodeTypes {
66
N_NODE,
67
N_TEXT,
68
N_ARRAY
69
};
70
71
typedef struct listNodeStruct {
72
struct listNodeStruct *nextListNode;
73
struct listNodeStruct *prevListNode;
74
enum nodeTypes nodeType;
75
long width;
76
long realWidth;
77
union {
78
char *text;
79
struct listNodeStruct *node;
80
struct arrayNodeStruct *array;
81
} data;
82
} listNode;
83
84
typedef struct arrayNodeStruct {
85
int cols;
86
listNode *argsNode;
87
listNode *entries; /* linked list of nodes, each pointing to a
88
* row */
89
} arrayNode;
90
91
92
/*
93
* Global Variables.
94
*/
95
96
char line[1024];
97
char blanks[] = " ";
98
char lastPrinted = '\0';
99
int indent = 0;
100
char mathBuffer[MATHBUFLEN], mathToken[MAXMATHTOKEN];
101
char bufout[2*MATHBUFLEN];
102
int lineLen, mathBufferLen, mathBufferPtr, mathTokenLen;
103
listNode *mathList;
104
int charsOut, fatDelimiter;
105
int maxLineWidth = 4500; /* 4.5 inches * 1000 */
106
int maxLineSlop = 0; /* 0.0 inches * 1000 */
107
int charTable[256];
108
int avgCharWidth;
109
int spaceWidths[5], extraOverWidth;
110
int arrayDepth = 0, arrayMaxDepth = 3;
111
int charMultNum, charMultDenom;
112
int sinWidth, cosWidth, tanWidth, erfWidth;
113
long outLineNum = 0L;
114
115
/*
116
* Function Prototypes.
117
*/
118
#ifndef _NO_PROTO
119
void arrayTooDeep();
120
int breakFracProd(listNode *, long, char *, char *, int);
121
int breakFunction(listNode *, long);
122
int breakList(listNode *, long);
123
int breakMathList(listNode *, long);
124
int breakNumber(listNode *, long);
125
int breakPMEB(listNode *, long);
126
int breakParen(listNode *, long);
127
int bufferMathLines();
128
void buildMathList();
129
int charWidth(char);
130
void computeNodeWidth(listNode *);
131
long computeWidth(listNode *);
132
void displaySplitMsg(char *, int);
133
void error(char *, char *);
134
void freeMathList(listNode *);
135
void getOptions(int, char **);
136
void initCharTable();
137
listNode *insertStringAfter(char *, listNode *);
138
listNode *insertStringAtBack(char *, listNode *);
139
listNode *insertStringAtFront(char *, listNode *);
140
int newLineIfNecessary(int);
141
listNode *newListNode(enum nodeTypes);
142
int nextMathToken();
143
int printChar(char);
144
int printMathList(listNode *, int);
145
int printString(char *);
146
void putcBuf(char, char []);
147
void putsBuf(char [], char []);
148
void resetCharMults();
149
listNode *string2NodeList(char *, listNode *);
150
void ttCharMults();
151
#else
152
void arrayTooDeep();
153
int breakFracProd();
154
int breakFunction();
155
int breakList();
156
int breakMathList();
157
int breakNumber();
158
int breakPMEB();
159
int breakParen();
160
int bufferMathLines();
161
void buildMathList();
162
int charWidth();
163
void computeNodeWidth();
164
long computeWidth();
165
void displaySplitMsg();
166
void error();
167
void freeMathList();
168
void getOptions();
169
void initCharTable();
170
listNode *insertStringAfter();
171
listNode *insertStringAtBack();
172
listNode *insertStringAtFront();
173
int newLineIfNecessary();
174
listNode *newListNode();
175
int nextMathToken();
176
int printChar();
177
int printMathList();
178
int printString();
179
void putsBuf();
180
void putcBuf();
181
void resetCharMults();
182
listNode *string2NodeList();
183
void ttCharMults();
184
#endif
185
186
187
/*
188
* Function Definitions.
189
*/
190
191
int
192
#ifndef _NO_PROTO
193
texbreak(char bufinp[])
194
#else
195
texbreak(bufinp)
196
char bufinp[];
197
#endif
198
{
199
initCharTable();
200
strcpy(bufout,"");
201
mathBufferLen=strlen(bufinp);
202
if (mathBufferLen > MATHBUFLEN) {
203
fprintf(stderr, "mathBuffer too small. Need: %d\n", mathBufferLen);
204
return 0;
205
};
206
mathBufferPtr = 0;
207
strcpy(mathBuffer,bufinp);
208
/* fprintf(stderr, "bufinp: %s\n", mathBuffer); */
209
fatDelimiter = 1;
210
arrayDepth = 0;
211
mathList = newListNode(N_NODE);
212
buildMathList(mathList);
213
resetCharMults();
214
computeWidth(mathList);
215
/* if (mathList->width > maxLineWidth)
216
fprintf(stderr, "Width = %ld units, Output line = %ld.\n", mathList->width, outLineNum); */
217
breakMathList(mathList, maxLineWidth);
218
charsOut = 0;
219
printMathList(mathList->data.node, TRUE);
220
freeMathList(mathList);
221
return 1;
222
}
223
224
/*
225
* breakFracProd:
226
*
227
* Arguments:
228
*
229
* n : the starting node at which we are to try to break
230
*
231
* lineWidth : the maximum width of a line
232
*
233
* match : either "\\over" or "\\ "
234
*
235
* label : either "quotient" or "product"
236
*
237
* paren : add parentheses (TRUE or FALSE)
238
*
239
*
240
* Returns: TRUE or FALSE, depending on whether the expression was broken
241
*
242
*
243
* Function: Tries to break up a quotient t1 \over t2 or a product t1 \ t2 by
244
* splitting and parenthesizing the numerator and/or the denominator.
245
*/
246
247
int
248
#ifndef _NO_PROTO
249
breakFracProd(listNode * n, long lineWidth, char *match, char *label, int paren)
250
#else
251
breakFracProd(n,lineWidth,match,label,paren)
252
listNode * n;
253
long lineWidth;
254
char *match;
255
char *label;
256
int paren;
257
#endif
258
{
259
260
listNode *rootNode, *lastNode, *t1, *t2;
261
int ok;
262
long workWidth1, workWidth2;
263
264
if (n->nodeType != N_NODE)
265
return FALSE;
266
267
rootNode = n;
268
269
ok = FALSE;
270
t1 = n = rootNode->data.node;
271
n = n->nextListNode;
272
if (n) {
273
if ((n->nodeType == N_TEXT) &&
274
(0 == strcmp(n->data.text, match))) {
275
t2 = n->nextListNode;
276
if (t2 && (NULL == t2->nextListNode))
277
ok = TRUE;
278
}
279
}
280
281
displaySplitMsg(label, ok);
282
283
if (ok) {
284
285
/* for products, determine rough widths for the two factors */
286
287
if (0 == strcmp(label, "product")) {
288
computeNodeWidth(t1);
289
computeNodeWidth(t2);
290
workWidth1 = lineWidth - charWidth(' ');
291
292
if (workWidth1 / 2 > t1->realWidth) {
293
workWidth2 = workWidth1 - t1->realWidth;
294
workWidth1 = t1->realWidth;
295
}
296
else if (workWidth1 / 2 > t2->realWidth) {
297
workWidth1 = workWidth1 - t2->realWidth;
298
workWidth2 = t2->realWidth;
299
}
300
else
301
workWidth1 = workWidth2 = workWidth1 / 2;
302
303
if (paren) {
304
if (t1->realWidth > workWidth1)
305
workWidth1 = workWidth1 - 4 * FATDELIMMULT * charWidth('(');
306
if (t2->realWidth > workWidth2)
307
workWidth2 = workWidth2 - 4 * FATDELIMMULT * charWidth('(');
308
}
309
}
310
else /* "quotient" */
311
workWidth1 = workWidth2 =
312
lineWidth - paren * 4 * FATDELIMMULT * charWidth('(');
313
314
if ((t1->nodeType == N_NODE) && (t1->realWidth > workWidth1)) {
315
t1->width = t1->realWidth;
316
317
if (breakMathList(t1, workWidth1) && paren) {
318
/* insert the \left( */
319
lastNode = insertStringAtFront("\\left(", t1);
320
321
while (lastNode->nextListNode)
322
lastNode = lastNode->nextListNode;
323
324
/* insert the \right) */
325
insertStringAtBack("\\right)", lastNode);
326
}
327
}
328
329
if ((t2->nodeType == N_NODE) && (t2->realWidth > workWidth2)) {
330
t2->width = t2->realWidth;
331
332
if (breakMathList(t2, workWidth2) && paren) {
333
/* insert the \left( */
334
lastNode = insertStringAtFront("\\left(", t2);
335
336
while (lastNode->nextListNode)
337
lastNode = lastNode->nextListNode;
338
339
/* insert the \right) */
340
insertStringAtBack("\\right)", lastNode);
341
}
342
}
343
344
return TRUE;
345
}
346
return FALSE;
347
}
348
349
350
int
351
#ifndef _NO_PROTO
352
breakFunction(listNode * n, long lineWidth)
353
#else
354
breakFunction(n,lineWidth)
355
listNode * n;
356
long lineWidth;
357
#endif
358
{
359
listNode *rootNode, *tmpNode, *lastNode, *t1, *t2, *t3;
360
int ok = FALSE;
361
long workWidth, maxWidth = 0;
362
363
if (n->nodeType != N_NODE)
364
return FALSE;
365
366
n = n->data.node;
367
368
if (n->nodeType == N_NODE)
369
return FALSE;
370
371
if ((0 == strcmp(n->data.text, "\\sin")) ||
372
(0 == strcmp(n->data.text, "\\cos")) ||
373
(0 == strcmp(n->data.text, "\\tan")) ||
374
(0 == strcmp(n->data.text, "\\log")) ||
375
(0 == strcmp(n->data.text, "\\arctan")) ||
376
(0 == strcmp(n->data.text, "\\erf"))) {
377
computeNodeWidth(n);
378
ok = TRUE;
379
}
380
381
displaySplitMsg("function", ok);
382
383
if (ok) {
384
t2 = newListNode(N_NODE);
385
t2->data.node = n->nextListNode;
386
t2->prevListNode = n;
387
n->nextListNode = t2;
388
ok = breakMathList(t2, lineWidth - n->realWidth);
389
}
390
391
return ok;
392
}
393
394
/*
395
* breakList:
396
*
397
* Arguments:
398
*
399
* n : the starting node at which we are to try to break
400
*
401
* lineWidth : the maximum width of a line
402
*
403
*
404
* Returns: TRUE or FALSE, depending on whether the expression was broken
405
*
406
*
407
* Function: Tries to split an expression that is bracketed by \left[ and
408
* \right] (or \left\{ and \right\} and contains at least one comma.
409
*/
410
411
int
412
#ifndef _NO_PROTO
413
breakList(listNode * n, long lineWidth)
414
#else
415
breakList(n,lineWidth)
416
listNode * n;
417
long lineWidth;
418
#endif
419
{
420
listNode *rootNode, *tmpNode, *lastNode, *t1, *t2, *t3;
421
int ok, comma;
422
long workWidth, maxWidth = 0;
423
424
if (n->nodeType != N_NODE)
425
return FALSE;
426
427
rootNode = n;
428
429
t1 = n = rootNode->data.node;
430
comma = ok = FALSE;
431
432
if ((t1->nodeType == N_TEXT) &&
433
(0 == strcmp(t1->data.text, "\\left")) &&
434
(t1->nextListNode) &&
435
(t1->nextListNode->nodeType == N_TEXT) &&
436
((0 == strcmp(t1->nextListNode->data.text, "[")) ||
437
(0 == strcmp(t1->nextListNode->data.text, "\\{")))) {
438
439
t1 = t1->nextListNode->nextListNode;
440
441
/*
442
* Check for a special case: sometimes the whole body of the list is
443
* a node. Flatten this, if possible.
444
*/
445
446
if ((t1->nodeType == N_NODE) &&
447
(t1->nextListNode->nodeType == N_TEXT) &&
448
(0 == strcmp(t1->nextListNode->data.text, "\\right"))) {
449
tmpNode = t1->prevListNode;
450
t2 = t1->nextListNode;
451
t3 = t1->data.node;
452
tmpNode->nextListNode = t3;
453
t3->prevListNode = tmpNode;
454
while (t3->nextListNode)
455
t3 = t3->nextListNode;
456
t3->nextListNode = t2;
457
t2->prevListNode = t3;
458
free(t1);
459
t1 = tmpNode->nextListNode;
460
}
461
462
while (t1->nextListNode && !ok) {
463
if ((t1->nodeType == N_TEXT) &&
464
(0 == strcmp(t1->data.text, ",")))
465
comma = TRUE;
466
else if ((t1->nodeType == N_TEXT) &&
467
(0 == strcmp(t1->data.text, "\\right")) &&
468
(t1->nextListNode->nodeType == N_TEXT) &&
469
((0 == strcmp(t1->nextListNode->data.text, "]")) ||
470
(0 == strcmp(t1->nextListNode->data.text, "\\}"))) &&
471
(NULL == t1->nextListNode->nextListNode)) {
472
ok = comma;
473
tmpNode = t1->nextListNode;
474
}
475
t1 = t1->nextListNode;
476
}
477
}
478
479
displaySplitMsg("list", ok);
480
481
if (ok) {
482
if (arrayDepth >= arrayMaxDepth) {
483
arrayTooDeep();
484
return FALSE;
485
}
486
487
/*
488
* Create array environment
489
*/
490
491
lastNode = insertStringAtFront("\\begin{array}{@{}l}\\displaystyle", rootNode);
492
arrayDepth++;
493
insertStringAtBack("\\end{array}", tmpNode);
494
495
/*
496
* Now break at best place short of width. Start after the
497
* environment begins and after the \left(
498
*/
499
500
n = lastNode->nextListNode->nextListNode->nextListNode;
501
502
/*
503
* try to split the first expression if too big
504
*/
505
506
tmpNode = n->nextListNode;
507
if (breakMathList(n, lineWidth)) {
508
workWidth = n->width;
509
n = tmpNode;
510
}
511
else
512
workWidth = n->width;
513
maxWidth = workWidth;
514
515
while (n->nextListNode) {
516
if ((n->nodeType == N_TEXT) &&
517
((0 == strcmp(n->data.text, ",")) ||
518
(0 == strcmp(n->data.text, "\\:"))) &&
519
(workWidth + n->nextListNode->width > lineWidth)) {
520
maxWidth = max(maxWidth, workWidth);
521
n = insertStringAfter("\\right. \\\\ \\\\ \\displaystyle \\left.", n);
522
523
/*
524
* try to split the next expression if too big
525
*/
526
527
tmpNode = n->nextListNode;
528
if (breakMathList(n, lineWidth)) {
529
workWidth = n->width;
530
n = tmpNode;
531
}
532
else
533
workWidth = n->width;
534
}
535
else {
536
workWidth += n->nextListNode->width;
537
n = n->nextListNode;
538
}
539
}
540
541
rootNode->width = rootNode->realWidth =
542
rootNode->data.node->width = rootNode->data.node->realWidth =
543
maxWidth;
544
arrayDepth--;
545
546
return TRUE;
547
}
548
549
return FALSE;
550
}
551
552
/*
553
* breakNumber:
554
*
555
* Arguments:
556
*
557
* rootNode : the starting node at which we are to try to break
558
*
559
* lineWidth : the maximum width of a line
560
*
561
*
562
* Returns: TRUE or FALSE, depending on whether the expression was broken
563
*
564
*
565
* Function: Tries to break an expression that contains only digits and possibly
566
* a decimal point.
567
*/
568
569
int
570
#ifndef _NO_PROTO
571
breakNumber(listNode * rootNode, long lineWidth)
572
#else
573
breakNumber(rootNode,lineWidth)
574
listNode * rootNode;
575
long lineWidth;
576
#endif
577
{
578
int ok = TRUE;
579
listNode *n, *arrNode, *rowNode, *colNode;
580
long workWidth, maxWidth = 0;
581
582
if (rootNode->nodeType != N_NODE)
583
return FALSE;
584
585
n = rootNode->data.node;
586
while (n && ok) {
587
if ((n->nodeType == N_TEXT) &&
588
(n->data.text[1] == '\0') &&
589
(isdigit(n->data.text[0]) || ('.' == n->data.text[0]))) {
590
n = n->nextListNode;
591
}
592
else
593
ok = FALSE;
594
}
595
596
displaySplitMsg("number", ok);
597
598
if (ok) {
599
if (arrayDepth >= arrayMaxDepth) {
600
arrayTooDeep();
601
return FALSE;
602
}
603
604
arrayDepth++;
605
arrNode = newListNode(N_ARRAY);
606
arrNode->data.array->entries = rowNode = newListNode(N_NODE);
607
arrNode->data.array->cols = 1;
608
arrNode->data.array->argsNode = newListNode(N_NODE);
609
string2NodeList("{@{}l}", arrNode->data.array->argsNode);
610
611
n = rootNode->data.node;
612
computeWidth(n);
613
maxWidth = workWidth = n->width;
614
rowNode->data.node = colNode = newListNode(N_NODE);
615
colNode->data.node = n;
616
n = n->nextListNode;
617
618
while (n) {
619
computeWidth(n);
620
621
if (workWidth + n->width > lineWidth) {
622
maxWidth = max(maxWidth, workWidth);
623
624
/*
625
* time to start a new row
626
*/
627
628
n->prevListNode->nextListNode = NULL;
629
n->prevListNode = NULL;
630
workWidth = n->width;
631
rowNode->nextListNode = newListNode(N_NODE);
632
rowNode = rowNode->nextListNode;
633
rowNode->data.node = colNode = newListNode(N_NODE);
634
colNode->data.node = n;
635
}
636
else
637
workWidth += (n->nextListNode) ? n->nextListNode->width :0 ;
638
639
n = n->nextListNode;
640
}
641
642
rootNode->data.node = arrNode;
643
rootNode->width = rootNode->realWidth =
644
arrNode->width = arrNode->realWidth = maxWidth;
645
arrayDepth--;
646
647
return TRUE;
648
}
649
650
return FALSE;
651
}
652
653
void
654
#ifndef _NO_PROTO
655
resetWidths(listNode * n)
656
#else
657
resetWidths(n)
658
listNode * n;
659
#endif
660
{
661
if (n) {
662
n->width = -1;
663
n->realWidth = 0;
664
if (n->nodeType == N_NODE)
665
resetWidths(n->data.node);
666
resetWidths(n->nextListNode);
667
}
668
}
669
670
/*
671
* breakParen:
672
*
673
* Arguments:
674
*
675
* n : the starting node at which we are to try to break
676
*
677
* lineWidth : the maximum width of a line
678
*
679
*
680
* Returns: TRUE or FALSE, depending on whether the expression was broken
681
*
682
*
683
* Function: Tries to split an expression that is bracketed by left( and \right)
684
* (e.g., a factor).
685
*/
686
687
int
688
#ifndef _NO_PROTO
689
breakParen(listNode * n, long lineWidth)
690
#else
691
breakParen(n,lineWidth)
692
listNode * n;
693
long lineWidth;
694
#endif
695
{
696
listNode *tmpNode, *workNode;
697
int ok = FALSE;
698
699
if (n->nodeType != N_NODE)
700
goto say_msg;
701
702
tmpNode = n->data.node;
703
704
/*
705
* check for \left
706
*/
707
708
if ((tmpNode == NULL) ||
709
(tmpNode->nodeType == N_NODE) ||
710
(0 != strcmp(tmpNode->data.text, "\\left")))
711
goto say_msg;
712
713
/*
714
* check for '('
715
*/
716
717
tmpNode = tmpNode->nextListNode;
718
719
if ((tmpNode == NULL) ||
720
(tmpNode->nodeType == N_NODE) ||
721
('(' != tmpNode->data.text[0]))
722
goto say_msg;
723
724
/*
725
* now move to the end
726
*/
727
728
tmpNode = tmpNode->nextListNode;
729
730
if (tmpNode != NULL) {
731
while (tmpNode->nextListNode)
732
tmpNode = tmpNode->nextListNode;
733
tmpNode = tmpNode->prevListNode;
734
}
735
736
/*
737
* check for \right
738
*/
739
740
if ((tmpNode == NULL) ||
741
(tmpNode->nodeType == N_NODE) ||
742
(0 != strcmp(tmpNode->data.text, "\\right")))
743
goto say_msg;
744
745
/*
746
* check for ')'
747
*/
748
749
tmpNode = tmpNode->nextListNode;
750
751
if ((tmpNode == NULL) ||
752
(tmpNode->nodeType == N_NODE) ||
753
(')' != tmpNode->data.text[0]))
754
goto say_msg;
755
756
ok = TRUE;
757
758
say_msg:
759
displaySplitMsg("parenthesized expression", ok);
760
761
if (ok) {
762
763
/*
764
* nest the whole inside if necessary, i.e., there is more than one
765
* term between the ( and the \right
766
*/
767
768
if (tmpNode->prevListNode->prevListNode !=
769
n->data.node->nextListNode->nextListNode) {
770
workNode = newListNode(N_NODE);
771
workNode->data.node = n->data.node->nextListNode->nextListNode;
772
n->data.node->nextListNode->nextListNode = workNode;
773
tmpNode->prevListNode->prevListNode->nextListNode = NULL;
774
tmpNode->prevListNode->prevListNode = workNode;
775
workNode->prevListNode = n->data.node->nextListNode;
776
workNode->nextListNode = tmpNode->prevListNode;
777
resetWidths(workNode);
778
computeWidth(workNode);
779
}
780
781
return breakMathList(n->data.node->nextListNode->nextListNode,
782
lineWidth - 4 * FATDELIMMULT * charWidth('('));
783
}
784
785
return FALSE;
786
}
787
788
/*
789
* breakPMEB:
790
*
791
* Arguments:
792
*
793
* n : the starting node at which we are to try to break
794
*
795
* lineWidth : the maximum width of a line
796
*
797
*
798
* Returns: TRUE or FALSE, depending on whether the expression was broken
799
*
800
*
801
* Function: Tries to split an expression that contains only +, -, = or \ as
802
* operators. The split occurs after the operator.
803
*/
804
805
int
806
#ifndef _NO_PROTO
807
breakPMEB(listNode * n, long lineWidth)
808
#else
809
breakPMEB(n,lineWidth)
810
listNode * n;
811
long lineWidth;
812
#endif
813
{
814
char *s;
815
listNode *rootNode, *tmpNode, *lastNode;
816
int ok, op;
817
long workWidth, maxWidth = 0;
818
819
if (n->nodeType != N_NODE)
820
return FALSE;
821
822
if (n->width <= lineWidth + maxLineSlop) /* allow a little slop here */
823
return FALSE;
824
825
rootNode = n;
826
tmpNode = n = n->data.node;
827
ok = TRUE;
828
op = FALSE;
829
830
while (n && ok) {
831
if (n->nodeType == N_TEXT) {
832
s = n->data.text;
833
if (STRCHREQ(s, '+') || STRCHREQ(s, '-') || STRCHREQ(s, '=') ||
834
(0 == strcmp(s, "\\ ")))
835
op = TRUE;
836
else if ((0 == strcmp(s, "\\left")) ||
837
(0 == strcmp(s, "\\right")) ||
838
(0 == strcmp(s, "\\over")) ||
839
STRCHREQ(s, ',')) {
840
ok = FALSE;
841
break;
842
}
843
}
844
tmpNode = n;
845
n = n->nextListNode;
846
}
847
ok = ok & op;
848
849
displaySplitMsg("(+,-,=, )-expression", ok);
850
851
852
if (ok) {
853
if (arrayDepth >= arrayMaxDepth) {
854
arrayTooDeep();
855
return FALSE;
856
}
857
858
/*
859
* Create array environment
860
*/
861
862
lastNode = insertStringAtFront("\\begin{array}{@{}l}\\displaystyle", rootNode);
863
arrayDepth++;
864
insertStringAtBack("\\end{array}", tmpNode);
865
866
/*
867
* Now break at best place short of width. Start after the
868
* environment begins.
869
*/
870
871
n = lastNode->nextListNode;
872
873
/*
874
* try to split the first expression if too big
875
*/
876
877
tmpNode = n->nextListNode;
878
if (breakMathList(n, lineWidth)) {
879
workWidth = n->width;
880
n = tmpNode;
881
}
882
else
883
workWidth = n->width;
884
maxWidth = workWidth;
885
886
while (n->nextListNode) {
887
loop_top:
888
if ((n->nodeType == N_TEXT) &&
889
(STRCHREQ(n->data.text, '+') || STRCHREQ(n->data.text, '-') ||
890
STRCHREQ(n->data.text, '=') ||
891
(0 == strcmp(n->data.text, "\\ "))) &&
892
(workWidth > 24) && /* avoid - or + on their own line */
893
(workWidth + n->nextListNode->width > lineWidth)) {
894
895
if ((workWidth < lineWidth / 3) &&
896
(breakMathList(n->nextListNode, lineWidth - workWidth))) {
897
n->nextListNode->width = -1;
898
n->nextListNode->realWidth = 0;
899
computeNodeWidth(n->nextListNode);
900
goto loop_top;
901
}
902
903
/*
904
* \ means multiplication. Use a \cdot to make this clearer
905
*/
906
907
if (0 == strcmp(n->data.text, "\\ "))
908
n = insertStringAfter("\\cdot \\\\ \\\\ \\displaystyle", n);
909
else
910
n = insertStringAfter("\\\\ \\\\ \\displaystyle", n);
911
maxWidth = max(maxWidth, workWidth);
912
913
/*
914
* try to split the next expression if too big
915
*/
916
917
tmpNode = n->nextListNode;
918
if (breakMathList(n, lineWidth)) {
919
workWidth = n->width;
920
n = tmpNode;
921
}
922
else
923
workWidth = n->width;
924
}
925
else {
926
workWidth += n->nextListNode->width;
927
n = n->nextListNode;
928
}
929
}
930
931
rootNode->width = rootNode->realWidth =
932
rootNode->data.node->width = rootNode->data.node->realWidth =
933
maxWidth;
934
arrayDepth--;
935
936
return TRUE;
937
}
938
939
return FALSE;
940
}
941
942
/*
943
* breakMathList:
944
*
945
* Arguments:
946
*
947
* n : the starting node at which we are to try to break
948
*
949
* lineWidth : the maximum width of a line
950
*
951
*
952
* Returns: TRUE or FALSE, depending on whether the expression was broken
953
*
954
*
955
* Function: Tries various methods to break the expression up into multiple
956
* lines if the expression is too big.
957
*/
958
959
int
960
#ifndef _NO_PROTO
961
breakMathList(listNode * n, long lineWidth)
962
#else
963
breakMathList(n,lineWidth)
964
listNode * n;
965
long lineWidth;
966
#endif
967
{
968
int split = FALSE;
969
970
/*
971
* Don't do anything if already short enough.
972
*/
973
974
if (n->width <= lineWidth)
975
return FALSE;
976
977
/*
978
* Can't split strings, so just return.
979
*/
980
981
if (n->nodeType == N_TEXT)
982
return FALSE;
983
984
blanks[indent] = ' ';
985
indent += 2;
986
blanks[indent] = '\0';
987
988
/*
989
* We know we have a node, so see what we can do.
990
*/
991
992
/*
993
* Case 1: a product: t1 \ t2
994
*/
995
996
if (split = breakFracProd(n, lineWidth, "\\ ", "product", FALSE))
997
goto done;
998
999
/*
1000
* Case 2: a sequence of tokens separated by +, - or =
1001
*/
1002
1003
if (split = breakPMEB(n, lineWidth))
1004
goto done;
1005
1006
/*
1007
* Case 3: a fraction of terms: t1 \over t2
1008
*/
1009
1010
if (split = breakFracProd(n, lineWidth, "\\over", "quotient", TRUE))
1011
goto done;
1012
1013
/*
1014
* Case 4: a list of terms bracketed by \left[ and \right] with a comma
1015
*/
1016
1017
if (split = breakList(n, lineWidth))
1018
goto done;
1019
1020
/*
1021
* Case 5: a list of digits, possibly with one "."
1022
*/
1023
1024
if (split = breakNumber(n, lineWidth))
1025
goto done;
1026
1027
/*
1028
* Case 6: a parenthesized expression (e.g., a factor)
1029
*/
1030
1031
if (split = breakParen(n, lineWidth))
1032
goto done;
1033
1034
/*
1035
* Case 7: a function application
1036
*/
1037
1038
if (split = breakFunction(n, lineWidth))
1039
goto done;
1040
1041
done:
1042
blanks[indent] = ' ';
1043
indent -= 2;
1044
blanks[indent] = '\0';
1045
1046
return split;
1047
}
1048
1049
void
1050
#ifndef _NO_PROTO
1051
buildMathList(listNode * oldNode)
1052
#else
1053
buildMathList(oldNode)
1054
listNode * oldNode;
1055
#endif
1056
{
1057
listNode *curNode, *tmpNode;
1058
1059
curNode = NULL;
1060
while (nextMathToken()) {
1061
if (mathToken[0] == '}')
1062
break;
1063
if (mathToken[0] == '{') {
1064
tmpNode = newListNode(N_NODE);
1065
buildMathList(tmpNode);
1066
}
1067
else {
1068
tmpNode = newListNode(N_TEXT);
1069
tmpNode->data.text = strdup(mathToken);
1070
}
1071
if (curNode == NULL) {
1072
oldNode->data.node = tmpNode;
1073
}
1074
else {
1075
tmpNode->prevListNode = curNode;
1076
curNode->nextListNode = tmpNode;
1077
}
1078
curNode = tmpNode;
1079
}
1080
1081
/*
1082
* leave with one level of nesting, e.g., {{{x}}} --> {x}
1083
*/
1084
1085
tmpNode = oldNode->data.node;
1086
while ( tmpNode && (tmpNode->nodeType == N_NODE) &&
1087
(tmpNode->nextListNode == NULL) ) {
1088
oldNode->data.node = tmpNode->data.node;
1089
free(tmpNode);
1090
tmpNode = oldNode->data.node;
1091
}
1092
}
1093
1094
void
1095
#ifndef _NO_PROTO
1096
computeNodeWidth(listNode * n)
1097
#else
1098
computeNodeWidth(n)
1099
listNode * n;
1100
#endif
1101
{
1102
char *s;
1103
int i;
1104
listNode *tmp;
1105
1106
if (n->width != -1) /* only = -1 if unprocessed */
1107
return;
1108
1109
n->realWidth = 0;
1110
1111
if (n->nodeType == N_TEXT) {
1112
s = n->data.text;
1113
if (s[0] == '\\') {
1114
if (s[2] == '\0') {
1115
switch (s[1]) {
1116
case ' ':
1117
n->width = spaceWidths[0];
1118
break;
1119
case ',':
1120
n->width = spaceWidths[1];
1121
break;
1122
case '!':
1123
n->width = spaceWidths[2];
1124
break;
1125
case ':':
1126
n->width = spaceWidths[3];
1127
break;
1128
case ';':
1129
n->width = spaceWidths[4];
1130
break;
1131
default:
1132
n->width = avgCharWidth;
1133
}
1134
n->realWidth = n->width;
1135
}
1136
else if ((0 == strcmp(s, "\\displaystyle")) ||
1137
(0 == strcmp(s, "\\bf")) ||
1138
(0 == strcmp(s, "\\sf")) ||
1139
(0 == strcmp(s, "\\tt")) ||
1140
(0 == strcmp(s, "\\rm")) ||
1141
(0 == strcmp(s, "\\hbox")) ||
1142
(0 == strcmp(s, "\\mbox")) ||
1143
(0 == strcmp(s, "\\overline")) ||
1144
(0 == strcmp(s, "\\textstyle")) ||
1145
(0 == strcmp(s, "\\scriptstyle")) ||
1146
(0 == strcmp(s, "\\scriptscriptstyle"))) {
1147
n->width = 0;
1148
}
1149
else if (0 == strcmp(s, "\\ldots"))
1150
n->width = 3 * charWidth('.');
1151
else if (0 == strcmp(s, "\\left")) {
1152
tmp = n->nextListNode;
1153
if (tmp->nodeType != N_TEXT)
1154
error("unusual token following \\left", "");
1155
n->realWidth = n->width = (tmp->data.text[0] == '.')
1156
? 0
1157
: charWidth(tmp->data.text[0]);
1158
tmp->width = 0;
1159
fatDelimiter = 1;
1160
}
1161
else if (0 == strcmp(s, "\\over")) {
1162
1163
/*
1164
* have already added in width of numerator
1165
*/
1166
computeNodeWidth(n->nextListNode);
1167
n->realWidth = extraOverWidth + max(n->prevListNode->width, n->nextListNode->width);
1168
n->width = n->realWidth - n->prevListNode->width;
1169
n->nextListNode->width = 0;
1170
fatDelimiter = FATDELIMMULT;
1171
}
1172
else if (0 == strcmp(s, "\\right")) {
1173
tmp = n->nextListNode;
1174
if (tmp->nodeType != N_TEXT)
1175
error("unusual token following \\right", "");
1176
n->realWidth = n->width = fatDelimiter *
1177
((tmp->data.text[0] == '.') ? 0 : charWidth(tmp->data.text[0]));
1178
tmp->width = 0;
1179
fatDelimiter = 1;
1180
}
1181
else if (0 == strcmp(s, "\\root")) {
1182
computeNodeWidth(n->nextListNode); /* which root */
1183
n->nextListNode->nextListNode->width = 0; /* \of */
1184
tmp = n->nextListNode->nextListNode->nextListNode;
1185
computeNodeWidth(tmp); /* root of */
1186
n->realWidth = n->width = tmp->width + (avgCharWidth / 2) +
1187
max(avgCharWidth, n->nextListNode->width);
1188
n->nextListNode->width = 0;
1189
tmp->width = 0;
1190
}
1191
else if (0 == strcmp(s, "\\sqrt")) {
1192
computeNodeWidth(n->nextListNode);
1193
n->realWidth = n->width =
1194
avgCharWidth + (avgCharWidth / 2) + n->nextListNode->width;
1195
n->nextListNode->width = 0;
1196
}
1197
else if (0 == strcmp(s, "\\zag")) {
1198
computeNodeWidth(n->nextListNode);
1199
computeNodeWidth(n->nextListNode->nextListNode);
1200
n->realWidth = n->width = avgCharWidth + max(n->nextListNode->width,
1201
n->nextListNode->nextListNode->width);
1202
n->nextListNode->width = 0;
1203
n->nextListNode->nextListNode->width = 0;
1204
fatDelimiter = FATDELIMMULT;
1205
}
1206
else if ((0 == strcmp(s, "\\alpha")) ||
1207
(0 == strcmp(s, "\\beta")) ||
1208
(0 == strcmp(s, "\\pi"))) {
1209
n->realWidth = n->width = avgCharWidth;
1210
}
1211
else if (0 == strcmp(s, "\\sin"))
1212
/* should use table lookup here */
1213
n->realWidth = n->width = sinWidth;
1214
else if (0 == strcmp(s, "\\cos"))
1215
n->realWidth = n->width = cosWidth;
1216
else if (0 == strcmp(s, "\\tan"))
1217
n->realWidth = n->width = tanWidth;
1218
else if (0 == strcmp(s, "\\erf"))
1219
n->realWidth = n->width = erfWidth;
1220
1221
/*
1222
* otherwise just compute length of token after \
1223
*/
1224
else {
1225
n->width = 0;
1226
for (i = 1; i < strlen(s); i++)
1227
n->width += charWidth(s[i]);
1228
n->realWidth = n->width;
1229
}
1230
}
1231
else if (s[1] == '\0')
1232
switch (s[0]) {
1233
case '^':
1234
case '_':
1235
tmp = n->nextListNode;
1236
computeNodeWidth(tmp);
1237
n->width = n->width = tmp->width;
1238
tmp->width = 0;
1239
break;
1240
default:
1241
n->realWidth = n->width = charWidth(s[0]);
1242
}
1243
else {
1244
n->width = 0;
1245
for (i = 0; i < strlen(s); i++)
1246
n->width += charWidth(s[i]);
1247
n->realWidth = n->width;
1248
}
1249
}
1250
else {
1251
n->realWidth = n->width = computeWidth(n->data.node);
1252
}
1253
}
1254
1255
long
1256
#ifndef _NO_PROTO
1257
computeWidth(listNode * n)
1258
#else
1259
computeWidth(n)
1260
listNode * n;
1261
#endif
1262
{
1263
long w = 0;
1264
1265
while (n != NULL) {
1266
if (n->width == -1) {
1267
computeNodeWidth(n);
1268
w += n->width;
1269
}
1270
n = n->nextListNode;
1271
}
1272
return w;
1273
}
1274
1275
/*
1276
* displaySplitMsg:
1277
*
1278
* Arguments:
1279
*
1280
* s : a string describing the kind of expression we are trying to split.
1281
*
1282
* ok : whether we can split it (TRUE or FALSE)
1283
*
1284
*
1285
* Returns: nothing
1286
*
1287
*
1288
* Function: Displays a message on stderr about whether a particular method of
1289
* line breaking will be successful.
1290
*/
1291
1292
void
1293
#ifndef _NO_PROTO
1294
displaySplitMsg(char *s, int ok)
1295
#else
1296
displaySplitMsg(s,ok)
1297
char *s;
1298
int ok;
1299
#endif
1300
{
1301
/* fprintf(stderr, "%sCan split %s: %s\n", blanks, s, ok ? "TRUE" : "FALSE"); */
1302
}
1303
1304
void
1305
arrayTooDeep()
1306
{
1307
fprintf(stderr, "%s->Array nesting too deep!\n", blanks);
1308
}
1309
1310
void
1311
#ifndef _NO_PROTO
1312
error(char *msg, char *insert)
1313
#else
1314
error(msg,insert)
1315
char *msg;
1316
char *insert;
1317
#endif
1318
{
1319
fputs("Error (texbreak): ", stderr);
1320
fputs(msg, stderr);
1321
fputs(insert, stderr);
1322
fputc('\n', stderr);
1323
1324
fputs("% Error (texbreak): ", stdout);
1325
fputs(msg, stdout);
1326
fputs(insert, stdout);
1327
fputc('\n', stdout);
1328
exit(1);
1329
}
1330
1331
void
1332
#ifndef _NO_PROTO
1333
freeMathList(listNode * n)
1334
#else
1335
freeMathList(n)
1336
listNode * n;
1337
#endif
1338
{
1339
listNode *tmpNode;
1340
1341
while (n != NULL) {
1342
if (n->nodeType == N_NODE)
1343
freeMathList(n->data.node);
1344
else if (n->nodeType == N_TEXT)
1345
free(n->data.text);
1346
else {
1347
freeMathList(n->data.array->argsNode);
1348
freeMathList(n->data.array->entries);
1349
free(n->data.array);
1350
}
1351
tmpNode = n->nextListNode;
1352
free(n);
1353
n = tmpNode;
1354
}
1355
}
1356
1357
listNode *
1358
#ifndef _NO_PROTO
1359
insertStringAfter(char *s, listNode * n)
1360
#else
1361
insertStringAfter(s,n)
1362
char *s;
1363
listNode * n;
1364
#endif
1365
{
1366
1367
/*
1368
* returns node after inserted string
1369
*/
1370
listNode *workNode, *lastNode;
1371
1372
workNode = newListNode(N_NODE);
1373
lastNode = string2NodeList(s, workNode);
1374
1375
n->nextListNode->prevListNode = lastNode;
1376
lastNode->nextListNode = n->nextListNode;
1377
n->nextListNode = workNode->data.node;
1378
workNode->data.node->prevListNode = n;
1379
1380
free(workNode);
1381
return lastNode->nextListNode;
1382
}
1383
1384
listNode *
1385
#ifndef _NO_PROTO
1386
insertStringAtBack(char *s, listNode * n)
1387
#else
1388
insertStringAtBack(s,n)
1389
char *s;
1390
listNode * n;
1391
#endif
1392
{
1393
1394
/*
1395
* Breaks s up into a list of tokens and appends them onto the end of n.
1396
* n must be non-NULL.
1397
*/
1398
1399
listNode *workNode, *lastNode;
1400
1401
workNode = newListNode(N_NODE);
1402
lastNode = string2NodeList(s, workNode);
1403
n->nextListNode = workNode->data.node;
1404
workNode->data.node->prevListNode = n;
1405
free(workNode);
1406
1407
return lastNode;
1408
}
1409
1410
listNode *
1411
#ifndef _NO_PROTO
1412
insertStringAtFront(char *s, listNode * n)
1413
#else
1414
insertStringAtFront(s,n)
1415
char *s;
1416
listNode * n;
1417
#endif
1418
{
1419
1420
/*
1421
* Breaks s up into a list of tokens and appends them onto the front of
1422
* n. n must be a node.
1423
*/
1424
1425
listNode *workNode, *lastNode;
1426
1427
workNode = newListNode(N_NODE);
1428
lastNode = string2NodeList(s, workNode);
1429
lastNode->nextListNode = n->data.node;
1430
n->data.node->prevListNode = lastNode;
1431
n->data.node = workNode->data.node;
1432
free(workNode);
1433
1434
return lastNode;
1435
}
1436
1437
int
1438
#ifndef _NO_PROTO
1439
newLineIfNecessary(int lastWasNewLine)
1440
#else
1441
newLineIfNecessary(lastWasNewLine)
1442
int lastWasNewLine;
1443
#endif
1444
{
1445
if (!lastWasNewLine || (charsOut > 0)) {
1446
putcBuf('\n', bufout);
1447
outLineNum++;
1448
charsOut = 0;
1449
}
1450
return TRUE;
1451
}
1452
1453
listNode *
1454
#ifndef _NO_PROTO
1455
newListNode(enum nodeTypes nt)
1456
#else
1457
newListNode(nt)
1458
enum nodeTypes nt;
1459
#endif
1460
{
1461
listNode *n;
1462
1463
n = (listNode *) malloc(sizeof(listNode));
1464
n->nextListNode = n->prevListNode = NULL;
1465
n->nodeType = nt;
1466
n->width = -1;
1467
n->realWidth = -1;
1468
if (nt == N_NODE)
1469
n->data.node = NULL;
1470
else if (nt == N_TEXT)
1471
n->data.text = NULL;
1472
else {
1473
n->data.array = (arrayNode *) malloc(sizeof(arrayNode));
1474
n->data.array->argsNode = NULL;
1475
n->data.array->entries = NULL;
1476
n->data.array->cols = 0;
1477
}
1478
return n;
1479
}
1480
1481
int
1482
nextMathToken()
1483
{
1484
1485
1486
/*
1487
* Sets mathToken. Returns 1 if ok, 0 if no more tokens.
1488
*/
1489
1490
char curChar, errChar[2];
1491
1492
errChar[1] = '\0';
1493
mathToken[0] = '\0';
1494
mathTokenLen = 0;
1495
1496
/*
1497
* Kill any blanks.
1498
*/
1499
1500
while ((mathBufferPtr < mathBufferLen) && (mathBuffer[mathBufferPtr] == ' '))
1501
mathBufferPtr++;
1502
1503
/*
1504
* If at end, exit saying so.
1505
*/
1506
1507
if (mathBufferPtr >= mathBufferLen)
1508
return 0;
1509
1510
mathToken[mathTokenLen++] = curChar = mathBuffer[mathBufferPtr++];
1511
1512
if (curChar == '\\') {
1513
curChar = mathBuffer[mathBufferPtr++];
1514
switch (curChar) {
1515
case '\0': /* at end of buffer */
1516
mathToken[mathTokenLen++] = ' ';
1517
goto done;
1518
case '\\':
1519
case ' ':
1520
case '!':
1521
case '#':
1522
case '$':
1523
case '%':
1524
case '&':
1525
case ',':
1526
case ':':
1527
case ';':
1528
case '^':
1529
case '_':
1530
case '{':
1531
case '}':
1532
mathToken[mathTokenLen++] = curChar;
1533
goto done;
1534
}
1535
if (isalpha(curChar) || (curChar == '@')) {
1536
mathToken[mathTokenLen++] = curChar;
1537
while ((curChar = mathBuffer[mathBufferPtr]) &&
1538
(isalpha(curChar) || (curChar == '@'))) {
1539
mathToken[mathTokenLen++] = curChar;
1540
mathBufferPtr++;
1541
}
1542
}
1543
else {
1544
errChar[0] = curChar;
1545
errChar[1] = '\0';
1546
error("strange character following \\: ", errChar);
1547
}
1548
}
1549
else if (isdigit(curChar)) /* digits are individual tokens */
1550
;
1551
else if (isalpha(curChar)) {
1552
while ((curChar = mathBuffer[mathBufferPtr]) &&
1553
(isalpha(curChar))) {
1554
mathToken[mathTokenLen++] = curChar;
1555
mathBufferPtr++;
1556
}
1557
}
1558
else if (curChar == '"') { /* handle strings */
1559
while ((curChar = mathBuffer[mathBufferPtr]) &&
1560
(curChar != '"')) {
1561
mathToken[mathTokenLen++] = curChar;
1562
mathBufferPtr++;
1563
}
1564
mathToken[mathTokenLen++] = '"';
1565
mathBufferPtr++;
1566
}
1567
1568
done:
1569
mathToken[mathTokenLen--] = '\0';
1570
1571
/*
1572
* Some translations.
1573
*/
1574
if (0 == strcmp(mathToken, "\\sp")) {
1575
mathToken[0] = '^';
1576
mathToken[1] = '\0';
1577
mathTokenLen = 1;
1578
}
1579
else if (0 == strcmp(mathToken, "\\sb")) {
1580
mathToken[0] = '_';
1581
mathToken[1] = '\0';
1582
mathTokenLen = 1;
1583
}
1584
1585
return 1;
1586
}
1587
1588
int
1589
#ifndef _NO_PROTO
1590
printChar(char c)
1591
#else
1592
printChar(c)
1593
char c;
1594
#endif
1595
{
1596
if ((charsOut > MAXCHARSINLINE) &&
1597
isdigit(lastPrinted) && isdigit(c)) {
1598
putcBuf('\n', bufout);
1599
outLineNum++;
1600
charsOut = 0;
1601
}
1602
1603
putcBuf(c, bufout);
1604
lastPrinted = c;
1605
charsOut++;
1606
1607
/*
1608
* break lines after following characters
1609
*/
1610
1611
if ((charsOut > MAXCHARSINLINE) && strchr("+- ,_^", c)) {
1612
putcBuf('\n', bufout);
1613
outLineNum++;
1614
charsOut = 0;
1615
lastPrinted = '\0';
1616
return TRUE;
1617
}
1618
return FALSE;
1619
}
1620
1621
int
1622
#ifndef _NO_PROTO
1623
printMathList(listNode * n, int lastWasNewLine)
1624
#else
1625
printMathList(n,lastWasNewLine)
1626
listNode * n;
1627
int lastWasNewLine;
1628
#endif
1629
{
1630
listNode *tmpNode, *rowNode, *colNode;
1631
int begin, group, r, c;
1632
1633
while (n != NULL) {
1634
if (n->nodeType == N_NODE) {
1635
lastWasNewLine = printChar('{');
1636
lastWasNewLine = printMathList(n->data.node, lastWasNewLine);
1637
lastWasNewLine = printChar('}');
1638
}
1639
else if (n->nodeType == N_ARRAY) {
1640
lastWasNewLine = newLineIfNecessary(lastWasNewLine);
1641
lastWasNewLine = printString("\\begin{array}");
1642
lastWasNewLine = printMathList(n->data.array->argsNode, lastWasNewLine);
1643
lastWasNewLine = printString("\\displaystyle");
1644
lastWasNewLine = newLineIfNecessary(lastWasNewLine);
1645
1646
rowNode = n->data.array->entries; /* node pointing to first row */
1647
while (rowNode) {
1648
colNode = rowNode->data.node;
1649
while (colNode) {
1650
if (colNode->prevListNode) { /* if not first column */
1651
lastWasNewLine = printString(" & ");
1652
lastWasNewLine = newLineIfNecessary(lastWasNewLine);
1653
}
1654
lastWasNewLine = printMathList(colNode->data.node, lastWasNewLine);
1655
colNode = colNode->nextListNode;
1656
}
1657
if (rowNode->nextListNode) /* if not last row */
1658
lastWasNewLine = printString(" \\\\");
1659
1660
lastWasNewLine = newLineIfNecessary(lastWasNewLine);
1661
rowNode = rowNode->nextListNode;
1662
}
1663
1664
lastWasNewLine = printString("\\end{array}");
1665
lastWasNewLine = newLineIfNecessary(lastWasNewLine);
1666
}
1667
else if (n->nodeType == N_TEXT) {
1668
1669
/*
1670
* handle keywords that might appear in math mode
1671
*/
1672
1673
if ((0 == strcmp(n->data.text, "by")) ||
1674
(0 == strcmp(n->data.text, "if")) ||
1675
(0 == strcmp(n->data.text, "then")) ||
1676
(0 == strcmp(n->data.text, "else"))) {
1677
lastWasNewLine = printString(" \\hbox{ ");
1678
lastWasNewLine = printString(n->data.text);
1679
lastWasNewLine = printString(" } ");
1680
}
1681
1682
/*
1683
* handle things that should be in a special font
1684
*/
1685
1686
else if ((0 == strcmp(n->data.text, "true")) ||
1687
(0 == strcmp(n->data.text, "false")) ||
1688
(0 == strcmp(n->data.text, "table")) ||
1689
(0 == strcmp(n->data.text, "Aleph"))
1690
) {
1691
lastWasNewLine = printString(" \\mbox{\\rm ");
1692
lastWasNewLine = printString(n->data.text);
1693
lastWasNewLine = printString("} ");
1694
}
1695
1696
/*
1697
* handle things that should always be on their own line
1698
*/
1699
1700
else if ((0 == strcmp(n->data.text, "\\\\")) ||
1701
(0 == strcmp(n->data.text, "\\displaystyle"))) {
1702
lastWasNewLine = newLineIfNecessary(lastWasNewLine);
1703
lastWasNewLine = printString(n->data.text);
1704
lastWasNewLine = newLineIfNecessary(lastWasNewLine);
1705
}
1706
1707
/*
1708
* handle phrases that should be on their own line.
1709
*/
1710
1711
else if ((0 == strcmp(n->data.text, "\\begin")) ||
1712
(0 == strcmp(n->data.text, "\\end"))) {
1713
lastWasNewLine = newLineIfNecessary(lastWasNewLine);
1714
lastWasNewLine = printString(n->data.text);
1715
begin = (n->data.text[1] == 'b') ? TRUE : FALSE;
1716
1717
n = n->nextListNode; /* had better be a node */
1718
tmpNode = n->data.node;
1719
lastWasNewLine = printChar('{');
1720
lastWasNewLine = printMathList(tmpNode, lastWasNewLine);
1721
lastWasNewLine = printChar('}');
1722
1723
if (begin) {
1724
1725
/*
1726
* if array, print the argument.
1727
*/
1728
1729
if (0 == strcmp(tmpNode->data.text, "array")) {
1730
n = n->nextListNode; /* had better be a node */
1731
lastWasNewLine = printChar('{');
1732
lastWasNewLine = printMathList(n->data.node, lastWasNewLine);
1733
lastWasNewLine = printChar('}');
1734
}
1735
}
1736
lastWasNewLine = newLineIfNecessary(FALSE);
1737
}
1738
1739
/*
1740
* handle everything else, paying attention as to whether we
1741
* should include a trailing blank.
1742
*/
1743
1744
else {
1745
group = 0;
1746
/* guess whether next word is part of a type */
1747
if ((strlen(n->data.text) > 2) &&
1748
('A' <= n->data.text[0]) &&
1749
('Z' >= n->data.text[0])) {
1750
group = 1;
1751
lastWasNewLine = printString("\\hbox{\\axiomType{");
1752
}
1753
lastWasNewLine = printString(n->data.text);
1754
if (group) {
1755
lastWasNewLine = printString("}\\ }");
1756
group = 0;
1757
}
1758
tmpNode = n->nextListNode;
1759
if ((n->data.text[0] == '_') ||
1760
(n->data.text[0] == '^') ||
1761
(n->data.text[0] == '.') ||
1762
(n->data.text[0] == '(') ||
1763
(0 == strcmp(n->data.text, "\\left")) ||
1764
(0 == strcmp(n->data.text, "\\right")) ||
1765
(0 == strcmp(n->data.text, "\\%")));
1766
else if (tmpNode && (tmpNode->nodeType == N_TEXT)) {
1767
if (((isdigit(n->data.text[0])) &&
1768
(isdigit(tmpNode->data.text[0]))) ||
1769
((isdigit(n->data.text[0])) &&
1770
(',' == tmpNode->data.text[0])) ||
1771
(tmpNode->data.text[0] == '\'') ||
1772
(tmpNode->data.text[0] == '_') ||
1773
(tmpNode->data.text[0] == '^') ||
1774
(tmpNode->data.text[0] == '.') ||
1775
(tmpNode->data.text[0] == ')'));
1776
else
1777
lastWasNewLine = printChar(' ');
1778
}
1779
}
1780
}
1781
n = n->nextListNode;
1782
}
1783
return lastWasNewLine;
1784
}
1785
1786
int
1787
#ifndef _NO_PROTO
1788
printString(char *s)
1789
#else
1790
printString(s)
1791
char *s;
1792
#endif
1793
{
1794
if (s[0]) {
1795
if (!s[1])
1796
return printChar(s[0]);
1797
else {
1798
putsBuf(s,bufout);
1799
charsOut += strlen(s);
1800
}
1801
}
1802
return FALSE;
1803
}
1804
1805
void
1806
#ifndef _NO_PROTO
1807
putsBuf(char s[], char buf[])
1808
#else
1809
putsBuf(s,buf)
1810
char s[], buf[]
1811
#endif
1812
{
1813
strcat(buf,s);
1814
}
1815
1816
void
1817
#ifndef _NO_PROTO
1818
putcBuf(char c,char buf[])
1819
#else
1820
putcBuf(c,buf)
1821
char c, buf[]
1822
#endif
1823
{
1824
char s[2];
1825
1826
s[0] = c;
1827
s[1] = '\0';
1828
strcat(buf,s);
1829
}
1830
1831
listNode *
1832
#ifndef _NO_PROTO
1833
string2NodeList(char *s, listNode * n)
1834
#else
1835
string2NodeList(s,n)
1836
char *s;
1837
listNode * n;
1838
#endif
1839
{
1840
1841
/*
1842
* First argument is string to be broken up, second is a node. Return
1843
* value is last item in list.
1844
*/
1845
1846
mathBufferPtr = 0;
1847
strcpy(mathBuffer, s);
1848
mathBufferLen = strlen(s);
1849
buildMathList(n);
1850
n = n->data.node;
1851
while (n->nextListNode) {
1852
1853
/*
1854
* set width to 0: other funs will have to set for real
1855
*/
1856
n->width = 0;
1857
n = n->nextListNode;
1858
}
1859
n->width = 0;
1860
return n;
1861
}
1862
1863
void
1864
resetCharMults()
1865
{
1866
1867
/*
1868
* this is a ratio by which the standard \mit should be multiplied to get
1869
* other fonts, roughly
1870
*/
1871
1872
charMultNum = charMultDenom = 1;
1873
}
1874
1875
void
1876
ttCharMults()
1877
{
1878
1879
/*
1880
* this is a ratio by which the standard \mit should be multiplied to get
1881
* the \tt font, roughly
1882
*/
1883
1884
charMultNum = 11;
1885
charMultDenom = 10;
1886
}
1887
1888
int
1889
#ifndef _NO_PROTO
1890
charWidth(char c)
1891
#else
1892
charWidth(c)
1893
char c;
1894
#endif
1895
{
1896
return (charMultNum * charTable[c]) / charMultDenom;
1897
}
1898
1899
void
1900
#ifndef _NO_PROTO
1901
#else
1902
#endif
1903
initCharTable()
1904
{
1905
int i;
1906
1907
avgCharWidth = 95; /* where 1000 = 1 inch */
1908
1909
spaceWidths[0] = 51; /* \ */
1910
spaceWidths[1] = 25; /* \, */
1911
spaceWidths[2] = -25; /* \! */
1912
spaceWidths[3] = 37; /* \: */
1913
spaceWidths[4] = 42; /* \; */
1914
1915
extraOverWidth = 33; /* extra space in fraction bar */
1916
1917
sinWidth = 186; /* width of \sin */
1918
cosWidth = 203;
1919
tanWidth = 219;
1920
erfWidth = 185;
1921
1922
for (i = 0; i < 256; i++)
1923
charTable[i] = avgCharWidth;
1924
1925
charTable['!'] = 42;
1926
charTable['"'] = 76;
1927
charTable['%'] = 126;
1928
charTable['('] = 59;
1929
charTable[')'] = 59;
1930
charTable['+'] = 185;
1931
charTable[','] = 42;
1932
charTable['-'] = 185;
1933
charTable['.'] = 42;
1934
charTable['/'] = 76;
1935
charTable['0'] = 76;
1936
charTable['1'] = 76;
1937
charTable['2'] = 76;
1938
charTable['3'] = 76;
1939
charTable['4'] = 76;
1940
charTable['5'] = 76;
1941
charTable['6'] = 76;
1942
charTable['7'] = 76;
1943
charTable['8'] = 76;
1944
charTable['9'] = 76;
1945
charTable[':'] = 42;
1946
charTable[';'] = 42;
1947
charTable['<'] = 202;
1948
charTable['='] = 202;
1949
charTable['>'] = 202;
1950
charTable['A'] = 114;
1951
charTable['B'] = 123;
1952
charTable['C'] = 119;
1953
charTable['D'] = 130;
1954
charTable['E'] = 121;
1955
charTable['F'] = 119;
1956
charTable['G'] = 119;
1957
charTable['H'] = 138;
1958
charTable['I'] = 79;
1959
charTable['J'] = 99;
1960
charTable['K'] = 140;
1961
charTable['L'] = 103;
1962
charTable['M'] = 164;
1963
charTable['N'] = 138;
1964
charTable['O'] = 120;
1965
charTable['P'] = 118;
1966
charTable['Q'] = 120;
1967
charTable['R'] = 116;
1968
charTable['S'] = 102;
1969
charTable['T'] = 110;
1970
charTable['U'] = 120;
1971
charTable['V'] = 122;
1972
charTable['W'] = 164;
1973
charTable['X'] = 137;
1974
charTable['Y'] = 122;
1975
charTable['Z'] = 114;
1976
charTable['['] = 42;
1977
charTable[']'] = 42;
1978
charTable['a'] = 80;
1979
charTable['b'] = 65;
1980
charTable['c'] = 66;
1981
charTable['d'] = 79;
1982
charTable['e'] = 71;
1983
charTable['f'] = 91;
1984
charTable['g'] = 78;
1985
charTable['h'] = 87;
1986
charTable['i'] = 52;
1987
charTable['j'] = 71;
1988
charTable['k'] = 84;
1989
charTable['l'] = 48;
1990
charTable['m'] = 133;
1991
charTable['n'] = 91;
1992
charTable['o'] = 73;
1993
charTable['p'] = 76;
1994
charTable['q'] = 73;
1995
charTable['r'] = 73;
1996
charTable['s'] = 71;
1997
charTable['t'] = 55;
1998
charTable['u'] = 87;
1999
charTable['v'] = 79;
2000
charTable['w'] = 113;
2001
charTable['x'] = 87;
2002
charTable['y'] = 80;
2003
charTable['z'] = 77;
2004
charTable['{'] = 76;
2005
charTable['|'] = 42;
2006
charTable['}'] = 76;
2007
}
2008
2009