| /* Example program for GNU libtextstyle. |
| Copyright (C) 2018-2019 Free Software Foundation, Inc. |
| Written by Bruno Haible <bruno@clisp.org>, 2018. |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
| |
| /* Source code of the C program. */ |
| |
| #include <textstyle.h> |
| |
| #include <regex.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/stat.h> |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| const char *program_name = argv[0]; |
| const char *regex = NULL; |
| int i; |
| |
| /* Parse the command-line arguments. */ |
| for (i = 1; i < argc; i++) |
| { |
| const char *arg = argv[i]; |
| if (strncmp (arg, "--color=", 8) == 0) |
| handle_color_option (arg + 8); |
| else if (strncmp (arg, "--style=", 8) == 0) |
| handle_style_option (arg + 8); |
| else if (arg[0] == '-') |
| { |
| fprintf (stderr, "%s: invalid argument: %s\n", program_name, arg); |
| exit (1); |
| } |
| else |
| { |
| if (regex != NULL) |
| { |
| fprintf (stderr, |
| "%s: too many regular expressions: '%s' and '%s'\n", |
| program_name, regex, arg); |
| exit (1); |
| } |
| regex = arg; |
| } |
| } |
| |
| if (regex == NULL) |
| { |
| fprintf (stderr, "%s: missing regular expression\n", program_name); |
| exit (1); |
| } |
| |
| /* Handle the --color=test special argument. */ |
| if (color_test_mode) |
| { |
| print_color_test (); |
| exit (0); |
| } |
| |
| /* Compile the regular expression. */ |
| regex_t cregex; |
| { |
| int err = regcomp (&cregex, regex, 0); |
| if (err != 0) |
| { |
| char errbuf[1024]; |
| regerror (err, &cregex, errbuf, sizeof (errbuf)); |
| fprintf (stderr, "%s: invalid regular expression '%s': %s\n", |
| program_name, regex, errbuf); |
| exit (1); |
| } |
| } |
| |
| if (color_mode == color_yes |
| || (color_mode == color_tty |
| && isatty (STDOUT_FILENO) |
| && getenv ("NO_COLOR") == NULL) |
| || color_mode == color_html) |
| { |
| /* Find the style file. */ |
| style_file_prepare ("FILTER_STYLE", "FILTER_STYLESDIR", STYLESDIR, |
| "filter-default.css"); |
| /* As a fallback, use the default in the current directory. */ |
| { |
| struct stat statbuf; |
| |
| if (style_file_name == NULL || stat (style_file_name, &statbuf) < 0) |
| style_file_name = "filter-default.css"; |
| } |
| } |
| else |
| /* No styling. */ |
| style_file_name = NULL; |
| |
| /* Create a terminal output stream that uses this style file. */ |
| styled_ostream_t stream = |
| (color_mode == color_html |
| ? html_styled_ostream_create (file_ostream_create (stdout), |
| style_file_name) |
| : styled_ostream_create (STDOUT_FILENO, "(stdout)", TTYCTL_AUTO, |
| style_file_name)); |
| |
| /* Allocate initial storage for the loop. */ |
| size_t buflen = 10; |
| char *buffer = (char *) malloc (buflen); |
| if (buffer == NULL) |
| { |
| fprintf (stderr, "%s: out of memory\n", program_name); |
| exit (1); |
| } |
| |
| /* Loop over the input, reading line after line. */ |
| int exit_code; |
| for (;;) |
| { |
| /* Read a line. */ |
| bool seen_eof = false; |
| size_t linelen = 0; |
| for (;;) |
| { |
| |
| if (linelen == buflen) |
| { |
| buflen = 2 * buflen; |
| buffer = (char *) realloc (buffer, buflen); |
| if (buffer == NULL) |
| { |
| exit_code = 1; |
| goto done; |
| } |
| } |
| /* Here linelen < buflen. */ |
| int c = getc (stdin); |
| if (c < 0) |
| { |
| seen_eof = true; |
| break; |
| } |
| |
| buffer[linelen++] = (unsigned char) c; |
| |
| if (buffer[linelen - 1] == '\n') |
| break; |
| } |
| size_t end_of_line = |
| (linelen > 0 && buffer[linelen - 1] == '\n' |
| ? (linelen > 1 && buffer[linelen - 2] == '\r' |
| ? linelen - 2 |
| : linelen - 1) |
| : linelen); |
| /* Here linelen-2 <= end_of_line <= linelen and end_of_line < buflen. */ |
| |
| /* NUL-terminate the line. */ |
| buffer[end_of_line] = '\0'; |
| |
| /* Search for occurrences of the regex in the line. */ |
| size_t index = 0; |
| while (index < end_of_line) |
| { |
| regmatch_t match[1]; |
| if (regexec (&cregex, buffer + index, 1, match, |
| index > 0 ? REG_NOTBOL : 0) |
| == REG_NOMATCH) |
| break; |
| |
| /* Output the part of the line before the match. */ |
| if (match[0].rm_so > 0) |
| ostream_write_mem (stream, buffer + index, match[0].rm_so); |
| |
| /* Output the match. */ |
| if (match[0].rm_so < match[0].rm_eo) |
| { |
| styled_ostream_begin_use_class (stream, "match"); |
| ostream_write_mem (stream, buffer + index + match[0].rm_so, |
| match[0].rm_eo - match[0].rm_so); |
| styled_ostream_end_use_class (stream, "match"); |
| } |
| |
| index += match[0].rm_eo; |
| } |
| |
| /* No further match. Output the rest of the line. */ |
| if (index < end_of_line) |
| ostream_write_mem (stream, buffer + index, end_of_line - index); |
| switch (linelen - end_of_line) |
| { |
| case 2: ostream_write_mem (stream, "\r\n", 2); break; |
| case 1: ostream_write_mem (stream, "\n", 1); break; |
| default: break; |
| } |
| |
| if (seen_eof) |
| { |
| exit_code = 0; |
| break; |
| } |
| } |
| done: |
| /* exit_code is set here. */ |
| |
| /* Flush and close the terminal stream. */ |
| styled_ostream_free (stream); |
| |
| /* Do output to stderr only after we have flushed the terminal stream. |
| Otherwise this output may come out with the wrong text attributes. */ |
| if (exit_code == 1) |
| fprintf (stderr, "%s: out of memory\n", program_name); |
| |
| return exit_code; |
| } |