]> git.ipfire.org Git - thirdparty/cups.git/blob - filter/textcommon.c
Merge changes from CUPS 1.5.1-r9875.
[thirdparty/cups.git] / filter / textcommon.c
1 /*
2 * "$Id: textcommon.c 6649 2007-07-11 21:46:42Z mike $"
3 *
4 * Common text filter routines for CUPS.
5 *
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 * TextMain() - Standard main entry for text filters.
20 * compare_keywords() - Compare two C/C++ keywords.
21 * getutf8() - Get a UTF-8 encoded wide character...
22 */
23
24 /*
25 * Include necessary headers...
26 */
27
28 #include "textcommon.h"
29 #include <cups/language-private.h>
30
31
32 /*
33 * Globals...
34 */
35
36 int WrapLines = 1, /* Wrap text in lines */
37 SizeLines = 60, /* Number of lines on a page */
38 SizeColumns = 80, /* Number of columns on a line */
39 PageColumns = 1, /* Number of columns on a page */
40 ColumnGutter = 0, /* Number of characters between text columns */
41 ColumnWidth = 80, /* Width of each column */
42 PrettyPrint = 0, /* Do pretty code formatting */
43 Copies = 1; /* Number of copies */
44 lchar_t **Page = NULL; /* Page characters */
45 int NumPages = 0; /* Number of pages in document */
46 float CharsPerInch = 10; /* Number of character columns per inch */
47 float LinesPerInch = 6; /* Number of lines per inch */
48 int NumKeywords = 0; /* Number of known keywords */
49 char **Keywords = NULL; /* List of known keywords */
50
51
52 /*
53 * Local globals...
54 */
55
56 static char *code_keywords[] = /* List of known C/C++ keywords... */
57 {
58 "and",
59 "and_eq",
60 "asm",
61 "auto",
62 "bitand",
63 "bitor",
64 "bool",
65 "break",
66 "case",
67 "catch",
68 "char",
69 "class",
70 "compl",
71 "const",
72 "const_cast",
73 "continue",
74 "default",
75 "delete",
76 "do",
77 "double",
78 "dynamic_cast",
79 "else",
80 "enum",
81 "explicit",
82 "extern",
83 "false",
84 "float",
85 "for",
86 "friend",
87 "goto",
88 "if",
89 "inline",
90 "int",
91 "long",
92 "mutable",
93 "namespace",
94 "new",
95 "not",
96 "not_eq",
97 "operator",
98 "or",
99 "or_eq",
100 "private",
101 "protected",
102 "public",
103 "register",
104 "reinterpret_cast",
105 "return",
106 "short",
107 "signed",
108 "sizeof",
109 "static",
110 "static_cast",
111 "struct",
112 "switch",
113 "template",
114 "this",
115 "throw",
116 "true",
117 "try",
118 "typedef",
119 "typename",
120 "union",
121 "unsigned",
122 "virtual",
123 "void",
124 "volatile",
125 "while",
126 "xor",
127 "xor_eq"
128 },
129 *sh_keywords[] = /* List of known Boure/Korn/zsh/bash keywords... */
130 {
131 "alias",
132 "bg",
133 "break",
134 "case",
135 "cd",
136 "command",
137 "continue",
138 "do",
139 "done",
140 "echo",
141 "elif",
142 "else",
143 "esac",
144 "eval",
145 "exec",
146 "exit",
147 "export",
148 "fc",
149 "fg",
150 "fi",
151 "for",
152 "function",
153 "getopts",
154 "if",
155 "in",
156 "jobs",
157 "kill",
158 "let",
159 "limit",
160 "newgrp",
161 "print",
162 "pwd",
163 "read",
164 "readonly",
165 "return",
166 "select",
167 "set",
168 "shift",
169 "test",
170 "then",
171 "time",
172 "times",
173 "trap",
174 "typeset",
175 "ulimit",
176 "umask",
177 "unalias",
178 "unlimit",
179 "unset",
180 "until",
181 "wait",
182 "whence"
183 "while",
184 },
185 *csh_keywords[] = /* List of known csh/tcsh keywords... */
186 {
187 "alias",
188 "aliases",
189 "bg",
190 "bindkey",
191 "break",
192 "breaksw",
193 "builtins",
194 "case",
195 "cd",
196 "chdir",
197 "complete",
198 "continue",
199 "default",
200 "dirs",
201 "echo",
202 "echotc",
203 "else",
204 "end",
205 "endif",
206 "eval",
207 "exec",
208 "exit",
209 "fg",
210 "foreach",
211 "glob",
212 "goto",
213 "history",
214 "if",
215 "jobs",
216 "kill",
217 "limit",
218 "login",
219 "logout",
220 "ls",
221 "nice",
222 "nohup",
223 "notify",
224 "onintr",
225 "popd",
226 "pushd",
227 "pwd",
228 "rehash",
229 "repeat",
230 "set",
231 "setenv",
232 "settc",
233 "shift",
234 "source",
235 "stop",
236 "suspend",
237 "switch",
238 "telltc",
239 "then",
240 "time",
241 "umask",
242 "unalias",
243 "unbindkey",
244 "unhash",
245 "unlimit",
246 "unset",
247 "unsetenv",
248 "wait",
249 "where",
250 "which",
251 "while"
252 },
253 *perl_keywords[] = /* List of known perl keywords... */
254 {
255 "abs",
256 "accept",
257 "alarm",
258 "and",
259 "atan2",
260 "bind",
261 "binmode",
262 "bless",
263 "caller",
264 "chdir",
265 "chmod",
266 "chomp",
267 "chop",
268 "chown",
269 "chr",
270 "chroot",
271 "closdir",
272 "close",
273 "connect",
274 "continue",
275 "cos",
276 "crypt",
277 "dbmclose",
278 "dbmopen",
279 "defined",
280 "delete",
281 "die",
282 "do",
283 "dump",
284 "each",
285 "else",
286 "elsif",
287 "endgrent",
288 "endhostent",
289 "endnetent",
290 "endprotoent",
291 "endpwent",
292 "endservent",
293 "eof",
294 "eval",
295 "exec",
296 "exists",
297 "exit",
298 "exp",
299 "fcntl",
300 "fileno",
301 "flock",
302 "for",
303 "foreach",
304 "fork",
305 "format",
306 "formline",
307 "getc",
308 "getgrent",
309 "getgrgid",
310 "getgrnam",
311 "gethostbyaddr",
312 "gethostbyname",
313 "gethostent",
314 "getlogin",
315 "getnetbyaddr",
316 "getnetbyname",
317 "getnetent",
318 "getpeername",
319 "getpgrp",
320 "getppid",
321 "getpriority",
322 "getprotobyname",
323 "getprotobynumber",
324 "getprotoent",
325 "getpwent",
326 "getpwnam",
327 "getpwuid",
328 "getservbyname",
329 "getservbyport",
330 "getservent",
331 "getsockname",
332 "getsockopt",
333 "glob",
334 "gmtime",
335 "goto",
336 "grep",
337 "hex",
338 "if",
339 "import",
340 "index",
341 "int",
342 "ioctl",
343 "join",
344 "keys",
345 "kill",
346 "last",
347 "lc",
348 "lcfirst",
349 "length",
350 "link",
351 "listen",
352 "local",
353 "localtime",
354 "log",
355 "lstat",
356 "map",
357 "mkdir",
358 "msgctl",
359 "msgget",
360 "msgrcv",
361 "msgsend",
362 "my",
363 "next",
364 "no",
365 "not",
366 "oct",
367 "open",
368 "opendir",
369 "or",
370 "ord",
371 "pack",
372 "package",
373 "pipe",
374 "pop",
375 "pos",
376 "print",
377 "printf",
378 "push",
379 "quotemeta",
380 "rand",
381 "read",
382 "readdir",
383 "readlink",
384 "recv",
385 "redo",
386 "ref",
387 "rename",
388 "require",
389 "reset",
390 "return",
391 "reverse",
392 "rewinddir",
393 "rindex",
394 "rmdir",
395 "scalar",
396 "seek",
397 "seekdir",
398 "select",
399 "semctl",
400 "semget",
401 "semop",
402 "send",
403 "setgrent",
404 "sethostent",
405 "setnetent",
406 "setpgrp",
407 "setpriority",
408 "setprotoent",
409 "setpwent",
410 "setservent",
411 "setsockopt",
412 "shift",
413 "shmctl",
414 "shmget",
415 "shmread",
416 "shmwrite",
417 "shutdown",
418 "sin",
419 "sleep",
420 "socket",
421 "socketpair",
422 "sort",
423 "splice",
424 "split",
425 "sprintf",
426 "sqrt",
427 "srand",
428 "stat",
429 "study",
430 "sub",
431 "substr",
432 "symlink",
433 "syscall",
434 "sysread",
435 "sysseek",
436 "system",
437 "syswrite",
438 "tell",
439 "telldir",
440 "tie",
441 "tied",
442 "time",
443 "times"
444 "times",
445 "truncate",
446 "uc",
447 "ucfirst",
448 "umask",
449 "undef",
450 "unless",
451 "unlink",
452 "unpack",
453 "unshift",
454 "untie",
455 "until",
456 "use",
457 "utime",
458 "values",
459 "vec",
460 "wait",
461 "waitpid",
462 "wantarray",
463 "warn",
464 "while",
465 "write"
466 };
467
468
469 /*
470 * Local functions...
471 */
472
473 static int compare_keywords(const void *, const void *);
474 static int getutf8(FILE *fp);
475
476
477 /*
478 * 'TextMain()' - Standard main entry for text filters.
479 */
480
481 int /* O - Exit status */
482 TextMain(const char *name, /* I - Name of filter */
483 int argc, /* I - Number of command-line arguments */
484 char *argv[]) /* I - Command-line arguments */
485 {
486 FILE *fp; /* Print file */
487 ppd_file_t *ppd; /* PPD file */
488 int i, /* Looping var */
489 ch, /* Current char from file */
490 lastch, /* Previous char from file */
491 attr, /* Current attribute */
492 line, /* Current line */
493 column, /* Current column */
494 page_column; /* Current page column */
495 int num_options; /* Number of print options */
496 cups_option_t *options; /* Print options */
497 const char *val; /* Option value */
498 char keyword[64], /* Keyword string */
499 *keyptr; /* Pointer into string */
500 int keycol; /* Column where keyword starts */
501 int ccomment; /* Inside a C-style comment? */
502 int cstring; /* Inside a C string */
503
504
505 /*
506 * Make sure status messages are not buffered...
507 */
508
509 setbuf(stderr, NULL);
510
511 /*
512 * Check command-line...
513 */
514
515 if (argc < 6 || argc > 7)
516 {
517 _cupsLangPrintf(stderr,
518 _("Usage: %s job-id user title copies options [file]"),
519 name);
520 return (1);
521 }
522
523 /*
524 * If we have 7 arguments, print the file named on the command-line.
525 * Otherwise, send stdin instead...
526 */
527
528 if (argc == 6)
529 fp = stdin;
530 else
531 {
532 /*
533 * Try to open the print file...
534 */
535
536 if ((fp = fopen(argv[6], "rb")) == NULL)
537 {
538 perror("DEBUG: unable to open print file - ");
539 return (1);
540 }
541 }
542
543 /*
544 * Process command-line options and write the prolog...
545 */
546
547 options = NULL;
548 num_options = cupsParseOptions(argv[5], 0, &options);
549
550 if ((val = cupsGetOption("prettyprint", num_options, options)) != NULL &&
551 _cups_strcasecmp(val, "no") && _cups_strcasecmp(val, "off") &&
552 _cups_strcasecmp(val, "false"))
553 {
554 PageLeft = 72.0f;
555 PageRight = PageWidth - 36.0f;
556 PageBottom = PageBottom > 36.0f ? PageBottom : 36.0f;
557 PageTop = PageLength - 36.0f;
558 CharsPerInch = 12;
559 LinesPerInch = 8;
560
561 if ((val = getenv("CONTENT_TYPE")) == NULL)
562 {
563 PrettyPrint = PRETTY_PLAIN;
564 NumKeywords = 0;
565 Keywords = NULL;
566 }
567 else if (_cups_strcasecmp(val, "application/x-cshell") == 0)
568 {
569 PrettyPrint = PRETTY_SHELL;
570 NumKeywords = sizeof(csh_keywords) / sizeof(csh_keywords[0]);
571 Keywords = csh_keywords;
572 }
573 else if (_cups_strcasecmp(val, "application/x-csource") == 0)
574 {
575 PrettyPrint = PRETTY_CODE;
576 NumKeywords = sizeof(code_keywords) / sizeof(code_keywords[0]);
577 Keywords = code_keywords;
578 }
579 else if (_cups_strcasecmp(val, "application/x-perl") == 0)
580 {
581 PrettyPrint = PRETTY_PERL;
582 NumKeywords = sizeof(perl_keywords) / sizeof(perl_keywords[0]);
583 Keywords = perl_keywords;
584 }
585 else if (_cups_strcasecmp(val, "application/x-shell") == 0)
586 {
587 PrettyPrint = PRETTY_SHELL;
588 NumKeywords = sizeof(sh_keywords) / sizeof(sh_keywords[0]);
589 Keywords = sh_keywords;
590 }
591 else
592 {
593 PrettyPrint = PRETTY_PLAIN;
594 NumKeywords = 0;
595 Keywords = NULL;
596 }
597 }
598
599 ppd = SetCommonOptions(num_options, options, 1);
600
601 if ((val = cupsGetOption("wrap", num_options, options)) == NULL)
602 WrapLines = 1;
603 else
604 WrapLines = !_cups_strcasecmp(val, "true") || !_cups_strcasecmp(val, "on") ||
605 !_cups_strcasecmp(val, "yes");
606
607 if ((val = cupsGetOption("columns", num_options, options)) != NULL)
608 {
609 PageColumns = atoi(val);
610
611 if (PageColumns < 1)
612 {
613 _cupsLangPrintFilter(stderr, "ERROR", _("Bad columns value %d."),
614 PageColumns);
615 return (1);
616 }
617 }
618
619 if ((val = cupsGetOption("cpi", num_options, options)) != NULL)
620 {
621 CharsPerInch = atof(val);
622
623 if (CharsPerInch <= 0.0)
624 {
625 _cupsLangPrintFilter(stderr, "ERROR", _("Bad cpi value %f."),
626 CharsPerInch);
627 return (1);
628 }
629 }
630
631 if ((val = cupsGetOption("lpi", num_options, options)) != NULL)
632 {
633 LinesPerInch = atof(val);
634
635 if (LinesPerInch <= 0.0)
636 {
637 _cupsLangPrintFilter(stderr, "ERROR", _("Bad lpi value %f."),
638 LinesPerInch);
639 return (1);
640 }
641 }
642
643 if (PrettyPrint)
644 PageTop -= 216.0f / LinesPerInch;
645
646 Copies = atoi(argv[4]);
647
648 WriteProlog(argv[3], argv[2], getenv("CLASSIFICATION"),
649 cupsGetOption("page-label", num_options, options), ppd);
650
651 /*
652 * Read text from the specified source and print it...
653 */
654
655 lastch = 0;
656 column = 0;
657 line = 0;
658 page_column = 0;
659 attr = 0;
660 keyptr = keyword;
661 keycol = 0;
662 ccomment = 0;
663 cstring = 0;
664
665 while ((ch = getutf8(fp)) >= 0)
666 {
667 /*
668 * Control codes:
669 *
670 * BS Backspace (0x08)
671 * HT Horizontal tab; next 8th column (0x09)
672 * LF Line feed; forward full line (0x0a)
673 * VT Vertical tab; reverse full line (0x0b)
674 * FF Form feed (0x0c)
675 * CR Carriage return (0x0d)
676 * ESC 7 Reverse full line (0x1b 0x37)
677 * ESC 8 Reverse half line (0x1b 0x38)
678 * ESC 9 Forward half line (0x1b 0x39)
679 */
680
681 switch (ch)
682 {
683 case 0x08 : /* BS - backspace for boldface & underline */
684 if (column > 0)
685 column --;
686
687 keyptr = keyword;
688 keycol = column;
689 break;
690
691 case 0x09 : /* HT - tab to next 8th column */
692 if (PrettyPrint && keyptr > keyword)
693 {
694 *keyptr = '\0';
695 keyptr = keyword;
696
697 if (bsearch(&keyptr, Keywords, NumKeywords, sizeof(char *),
698 compare_keywords))
699 {
700 /*
701 * Put keywords in boldface...
702 */
703
704 i = page_column * (ColumnWidth + ColumnGutter);
705
706 while (keycol < column)
707 {
708 Page[line][keycol + i].attr |= ATTR_BOLD;
709 keycol ++;
710 }
711 }
712 }
713
714 column = (column + 8) & ~7;
715
716 if (column >= ColumnWidth && WrapLines)
717 { /* Wrap text to margins */
718 line ++;
719 column = 0;
720
721 if (line >= SizeLines)
722 {
723 page_column ++;
724 line = 0;
725
726 if (page_column >= PageColumns)
727 {
728 WritePage();
729 page_column = 0;
730 }
731 }
732 }
733
734 keycol = column;
735
736 attr &= ~ATTR_BOLD;
737 break;
738
739 case 0x0d : /* CR */
740 #ifndef __APPLE__
741 /*
742 * All but MacOS/Darwin treat CR as was intended by ANSI
743 * folks, namely to move to column 0/1. Some programs still
744 * use this to do boldfacing and underlining...
745 */
746
747 column = 0;
748 break;
749 #else
750 /*
751 * MacOS/Darwin still need to treat CR as a line ending.
752 */
753
754 {
755 int nextch;
756 if ((nextch = getc(fp)) != 0x0a)
757 ungetc(nextch, fp);
758 else
759 ch = nextch;
760 }
761 #endif /* !__APPLE__ */
762
763 case 0x0a : /* LF - output current line */
764 if (PrettyPrint && keyptr > keyword)
765 {
766 *keyptr = '\0';
767 keyptr = keyword;
768
769 if (bsearch(&keyptr, Keywords, NumKeywords, sizeof(char *),
770 compare_keywords))
771 {
772 /*
773 * Put keywords in boldface...
774 */
775
776 i = page_column * (ColumnWidth + ColumnGutter);
777
778 while (keycol < column)
779 {
780 Page[line][keycol + i].attr |= ATTR_BOLD;
781 keycol ++;
782 }
783 }
784 }
785
786 line ++;
787 column = 0;
788 keycol = 0;
789
790 if (!ccomment && !cstring)
791 attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | ATTR_BLUE);
792
793 if (line >= SizeLines)
794 {
795 page_column ++;
796 line = 0;
797
798 if (page_column >= PageColumns)
799 {
800 WritePage();
801 page_column = 0;
802 }
803 }
804 break;
805
806 case 0x0b : /* VT - move up 1 line */
807 if (line > 0)
808 line --;
809
810 keyptr = keyword;
811 keycol = column;
812
813 if (!ccomment && !cstring)
814 attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | ATTR_BLUE);
815 break;
816
817 case 0x0c : /* FF - eject current page... */
818 if (PrettyPrint && keyptr > keyword)
819 {
820 *keyptr = '\0';
821 keyptr = keyword;
822
823 if (bsearch(&keyptr, Keywords, NumKeywords, sizeof(char *),
824 compare_keywords))
825 {
826 /*
827 * Put keywords in boldface...
828 */
829
830 i = page_column * (ColumnWidth + ColumnGutter);
831
832 while (keycol < column)
833 {
834 Page[line][keycol + i].attr |= ATTR_BOLD;
835 keycol ++;
836 }
837 }
838 }
839
840 page_column ++;
841 column = 0;
842 keycol = 0;
843 line = 0;
844
845 if (!ccomment && !cstring)
846 attr &= ~(ATTR_ITALIC | ATTR_BOLD | ATTR_RED | ATTR_GREEN | ATTR_BLUE);
847
848 if (page_column >= PageColumns)
849 {
850 WritePage();
851 page_column = 0;
852 }
853 break;
854
855 case 0x1b : /* Escape sequence */
856 ch = getutf8(fp);
857 if (ch == '7')
858 {
859 /*
860 * ESC 7 Reverse full line (0x1b 0x37)
861 */
862
863 if (line > 0)
864 line --;
865 }
866 else if (ch == '8')
867 {
868 /*
869 * ESC 8 Reverse half line (0x1b 0x38)
870 */
871
872 if ((attr & ATTR_RAISED) && line > 0)
873 {
874 attr &= ~ATTR_RAISED;
875 line --;
876 }
877 else if (attr & ATTR_LOWERED)
878 attr &= ~ATTR_LOWERED;
879 else
880 attr |= ATTR_RAISED;
881 }
882 else if (ch == '9')
883 {
884 /*
885 * ESC 9 Forward half line (0x1b 0x39)
886 */
887
888 if ((attr & ATTR_LOWERED) && line < (SizeLines - 1))
889 {
890 attr &= ~ATTR_LOWERED;
891 line ++;
892 }
893 else if (attr & ATTR_RAISED)
894 attr &= ~ATTR_RAISED;
895 else
896 attr |= ATTR_LOWERED;
897 }
898 break;
899
900 default : /* All others... */
901 if (ch < ' ')
902 break; /* Ignore other control chars */
903
904 if (PrettyPrint > PRETTY_PLAIN)
905 {
906 /*
907 * Do highlighting of C/C++ keywords, preprocessor commands,
908 * and comments...
909 */
910
911 if (ch == ' ' && (attr & ATTR_BOLD))
912 {
913 /*
914 * Stop bolding preprocessor command...
915 */
916
917 attr &= ~ATTR_BOLD;
918 }
919 else if (!(isalnum(ch & 255) || ch == '_') && keyptr > keyword)
920 {
921 /*
922 * Look for a keyword...
923 */
924
925 *keyptr = '\0';
926 keyptr = keyword;
927
928 if (!(attr & ATTR_ITALIC) &&
929 bsearch(&keyptr, Keywords, NumKeywords, sizeof(char *),
930 compare_keywords))
931 {
932 /*
933 * Put keywords in boldface...
934 */
935
936 i = page_column * (ColumnWidth + ColumnGutter);
937
938 while (keycol < column)
939 {
940 Page[line][keycol + i].attr |= ATTR_BOLD;
941 keycol ++;
942 }
943 }
944 }
945 else if ((isalnum(ch & 255) || ch == '_') && !ccomment && !cstring)
946 {
947 /*
948 * Add characters to the current keyword (if they'll fit).
949 */
950
951 if (keyptr == keyword)
952 keycol = column;
953
954 if (keyptr < (keyword + sizeof(keyword) - 1))
955 *keyptr++ = ch;
956 }
957 else if (ch == '\"' && lastch != '\\' && !ccomment && !cstring)
958 {
959 /*
960 * Start a C string constant...
961 */
962
963 cstring = -1;
964 attr = ATTR_BLUE;
965 }
966 else if (ch == '*' && lastch == '/' && !cstring &&
967 PrettyPrint != PRETTY_SHELL)
968 {
969 /*
970 * Start a C-style comment...
971 */
972
973 ccomment = 1;
974 attr = ATTR_ITALIC | ATTR_GREEN;
975 }
976 else if (ch == '/' && lastch == '/' && !cstring &&
977 PrettyPrint == PRETTY_CODE)
978 {
979 /*
980 * Start a C++-style comment...
981 */
982
983 attr = ATTR_ITALIC | ATTR_GREEN;
984 }
985 else if (ch == '#' && !cstring && PrettyPrint != PRETTY_CODE)
986 {
987 /*
988 * Start a shell-style comment...
989 */
990
991 attr = ATTR_ITALIC | ATTR_GREEN;
992 }
993 else if (ch == '#' && column == 0 && !ccomment && !cstring &&
994 PrettyPrint == PRETTY_CODE)
995 {
996 /*
997 * Start a preprocessor command...
998 */
999
1000 attr = ATTR_BOLD | ATTR_RED;
1001 }
1002 }
1003
1004 if (column >= ColumnWidth && WrapLines)
1005 { /* Wrap text to margins */
1006 column = 0;
1007 line ++;
1008
1009 if (line >= SizeLines)
1010 {
1011 page_column ++;
1012 line = 0;
1013
1014 if (page_column >= PageColumns)
1015 {
1016 WritePage();
1017 page_column = 0;
1018 }
1019 }
1020 }
1021
1022 /*
1023 * Add text to the current column & line...
1024 */
1025
1026 if (column < ColumnWidth)
1027 {
1028 i = column + page_column * (ColumnWidth + ColumnGutter);
1029
1030 if (PrettyPrint)
1031 Page[line][i].attr = attr;
1032 else if (ch == ' ' && Page[line][i].ch)
1033 ch = Page[line][i].ch;
1034 else if (ch == Page[line][i].ch)
1035 Page[line][i].attr |= ATTR_BOLD;
1036 else if (Page[line][i].ch == '_')
1037 Page[line][i].attr |= ATTR_UNDERLINE;
1038 else if (ch == '_')
1039 {
1040 Page[line][i].attr |= ATTR_UNDERLINE;
1041
1042 if (Page[line][i].ch)
1043 ch = Page[line][i].ch;
1044 }
1045 else
1046 Page[line][i].attr = attr;
1047
1048 Page[line][i].ch = ch;
1049 }
1050
1051 if (PrettyPrint)
1052 {
1053 if ((ch == '{' || ch == '}') && !ccomment && !cstring &&
1054 column < ColumnWidth)
1055 {
1056 /*
1057 * Highlight curley braces...
1058 */
1059
1060 Page[line][column].attr |= ATTR_BOLD;
1061 }
1062 else if ((ch == '/' || ch == '*') && lastch == '/' &&
1063 column < ColumnWidth && PrettyPrint != PRETTY_SHELL)
1064 {
1065 /*
1066 * Highlight first comment character...
1067 */
1068
1069 Page[line][column - 1].attr = attr;
1070 }
1071 else if (ch == '\"' && lastch != '\\' && !ccomment && cstring > 0)
1072 {
1073 /*
1074 * End a C string constant...
1075 */
1076
1077 cstring = 0;
1078 attr &= ~ATTR_BLUE;
1079 }
1080 else if (ch == '/' && lastch == '*' && ccomment)
1081 {
1082 /*
1083 * End a C-style comment...
1084 */
1085
1086 ccomment = 0;
1087 attr &= ~(ATTR_ITALIC | ATTR_GREEN);
1088 }
1089
1090 if (cstring < 0)
1091 cstring = 1;
1092 }
1093
1094 column ++;
1095 break;
1096 }
1097
1098 /*
1099 * Save this character for the next cycle.
1100 */
1101
1102 lastch = ch;
1103 }
1104
1105 /*
1106 * Write any remaining page data...
1107 */
1108
1109 if (line > 0 || page_column > 0 || column > 0)
1110 WritePage();
1111
1112 /*
1113 * Write the epilog and return...
1114 */
1115
1116 WriteEpilogue();
1117
1118 if (ppd != NULL)
1119 ppdClose(ppd);
1120
1121 return (0);
1122 }
1123
1124
1125 /*
1126 * 'compare_keywords()' - Compare two C/C++ keywords.
1127 */
1128
1129 static int /* O - Result of strcmp */
1130 compare_keywords(const void *k1, /* I - First keyword */
1131 const void *k2) /* I - Second keyword */
1132 {
1133 return (strcmp(*((const char **)k1), *((const char **)k2)));
1134 }
1135
1136
1137 /*
1138 * 'getutf8()' - Get a UTF-8 encoded wide character...
1139 */
1140
1141 static int /* O - Character or -1 on error */
1142 getutf8(FILE *fp) /* I - File to read from */
1143 {
1144 int ch; /* Current character value */
1145 int next; /* Next character from file */
1146
1147
1148 /*
1149 * Read the first character and process things accordingly...
1150 *
1151 * UTF-8 maps 16-bit characters to:
1152 *
1153 * 0 to 127 = 0xxxxxxx
1154 * 128 to 2047 = 110xxxxx 10yyyyyy (xxxxxyyyyyy)
1155 * 2048 to 65535 = 1110xxxx 10yyyyyy 10zzzzzz (xxxxyyyyyyzzzzzz)
1156 *
1157 * We also accept:
1158 *
1159 * 128 to 191 = 10xxxxxx
1160 *
1161 * since this range of values is otherwise undefined unless you are
1162 * in the middle of a multi-byte character...
1163 *
1164 * This code currently does not support anything beyond 16-bit
1165 * characters, in part because PostScript doesn't support more than
1166 * 16-bit characters...
1167 */
1168
1169 if ((ch = getc(fp)) == EOF)
1170 return (EOF);
1171
1172 if (ch < 0xc0) /* One byte character? */
1173 return (ch);
1174 else if ((ch & 0xe0) == 0xc0)
1175 {
1176 /*
1177 * Two byte character...
1178 */
1179
1180 if ((next = getc(fp)) == EOF)
1181 return (EOF);
1182 else
1183 return (((ch & 0x1f) << 6) | (next & 0x3f));
1184 }
1185 else if ((ch & 0xf0) == 0xe0)
1186 {
1187 /*
1188 * Three byte character...
1189 */
1190
1191 if ((next = getc(fp)) == EOF)
1192 return (EOF);
1193
1194 ch = ((ch & 0x0f) << 6) | (next & 0x3f);
1195
1196 if ((next = getc(fp)) == EOF)
1197 return (EOF);
1198 else
1199 return ((ch << 6) | (next & 0x3f));
1200 }
1201 else
1202 {
1203 /*
1204 * More than three bytes... We don't support that...
1205 */
1206
1207 return (EOF);
1208 }
1209 }
1210
1211
1212 /*
1213 * End of "$Id: textcommon.c 6649 2007-07-11 21:46:42Z mike $".
1214 */