(root)/
man-db-2.12.0/
src/
catman.c
       1  /*
       2   * catman.c: create and/or update cat files
       3   *
       4   * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.)
       5   * Copyright (C) 2001, 2002, 2003, 2006, 2007, 2008, 2009, 2010, 2011
       6   *               Colin Watson.
       7   *
       8   * This file is part of man-db.
       9   *
      10   * man-db is free software; you can redistribute it and/or modify it
      11   * under the terms of the GNU General Public License as published by
      12   * the Free Software Foundation; either version 2 of the License, or
      13   * (at your option) any later version.
      14   *
      15   * man-db is distributed in the hope that it will be useful, but
      16   * WITHOUT ANY WARRANTY; without even the implied warranty of
      17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18   * GNU General Public License for more details.
      19   *
      20   * You should have received a copy of the GNU General Public License
      21   * along with man-db; if not, write to the Free Software Foundation,
      22   * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      23   *
      24   * Thu Dec  8 00:03:12 GMT 1994  Wilf. (G.Wilford@ee.surrey.ac.uk)
      25   */
      26  
      27  /* MAX_ARGS must be >= 7, 5 for options, 1 for page and 1 for NULL */
      28  #define MAX_ARGS	1024
      29  
      30  #ifdef HAVE_CONFIG_H
      31  #  include "config.h"
      32  #endif /* HAVE_CONFIG_H */
      33  
      34  #include <stdbool.h>
      35  #include <stdio.h>
      36  #include <assert.h>
      37  #include <sys/types.h>
      38  #include <errno.h>
      39  #include <string.h>
      40  #include <stdlib.h>
      41  #include <unistd.h>
      42  #include <limits.h>
      43  
      44  #ifndef NAME_MAX
      45  #  if defined(_POSIX_VERSION) && defined(_POSIX_NAME_MAX)
      46  #    define NAME_MAX _POSIX_NAME_MAX
      47  #  else /* !_POSIX_VERSION */
      48  #    ifdef MAXNAMLEN
      49  #      define NAME_MAX MAXNAMLEN
      50  #    else /* !MAXNAMLEN */
      51  #      define NAME_MAX 255 		/* default to max */
      52  #    endif /* MAXNAMLEN */
      53  #  endif /* _POSIX_VERSION */
      54  #endif /* !NAME_MAX */
      55  
      56  #ifndef ARG_MAX
      57  #  if defined(_POSIX_VERSION) && defined(_POSIX_ARG_MAX)
      58  #    define ARG_MAX _POSIX_ARG_MAX
      59  #  else /* !_POSIX_VERSION */
      60  #    define ARG_MAX 4096 		/* default to min */
      61  #  endif /* _POSIX_VERSION */
      62  #endif /* !ARG_MAX */
      63  
      64  #include "argp.h"
      65  #include "error.h"
      66  #include "gl_list.h"
      67  #include "progname.h"
      68  #include "xalloc.h"
      69  
      70  #include "gettext.h"
      71  #include <locale.h>
      72  #define _(String) gettext (String)
      73  #define N_(String) gettext_noop (String)
      74  
      75  #include "manconfig.h"
      76  
      77  #include "appendstr.h"
      78  #include "cleanup.h"
      79  #include "debug.h"
      80  #include "fatal.h"
      81  #include "filenames.h"
      82  #include "glcontainers.h"
      83  #include "pipeline.h"
      84  #include "util.h"
      85  
      86  #include "mydbm.h"
      87  #include "db_storage.h"
      88  
      89  #include "manp.h"
      90  
      91  /* globals */
      92  int quiet = 1;
      93  MYDBM_FILE dbf_close_post_fork;
      94  char *manp;
      95  extern char *user_config_file;
      96  
      97  static const char **sections;
      98  
      99  const char *argp_program_version = "catman " PACKAGE_VERSION;
     100  const char *argp_program_bug_address = PACKAGE_BUGREPORT;
     101  error_t argp_err_exit_status = FAIL;
     102  
     103  static const char args_doc[] = N_("[SECTION...]");
     104  
     105  static struct argp_option options[] = {
     106  	OPT ("debug", 'd', 0, N_("emit debugging messages")),
     107  	OPT ("manpath", 'M', N_("PATH"),
     108  	     N_("set search path for manual pages to PATH")),
     109  	OPT ("config-file", 'C', N_("FILE"),
     110  	     N_("use this user configuration file")),
     111  	OPT_HELP_COMPAT,
     112  	{ 0 }
     113  };
     114  
     115  static error_t parse_opt (int key, char *arg, struct argp_state *state)
     116  {
     117  	char *mansect;
     118  
     119  	switch (key) {
     120  		case 'd':
     121  			debug_level = true;
     122  			return 0;
     123  		case 'M':
     124  			manp = arg;
     125  			return 0;
     126  		case 'C':
     127  			user_config_file = arg;
     128  			return 0;
     129  		case 'h':
     130  			argp_state_help (state, state->out_stream,
     131  					 ARGP_HELP_STD_HELP);
     132  			break;
     133  		case ARGP_KEY_ARGS:
     134  			sections = xmalloc ((state->argc - state->next + 1) *
     135  					    sizeof *sections);
     136  			memcpy (sections, state->argv + state->next,
     137  				(state->argc - state->next) *
     138  				sizeof *sections);
     139  			sections[state->argc - state->next] = NULL;
     140  			return 0;
     141  		case ARGP_KEY_NO_ARGS:
     142  			mansect = getenv ("MANSECT");
     143  			if (mansect && *mansect) {
     144  				/* MANSECT contains sections */
     145  				const char *sec;
     146  				int i = 0;
     147  
     148  				mansect = xstrdup (mansect);
     149  				sections = NULL;
     150  				for (sec = strtok (mansect, ":"); sec;
     151  				     sec = strtok (NULL, ":")) {
     152  					sections = xnrealloc
     153  						(sections, i + 2,
     154  						 sizeof *sections);
     155  					sections[i++] = sec;
     156  				}
     157  				if (sections)
     158  					sections[i] = NULL;
     159  				free (mansect);
     160  			} else {
     161  				/* use default sections */
     162  				static const char *std_sections[] =
     163  					STD_SECTIONS;
     164  				sections = std_sections;
     165  			}
     166  			return 0;
     167  	}
     168  	return ARGP_ERR_UNKNOWN;
     169  }
     170  
     171  static struct argp argp = { options, parse_opt, args_doc };
     172  
     173  static char *locale;
     174  
     175  static gl_list_t manpathlist;
     176  
     177  static void post_fork (void)
     178  {
     179  	pop_all_cleanups ();
     180  	MYDBM_FREE (dbf_close_post_fork);
     181  }
     182  
     183  /* Execute man with the appropriate catman args.  Always frees cmd. */
     184  static void catman (pipecmd *cmd)
     185  {
     186  	pipeline *p;
     187  	int status;
     188  
     189  	if (debug_level) {
     190  		/* just show the command, but don't execute it */
     191  		fputs ("man command = ", stderr);
     192  		pipecmd_dump (cmd, stderr);
     193  		putc ('\n', stderr);
     194  		pipecmd_free (cmd);
     195  		return;
     196  	}
     197  
     198  	p = pipeline_new_commands (cmd, (void *) 0);
     199  	status = pipeline_run (p);
     200  	if (status)
     201  		error (CHILD_FAIL, 0,
     202  		       _("man command failed with exit status %d"), status);
     203  }
     204  
     205  /* Add key to this command, stripping off tab-and-following if necessary.
     206   * Return length of argument.
     207   */
     208  static size_t add_arg (pipecmd *cmd, datum key)
     209  {
     210  	char *tab;
     211  	size_t len;
     212  
     213  	tab = strrchr (MYDBM_DPTR (key), '\t');
     214  	if (tab == MYDBM_DPTR (key))
     215  		tab = NULL;
     216  
     217  	if (tab)
     218  		*tab = '\0';
     219  	pipecmd_arg (cmd, MYDBM_DPTR (key));
     220  	len = strlen (MYDBM_DPTR (key));
     221  	debug ("key: '%s' (%zu), len: %zu\n",
     222  	       MYDBM_DPTR (key), (size_t) MYDBM_DSIZE (key), len);
     223  	if (tab)
     224  		*tab = '\t';
     225  
     226  	return len;
     227  }
     228  
     229  /* find all pages that are in the supplied manpath and section and that are
     230     ultimate source files. */
     231  static int parse_for_sec (MYDBM_FILE dbf,
     232  			  const char *manpath, const char *section)
     233  {
     234  	pipecmd *basecmd, *cmd;
     235  	datum key;
     236  	size_t arg_size, initial_bit;
     237  	bool message = true;
     238  	int first_arg;
     239  
     240  	basecmd = pipecmd_new (MAN);
     241  	pipecmd_clearenv (basecmd);
     242  
     243  	/* As we supply a NULL environment to save precious execve() space,
     244  	   we must also supply a locale if necessary */
     245  	if (locale) {
     246  		pipecmd_args (basecmd, "-L", locale, (void *) 0);
     247  		initial_bit = sizeof "-L" + strlen (locale) + 1;
     248  	} else
     249  		initial_bit = 0;
     250  
     251  	pipecmd_args (basecmd, "-caM", manpath, (void *) 0);	/* manpath */
     252  	pipecmd_args (basecmd, "-S", section, (void *) 0);	/* section */
     253  
     254  	initial_bit += sizeof MAN + sizeof "-caM" +
     255  		       strlen (manpath) + strlen (section) + 2;
     256  
     257  	cmd = pipecmd_dup (basecmd);
     258  	first_arg = pipecmd_get_nargs (cmd);
     259  
     260  	arg_size = initial_bit;
     261  	key = MYDBM_FIRSTKEY (dbf);
     262  
     263  	while (MYDBM_DPTR (key) != NULL) {
     264  		datum nextkey;
     265  
     266  		/* ignore db identifier keys */
     267  #pragma GCC diagnostic push
     268  #if GNUC_PREREQ(10,0)
     269  #  pragma GCC diagnostic ignored "-Wanalyzer-use-after-free"
     270  #endif
     271  		if (*MYDBM_DPTR (key) != '$') {
     272  #pragma GCC diagnostic pop
     273  			datum content;
     274  
     275  			content = MYDBM_FETCH (dbf, key);
     276  
     277  			if (!MYDBM_DPTR (content))
     278  				fatal (0,
     279  				       _( "NULL content for key: %s"),
     280  				       MYDBM_DPTR (key));
     281  
     282  			/* ignore overflow entries */
     283  #pragma GCC diagnostic push
     284  #if GNUC_PREREQ(10,0)
     285  #  pragma GCC diagnostic ignored "-Wanalyzer-use-after-free"
     286  #endif
     287  			if (*MYDBM_DPTR (content) != '\t') {
     288  #pragma GCC diagnostic pop
     289  				struct mandata *entry;
     290  
     291  				entry = split_content (dbf,
     292  						       MYDBM_DPTR (content));
     293  
     294  				/* Accept if the entry is an ultimate manual
     295  				   page and the section matches the one we're
     296  				   currently dealing with */
     297  				if (entry->id == ULT_MAN &&
     298  				    strcmp (entry->sec, section) == 0) {
     299  					if (message) {
     300  						printf (_("\nUpdating cat files for section %s of man hierarchy %s\n"),
     301  							section, manpath);
     302  						message = false;
     303  					}
     304  
     305  					arg_size += add_arg (cmd, key) + 1;
     306  
     307  					debug ("arg space free: %zu bytes\n",
     308  					       ARG_MAX - arg_size);
     309  
     310  					/* Check to see if we have enough room
     311  					   to add another max sized filename
     312  					   and that we haven't run out of array
     313  					   space too */
     314  				    	if (arg_size >= ARG_MAX - NAME_MAX ||
     315  				    	    pipecmd_get_nargs (cmd) ==
     316  						    MAX_ARGS) {
     317  						catman (cmd);
     318  
     319  						cmd = pipecmd_dup (basecmd);
     320  				    		arg_size = initial_bit;
     321  				    	}
     322  				}
     323  
     324  				free_mandata_struct (entry);
     325  			}
     326  
     327  			/* we don't need the content ever again */
     328  			assert (MYDBM_DPTR (content)); /* just to be sure */
     329  			MYDBM_FREE_DPTR (content);
     330  		}
     331  
     332  		nextkey = MYDBM_NEXTKEY (dbf, key);
     333  		MYDBM_FREE_DPTR (key);
     334  		key = nextkey;
     335  	}
     336  
     337  	if (pipecmd_get_nargs (cmd) > first_arg)
     338  		catman (cmd);
     339  	else
     340  		pipecmd_free (cmd);
     341  
     342  	pipecmd_free (basecmd);
     343  
     344  	return 0;
     345  }
     346  
     347  static bool check_access (const char *directory)
     348  {
     349  	if (!CAN_ACCESS (directory, W_OK)) {
     350  		error (0, errno, _("cannot write within %s"), directory);
     351  		return true;
     352  	}
     353  
     354  	return false;
     355  }
     356  
     357  int main (int argc, char *argv[])
     358  {
     359  	char *sys_manp;
     360  	char *mp;
     361  	const char **sp;
     362  
     363  	set_program_name (argv[0]);
     364  
     365  	init_debug ();
     366  	pipeline_install_post_fork (post_fork);
     367  
     368  	init_locale ();
     369  	locale = setlocale (LC_MESSAGES, NULL);
     370  	if (locale)
     371  		locale = xstrdup (locale);
     372  	else
     373  		locale = xstrdup ("C");
     374  
     375  	if (argp_parse (&argp, argc, argv, 0, 0, 0))
     376  		exit (FAIL);
     377  
     378  	for (sp = sections; sp && *sp; sp++)
     379  		debug ("sections: %s\n", *sp);
     380  
     381  	/* Deal with the MANPATH */
     382  
     383  	/* This is required for get_catpath(), regardless */
     384  	sys_manp = get_manpath (NULL);
     385  
     386  	/* pick up the system manpath or use the supplied one */
     387  	if (!manp) {
     388  		manp = get_mandb_manpath ();
     389  		if (!manp)
     390  			manp = sys_manp;
     391  	}
     392  
     393  	/* get the manpath as a list of pointers */
     394  	manpathlist = create_pathlist (manp);
     395  
     396  	GL_LIST_FOREACH (manpathlist, mp) {
     397  		char *catpath, *database;
     398  		MYDBM_FILE dbf;
     399  		size_t len;
     400  
     401  		catpath = get_catpath (mp, SYSTEM_CAT | USER_CAT);
     402  
     403  		if (catpath) {
     404  			if (is_directory (catpath) != 1) {
     405  				free (catpath);
     406  				continue;
     407  			}
     408  			database = mkdbname (catpath);
     409  		} else {
     410  			if (is_directory (mp) != 1)
     411  				continue;
     412  			database = mkdbname (mp);
     413  			catpath = xstrdup (mp);
     414  		}
     415  		dbf = MYDBM_NEW (database);
     416  		if (!MYDBM_RDOPEN (dbf) || dbver_rd (dbf)) {
     417  			error (0, errno, _("cannot read database %s"),
     418  			       database);
     419  			goto next;
     420  		}
     421  		dbf_close_post_fork = dbf;
     422  
     423  		len = strlen (catpath);
     424  
     425  		for (sp = sections; sp && *sp; sp++) {
     426  			*(catpath + len) = '\0';
     427  			catpath = appendstr (catpath, "/cat", *sp, (void *) 0);
     428  			if (is_directory (catpath) != 1)
     429  				continue;
     430  			if (check_access (catpath))
     431  				continue;
     432  			if (parse_for_sec (dbf, mp, *sp)) {
     433  				error (0, 0, _("unable to update %s"), mp);
     434  				break;
     435  			}
     436  		}
     437  
     438  next:
     439  		dbf_close_post_fork = NULL;
     440  		MYDBM_FREE (dbf);
     441  		free (database);
     442  		free (catpath);
     443  	}
     444  
     445  	free_pathlist (manpathlist);
     446  	free (locale);
     447  	exit (OK);
     448  }