Back to index

tetex-bin  3.0
index.c
Go to the documentation of this file.
00001 /* index.c -- indexing for Texinfo.
00002    $Id: index.c,v 1.17 2004/11/30 02:03:23 karl Exp $
00003 
00004    Copyright (C) 1998, 1999, 2002, 2003, 2004 Free Software Foundation,
00005    Inc.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2, or (at your option)
00010    any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software Foundation,
00019    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00020 
00021 #include "system.h"
00022 #include "files.h"
00023 #include "footnote.h"
00024 #include "html.h"
00025 #include "index.h"
00026 #include "lang.h"
00027 #include "macro.h"
00028 #include "sectioning.h"
00029 #include "toc.h"
00030 #include "xml.h"
00031 
00032 INDEX_ALIST **name_index_alist = NULL;
00033 
00034 /* An array of pointers.  Each one is for a different index.  The
00035    "synindex" command changes which array slot is pointed to by a
00036    given "index". */
00037 INDEX_ELT **the_indices = NULL;
00038 
00039 /* The number of defined indices. */
00040 int defined_indices = 0;
00041 
00042 /* This is the order of the index.  */
00043 int index_counter = 0;
00044 
00045 /* Stuff for defining commands on the fly. */
00046 COMMAND **user_command_array = NULL;
00047 int user_command_array_len = 0;
00048 
00049 /* How to compare index entries for sorting.  May be set to strcoll.  */
00050 int (*index_compare_fn) (const char *a, const char *b) = strcasecmp;
00051 
00052 /* Function to compare index entries for sorting.  (Calls
00053    `index_compare_fn' above.)  */
00054 int index_element_compare (const void *element1, const void *element2);
00055 
00056 /* Find which element in the known list of indices has this name.
00057    Returns -1 if NAME isn't found. */
00058 static int
00059 find_index_offset (char *name)
00060 {
00061   int i;
00062   for (i = 0; i < defined_indices; i++)
00063     if (name_index_alist[i] && STREQ (name, name_index_alist[i]->name))
00064       return i;
00065   return -1;
00066 }
00067 
00068 /* Return a pointer to the entry of (name . index) for this name.
00069    Return NULL if the index doesn't exist. */
00070 static INDEX_ALIST *
00071 find_index (char *name)
00072 {
00073   int offset = find_index_offset (name);
00074   if (offset > -1)
00075     return name_index_alist[offset];
00076   else
00077     return NULL;
00078 }
00079 
00080 /* User-defined commands, which happens only from user-defined indexes.
00081    Used to initialize the builtin indices, too.  */
00082 static void
00083 define_user_command (char *name, COMMAND_FUNCTION (*proc), int needs_braces_p)
00084 {
00085   int slot = user_command_array_len;
00086   user_command_array_len++;
00087 
00088   if (!user_command_array)
00089     user_command_array = xmalloc (1 * sizeof (COMMAND *));
00090 
00091   user_command_array = xrealloc (user_command_array,
00092                             (1 + user_command_array_len) * sizeof (COMMAND *));
00093 
00094   user_command_array[slot] = xmalloc (sizeof (COMMAND));
00095   user_command_array[slot]->name = xstrdup (name);
00096   user_command_array[slot]->proc = proc;
00097   user_command_array[slot]->argument_in_braces = needs_braces_p;
00098 }
00099 
00100 /* Please release me, let me go... */
00101 static void
00102 free_index (INDEX_ELT *index)
00103 {
00104   INDEX_ELT *temp;
00105 
00106   while ((temp = index))
00107     {
00108       free (temp->entry);
00109       free (temp->entry_text);
00110       /* Do not free the node, because we already freed the tag table,
00111          which freed all the node names.  */
00112       /* free (temp->node); */
00113       index = index->next;
00114       free (temp);
00115     }
00116 }
00117 
00118 /* Flush an index by name.  This will delete the list of entries that
00119    would be written by a @printindex command for this index. */
00120 static void
00121 undefindex (char *name)
00122 {
00123   int i;
00124   int which = find_index_offset (name);
00125 
00126   /* The index might have already been freed if this was the target of
00127      an @synindex.  */
00128   if (which < 0 || !name_index_alist[which])
00129     return;
00130 
00131   i = name_index_alist[which]->read_index;
00132 
00133   free_index (the_indices[i]);
00134   the_indices[i] = NULL;
00135 
00136   free (name_index_alist[which]->name);
00137   free (name_index_alist[which]);
00138   name_index_alist[which] = NULL;
00139 }
00140 
00141 /* Add the arguments to the current index command to the index NAME.  */
00142 static void
00143 index_add_arg (char *name)
00144 {
00145   int which;
00146   char *index_entry;
00147   INDEX_ALIST *tem;
00148 
00149   tem = find_index (name);
00150 
00151   which = tem ? tem->write_index : -1;
00152 
00153   if (macro_expansion_output_stream && !executing_string)
00154     append_to_expansion_output (input_text_offset + 1);
00155 
00156   get_rest_of_line (0, &index_entry);
00157   ignore_blank_line ();
00158 
00159   if (macro_expansion_output_stream && !executing_string)
00160     {
00161       char *index_line = xmalloc (strlen (index_entry) + 2);
00162       sprintf (index_line, "%s\n", index_entry);
00163       me_execute_string_keep_state (index_line, NULL);
00164       free (index_line);
00165     }
00166 
00167   if (which < 0)
00168     {
00169       line_error (_("Unknown index `%s'"), name);
00170       free (index_entry);
00171     }
00172   else
00173     {
00174       INDEX_ELT *new = xmalloc (sizeof (INDEX_ELT));
00175 
00176       index_counter++;
00177 
00178       /* Get output line number updated before doing anything.  */
00179       if (!html && !xml)
00180         flush_output ();
00181 
00182       new->next = the_indices[which];
00183       new->entry = NULL;
00184       new->entry_text = index_entry;
00185       /* Since footnotes are handled at the very end of the document,
00186          node name in the non-split HTML outputs always show the last
00187          node.  We artificially make it ``Footnotes''.  */
00188       if (html && !splitting && already_outputting_pending_notes)
00189         new->node = xstrdup (_("Footnotes"));
00190       else
00191         new->node = current_node ? current_node : xstrdup ("");
00192       if (!html && !xml && no_headers)
00193         {
00194           new->section = current_sectioning_number ();
00195           if (strlen (new->section) == 0)
00196             new->section_name = current_sectioning_name ();
00197           else
00198             new->section_name = "";
00199         }
00200       else
00201         {
00202           new->section = NULL;
00203           new->section_name = NULL;
00204         }
00205       new->code = tem->code;
00206       new->defining_line = line_number - 1;
00207       new->output_line = no_headers ? output_line_number : node_line_number;
00208       /* We need to make a copy since input_filename may point to
00209          something that goes away, for example, inside a macro.
00210          (see the findexerr test).  */
00211       new->defining_file = xstrdup (input_filename);
00212 
00213       if (html && splitting)
00214         {
00215           if (current_output_filename && *current_output_filename)
00216             new->output_file = filename_part (current_output_filename);
00217           else
00218             new->output_file = xstrdup ("");
00219         }
00220       else
00221         new->output_file = NULL;        
00222 
00223       new->entry_number = index_counter;
00224       the_indices[which] = new;
00225 
00226 #if 0
00227       /* The index breaks if there are colons in the entry.
00228          -- This is true, but it's too painful to force changing index
00229          entries to use `colon', and too confusing for users.  The real
00230          fix is to change Info support to support arbitrary characters
00231          in node names, and we're not ready to do that.  --karl,
00232          19mar02.  */
00233       if (strchr (new->entry_text, ':'))
00234         warning (_("Info cannot handle `:' in index entry `%s'"),
00235                  new->entry_text);
00236 #endif
00237 
00238       if (html)
00239         {
00240           /* Anchor.  */
00241           int removed_empty_elt = 0;
00242 
00243           /* We must put the anchor outside the <dl> and <ul> blocks.  */
00244           if (rollback_empty_tag ("dl"))
00245             removed_empty_elt = 1;
00246           else if (rollback_empty_tag ("ul"))
00247             removed_empty_elt = 2;
00248 
00249           add_word ("<a name=\"index-");
00250           add_escaped_anchor_name (index_entry, 0);
00251           add_word_args ("-%d\"></a>", index_counter);
00252 
00253           if (removed_empty_elt == 1)
00254             add_html_block_elt_args ("\n<dl>");
00255           else if (removed_empty_elt == 2)
00256             add_html_block_elt_args ("\n<ul>");
00257         }
00258     }
00259 
00260   if (xml)
00261     xml_insert_indexterm (index_entry, name);
00262 }
00263 
00264 /* The function which user defined index commands call. */
00265 static void
00266 gen_index (void)
00267 {
00268   char *name = xstrdup (command);
00269   if (strlen (name) >= strlen ("index"))
00270     name[strlen (name) - strlen ("index")] = 0;
00271   index_add_arg (name);
00272   free (name);
00273 }
00274 
00275 /* Define an index known as NAME.  We assign the slot number.
00276    If CODE is nonzero, make this a code index. */
00277 static void
00278 defindex (char *name, int code)
00279 {
00280   int i, slot;
00281 
00282   /* If it already exists, flush it. */
00283   undefindex (name);
00284 
00285   /* Try to find an empty slot. */
00286   slot = -1;
00287   for (i = 0; i < defined_indices; i++)
00288     if (!name_index_alist[i])
00289       {
00290         slot = i;
00291         break;
00292       }
00293 
00294   if (slot < 0)
00295     { /* No such luck.  Make space for another index. */
00296       slot = defined_indices;
00297       defined_indices++;
00298 
00299       name_index_alist = (INDEX_ALIST **)
00300         xrealloc (name_index_alist, (1 + defined_indices)
00301                                     * sizeof (INDEX_ALIST *));
00302       the_indices = (INDEX_ELT **)
00303         xrealloc (the_indices, (1 + defined_indices) * sizeof (INDEX_ELT *));
00304     }
00305 
00306   /* We have a slot.  Start assigning. */
00307   name_index_alist[slot] = xmalloc (sizeof (INDEX_ALIST));
00308   name_index_alist[slot]->name = xstrdup (name);
00309   name_index_alist[slot]->read_index = slot;
00310   name_index_alist[slot]->write_index = slot;
00311   name_index_alist[slot]->code = code;
00312 
00313   the_indices[slot] = NULL;
00314 }
00315 
00316 /* Define an index NAME, implicitly @code if CODE is nonzero.  */
00317 static void
00318 top_defindex (char *name, int code)
00319 {
00320   char *temp;
00321 
00322   temp = xmalloc (1 + strlen (name) + strlen ("index"));
00323   sprintf (temp, "%sindex", name);
00324   define_user_command (temp, gen_index, 0);
00325   defindex (name, code);
00326   free (temp);
00327 }
00328 
00329 /* Set up predefined indices.  */
00330 void
00331 init_indices (void)
00332 {
00333   int i;
00334 
00335   /* Create the default data structures. */
00336 
00337   /* Initialize data space. */
00338   if (!the_indices)
00339     {
00340       the_indices = xmalloc ((1 + defined_indices) * sizeof (INDEX_ELT *));
00341       the_indices[defined_indices] = NULL;
00342 
00343       name_index_alist = xmalloc ((1 + defined_indices)
00344                                   * sizeof (INDEX_ALIST *));
00345       name_index_alist[defined_indices] = NULL;
00346     }
00347 
00348   /* If there were existing indices, get rid of them now. */
00349   for (i = 0; i < defined_indices; i++)
00350     {
00351       if (name_index_alist[i])
00352         { /* Suppose we're called with two input files, and the first
00353              does a @synindex pg cp.  Then, when we get here to start
00354              the second file, the "pg" element won't get freed by
00355              undefindex (because it's pointing to "cp").  So free it
00356              here; otherwise, when we try to define the pg index again
00357              just below, it will still point to cp.  */
00358           undefindex (name_index_alist[i]->name);
00359 
00360           /* undefindex sets all this to null in some cases.  */
00361           if (name_index_alist[i])
00362             {
00363               free (name_index_alist[i]->name);
00364               free (name_index_alist[i]);
00365               name_index_alist[i] = NULL;
00366             }
00367         }
00368     }
00369 
00370   /* Add the default indices. */
00371   top_defindex ("cp", 0);           /* cp is the only non-code index.  */
00372   top_defindex ("fn", 1);
00373   top_defindex ("ky", 1);
00374   top_defindex ("pg", 1);
00375   top_defindex ("tp", 1);
00376   top_defindex ("vr", 1);
00377 }
00378 
00379 /* Given an index name, return the offset in the_indices of this index,
00380    or -1 if there is no such index. */
00381 static int
00382 translate_index (char *name)
00383 {
00384   INDEX_ALIST *which = find_index (name);
00385 
00386   if (which)
00387     return which->read_index;
00388   else
00389     return -1;
00390 }
00391 
00392 /* Return the index list which belongs to NAME. */
00393 INDEX_ELT *
00394 index_list (char *name)
00395 {
00396   int which = translate_index (name);
00397   if (which < 0)
00398     return (INDEX_ELT *) -1;
00399   else
00400     return the_indices[which];
00401 }
00402 
00403 /* Define a new index command.  Arg is name of index. */
00404 static void
00405 gen_defindex (int code)
00406 {
00407   char *name;
00408   get_rest_of_line (0, &name);
00409 
00410   if (find_index (name))
00411     {
00412       line_error (_("Index `%s' already exists"), name);
00413     }
00414   else
00415     {
00416       char *temp = xmalloc (strlen (name) + sizeof ("index"));
00417       sprintf (temp, "%sindex", name);
00418       define_user_command (temp, gen_index, 0);
00419       defindex (name, code);
00420       free (temp);
00421     }
00422 
00423   free (name);
00424 }
00425 
00426 void
00427 cm_defindex (void)
00428 {
00429   gen_defindex (0);
00430 }
00431 
00432 void
00433 cm_defcodeindex (void)
00434 {
00435   gen_defindex (1);
00436 }
00437 
00438 /* Expects 2 args, on the same line.  Both are index abbreviations.
00439    Make the first one be a synonym for the second one, i.e. make the
00440    first one have the same index as the second one. */
00441 void
00442 cm_synindex (void)
00443 {
00444   int source, target;
00445   char *abbrev1, *abbrev2;
00446 
00447   skip_whitespace ();
00448   get_until_in_line (0, " ", &abbrev1);
00449   target = find_index_offset (abbrev1);
00450   skip_whitespace ();
00451   get_until_in_line (0, " ", &abbrev2);
00452   source = find_index_offset (abbrev2);
00453   if (source < 0 || target < 0)
00454     {
00455       line_error (_("Unknown index `%s' and/or `%s' in @synindex"),
00456                   abbrev1, abbrev2);
00457     }
00458   else
00459     {
00460       if (xml && !docbook)
00461         xml_synindex (abbrev1, abbrev2);
00462       else
00463         name_index_alist[target]->write_index
00464           = name_index_alist[source]->write_index;
00465     }
00466 
00467   free (abbrev1);
00468   free (abbrev2);
00469 }
00470 
00471 void
00472 cm_pindex (void)                    /* Pinhead index. */
00473 {
00474   index_add_arg ("pg");
00475 }
00476 
00477 void
00478 cm_vindex (void)                    /* Variable index. */
00479 {
00480   index_add_arg ("vr");
00481 }
00482 
00483 void
00484 cm_kindex (void)                    /* Key index. */
00485 {
00486   index_add_arg ("ky");
00487 }
00488 
00489 void
00490 cm_cindex (void)                    /* Concept index. */
00491 {
00492   index_add_arg ("cp");
00493 }
00494 
00495 void
00496 cm_findex (void)                    /* Function index. */
00497 {
00498   index_add_arg ("fn");
00499 }
00500 
00501 void
00502 cm_tindex (void)                    /* Data Type index. */
00503 {
00504   index_add_arg ("tp");
00505 }
00506 
00507 int
00508 index_element_compare (const void *element1, const void *element2)
00509 {
00510   INDEX_ELT **elt1 = (INDEX_ELT **) element1;
00511   INDEX_ELT **elt2 = (INDEX_ELT **) element2;
00512 
00513   return index_compare_fn ((*elt1)->entry, (*elt2)->entry);
00514 }
00515 
00516 /* Force all index entries to be unique. */
00517 static void
00518 make_index_entries_unique (INDEX_ELT **array, int count)
00519 {
00520   int i, j;
00521   INDEX_ELT **copy;
00522   int counter = 1;
00523 
00524   copy = xmalloc ((1 + count) * sizeof (INDEX_ELT *));
00525 
00526   for (i = 0, j = 0; i < count; i++)
00527     {
00528       if (i == (count - 1)
00529           || array[i]->node != array[i + 1]->node
00530           || !STREQ (array[i]->entry, array[i + 1]->entry))
00531         copy[j++] = array[i];
00532       else
00533         {
00534           free (array[i]->entry);
00535           free (array[i]->entry_text);
00536           free (array[i]);
00537         }
00538     }
00539   copy[j] = NULL;
00540 
00541   /* Now COPY contains only unique entries.  Duplicated entries in the
00542      original array have been freed.  Replace the current array with
00543      the copy, fixing the NEXT pointers. */
00544   for (i = 0; copy[i]; i++)
00545     {
00546       copy[i]->next = copy[i + 1];
00547 
00548       /* Fix entry names which are the same.  They point to different nodes,
00549          so we make the entry name unique. */
00550       if (copy[i+1]
00551           && STREQ (copy[i]->entry, copy[i + 1]->entry)
00552           && !html)
00553         {
00554           char *new_entry_name;
00555 
00556           new_entry_name = xmalloc (10 + strlen (copy[i]->entry));
00557           sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter);
00558           free (copy[i]->entry);
00559           copy[i]->entry = new_entry_name;
00560           counter++;
00561         }
00562       else
00563         counter = 1;
00564 
00565       array[i] = copy[i];
00566     }
00567   array[i] = NULL;
00568 
00569   /* Free the storage used only by COPY. */
00570   free (copy);
00571 }
00572 
00573 
00574 /* Sort the index passed in INDEX, returning an array of pointers to
00575    elements.  The array is terminated with a NULL pointer.  */
00576 
00577 static INDEX_ELT **
00578 sort_index (INDEX_ELT *index)
00579 {
00580   INDEX_ELT **array;
00581   INDEX_ELT *temp;
00582   int count = 0;
00583   int save_line_number = line_number;
00584   char *save_input_filename = input_filename;
00585   int save_html = html;
00586 
00587   /* Pretend we are in non-HTML mode, for the purpose of getting the
00588      expanded index entry that lacks any markup and other HTML escape
00589      characters which could produce a wrong sort order.  */
00590   /* fixme: html: this still causes some markup, such as non-ASCII
00591      characters @AE{} etc., to sort incorrectly.  */
00592   html = 0;
00593 
00594   for (temp = index, count = 0; temp; temp = temp->next, count++)
00595     ;
00596   /* We have the length, now we can allocate an array. */
00597   array = xmalloc ((count + 1) * sizeof (INDEX_ELT *));
00598 
00599   for (temp = index, count = 0; temp; temp = temp->next, count++)
00600     {
00601       /* Allocate new memory for the return array, since parts of the
00602          original INDEX get freed.  Otherwise, if the document calls
00603          @printindex twice on the same index, with duplicate entries,
00604          we'll have garbage the second time.  There are cleaner ways to
00605          deal, but this will suffice for now.  */
00606       array[count] = xmalloc (sizeof (INDEX_ELT));
00607       *(array[count]) = *(temp);  /* struct assignment, hope it's ok */
00608 
00609       /* Adjust next pointers to use the new memory.  */
00610       if (count > 0)
00611         array[count-1]->next = array[count];
00612 
00613       /* Set line number and input filename to the source line for this
00614          index entry, as this expansion finds any errors.  */
00615       line_number = array[count]->defining_line;
00616       input_filename = array[count]->defining_file;
00617 
00618       /* If this particular entry should be printed as a "code" index,
00619          then expand it as @code{entry}, i.e., as in fixed-width font.  */
00620       array[count]->entry = expansion (temp->entry_text, array[count]->code);
00621     }
00622   array[count] = NULL;    /* terminate the array. */
00623 
00624   line_number = save_line_number;
00625   input_filename = save_input_filename;
00626   html = save_html;
00627 
00628 #ifdef HAVE_STRCOLL
00629   /* This is not perfect.  We should set (then restore) the locale to the
00630      documentlanguage, so strcoll operates according to the document's
00631      locale, not the user's.  For now, I'm just going to assume that
00632      those few new documents which use @documentlanguage will be
00633      processed in the appropriate locale.  In any case, don't use
00634      strcoll in the C (aka POSIX) locale, that is the ASCII ordering.  */
00635   if (language_code != en)
00636     {
00637       char *lang_env = getenv ("LANG");
00638       if (lang_env && !STREQ (lang_env, "C") && !STREQ (lang_env, "POSIX"))
00639         index_compare_fn = strcoll;
00640     }
00641 #endif /* HAVE_STRCOLL */
00642 
00643   /* Sort the array. */
00644   qsort (array, count, sizeof (INDEX_ELT *), index_element_compare);
00645 
00646   /* Remove duplicate entries.  */
00647   make_index_entries_unique (array, count);
00648 
00649   /* Replace the original index with the sorted one, in case the
00650      document wants to print it again.  If the index wasn't empty.  */
00651   if (index)
00652     *index = **array;
00653 
00654   return array;
00655 }
00656 
00657 static void
00658 insert_index_output_line_no (int line_number, int output_line_number_len)
00659 {
00660   int last_column;
00661   int str_size = output_line_number_len + strlen (_("(line )"))
00662     + sizeof (NULL);
00663   char *out_line_no_str = (char *) xmalloc (str_size + 1);
00664 
00665   /* Do not translate ``(line NNN)'' below for !no_headers case (Info output),
00666      because it's something like the ``* Menu'' strings.  For plaintext output
00667      it should be translated though.  */
00668   sprintf (out_line_no_str,
00669       no_headers ? _("(line %*d)") : "(line %*d)",
00670       output_line_number_len, line_number);
00671 
00672   {
00673     int i = output_paragraph_offset; 
00674     while (0 < i && output_paragraph[i-1] != '\n')
00675       i--;
00676     last_column = output_paragraph_offset - i;
00677   }
00678 
00679   if (last_column + strlen (out_line_no_str) > fill_column)
00680     {
00681       insert ('\n');
00682       last_column = 0;
00683     }
00684 
00685   while (last_column + strlen (out_line_no_str) < fill_column)
00686     {
00687       insert (' ');
00688       last_column++;
00689     }
00690 
00691   insert_string (out_line_no_str);
00692   insert ('\n');
00693 
00694   free (out_line_no_str);
00695 }
00696 
00697 /* Nonzero means that we are in the middle of printing an index. */
00698 int printing_index = 0;
00699 
00700 /* Takes one arg, a short name of an index to print.
00701    Outputs a menu of the sorted elements of the index. */
00702 void
00703 cm_printindex (void)
00704 {
00705   char *index_name;
00706   get_rest_of_line (0, &index_name);
00707 
00708   /* get_rest_of_line increments the line number by one,
00709      so to make warnings/errors point to the correct line,
00710      we decrement the line_number again.  */
00711   if (!handling_delayed_writes)
00712     line_number--;
00713 
00714   if (xml && !docbook)
00715     {
00716       xml_insert_element (PRINTINDEX, START);
00717       insert_string (index_name);
00718       xml_insert_element (PRINTINDEX, END);
00719     }
00720   else if (!handling_delayed_writes)
00721     {
00722       int command_len = sizeof ("@ ") + strlen (command) + strlen (index_name);
00723       char *index_command = xmalloc (command_len + 1);
00724 
00725       close_paragraph ();
00726       if (docbook)
00727         xml_begin_index ();
00728 
00729       sprintf (index_command, "@%s %s", command, index_name);
00730       register_delayed_write (index_command);
00731       free (index_command);
00732     }
00733   else
00734     {
00735       int item;
00736       INDEX_ELT *index;
00737       INDEX_ELT *last_index = 0;
00738       INDEX_ELT **array;
00739       unsigned line_length;
00740       char *line;
00741       int saved_inhibit_paragraph_indentation = inhibit_paragraph_indentation;
00742       int saved_filling_enabled = filling_enabled;
00743       int saved_line_number = line_number;
00744       char *saved_input_filename = input_filename;
00745       unsigned output_line_number_len;
00746 
00747       index = index_list (index_name);
00748       if (index == (INDEX_ELT *)-1)
00749         {
00750           line_error (_("Unknown index `%s' in @printindex"), index_name);
00751           free (index_name);
00752           return;
00753         }
00754 
00755       /* Do this before sorting, so execute_string is in the good environment */
00756       if (xml && docbook)
00757         xml_begin_index ();
00758 
00759       /* Do this before sorting, so execute_string in index_element_compare
00760          will give the same results as when we actually print.  */
00761       printing_index = 1;
00762       filling_enabled = 0;
00763       inhibit_paragraph_indentation = 1;
00764       xml_sort_index = 1;
00765       array = sort_index (index);
00766       xml_sort_index = 0;
00767       close_paragraph ();
00768       if (html)
00769         add_html_block_elt_args ("<ul class=\"index-%s\" compact>",
00770                                  index_name);
00771       else if (!no_headers && !docbook)
00772         { /* Info.  Add magic cookie for info readers (to treat this
00773              menu differently), and the usual start-of-menu.  */
00774           add_char ('\0');
00775           add_word ("\010[index");
00776           add_char ('\0');
00777           add_word ("\010]\n");
00778           add_word ("* Menu:\n\n");
00779         }
00780 
00781       me_inhibit_expansion++;
00782 
00783       /* This will probably be enough.  */
00784       line_length = 100;
00785       line = xmalloc (line_length);
00786 
00787       {
00788         char *max_output_line_number = (char *) xmalloc (25 * sizeof (char));
00789 
00790         if (no_headers)
00791           sprintf (max_output_line_number, "%d", output_line_number);
00792         else
00793           {
00794             INDEX_ELT *tmp_entry = index;
00795             unsigned tmp = 0;
00796             for (tmp_entry = index; tmp_entry; tmp_entry = tmp_entry->next)
00797               tmp = tmp_entry->output_line > tmp ? tmp_entry->output_line : tmp;
00798             sprintf (max_output_line_number, "%d", tmp);
00799           }
00800 
00801         output_line_number_len = strlen (max_output_line_number);
00802         free (max_output_line_number);
00803       }
00804 
00805       for (item = 0; (index = array[item]); item++)
00806         {
00807           /* A pathological document might have an index entry outside of any
00808              node.  Don't crash; try using the section name instead.  */
00809           char *index_node = index->node;
00810 
00811           line_number = index->defining_line;
00812           input_filename = index->defining_file;
00813 
00814           if ((!index_node || !*index_node) && html)
00815             index_node = toc_find_section_of_node (index_node);
00816 
00817           if (!index_node || !*index_node)
00818             {
00819               line_error (_("Entry for index `%s' outside of any node"),
00820                           index_name);
00821               if (html || !no_headers)
00822                 index_node = (char *) _("(outside of any node)");
00823             }
00824 
00825           if (html)
00826             {
00827               /* For HTML, we need to expand and HTML-escape the
00828                  original entry text, at the same time.  Consider
00829                  @cindex J@"urgen.  We want J&uuml;urgen.  We can't
00830                  expand and then escape since we'll end up with
00831                  J&amp;uuml;rgen.  We can't escape and then expand
00832                  because then `expansion' will see J@&quot;urgen, and
00833                  @&quot;urgen is not a command.  */
00834               char *html_entry =
00835                 maybe_escaped_expansion (index->entry_text, index->code, 1);
00836 
00837               add_html_block_elt_args ("\n<li><a href=\"%s#index-",
00838                   (splitting && index->output_file) ? index->output_file : "");
00839               add_escaped_anchor_name (index->entry_text, 0);
00840               add_word_args ("-%d\">%s</a>: ", index->entry_number,
00841                   html_entry);
00842               free (html_entry);
00843 
00844               add_word ("<a href=\"");
00845               if (index->node && *index->node)
00846                 {
00847                   /* Ensure any non-macros in the node name are expanded.  */
00848                   char *expanded_index;
00849 
00850                   in_fixed_width_font++;
00851                   expanded_index = expansion (index_node, 0);
00852                   in_fixed_width_font--;
00853                   add_anchor_name (expanded_index, 1);
00854                 expanded_index = escape_string (expanded_index);
00855                   add_word_args ("\">%s</a>", expanded_index);
00856                   free (expanded_index);
00857                 }
00858               else if (STREQ (index_node, _("(outside of any node)")))
00859                 {
00860                   add_anchor_name (index_node, 1);
00861                   add_word_args ("\">%s</a>", index_node);
00862                 }
00863               else
00864                 /* If we use the section instead of the (missing) node, then
00865                    index_node already includes all we need except the #.  */
00866                 add_word_args ("#%s</a>", index_node);
00867 
00868               add_html_block_elt ("</li>");
00869             }
00870           else if (xml && docbook)
00871             {
00872               /* In the DocBook case, the expanded index entry is not
00873                  good for us, since it was expanded for non-DocBook mode
00874                  inside sort_index.  So we send the original entry text
00875                  to be used with execute_string.  */
00876               xml_insert_indexentry (index->entry_text, index_node);
00877             }
00878           else
00879             {
00880               unsigned new_length = strlen (index->entry);
00881 
00882               if (new_length < 50) /* minimum length used below */
00883                 new_length = 50;
00884               new_length += strlen (index_node) + 7; /* * : .\n\0 */
00885 
00886               if (new_length > line_length)
00887                 {
00888                   line_length = new_length;
00889                   line = xrealloc (line, line_length);
00890                 }
00891               /* Print the entry, nicely formatted.  We've already
00892                  expanded any commands in index->entry, including any
00893                  implicit @code.  Thus, can't call execute_string, since
00894                  @@ has turned into @. */
00895               if (!no_headers)
00896                 {
00897                   sprintf (line, "* %-37s  ", index->entry);
00898                   line[2 + strlen (index->entry)] = ':';
00899                   insert_string (line);
00900                   /* Make sure any non-macros in the node name are expanded.  */
00901                   in_fixed_width_font++;
00902                   execute_string ("%s. ", index_node);
00903                   insert_index_output_line_no (index->output_line,
00904                       output_line_number_len);
00905                   in_fixed_width_font--;
00906                 }
00907               else
00908                 {
00909                   /* With --no-headers, the @node lines are gone, so
00910                      there's little sense in referring to them in the
00911                      index.  Instead, output the number or name of the
00912                      section that corresponds to that node.  */
00913                   sprintf (line, "%-*s ", number_sections ? 46 : 1, index->entry);
00914                   line[strlen (index->entry)] = ':';
00915                   insert_string (line);
00916 
00917                   if (strlen (index->section) > 0)
00918                     { /* We got your number.  */
00919                       insert_string ((char *) _("See "));
00920                       insert_string (index->section);
00921                     }
00922                   else
00923                     { /* Sigh, index in an @unnumbered. :-\  */
00924                       insert_string ("\n          ");
00925                       insert_string ((char *) _("See "));
00926                       insert_string ("``");
00927                       insert_string (expansion (index->section_name, 0));
00928                       insert_string ("''");
00929                     }
00930 
00931                   insert_string (". ");
00932                   insert_index_output_line_no (index->output_line,
00933                       output_line_number_len);
00934                 }
00935             }
00936 
00937           /* Prevent `output_paragraph' from growing to the size of the
00938              whole index.  */
00939           flush_output ();
00940           last_index = index;
00941         }
00942 
00943       free (line);
00944 
00945       me_inhibit_expansion--;
00946       printing_index = 0;
00947 
00948       close_single_paragraph ();
00949       filling_enabled = saved_filling_enabled;
00950       inhibit_paragraph_indentation = saved_inhibit_paragraph_indentation;
00951       input_filename = saved_input_filename;
00952       line_number = saved_line_number;
00953 
00954       if (html)
00955         add_html_block_elt ("</ul>");
00956       else if (xml && docbook)
00957         xml_end_index ();
00958     }
00959 
00960   free (index_name);
00961   /* Re-increment the line number, because get_rest_of_line
00962      left us looking at the next line after the command.  */
00963   line_number++;
00964 }