(root)/
man-db-2.12.0/
src/
descriptions_store.c
       1  /*
       2   * descriptions_store.c: store man page descriptions in database
       3   *
       4   * Copyright (C) 2002, 2003, 2006, 2007, 2008, 2011 Colin Watson.
       5   *
       6   * This file is part of man-db.
       7   *
       8   * man-db is free software; you can redistribute it and/or modify it
       9   * under the terms of the GNU General Public License as published by
      10   * the Free Software Foundation; either version 2 of the License, or
      11   * (at your option) any later version.
      12   *
      13   * man-db is distributed in the hope that it will be useful, but
      14   * WITHOUT ANY WARRANTY; without even the implied warranty of
      15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16   * GNU General Public License for more details.
      17   *
      18   * You should have received a copy of the GNU General Public License
      19   * along with man-db; if not, write to the Free Software Foundation,
      20   * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      21   */
      22  
      23  #ifdef HAVE_CONFIG_H
      24  #  include "config.h"
      25  #endif /* HAVE_CONFIG_H */
      26  
      27  #include <assert.h>
      28  #include <stdbool.h>
      29  #include <stdio.h>
      30  #include <string.h>
      31  #include <stdlib.h>
      32  #include <sys/stat.h>
      33  #include <sys/types.h>
      34  #include <time.h>
      35  #include <unistd.h>
      36  
      37  #include "gettext.h"
      38  #define _(String) gettext (String)
      39  
      40  #include "error.h"
      41  #include "gl_array_list.h"
      42  #include "gl_hash_map.h"
      43  #include "gl_xlist.h"
      44  #include "gl_xmap.h"
      45  #include "stat-time.h"
      46  #include "xalloc.h"
      47  
      48  #include "manconfig.h"
      49  
      50  #include "debug.h"
      51  #include "filenames.h"
      52  #include "glcontainers.h"
      53  
      54  #include "db_storage.h"
      55  
      56  #include "ult_src.h"
      57  #include "descriptions.h"
      58  
      59  static void gripe_bad_store (const char *name, const char *ext)
      60  {
      61  	if (quiet < 2)
      62  		error (0, 0, _("warning: failed to store entry for %s(%s)"),
      63  		       name, ext);
      64  }
      65  
      66  /* Is PARENT a prefix of CHILD, such that CHILD is in the manual hierarchy
      67   * PARENT?  This requires that the part of CHILD following PARENT start with
      68   * "/man".
      69   */
      70  static int is_prefix (const char *parent, const char *child)
      71  {
      72  	return (STRNEQ (child, parent, strlen (parent)) &&
      73  		STRNEQ (child + strlen (parent), "/man", 4));
      74  }
      75  
      76  /* Take a list of descriptions returned by parse_descriptions() and store
      77   * it into the database.
      78   */
      79  void store_descriptions (MYDBM_FILE dbf, gl_list_t descs, struct mandata *info,
      80  			 const char *path, const char *base, gl_list_t trace)
      81  {
      82  	const struct page_description *desc;
      83  	const char *trace_name;
      84  	gl_map_t trace_infos;
      85  	gl_list_t whatis_infos;
      86  	struct mandata *whatis_info;
      87  	const struct mandata *pointer_info;
      88  
      89  	assert (trace);
      90  	assert (gl_list_size (trace) > 0);
      91  
      92  	if (gl_list_size (descs)) {
      93  		GL_LIST_FOREACH (trace, trace_name)
      94  			debug ("trace: '%s'\n", trace_name);
      95  	}
      96  
      97  	trace_infos = new_string_map (GL_HASH_MAP,
      98  				      (gl_mapvalue_dispose_fn)
      99  				      free_mandata_struct);
     100  	whatis_infos = gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL,
     101  					     (gl_listelement_dispose_fn)
     102  					     free_mandata_struct,
     103  					     true);
     104  
     105  	GL_LIST_FOREACH (trace, trace_name)
     106  		gl_map_put (trace_infos, xstrdup (trace_name),
     107  			    filename_info (trace_name, quiet < 2));
     108  
     109  	GL_LIST_FOREACH (descs, desc) {
     110  		/* Either it's the real thing or merely a reference. Get the
     111  		 * id and pointer right in either case.
     112  		 */
     113  		bool found_real_page = false;
     114  		bool found_external = false;
     115  
     116  		whatis_info = XZALLOC (struct mandata);
     117  		whatis_info->ext = xstrdup (info->ext);
     118  		whatis_info->sec = xstrdup (info->sec);
     119  		whatis_info->id = info->id;
     120  		if (info->comp)
     121  			whatis_info->comp = xstrdup (info->comp);
     122  		if (info->filter)
     123  			whatis_info->filter = xstrdup (info->filter);
     124  		if (desc->whatis)
     125  			whatis_info->whatis = xstrdup (desc->whatis);
     126  		whatis_info->mtime = info->mtime;
     127  
     128  		if (STREQ (base, desc->name))
     129  			found_real_page = true;
     130  		else {
     131  			GL_LIST_FOREACH (trace, trace_name) {
     132  				const struct mandata *trace_info;
     133  				struct stat st;
     134  
     135  				trace_info = gl_map_get (trace_infos,
     136  							 trace_name);
     137  				if (!trace_info ||
     138  				    !STREQ (trace_info->name, desc->name))
     139  					continue;
     140  
     141  				if (path && !is_prefix (path, trace_name)) {
     142  					/* Link outside this manual
     143  					 * hierarchy; skip this description.
     144  					 */
     145  					found_external = true;
     146  					break;
     147  				}
     148  				free (whatis_info->ext);
     149  				whatis_info->ext = xstrdup (trace_info->ext);
     150  				free (whatis_info->sec);
     151  				whatis_info->sec = xstrdup (trace_info->sec);
     152  				if (!gl_list_next_node (trace, trace_node)) {
     153  					if (info->id == SO_MAN)
     154  						whatis_info->id = ULT_MAN;
     155  				} else {
     156  					if (info->id == ULT_MAN)
     157  						whatis_info->id = SO_MAN;
     158  				}
     159  				free (whatis_info->comp);
     160  				if (trace_info->comp)
     161  					whatis_info->comp = xstrdup
     162  						(trace_info->comp);
     163  				else
     164  					whatis_info->comp = NULL;
     165  				if (lstat (trace_name, &st) == 0)
     166  					whatis_info->mtime = get_stat_mtime
     167  						(&st);
     168  				else
     169  					whatis_info->mtime = info->mtime;
     170  				found_real_page = true;
     171  			}
     172  		}
     173  
     174  		if (found_external) {
     175  			debug ("skipping '%s'; link outside manual "
     176  			       "hierarchy\n", desc->name);
     177  			free_mandata_struct (whatis_info);
     178  			continue;
     179  		}
     180  
     181  		if (!found_real_page) {
     182  			whatis_info->name = xstrdup (desc->name);
     183  			if (info->id < STRAY_CAT)
     184  				whatis_info->id = WHATIS_MAN;
     185  			else
     186  				whatis_info->id = WHATIS_CAT;
     187  			/* Don't waste space storing the whatis in the db
     188  			 * more than once.
     189  			 */
     190  			free (whatis_info->whatis);
     191  			whatis_info->whatis = NULL;
     192  			gl_list_add_last (whatis_infos, whatis_info);
     193  			continue;
     194  		}
     195  
     196  		debug ("name = '%s', ext = '%s', id = %c\n",
     197  		       desc->name, whatis_info->ext, whatis_info->id);
     198  		if (dbstore (dbf, whatis_info, desc->name) > 0) {
     199  			gripe_bad_store (base, whatis_info->ext);
     200  			free_mandata_struct (whatis_info);
     201  			goto out;
     202  		}
     203  
     204  		free_mandata_struct (whatis_info);
     205  	}
     206  
     207  	/* The pointer for a WHATIS_MAN or WHATIS_CAT entry should be the
     208  	 * last entry in the trace that has the same section and extension
     209  	 * as the starting page (which is always the first entry in the
     210  	 * trace).  If we were to add WHATIS_* entries for different
     211  	 * extensions, then try_db -> add_candidate -> make_filename in
     212  	 * man(1) would end up constructing a path that doesn't exist and is
     213  	 * thus unusable.
     214  	 */
     215  	pointer_info = NULL;
     216  	GL_LIST_FOREACH (trace, trace_name) {
     217  		const struct mandata *trace_info;
     218  
     219  		trace_info = gl_map_get (trace_infos, trace_name);
     220  		if (trace_info &&
     221  		    STREQ (trace_info->sec, info->sec) &&
     222  		    STREQ (trace_info->ext, info->ext))
     223  			pointer_info = trace_info;
     224  	}
     225  	assert (pointer_info);
     226  
     227  	GL_LIST_FOREACH (whatis_infos, whatis_info) {
     228  		char *name;
     229  
     230  		name = whatis_info->name;
     231  		whatis_info->name = NULL;
     232  
     233  		whatis_info->pointer = xstrdup (pointer_info->name);
     234  
     235  		debug ("name = '%s', ext = '%s', id = %c, pointer = '%s'\n",
     236  		       name, whatis_info->ext, whatis_info->id,
     237  		       whatis_info->pointer);
     238  		if (dbstore (dbf, whatis_info, name) > 0) {
     239  			gripe_bad_store (base, whatis_info->ext);
     240  			free (name);
     241  			goto out;
     242  		}
     243  
     244  		free (name);
     245  	}
     246  
     247  out:
     248  	gl_list_free (whatis_infos);
     249  	gl_map_free (trace_infos);
     250  }