%top{ #ifdef HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ #include "manconfig.h" /* Flex emits several functions which might reasonably have various * attributes applied and many unused macros; none of these are our problem. */ #if GNUC_PREREQ(8,0) # pragma GCC diagnostic ignored "-Wsuggest-attribute=malloc" #endif #pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" #pragma GCC diagnostic ignored "-Wunused-macros" } %{ /* * zsoelim.l: eliminate .so includes within *roff source * * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) * Copyright (C) 1997 Fabrizio Polacco. * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 * Colin Watson. * * This file is part of man-db. * * man-db 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 2 of the License, or * (at your option) any later version. * * man-db 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 man-db; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Added functionality over gsoelim to allow for compressed .so includes. * This is required as the first *roff preprocessor in order to deal with * 100% of compressed source files correctly. A replacement tmac.andoc was * considered, but would not have been portable to all systems. * * Wed Oct 12 18:46:11 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) * * Tue, 14 Oct 1997 Fabrizio Polacco * - added changes that were done to .c instead of -l source * - added new start condition to avoid execution of .so requests * inside a macro definition. */ #define MAX_SO_DEPTH 10 /* max .so recursion depth */ #undef ACCEPT_QUOTES /* accept quoted roff requests */ #include #include #include #include #include #include #include #include #include #define NAME so_name[so_stack_ptr] #define LINE so_line[so_stack_ptr] #define PIPE so_pipe[so_stack_ptr] #include "dirname.h" #include "error.h" #include "gl_linkedhash_list.h" #include "gl_xlist.h" #include "xalloc.h" #include "xgetcwd.h" #include "xvasprintf.h" #include "gettext.h" #include #define _(String) gettext (String) #include "appendstr.h" #include "compression.h" #include "debug.h" #include "fatal.h" #include "glcontainers.h" #include "decompress.h" #include "globbing.h" #include "zsoelim.h" #ifdef ACCEPT_QUOTES # define ZAP_QUOTES zap_quotes () static void zap_quotes (void); #else # define ZAP_QUOTES #endif static YY_BUFFER_STATE so_stack[MAX_SO_DEPTH]; static char *so_name[MAX_SO_DEPTH]; static int so_line[MAX_SO_DEPTH]; static decompress *so_pipe[MAX_SO_DEPTH]; static int so_stack_ptr; static bool no_newline; static gl_list_t so_manpathlist; static const char *so_parent_path; struct zsoelim_stdin_data { char *path; gl_list_t manpathlist; }; /* The flex documentation says that yyin is only used by YY_INPUT, so we * should safely be able to abuse it as a handy way to keep track of the * current 'decompress *' rather than the usual 'FILE *'. */ #define YY_INPUT(buf,result,max_size) { \ size_t size = max_size; \ const char *block = decompress_read ((decompress *) yyin, &size); \ if (block && size != 0) { \ memcpy (buf, block, size); \ buf[size] = '\0'; \ result = size; \ } else \ result = YY_NULL; \ } #define YY_NO_INPUT %} %x so %x de %x end_request %x lfnumber %x lfname W [ \t] %option full noread ecs %option 8bit batch never-interactive %option noyywrap nounput %% ^\.de{W}*.+ { no_newline = true; ECHO; BEGIN (de); /* Now we're inside of a macro definition: ends with a comment */ } ^\.so{W}* { no_newline = true; BEGIN (so); /* Now we're in the .so environment */ } ^\.lf{W}* { no_newline = true; ECHO; /* Now we're in the .lf environment */ BEGIN (lfnumber); } ^[^\.\n].* | /* fallback */ ^\.[^sl].* | ^\.l[^f].* | ^\.s[^o].* | ^\.s | ^\.l | . { no_newline = true; ECHO; } \n { no_newline = false; putchar ('\n'); LINE++; } \"?[^ \t\n\"]+\"? { /* file names including whitespace ? */ if (so_stack_ptr == MAX_SO_DEPTH - 1) fatal (0, _("%s:%d: .so requests nested too " "deeply or are recursive"), NAME, LINE); ZAP_QUOTES; so_stack[so_stack_ptr++] = YY_CURRENT_BUFFER; LINE = 1; no_newline = false; if (zsoelim_open_file (yytext, so_manpathlist, so_parent_path)) { --so_stack_ptr; #ifndef __alpha error (OK, 0, _("%s:%d: warning: failed .so request"), NAME, LINE); printf (".so %s\n", yytext); #endif BEGIN (end_request); } else { printf (".lf 1 %s\n", yytext); yy_switch_to_buffer (yy_create_buffer (yyin, YY_BUF_SIZE)); BEGIN (INITIAL); } } {W}*\n { no_newline = false; BEGIN (INITIAL); } \n { no_newline = false; error (OK, 0, _("%s:%d: warning: newline in .so request, " "ignoring"), NAME, LINE); putchar ('\n'); LINE++; BEGIN (INITIAL); } ^\.\..* { no_newline = true; ECHO; BEGIN (INITIAL); } .* { no_newline = true; ECHO; } \n { no_newline = false; putchar ('\n'); LINE++; } \"?[0-9]+\"? { no_newline = true; ECHO; ZAP_QUOTES; LINE = atoi (yytext); BEGIN (lfname); } \"?[^ \t\n\"]+\"? { /* file names including whitespace ?? */ no_newline = true; ECHO; putchar ('\n'); ZAP_QUOTES; if (NAME) free (NAME); NAME = xstrdup (yytext); BEGIN (end_request); } {W}+ { no_newline = true; ECHO; } \n { no_newline = false; putchar ('\n'); LINE++; BEGIN (INITIAL); } . { no_newline = true; debug ( "%s:%d: warning: unhandled .lf request; " "line numbers may be wrong\n", NAME, LINE); putchar (*yytext); BEGIN (INITIAL); } \n { no_newline = false; error (OK, 0, _("%s:%d: warning: newline in .lf request, " "ignoring"), NAME, LINE); putchar ('\n'); LINE++; BEGIN (INITIAL); } <> { decompress_wait (PIPE); decompress_free (PIPE); PIPE = NULL; free (NAME); NAME = NULL; if (no_newline) putchar ('\n'); if (--so_stack_ptr < 0) { yyterminate (); } else { yy_delete_buffer (YY_CURRENT_BUFFER); yy_switch_to_buffer (so_stack[so_stack_ptr]); printf (".lf %d %s\n", LINE += 1, NAME); } no_newline = false; BEGIN (end_request); } %% #ifdef ACCEPT_QUOTES /* remove leading and trailing quotes in requests */ static void zap_quotes (void) { if (*yytext == '"') { if (yytext[yyleng - 1] == '"') { yytext[yyleng - 1] = '\0'; yytext++; } else error (OK, 0, _("%s:%d: unterminated quote in roff request"), NAME, LINE); } } #endif /* initialise the stack and call the parser */ void zsoelim_parse_file (gl_list_t manpathlist, const char *parent_path) { const char *line; int linenum = 1; so_stack_ptr = 0; so_manpathlist = manpathlist; so_parent_path = parent_path; /* Skip over the first line if it's something that manconv might * need to know about. */ line = decompress_peekline ((decompress *) yyin); if (line && (STRNEQ (line, PP_COOKIE, 4) || STRNEQ (line, ".\\\" ", 4))) { fputs (line, stdout); decompress_peek_skip ((decompress *) yyin, strlen (line)); ++linenum; } printf (".lf %d %s\n", linenum, NAME); LINE = 1; yylex (); } static decompress *try_compressed (char **filename) { struct compression *comp; size_t len = strlen (*filename); decompress *decomp; /* Try the uncompressed name first. */ (*filename)[len - 1] = '\0'; debug ("trying %s\n", *filename); decomp = decompress_open (*filename, DECOMPRESS_ALLOW_INPROCESS); if (decomp) return decomp; (*filename)[len - 1] = '.'; for (comp = comp_list; comp->ext; ++comp) { *filename = appendstr (*filename, comp->ext, NULL); debug ("trying %s\n", *filename); decomp = decompress_open (*filename, DECOMPRESS_ALLOW_INPROCESS); if (decomp) return decomp; (*filename)[len] = '\0'; } return NULL; } /* This routine is used to open the specified file or uncompress a compressed version and open that instead */ bool zsoelim_open_file (const char *filename, gl_list_t manpathlist, const char *parent_path) { decompress *decomp = NULL; if (parent_path) debug ("opening %s (parent path: %s)\n", filename, parent_path); else debug ("opening %s\n", filename); if (strcmp (filename, "-") == 0) { decomp = decompress_fdopen (dup (STDIN_FILENO)); NAME = xstrdup (filename); } else { char *compfile; const char *mp; /* If there is no parent path, try opening directly first. */ if (!parent_path) { compfile = xasprintf ("%s.", filename); assert (compfile); decomp = try_compressed (&compfile); if (decomp) { NAME = compfile; goto out; } else free (compfile); } if (strchr (filename, '/')) { /* File name with a directory part. Try looking it * up within each manpath entry. */ if (parent_path) { compfile = xasprintf ("%s/%s.", parent_path, filename); assert (compfile); decomp = try_compressed (&compfile); if (decomp) { NAME = compfile; goto out; } free (compfile); } GL_LIST_FOREACH (manpathlist, mp) { if (parent_path && STREQ (mp, parent_path)) continue; compfile = xasprintf ("%s/%s.", mp, filename); assert (compfile); decomp = try_compressed (&compfile); if (decomp) { NAME = compfile; goto out; } free (compfile); } } else { /* File name with no directory part. Try searching * the manpath. */ char *name, *sec, *dot; gl_list_t names; const char *found_name; name = xstrdup (filename); dot = strchr (name, '.'); if (!dot) { free (name); goto out; } *dot++ = '\0'; sec = dot; dot = strchr (dot, '.'); if (dot) *dot = '\0'; if (parent_path) { names = look_for_file (parent_path, sec, name, false, LFF_MATCHCASE); GL_LIST_FOREACH (names, found_name) { decomp = decompress_open (found_name, DECOMPRESS_ALLOW_INPROCESS); if (decomp) { NAME = xstrdup (found_name); gl_list_free (names); goto out; } } gl_list_free (names); } GL_LIST_FOREACH (manpathlist, mp) { if (parent_path && STREQ (mp, parent_path)) continue; names = look_for_file (mp, sec, name, false, LFF_MATCHCASE); GL_LIST_FOREACH (names, found_name) { decomp = decompress_open (found_name, DECOMPRESS_ALLOW_INPROCESS); if (decomp) { NAME = xstrdup (found_name); gl_list_free (names); free (name); goto out; } } gl_list_free (names); } free (name); } /* If there is a parent path, try opening directly last. */ if (parent_path) { compfile = xasprintf ("%s.", filename); assert (compfile); decomp = try_compressed (&compfile); if (decomp) { NAME = compfile; goto out; } else free (compfile); } out: if (!decomp) { error (0, errno, _("can't open %s"), filename); return true; } } debug ("opened %s\n", NAME); decompress_start (decomp); PIPE = decomp; /* only used by YY_INPUT, which casts it back to 'decompress *' */ yyin = (FILE *) decomp; return false; } void zsoelim_stdin (void *data) { struct zsoelim_stdin_data *zsoelim_data = data; gl_list_t empty; empty = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, NULL, true); zsoelim_open_file ("-", empty, zsoelim_data->path); gl_list_free (empty); zsoelim_parse_file (zsoelim_data->manpathlist, zsoelim_data->path); } struct zsoelim_stdin_data *zsoelim_stdin_data_new (const char *path, gl_list_t manpathlist) { struct zsoelim_stdin_data *data = XMALLOC (struct zsoelim_stdin_data); data->path = path ? xstrdup (path) : NULL; data->manpathlist = manpathlist; return data; } void zsoelim_stdin_data_free (void *data) { struct zsoelim_stdin_data *zsoelim_data = data; free (zsoelim_data->path); free (zsoelim_data); }