]> git.ipfire.org Git - thirdparty/cups.git/blob - filter/textcommon.c
Import cups.org releases
[thirdparty/cups.git] / filter / textcommon.c
1 /*
2 * "$Id$"
3 *
4 * Common text filter routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-1999 by Easy Software Products.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * TextMain() - Standard main entry for text filters.
27 * compare_keywords() - Compare two C/C++ keywords.
28 * getutf8() - Get a UTF-8 encoded wide character...
29 */
30
31 /*
32 * Include necessary headers...
33 */
34
35 #include "textcommon.h"
36
37
38 /*
39 * Globals...
40 */
41
42 int WrapLines = 1, /* Wrap text in lines */
43 SizeLines = 60, /* Number of lines on a page */
44 SizeColumns = 80, /* Number of columns on a line */
45 PageColumns = 1, /* Number of columns on a page */
46 ColumnGutter = 0, /* Number of characters between text columns */
47 ColumnWidth = 80, /* Width of each column */
48 PrettyPrint = 0, /* Do pretty code formatting */
49 Copies = 1; /* Number of copies */
50 lchar_t **Page = NULL; /* Page characters */
51 int NumPages = 0; /* Number of pages in document */
52 int CharsPerInch = 10; /* Number of character columns per inch */
53 int LinesPerInch = 6; /* Number of lines per inch */
54 int UTF8 = 0; /* Use UTF-8 encoding? */
55 char *Keywords[] = /* List of known keywords... */
56 {
57 "and",
58 "and_eq",
59 "asm",
60 "auto",
61 "bitand",
62 "bitor",
63 "bool",
64 "break",
65 "case",
66 "catch",
67 "char",
68 "class",
69 "compl",
70 "const",
71 "continue",
72 "default",
73 "delete",
74 "do",
75 "double",
76 "else",
77 "enum",
78 "explicit",
79 "extern",
80 "false",
81 "float",
82 "for",
83 "friend",
84 "goto",
85 "if",
86 "inline",
87 "int",
88 "long",
89 "mutable",
90 "namespace",
91 "new",
92 "not",
93 "not_eq",
94 "operator",
95 "or",
96 "or_eq",
97 "private",
98 "protected",
99 "public",
100 "register",
101 "return",
102 "short",
103 "signed",
104 "sizeof",
105 "static",
106 "struct",
107 "switch",
108 "template",
109 "this",
110 "throw",
111 "true",
112 "try",
113 "typedef",
114 "typename",
115 "union",
116 "unsigned",
117 "virtual",
118 "void",
119 "volatile",
120 "while",
121 "xor",
122 "xor_eq"
123 };
124
125
126 /*
127 * Local functions...
128 */
129
130 static int compare_keywords(const void *, const void *);
131 static int getutf8(FILE *fp);
132
133
134 /*
135 * 'TextMain()' - Standard main entry for text filters.
136 */
137
138 int /* O - Exit status */
139 TextMain(char *name, /* I - Name of filter */
140 int argc, /* I - Number of command-line arguments */
141 char *argv[]) /* I - Command-line arguments */
142 {
143 FILE *fp; /* Print file */
144 ppd_file_t *ppd; /* PPD file */
145 int i, /* Looping var */
146 ch, /* Current char from file */
147 lastch, /* Previous char from file */
148 attr, /* Current attribute */
149 line, /* Current line */
150 column, /* Current column */
151 page_column; /* Current page column */
152 int num_options; /* Number of print options */
153 cups_option_t *options; /* Print options */
154 const char *val; /* Option value */
155 char keyword[64], /* Keyword string */
156 *keyptr; /* Pointer into string */
157 int keycol; /* Column where keyword starts */
158 int ccomment; /* Inside a C-style comment? */
159 int cstring; /* Inside a C string */
160
161
162 if (argc < 6 || argc > 7)
163 {
164 fprintf(stderr, "ERROR: %s job-id user title copies options [file]\n",
165 name);
166 return (1);
167 }
168
169 /*
170 * If we have 7 arguments, print the file named on the command-line.
171 * Otherwise, send stdin instead...
172 */
173
174 if (argc == 6)
175 fp = stdin;
176 else
177 {
178 /*
179 * Try to open the print file...
180 */
181
182 if ((fp = fopen(argv[6], "rb")) == NULL)
183 {
184 perror("ERROR: unable to open print file - ");
185 return (1);
186 }
187 }
188
189 /*
190 * Process command-line options and write the prolog...
191 */
192
193 options = NULL;
194 num_options = cupsParseOptions(argv[5], 0, &options);
195
196 if ((val = cupsGetOption("prettyprint", num_options, options)) != NULL)
197 {
198 PrettyPrint = 1;
199 PageLeft = 72.0f;
200 PageRight = PageWidth - 36.0f;
201 PageBottom = PageBottom > 36.0f ? PageBottom : 36.0f;
202 PageTop = PageLength - 36.0f;
203 CharsPerInch = 12;
204 LinesPerInch = 8;
205 }
206
207 if ((ppd = SetCommonOptions(num_options, options, 1)) != NULL)
208 ppdClose(ppd);
209
210 WrapLines = cupsGetOption("nowrap", num_options, options) == NULL;
211
212 if ((val = cupsGetOption("columns", num_options, options)) != NULL)
213 PageColumns = atoi(val);
214
215 if ((val = cupsGetOption("cpi", num_options, options)) != NULL)
216 CharsPerInch = atoi(val);
217
218 if ((val = cupsGetOption("lpi", num_options, options)) != NULL)
219 LinesPerInch = atoi(val);
220
221 if ((val = cupsGetOption("prettyprint", num_options, options)) != NULL)
222 PageTop -= 216.0f / LinesPerInch;
223
224 Copies = atoi(argv[4]);
225
226 WriteProlog(argv[3], argv[2], ppd);
227
228 /*
229 * Read text from the specified source and print it...
230 */
231
232 column = 0;
233 line = 0;
234 page_column = 0;
235 attr = 0;
236 keyptr = keyword;
237 keycol = 0;
238 ccomment = 0;
239 cstring = 0;
240
241 while ((ch = getutf8(fp)) >= 0)
242 {
243 /*
244 * Control codes:
245 *
246 * BS Backspace (0x08)
247 * HT Horizontal tab; next 8th column (0x09)
248 * LF Line feed; forward full line (0x0a)
249 * VT Vertical tab; reverse full line (0x0b)
250 * FF Form feed (0x0c)
251 * CR Carriage return (0x0d)
252 * ESC 7 Reverse full line (0x1b 0x37)
253 * ESC 8 Reverse half line (0x1b 0x38)
254 * ESC 9 Forward half line (0x1b 0x39)
255 */
256
257 switch (ch)
258 {
259 case 0x08 : /* BS - backspace for boldface & underline */
260 if (column > 0)
261 column --;
262
263 keyptr = keyword;
264 keycol = column;
265 break;
266
267 case 0x09 : /* HT - tab to next 8th column */
268 if (PrettyPrint && keyptr > keyword)
269 {
270 *keyptr = '\0';
271 keyptr = keyword;
272
273 if (bsearch(&keyptr, Keywords, sizeof(Keywords) / sizeof(Keywords[0]),
274 sizeof(Keywords[0]), compare_keywords))
275 {
276 /*
277 * Put keywords in boldface...
278 */
279
280 i = page_column * (ColumnWidth + ColumnGutter);
281
282 while (keycol < column)
283 {
284 Page[line][keycol + i].attr |= ATTR_BOLD;
285 keycol ++;
286 }
287 }
288 }
289
290 column = (column + 8) & ~7;
291
292 if (column >= ColumnWidth && WrapLines)
293 { /* Wrap text to margins */
294 line ++;
295 column = 0;
296
297 if (line >= SizeLines)
298 {
299 page_column ++;
300 line = 0;
301
302 if (page_column >= PageColumns)
303 {
304 WritePage();
305 page_column = 0;
306 }
307 }
308 }
309
310 keycol = column;
311 break;
312
313 case 0x0a : /* LF - output current line */
314 if (PrettyPrint && keyptr > keyword)
315 {
316 *keyptr = '\0';
317 keyptr = keyword;
318
319 if (bsearch(&keyptr, Keywords, sizeof(Keywords) / sizeof(Keywords[0]),
320 sizeof(Keywords[0]), compare_keywords))
321 {
322 /*
323 * Put keywords in boldface...
324 */
325
326 i = page_column * (ColumnWidth + ColumnGutter);
327
328 while (keycol < column)
329 {
330 Page[line][keycol + i].attr |= ATTR_BOLD;
331 keycol ++;
332 }
333 }
334 }
335
336 line ++;
337 column = 0;
338 keycol = 0;
339
340 if (!ccomment && !cstring)
341 attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | ATTR_BLUE);
342
343 if (line >= SizeLines)
344 {
345 page_column ++;
346 line = 0;
347
348 if (page_column >= PageColumns)
349 {
350 WritePage();
351 page_column = 0;
352 }
353 }
354 break;
355
356 case 0x0b : /* VT - move up 1 line */
357 if (line > 0)
358 line --;
359
360 keyptr = keyword;
361 keycol = column;
362
363 if (!ccomment && !cstring)
364 attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | ATTR_BLUE);
365 break;
366
367 case 0x0c : /* FF - eject current page... */
368 if (PrettyPrint && keyptr > keyword)
369 {
370 *keyptr = '\0';
371 keyptr = keyword;
372
373 if (bsearch(&keyptr, Keywords, sizeof(Keywords) / sizeof(Keywords[0]),
374 sizeof(Keywords[0]), compare_keywords))
375 {
376 /*
377 * Put keywords in boldface...
378 */
379
380 i = page_column * (ColumnWidth + ColumnGutter);
381
382 while (keycol < column)
383 {
384 Page[line][keycol + i].attr |= ATTR_BOLD;
385 keycol ++;
386 }
387 }
388 }
389
390 page_column ++;
391 column = 0;
392 keycol = 0;
393 line = 0;
394
395 if (!ccomment && !cstring)
396 attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | ATTR_BLUE);
397
398 if (page_column >= PageColumns)
399 {
400 WritePage();
401 page_column = 0;
402 }
403 break;
404
405 case 0x0d : /* CR */
406 column = 0;
407 break;
408
409 case 0x1b : /* Escape sequence */
410 ch = getutf8(fp);
411 if (ch == '7')
412 {
413 /*
414 * ESC 7 Reverse full line (0x1b 0x37)
415 */
416
417 if (line > 0)
418 line --;
419 }
420 else if (ch == '8')
421 {
422 /*
423 * ESC 8 Reverse half line (0x1b 0x38)
424 */
425
426 if ((attr & ATTR_RAISED) && line > 0)
427 {
428 attr &= ~ATTR_RAISED;
429 line --;
430 }
431 else if (attr & ATTR_LOWERED)
432 attr &= ~ATTR_LOWERED;
433 else
434 attr |= ATTR_RAISED;
435 }
436 else if (ch == '9')
437 {
438 /*
439 * ESC 9 Forward half line (0x1b 0x39)
440 */
441
442 if ((attr & ATTR_LOWERED) && line < (SizeLines - 1))
443 {
444 attr &= ~ATTR_LOWERED;
445 line ++;
446 }
447 else if (attr & ATTR_RAISED)
448 attr &= ~ATTR_RAISED;
449 else
450 attr |= ATTR_LOWERED;
451 }
452 break;
453
454 default : /* All others... */
455 if (ch < ' ')
456 break; /* Ignore other control chars */
457
458 if (PrettyPrint)
459 {
460 /*
461 * Do highlighting of C/C++ keywords, preprocessor commands,
462 * and comments...
463 */
464
465 if ((ch == ' ' || ch == '\t') && (attr & ATTR_BOLD))
466 {
467 /*
468 * Stop bolding preprocessor command...
469 */
470
471 attr &= ~ATTR_BOLD;
472 }
473 else if (!(isalnum(ch) || ch == '_') && keyptr > keyword)
474 {
475 /*
476 * Look for a keyword...
477 */
478
479 *keyptr = '\0';
480 keyptr = keyword;
481
482 if (!(attr & ATTR_ITALIC) &&
483 bsearch(&keyptr, Keywords, sizeof(Keywords) / sizeof(Keywords[0]),
484 sizeof(Keywords[0]), compare_keywords))
485 {
486 /*
487 * Put keywords in boldface...
488 */
489
490 i = page_column * (ColumnWidth + ColumnGutter);
491
492 while (keycol < column)
493 {
494 Page[line][keycol + i].attr |= ATTR_BOLD;
495 keycol ++;
496 }
497 }
498 }
499 else if ((isalnum(ch) || ch == '_') && !ccomment && !cstring)
500 {
501 /*
502 * Add characters to the current keyword (if they'll fit).
503 */
504
505 if (keyptr == keyword)
506 keycol = column;
507
508 if (keyptr < (keyword + sizeof(keyword) - 1))
509 *keyptr++ = ch;
510 }
511 else if (ch == '\"' && lastch != '\\' && !ccomment && !cstring)
512 {
513 /*
514 * Start a C string constant...
515 */
516
517 cstring = -1;
518 attr |= ATTR_BLUE;
519 }
520 else if (ch == '*' && lastch == '/' && !cstring)
521 {
522 /*
523 * Start a C-style comment...
524 */
525
526 ccomment = 1;
527 attr |= ATTR_ITALIC | ATTR_GREEN;
528 }
529 else if (ch == '/' && lastch == '/' && !cstring)
530 {
531 /*
532 * Start a C++-style comment...
533 */
534
535 attr |= ATTR_ITALIC | ATTR_GREEN;
536 }
537 else if (ch == '#' && column == 0 && !ccomment && !cstring)
538 {
539 /*
540 * Start a preprocessor command...
541 */
542
543 attr |= ATTR_BOLD | ATTR_RED;
544 }
545 }
546
547 if (column >= ColumnWidth && WrapLines)
548 { /* Wrap text to margins */
549 column = 0;
550 line ++;
551
552 if (line >= SizeLines)
553 {
554 page_column ++;
555 line = 0;
556
557 if (page_column >= PageColumns)
558 {
559 WritePage();
560 page_column = 0;
561 }
562 }
563 }
564
565 /*
566 * Add text to the current column & line...
567 */
568
569 if (column < ColumnWidth)
570 {
571 i = column + page_column * (ColumnWidth + ColumnGutter);
572
573 if (PrettyPrint)
574 Page[line][i].attr = attr;
575 else if (ch == Page[line][i].ch)
576 Page[line][i].attr |= ATTR_BOLD;
577 else if (Page[line][i].ch == '_')
578 Page[line][i].attr |= ATTR_UNDERLINE;
579 else
580 Page[line][i].attr = attr;
581
582 Page[line][i].ch = ch;
583 }
584
585 if (PrettyPrint)
586 {
587 if ((ch == '{' || ch == '}') && !ccomment && !cstring &&
588 column < ColumnWidth)
589 {
590 /*
591 * Highlight curley braces...
592 */
593
594 Page[line][i].attr |= ATTR_BOLD;
595 }
596 else if ((ch == '/' || ch == '*') && lastch == '/' &&
597 column < ColumnWidth)
598 {
599 /*
600 * Highlight first comment character...
601 */
602
603 Page[line][i - 1].attr = attr;
604 }
605 else if (ch == '\"' && lastch != '\\' && !ccomment && cstring > 0)
606 {
607 /*
608 * End a C string constant...
609 */
610
611 cstring = 0;
612 attr &= ~ATTR_BLUE;
613 }
614 else if (ch == '/' && lastch == '*' && ccomment)
615 {
616 /*
617 * End a C-style comment...
618 */
619
620 ccomment = 0;
621 attr &= ~(ATTR_ITALIC | ATTR_GREEN);
622 }
623
624 if (cstring < 0)
625 cstring = 1;
626 }
627
628 column ++;
629 break;
630 }
631
632 /*
633 * Save this character for the next cycle.
634 */
635
636 lastch = ch;
637 }
638
639 /*
640 * Write any remaining page data...
641 */
642
643 if (line > 0 || page_column > 0 || column > 0)
644 WritePage();
645
646 /*
647 * Write the epilog and return...
648 */
649
650 WriteEpilogue();
651
652 return (0);
653 }
654
655
656 /*
657 * 'compare_keywords()' - Compare two C/C++ keywords.
658 */
659
660 static int /* O - Result of strcmp */
661 compare_keywords(const void *k1, /* I - First keyword */
662 const void *k2) /* I - Second keyword */
663 {
664 return (strcmp(*((const char **)k1), *((const char **)k2)));
665 }
666
667
668 /*
669 * 'getutf8()' - Get a UTF-8 encoded wide character...
670 */
671
672 static int /* O - Character or -1 on error */
673 getutf8(FILE *fp) /* I - File to read from */
674 {
675 int ch; /* Current character value */
676 int next; /* Next character from file */
677
678
679 /*
680 * Read the first character and process things accordingly...
681 *
682 * UTF-8 maps 16-bit characters to:
683 *
684 * 0 to 127 = 0xxxxxxx
685 * 128 to 2047 = 110xxxxx 10yyyyyy (xxxxxyyyyyy)
686 * 2048 to 65535 = 1110xxxx 10yyyyyy 10zzzzzz (xxxxyyyyyyzzzzzz)
687 *
688 * We also accept:
689 *
690 * 128 to 191 = 10xxxxxx
691 *
692 * since this range of values is otherwise undefined unless you are
693 * in the middle of a multi-byte character...
694 *
695 * This code currently does not support anything beyond 16-bit
696 * characters, in part because PostScript doesn't support more than
697 * 16-bit characters...
698 */
699
700 if ((ch = getc(fp)) == EOF)
701 return (EOF);
702
703 if (ch < 0xc0 || !UTF8) /* One byte character? */
704 return (ch);
705 else if ((ch & 0xe0) == 0xc0)
706 {
707 /*
708 * Two byte character...
709 */
710
711 if ((next = getc(fp)) == EOF)
712 return (EOF);
713 else
714 return (((ch & 0x1f) << 6) | (next & 0x3f));
715 }
716 else if ((ch & 0xf0) == 0xe0)
717 {
718 /*
719 * Three byte character...
720 */
721
722 if ((next = getc(fp)) == EOF)
723 return (EOF);
724
725 ch = ((ch & 0x0f) << 6) | (next & 0x3f);
726
727 if ((next = getc(fp)) == EOF)
728 return (EOF);
729 else
730 return ((ch << 6) | (next & 0x3f));
731 }
732 else
733 {
734 /*
735 * More than three bytes... We don't support that...
736 */
737
738 return (EOF);
739 }
740 }
741
742
743 /*
744 * End of "$Id$".
745 */