Back to index

tetex-bin  3.0
echo-area.c
Go to the documentation of this file.
00001 /* echo-area.c -- how to read a line in the echo area.
00002    $Id: echo-area.c,v 1.7 2004/12/14 00:15:36 karl Exp $
00003 
00004    Copyright (C) 1993, 1997, 1998, 1999, 2001, 2004 Free Software
00005    Foundation, 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
00019    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 
00021    Written by Brian Fox (bfox@ai.mit.edu). */
00022 
00023 #include "info.h"
00024 
00025 #if defined (FD_SET)
00026 #  if defined (hpux)
00027 #    define fd_set_cast(x) (int *)(x)
00028 #  else
00029 #    define fd_set_cast(x) (fd_set *)(x)
00030 #  endif /* !hpux */
00031 #endif /* FD_SET */
00032 
00033 /* Non-zero means that C-g was used to quit reading input. */
00034 int info_aborted_echo_area = 0;
00035 
00036 /* Non-zero means that the echo area is being used to read input. */
00037 int echo_area_is_active = 0;
00038 
00039 /* The address of the last command executed in the echo area. */
00040 VFunction *ea_last_executed_command = (VFunction *)NULL;
00041 
00042 /* Non-zero means that the last command executed while reading input
00043    killed some text. */
00044 int echo_area_last_command_was_kill = 0;
00045 
00046 /* Variables which hold on to the current state of the input line. */
00047 static char input_line[1 + EA_MAX_INPUT];
00048 static char *input_line_prompt;
00049 static int input_line_point;
00050 static int input_line_beg;
00051 static int input_line_end;
00052 static NODE input_line_node = {
00053   (char *)NULL, (char *)NULL, (char *)NULL, input_line,
00054   EA_MAX_INPUT, 0, N_IsInternal
00055 };
00056 
00057 static void echo_area_initialize_node (void);
00058 static void push_echo_area (void), pop_echo_area (void);
00059 static int echo_area_stack_contains_completions_p (void);
00060 
00061 static void ea_kill_text (int from, int to);
00062 
00063 /* Non-zero means we force the user to complete. */
00064 static int echo_area_must_complete_p = 0;
00065 static int completions_window_p (WINDOW *window);
00066 
00067 /* If non-null, this is a window which was specifically created to display
00068    possible completions output.  We remember it so we can delete it when
00069    appropriate. */
00070 static WINDOW *echo_area_completions_window = (WINDOW *)NULL;
00071 
00072 /* Variables which keep track of the window which was active prior to
00073    entering the echo area. */
00074 static WINDOW *calling_window = (WINDOW *)NULL;
00075 static NODE *calling_window_node = (NODE *)NULL;
00076 static long calling_window_point = 0;
00077 static long calling_window_pagetop = 0;
00078 
00079 /* Remember the node and pertinent variables of the calling window. */
00080 static void
00081 remember_calling_window (WINDOW *window)
00082 {
00083   /* Only do this if the calling window is not the completions window, or,
00084      if it is the completions window and there is no other window. */
00085   if (!completions_window_p (window) ||
00086       ((window == windows) && !(window->next)))
00087     {
00088       calling_window = window;
00089       calling_window_node = window->node;
00090       calling_window_point = window->point;
00091       calling_window_pagetop = window->pagetop;
00092     }
00093 }
00094 
00095 /* Restore the caller's window so that it shows the node that it was showing
00096    on entry to info_read_xxx_echo_area (). */
00097 static void
00098 restore_calling_window (void)
00099 {
00100   register WINDOW *win, *compwin = (WINDOW *)NULL;
00101 
00102   /* If the calling window is still visible, and it is the window that
00103      we used for completions output, then restore the calling window. */
00104   for (win = windows; win; win = win->next)
00105     {
00106       if (completions_window_p (win))
00107         compwin = win;
00108 
00109       if (win == calling_window && win == compwin)
00110         {
00111           window_set_node_of_window (calling_window, calling_window_node);
00112           calling_window->point = calling_window_point;
00113           calling_window->pagetop = calling_window_pagetop;
00114           compwin = (WINDOW *)NULL;
00115           break;
00116         }
00117     }
00118 
00119   /* Delete the completions window if it is still present, it isn't the
00120      last window on the screen, and there aren't any prior echo area reads
00121      pending which created a completions window. */
00122   if (compwin)
00123     {
00124       if ((compwin != windows || windows->next) &&
00125           !echo_area_stack_contains_completions_p ())
00126         {
00127           WINDOW *next;
00128           int pagetop = 0;
00129           int start = 0;
00130           int end = 0;
00131           int amount = 0;
00132 
00133           next = compwin->next;
00134           if (next)
00135             {
00136               start = next->first_row;
00137               end = start + next->height;
00138               amount = - (compwin->height + 1);
00139               pagetop = next->pagetop;
00140             }
00141 
00142           info_delete_window_internal (compwin);
00143 
00144           /* This is not necessary because info_delete_window_internal ()
00145              calls echo_area_inform_of_deleted_window (), which does the
00146              right thing. */
00147 #if defined (UNNECESSARY)
00148           echo_area_completions_window = (WINDOW *)NULL;
00149 #endif /* UNNECESSARY */
00150 
00151           if (next)
00152             {
00153               display_scroll_display (start, end, amount);
00154               next->pagetop = pagetop;
00155               display_update_display (windows);
00156             }
00157         }
00158     }
00159 }
00160 
00161 /* Set up a new input line with PROMPT. */
00162 static void
00163 initialize_input_line (char *prompt)
00164 {
00165   input_line_prompt = prompt;
00166   if (prompt)
00167     strcpy (input_line, prompt);
00168   else
00169     input_line[0] = '\0';
00170 
00171   input_line_beg = input_line_end = input_line_point = strlen (prompt);
00172 }
00173 
00174 static char *
00175 echo_area_after_read (void)
00176 {
00177   char *return_value;
00178 
00179   if (info_aborted_echo_area)
00180     {
00181       info_aborted_echo_area = 0;
00182       return_value = (char *)NULL;
00183     }
00184   else
00185     {
00186       if (input_line_beg == input_line_end)
00187         return_value = xstrdup ("");
00188       else
00189         {
00190           int line_len = input_line_end - input_line_beg;
00191           return_value = (char *) xmalloc (1 + line_len);
00192           strncpy (return_value, &input_line[input_line_beg], line_len);
00193           return_value[line_len] = '\0';
00194         }
00195     }
00196   return (return_value);
00197 }
00198 
00199 /* Read a line of text in the echo area.  Return a malloc ()'ed string,
00200    or NULL if the user aborted out of this read.  WINDOW is the currently
00201    active window, so that we can restore it when we need to.  PROMPT, if
00202    non-null, is a prompt to print before reading the line. */
00203 char *
00204 info_read_in_echo_area (WINDOW *window, char *prompt)
00205 {
00206   char *line;
00207 
00208   /* If the echo area is already active, remember the current state. */
00209   if (echo_area_is_active)
00210     push_echo_area ();
00211 
00212   /* Initialize our local variables. */
00213   initialize_input_line (prompt);
00214 
00215   /* Initialize the echo area for the first (but maybe not the last) time. */
00216   echo_area_initialize_node ();
00217 
00218   /* Save away the original node of this window, and the window itself,
00219      so echo area commands can temporarily use this window. */
00220   remember_calling_window (window);
00221 
00222   /* Let the rest of Info know that the echo area is active. */
00223   echo_area_is_active++;
00224   active_window = the_echo_area;
00225 
00226   /* Read characters in the echo area. */
00227   info_read_and_dispatch ();
00228 
00229   echo_area_is_active--;
00230 
00231   /* Restore the original active window and show point in it. */
00232   active_window = calling_window;
00233   restore_calling_window ();
00234   display_cursor_at_point (active_window);
00235   fflush (stdout);
00236 
00237   /* Get the value of the line. */
00238   line = echo_area_after_read ();
00239 
00240   /* If there is a previous loop waiting for us, restore it now. */
00241   if (echo_area_is_active)
00242     pop_echo_area ();
00243 
00244   /* Return the results to the caller. */
00245   return (line);
00246 }
00247 
00248 /* (re) Initialize the echo area node. */
00249 static void
00250 echo_area_initialize_node (void)
00251 {
00252   register int i;
00253 
00254   for (i = input_line_end; (unsigned int) i < sizeof (input_line); i++)
00255     input_line[i] = ' ';
00256 
00257   input_line[i - 1] = '\n';
00258   window_set_node_of_window (the_echo_area, &input_line_node);
00259   input_line[input_line_end] = '\n';
00260 }
00261 
00262 /* Prepare to read characters in the echo area.  This can initialize the
00263    echo area node, but its primary purpose is to side effect the input
00264    line buffer contents. */
00265 void
00266 echo_area_prep_read (void)
00267 {
00268   if (the_echo_area->node != &input_line_node)
00269     echo_area_initialize_node ();
00270 
00271   the_echo_area->point = input_line_point;
00272   input_line[input_line_end] = '\n';
00273   display_update_one_window (the_echo_area);
00274   display_cursor_at_point (active_window);
00275 }
00276 
00277 
00278 /* **************************************************************** */
00279 /*                                                                  */
00280 /*                   Echo Area Movement Commands                    */
00281 /*                                                                  */
00282 /* **************************************************************** */
00283 
00284 DECLARE_INFO_COMMAND (ea_forward, _("Move forward a character"))
00285 {
00286   if (count < 0)
00287     ea_backward (window, -count, key);
00288   else
00289     {
00290       input_line_point += count;
00291       if (input_line_point > input_line_end)
00292         input_line_point = input_line_end;
00293     }
00294 }
00295 
00296 DECLARE_INFO_COMMAND (ea_backward, _("Move backward a character"))
00297 {
00298   if (count < 0)
00299     ea_forward (window, -count, key);
00300   else
00301     {
00302       input_line_point -= count;
00303       if (input_line_point < input_line_beg)
00304         input_line_point = input_line_beg;
00305     }
00306 }
00307 
00308 DECLARE_INFO_COMMAND (ea_beg_of_line, _("Move to the start of this line"))
00309 {
00310   input_line_point = input_line_beg;
00311 }
00312 
00313 DECLARE_INFO_COMMAND (ea_end_of_line, _("Move to the end of this line"))
00314 {
00315   input_line_point = input_line_end;
00316 }
00317 
00318 #define alphabetic(c) (islower (c) || isupper (c) || isdigit (c))
00319 
00320 /* Move forward a word in the input line. */
00321 DECLARE_INFO_COMMAND (ea_forward_word, _("Move forward a word"))
00322 {
00323   int c;
00324 
00325   if (count < 0)
00326     ea_backward_word (window, -count, key);
00327   else
00328     {
00329       while (count--)
00330         {
00331           if (input_line_point == input_line_end)
00332             return;
00333 
00334           /* If we are not in a word, move forward until we are in one.
00335              Then, move forward until we hit a non-alphabetic character. */
00336           c = input_line[input_line_point];
00337 
00338           if (!alphabetic (c))
00339             {
00340               while (++input_line_point < input_line_end)
00341                 {
00342                   c = input_line[input_line_point];
00343                   if (alphabetic (c))
00344                     break;
00345                 }
00346             }
00347 
00348           if (input_line_point == input_line_end)
00349             return;
00350 
00351           while (++input_line_point < input_line_end)
00352             {
00353               c = input_line[input_line_point];
00354               if (!alphabetic (c))
00355                 break;
00356             }
00357         }
00358     }
00359 }
00360 
00361 DECLARE_INFO_COMMAND (ea_backward_word, _("Move backward a word"))
00362 {
00363   int c;
00364 
00365   if (count < 0)
00366     ea_forward_word (window, -count, key);
00367   else
00368     {
00369       while (count--)
00370         {
00371           if (input_line_point == input_line_beg)
00372             return;
00373 
00374           /* Like ea_forward_word (), except that we look at the
00375              characters just before point. */
00376 
00377           c = input_line[input_line_point - 1];
00378 
00379           if (!alphabetic (c))
00380             {
00381               while ((--input_line_point) != input_line_beg)
00382                 {
00383                   c = input_line[input_line_point - 1];
00384                   if (alphabetic (c))
00385                     break;
00386                 }
00387             }
00388 
00389           while (input_line_point != input_line_beg)
00390             {
00391               c = input_line[input_line_point - 1];
00392               if (!alphabetic (c))
00393                 break;
00394               else
00395                 --input_line_point;
00396             }
00397         }
00398     }
00399 }
00400 
00401 DECLARE_INFO_COMMAND (ea_delete, _("Delete the character under the cursor"))
00402 {
00403   register int i;
00404 
00405   if (count < 0)
00406     ea_rubout (window, -count, key);
00407   else
00408     {
00409       if (input_line_point == input_line_end)
00410         return;
00411 
00412       if (info_explicit_arg || count > 1)
00413         {
00414           int orig_point;
00415 
00416           orig_point = input_line_point;
00417           ea_forward (window, count, key);
00418           ea_kill_text (orig_point, input_line_point);
00419           input_line_point = orig_point;
00420         }
00421       else
00422         {
00423           for (i = input_line_point; i < input_line_end; i++)
00424             input_line[i] = input_line[i + 1];
00425 
00426           input_line_end--;
00427         }
00428     }
00429 }
00430 
00431 DECLARE_INFO_COMMAND (ea_rubout, _("Delete the character behind the cursor"))
00432 {
00433   if (count < 0)
00434     ea_delete (window, -count, key);
00435   else
00436     {
00437       int start;
00438 
00439       if (input_line_point == input_line_beg)
00440         return;
00441 
00442       start = input_line_point;
00443       ea_backward (window, count, key);
00444 
00445       if (info_explicit_arg || count > 1)
00446         ea_kill_text (start, input_line_point);
00447       else
00448         ea_delete (window, count, key);
00449     }
00450 }
00451 
00452 DECLARE_INFO_COMMAND (ea_abort, _("Cancel or quit operation"))
00453 {
00454   /* If any text, just discard it, and restore the calling window's node.
00455      If no text, quit. */
00456   if (input_line_end != input_line_beg)
00457     {
00458       terminal_ring_bell ();
00459       input_line_end = input_line_point = input_line_beg;
00460       if (calling_window->node != calling_window_node)
00461         restore_calling_window ();
00462     }
00463   else
00464     info_aborted_echo_area = 1;
00465 }
00466 
00467 DECLARE_INFO_COMMAND (ea_newline, _("Accept (or force completion of) this line"))
00468 {
00469   /* Stub does nothing.  Simply here to see if it has been executed. */
00470 }
00471 
00472 DECLARE_INFO_COMMAND (ea_quoted_insert, _("Insert next character verbatim"))
00473 {
00474   unsigned char character;
00475 
00476   character = info_get_another_input_char ();
00477   ea_insert (window, count, character);
00478 }
00479 
00480 DECLARE_INFO_COMMAND (ea_insert, _("Insert this character"))
00481 {
00482   register int i;
00483 
00484   if ((input_line_end + 1) == EA_MAX_INPUT)
00485     {
00486       terminal_ring_bell ();
00487       return;
00488     }
00489 
00490   for (i = input_line_end + 1; i != input_line_point; i--)
00491     input_line[i] = input_line[i - 1];
00492 
00493   input_line[input_line_point] = key;
00494   input_line_point++;
00495   input_line_end++;
00496 }
00497 
00498 DECLARE_INFO_COMMAND (ea_tab_insert, _("Insert a TAB character"))
00499 {
00500   ea_insert (window, count, '\t');
00501 }
00502 
00503 /* Transpose the characters at point.  If point is at the end of the line,
00504    then transpose the characters before point. */
00505 DECLARE_INFO_COMMAND (ea_transpose_chars, _("Transpose characters at point"))
00506 {
00507   /* Handle conditions that would make it impossible to transpose
00508      characters. */
00509   if (!count || !input_line_point || (input_line_end - input_line_beg) < 2)
00510     return;
00511 
00512   while (count)
00513     {
00514       int t;
00515       if (input_line_point == input_line_end)
00516         {
00517           t = input_line[input_line_point - 1];
00518 
00519           input_line[input_line_point - 1] = input_line[input_line_point - 2];
00520           input_line[input_line_point - 2] = t;
00521         }
00522       else
00523         {
00524           t = input_line[input_line_point];
00525 
00526           input_line[input_line_point] = input_line[input_line_point - 1];
00527           input_line[input_line_point - 1] = t;
00528 
00529           if (count < 0 && input_line_point != input_line_beg)
00530             input_line_point--;
00531           else
00532             input_line_point++;
00533         }
00534 
00535       if (count < 0)
00536         count++;
00537       else
00538         count--;
00539     }
00540 }
00541 
00542 /* **************************************************************** */
00543 /*                                                                  */
00544 /*                   Echo Area Killing and Yanking                  */
00545 /*                                                                  */
00546 /* **************************************************************** */
00547 
00548 static char **kill_ring = (char **)NULL;
00549 static int kill_ring_index = 0; /* Number of kills appearing in KILL_RING. */
00550 static int kill_ring_slots = 0; /* Number of slots allocated to KILL_RING. */
00551 static int kill_ring_loc = 0;   /* Location of current yank pointer. */
00552 
00553 /* The largest number of kills that we remember at one time. */
00554 static int max_retained_kills = 15;
00555 
00556 DECLARE_INFO_COMMAND (ea_yank, _("Yank back the contents of the last kill"))
00557 {
00558   register int i;
00559   register char *text;
00560 
00561   if (!kill_ring_index)
00562     {
00563       inform_in_echo_area ((char *) _("Kill ring is empty"));
00564       return;
00565     }
00566 
00567   text = kill_ring[kill_ring_loc];
00568 
00569   for (i = 0; text[i]; i++)
00570     ea_insert (window, 1, text[i]);
00571 }
00572 
00573 /* If the last command was yank, or yank_pop, and the text just before
00574    point is identical to the current kill item, then delete that text
00575    from the line, rotate the index down, and yank back some other text. */
00576 DECLARE_INFO_COMMAND (ea_yank_pop, _("Yank back a previous kill"))
00577 {
00578   register int len;
00579 
00580   if (((ea_last_executed_command != (VFunction *) ea_yank) &&
00581        (ea_last_executed_command != (VFunction *) ea_yank_pop)) ||
00582       (kill_ring_index == 0))
00583     return;
00584 
00585   len = strlen (kill_ring[kill_ring_loc]);
00586 
00587   /* Delete the last yanked item from the line. */
00588   {
00589     register int i, counter;
00590 
00591     counter = input_line_end - input_line_point;
00592     
00593     for (i = input_line_point - len; counter; i++, counter--)
00594       input_line[i] = input_line[i + len];
00595 
00596     input_line_end -= len;
00597     input_line_point -= len;
00598   }
00599 
00600   /* Get a previous kill, and yank that. */
00601   kill_ring_loc--;
00602   if (kill_ring_loc < 0)
00603     kill_ring_loc = kill_ring_index - 1;
00604 
00605   ea_yank (window, count, key);
00606 }
00607 
00608 /* Delete the text from point to end of line. */
00609 DECLARE_INFO_COMMAND (ea_kill_line, _("Kill to the end of the line"))
00610 {
00611   if (count < 0)
00612     {
00613       ea_kill_text (input_line_point, input_line_beg);
00614       input_line_point = input_line_beg;
00615     }
00616   else
00617     ea_kill_text (input_line_point, input_line_end);
00618 }
00619 
00620 /* Delete the text from point to beg of line. */
00621 DECLARE_INFO_COMMAND (ea_backward_kill_line,
00622                       _("Kill to the beginning of the line"))
00623 {
00624   if (count < 0)
00625     ea_kill_text (input_line_point, input_line_end);
00626   else
00627     {
00628       ea_kill_text (input_line_point, input_line_beg);
00629       input_line_point = input_line_beg;
00630     }
00631 }
00632 
00633 /* Delete from point to the end of the current word. */
00634 DECLARE_INFO_COMMAND (ea_kill_word, _("Kill the word following the cursor"))
00635 {
00636   int orig_point = input_line_point;
00637 
00638   if (count < 0)
00639     ea_backward_kill_word (window, -count, key);
00640   else
00641     {
00642       ea_forward_word (window, count, key);
00643 
00644       if (input_line_point != orig_point)
00645         ea_kill_text (orig_point, input_line_point);
00646 
00647       input_line_point = orig_point;
00648     }
00649 }
00650 
00651 /* Delete from point to the start of the current word. */
00652 DECLARE_INFO_COMMAND (ea_backward_kill_word,
00653                       _("Kill the word preceding the cursor"))
00654 {
00655   int orig_point = input_line_point;
00656 
00657   if (count < 0)
00658     ea_kill_word (window, -count, key);
00659   else
00660     {
00661       ea_backward_word (window, count, key);
00662 
00663       if (input_line_point != orig_point)
00664         ea_kill_text (orig_point, input_line_point);
00665     }
00666 }
00667 
00668 /* The way to kill something.  This appends or prepends to the last
00669    kill, if the last command was a kill command.  If FROM is less
00670    than TO, then the killed text is appended to the most recent kill,
00671    otherwise it is prepended.  If the last command was not a kill command,
00672    then a new slot is made for this kill. */
00673 static void
00674 ea_kill_text (int from, int to)
00675 {
00676   register int i, counter, distance;
00677   int killing_backwards, slot;
00678   char *killed_text;
00679 
00680   killing_backwards = (from > to);
00681 
00682   /* If killing backwards, reverse the values of FROM and TO. */
00683   if (killing_backwards)
00684     {
00685       int temp = from;
00686       from = to;
00687       to = temp;
00688     }
00689 
00690   /* Remember the text that we are about to delete. */
00691   distance = to - from;
00692   killed_text = (char *)xmalloc (1 + distance);
00693   strncpy (killed_text, &input_line[from], distance);
00694   killed_text[distance] = '\0';
00695 
00696   /* Actually delete the text from the line. */
00697   counter = input_line_end - to;
00698 
00699   for (i = from; counter; i++, counter--)
00700     input_line[i] = input_line[i + distance];
00701 
00702   input_line_end -= distance;
00703 
00704   /* If the last command was a kill, append or prepend the killed text to
00705      the last command's killed text. */
00706   if (echo_area_last_command_was_kill)
00707     {
00708       char *old, *new;
00709 
00710       slot = kill_ring_loc;
00711       old = kill_ring[slot];
00712       new = (char *)xmalloc (1 + strlen (old) + strlen (killed_text));
00713 
00714       if (killing_backwards)
00715         {
00716           /* Prepend TEXT to current kill. */
00717           strcpy (new, killed_text);
00718           strcat (new, old);
00719         }
00720       else
00721         {
00722           /* Append TEXT to current kill. */
00723           strcpy (new, old);
00724           strcat (new, killed_text);
00725         }
00726 
00727       free (old);
00728       free (killed_text);
00729       kill_ring[slot] = new;
00730     }
00731   else
00732     {
00733       /* Try to store the kill in a new slot, unless that would cause there
00734          to be too many remembered kills. */
00735       slot = kill_ring_index;
00736 
00737       if (slot == max_retained_kills)
00738         slot = 0;
00739 
00740       if (slot + 1 > kill_ring_slots)
00741         kill_ring = (char **) xrealloc
00742           (kill_ring,
00743            (kill_ring_slots += max_retained_kills) * sizeof (char *));
00744 
00745       if (slot != kill_ring_index)
00746         free (kill_ring[slot]);
00747       else
00748         kill_ring_index++;
00749 
00750       kill_ring[slot] = killed_text;
00751 
00752       kill_ring_loc = slot;
00753     }
00754 
00755   /* Notice that the last command was a kill. */
00756   echo_area_last_command_was_kill++;
00757 }
00758 
00759 /* **************************************************************** */
00760 /*                                                                  */
00761 /*                      Echo Area Completion                        */
00762 /*                                                                  */
00763 /* **************************************************************** */
00764 
00765 /* Pointer to an array of REFERENCE to complete over. */
00766 static REFERENCE **echo_area_completion_items = (REFERENCE **)NULL;
00767 
00768 /* Sorted array of REFERENCE * which is the possible completions found in
00769    the variable echo_area_completion_items.  If there is only one element,
00770    it is the only possible completion. */
00771 static REFERENCE **completions_found = (REFERENCE **)NULL;
00772 static int completions_found_index = 0;
00773 static int completions_found_slots = 0;
00774 
00775 /* The lowest common denominator found while completing. */
00776 static REFERENCE *LCD_completion;
00777 
00778 /* Internal functions used by the user calls. */
00779 static void build_completions (void), completions_must_be_rebuilt (void);
00780 
00781 /* Variable which holds the output of completions. */
00782 static NODE *possible_completions_output_node = (NODE *)NULL;
00783 
00784 static char *compwin_name = "*Completions*";
00785 
00786 /* Return non-zero if WINDOW is a window used for completions output. */
00787 static int
00788 completions_window_p (WINDOW *window)
00789 {
00790   int result = 0;
00791 
00792   if (internal_info_node_p (window->node) &&
00793       (strcmp (window->node->nodename, compwin_name) == 0))
00794     result = 1;
00795 
00796   return (result);
00797 }
00798 
00799 /* Workhorse for completion readers.  If FORCE is non-zero, the user cannot
00800    exit unless the line read completes, or is empty. */
00801 char *
00802 info_read_completing_internal (WINDOW *window, char *prompt,
00803     REFERENCE **completions, int force)
00804 {
00805   char *line;
00806 
00807   /* If the echo area is already active, remember the current state. */
00808   if (echo_area_is_active)
00809     push_echo_area ();
00810 
00811   echo_area_must_complete_p = force;
00812 
00813   /* Initialize our local variables. */
00814   initialize_input_line (prompt);
00815 
00816   /* Initialize the echo area for the first (but maybe not the last) time. */
00817   echo_area_initialize_node ();
00818 
00819   /* Save away the original node of this window, and the window itself,
00820      so echo area commands can temporarily use this window. */
00821   remember_calling_window (window);
00822 
00823   /* Save away the list of items to complete over. */
00824   echo_area_completion_items = completions;
00825   completions_must_be_rebuilt ();
00826 
00827   active_window = the_echo_area;
00828   echo_area_is_active++;
00829 
00830   /* Read characters in the echo area. */
00831   while (1)
00832     {
00833       info_read_and_dispatch ();
00834 
00835       line = echo_area_after_read ();
00836 
00837       /* Force the completion to take place if the user hasn't accepted
00838          a default or aborted, and if FORCE is active. */
00839       if (force && line && *line && completions)
00840         {
00841           register int i;
00842 
00843           build_completions ();
00844 
00845           /* If there is only one completion, then make the line be that
00846              completion. */
00847           if (completions_found_index == 1)
00848             {
00849               free (line);
00850               line = xstrdup (completions_found[0]->label);
00851               break;
00852             }
00853 
00854           /* If one of the completions matches exactly, then that is okay, so
00855              return the current line. */
00856           for (i = 0; i < completions_found_index; i++)
00857             if (strcasecmp (completions_found[i]->label, line) == 0)
00858               {
00859                 free (line);
00860                 line = xstrdup (completions_found[i]->label);
00861                 break;
00862               }
00863 
00864           /* If no match, go back and try again. */
00865           if (i == completions_found_index)
00866             {
00867               if (!completions_found_index)
00868                 inform_in_echo_area ((char *) _("No completions"));
00869               else
00870                 inform_in_echo_area ((char *) _("Not complete"));
00871               continue;
00872             }
00873         }
00874       break;
00875     }
00876   echo_area_is_active--;
00877 
00878   /* Restore the original active window and show point in it. */
00879   active_window = calling_window;
00880   restore_calling_window ();
00881   display_cursor_at_point (active_window);
00882   fflush (stdout);
00883 
00884   echo_area_completion_items = (REFERENCE **)NULL;
00885   completions_must_be_rebuilt ();
00886 
00887   /* If there is a previous loop waiting for us, restore it now. */
00888   if (echo_area_is_active)
00889     pop_echo_area ();
00890 
00891   return (line);
00892 }
00893   
00894 /* Read a line in the echo area with completion over COMPLETIONS. */
00895 char *
00896 info_read_completing_in_echo_area (WINDOW *window,
00897     char *prompt, REFERENCE **completions)
00898 {
00899   return (info_read_completing_internal (window, prompt, completions, 1));
00900 }
00901 
00902 /* Read a line in the echo area allowing completion over COMPLETIONS, but
00903    not requiring it. */
00904 char *
00905 info_read_maybe_completing (WINDOW *window,
00906     char *prompt, REFERENCE **completions)
00907 {
00908   return (info_read_completing_internal (window, prompt, completions, 0));
00909 }
00910 
00911 DECLARE_INFO_COMMAND (ea_possible_completions, _("List possible completions"))
00912 {
00913   if (!echo_area_completion_items)
00914     {
00915       ea_insert (window, count, key);
00916       return;
00917     }
00918 
00919   build_completions ();
00920 
00921   if (!completions_found_index)
00922     {
00923       terminal_ring_bell ();
00924       inform_in_echo_area ((char *) _("No completions"));
00925     }
00926   else if ((completions_found_index == 1) && (key != '?'))
00927     {
00928       inform_in_echo_area ((char *) _("Sole completion"));
00929     }
00930   else
00931     {
00932       register int i, l;
00933       int limit, iterations, max_label = 0;
00934 
00935       initialize_message_buffer ();
00936       printf_to_message_buffer (completions_found_index == 1
00937                                 ? (char *) _("One completion:\n")
00938                                 : (char *) _("%d completions:\n"),
00939                             (void *) (long) completions_found_index,
00940                             NULL, NULL);
00941 
00942       /* Find the maximum length of a label. */
00943       for (i = 0; i < completions_found_index; i++)
00944         {
00945           int len = strlen (completions_found[i]->label);
00946           if (len > max_label)
00947             max_label = len;
00948         }
00949 
00950       max_label += 4;
00951 
00952       /* Find out how many columns we should print in. */
00953       limit = calling_window->width / max_label;
00954       if (limit != 1 && (limit * max_label == calling_window->width))
00955         limit--;
00956 
00957       /* Avoid a possible floating exception.  If max_label > width then
00958          the limit will be 0 and a divide-by-zero fault will result. */
00959       if (limit == 0)
00960         limit = 1;
00961 
00962       /* How many iterations of the printing loop? */
00963       iterations = (completions_found_index + (limit - 1)) / limit;
00964 
00965       /* Watch out for special case.  If the number of completions is less
00966          than LIMIT, then just do the inner printing loop. */
00967       if (completions_found_index < limit)
00968         iterations = 1;
00969 
00970       /* Print the sorted items, up-and-down alphabetically. */
00971       for (i = 0; i < iterations; i++)
00972         {
00973           register int j;
00974 
00975           for (j = 0, l = i; j < limit; j++)
00976             {
00977               if (l >= completions_found_index)
00978                 break;
00979               else
00980                 {
00981                   char *label;
00982                   int printed_length, k;
00983 
00984                   label = completions_found[l]->label;
00985                   printed_length = strlen (label);
00986                   printf_to_message_buffer ("%s", label, NULL, NULL);
00987 
00988                   if (j + 1 < limit)
00989                     {
00990                       for (k = 0; k < max_label - printed_length; k++)
00991                         printf_to_message_buffer (" ", NULL, NULL, NULL);
00992                     }
00993                 }
00994               l += iterations;
00995             }
00996           printf_to_message_buffer ("\n", NULL, NULL, NULL);
00997         }
00998 
00999       /* Make a new node to hold onto possible completions.  Don't destroy
01000          dangling pointers. */
01001       {
01002         NODE *temp;
01003 
01004         temp = message_buffer_to_node ();
01005         add_gcable_pointer (temp->contents);
01006         name_internal_node (temp, compwin_name);
01007         possible_completions_output_node = temp;
01008       }
01009 
01010       /* Find a suitable window for displaying the completions output.
01011          First choice is an existing window showing completions output.
01012          If there is only one window, and it is large, make another
01013          (smaller) window, and use that one.  Otherwise, use the caller's
01014          window. */
01015       {
01016         WINDOW *compwin;
01017 
01018         compwin = get_internal_info_window (compwin_name);
01019 
01020         if (!compwin)
01021           {
01022             /* If we can split the window to display most of the completion
01023                items, then do so. */
01024             if (calling_window->height > (iterations * 2)
01025               && calling_window->height / 2 >= WINDOW_MIN_SIZE)
01026               {
01027                 int start, pagetop;
01028 #ifdef SPLIT_BEFORE_ACTIVE
01029                 int end;
01030 #endif
01031 
01032                 active_window = calling_window;
01033 
01034                 /* Perhaps we can scroll this window on redisplay. */
01035                 start = calling_window->first_row;
01036                 pagetop = calling_window->pagetop;
01037 
01038                 compwin =
01039                   window_make_window (possible_completions_output_node);
01040                 active_window = the_echo_area;
01041                 window_change_window_height
01042                   (compwin, -(compwin->height - (iterations + 2)));
01043 
01044                 window_adjust_pagetop (calling_window);
01045                 remember_calling_window (calling_window);
01046 
01047 #if defined (SPLIT_BEFORE_ACTIVE)
01048                 /* If the pagetop hasn't changed, scrolling the calling
01049                    window is a reasonable thing to do. */
01050                 if (pagetop == calling_window->pagetop)
01051                   {
01052                     end = start + calling_window->height;
01053                     display_scroll_display
01054                       (start, end, calling_window->prev->height + 1);
01055                   }
01056 #else /* !SPLIT_BEFORE_ACTIVE */
01057                 /* If the pagetop has changed, set the new pagetop here. */
01058                 if (pagetop != calling_window->pagetop)
01059                   {
01060                     int newtop = calling_window->pagetop;
01061                     calling_window->pagetop = pagetop;
01062                     set_window_pagetop (calling_window, newtop);
01063                   }
01064 #endif /* !SPLIT_BEFORE_ACTIVE */
01065 
01066                 echo_area_completions_window = compwin;
01067                 remember_window_and_node (compwin, compwin->node);
01068               }
01069             else
01070               compwin = calling_window;
01071           }
01072 
01073         if (compwin->node != possible_completions_output_node)
01074           {
01075             window_set_node_of_window
01076               (compwin, possible_completions_output_node);
01077             remember_window_and_node (compwin, compwin->node);
01078           }
01079 
01080         display_update_display (windows);
01081       }
01082     }
01083 }
01084 
01085 DECLARE_INFO_COMMAND (ea_complete, _("Insert completion"))
01086 {
01087   if (!echo_area_completion_items)
01088     {
01089       ea_insert (window, count, key);
01090       return;
01091     }
01092 
01093   /* If KEY is SPC, and we are not forcing completion to take place, simply
01094      insert the key. */
01095   if (!echo_area_must_complete_p && key == SPC)
01096     {
01097       ea_insert (window, count, key);
01098       return;
01099     }
01100 
01101   if (ea_last_executed_command == (VFunction *) ea_complete)
01102     {
01103       /* If the keypress is a SPC character, and we have already tried
01104          completing once, and there are several completions, then check
01105          the batch of completions to see if any continue with a space.
01106          If there are some, insert the space character and continue. */
01107       if (key == SPC && completions_found_index > 1)
01108         {
01109           register int i, offset;
01110 
01111           offset = input_line_end - input_line_beg;
01112 
01113           for (i = 0; i < completions_found_index; i++)
01114             if (completions_found[i]->label[offset] == ' ')
01115               break;
01116 
01117           if (completions_found[i])
01118             ea_insert (window, 1, ' ');
01119           else
01120             {
01121               ea_possible_completions (window, count, key);
01122               return;
01123             }
01124         }
01125       else
01126         {
01127           ea_possible_completions (window, count, key);
01128           return;
01129         }
01130     }
01131 
01132   input_line_point = input_line_end;
01133   build_completions ();
01134 
01135   if (!completions_found_index)
01136     terminal_ring_bell ();
01137   else if (LCD_completion->label[0] == '\0')
01138     ea_possible_completions (window, count, key);
01139   else
01140     {
01141       register int i;
01142       input_line_point = input_line_end = input_line_beg;
01143       for (i = 0; LCD_completion->label[i]; i++)
01144         ea_insert (window, 1, LCD_completion->label[i]);
01145     }
01146 }
01147 
01148 /* Utility REFERENCE used to store possible LCD. */
01149 static REFERENCE LCD_reference = {
01150     (char *)NULL, (char *)NULL, (char *)NULL, 0, 0, 0
01151 };
01152 
01153 static void remove_completion_duplicates (void);
01154 
01155 /* Variables which remember the state of the most recent call
01156    to build_completions (). */
01157 static char *last_completion_request = (char *)NULL;
01158 static REFERENCE **last_completion_items = (REFERENCE **)NULL;
01159 
01160 /* How to tell the completion builder to reset internal state. */
01161 static void
01162 completions_must_be_rebuilt (void)
01163 {
01164   maybe_free (last_completion_request);
01165   last_completion_request = (char *)NULL;
01166   last_completion_items = (REFERENCE **)NULL;
01167 }
01168 
01169 /* Build a list of possible completions from echo_area_completion_items,
01170    and the contents of input_line. */
01171 static void
01172 build_completions (void)
01173 {
01174   register int i, len;
01175   register REFERENCE *entry;
01176   char *request;
01177   int informed_of_lengthy_job = 0;
01178 
01179   /* If there are no items to complete over, exit immediately. */
01180   if (!echo_area_completion_items)
01181     {
01182       completions_found_index = 0;
01183       LCD_completion = (REFERENCE *)NULL;
01184       return;
01185     }
01186 
01187   /* Check to see if this call to build completions is the same as the last
01188      call to build completions. */
01189   len = input_line_end - input_line_beg;
01190   request = (char *)xmalloc (1 + len);
01191   strncpy (request, &input_line[input_line_beg], len);
01192   request[len] = '\0';
01193 
01194   if (last_completion_request && last_completion_items &&
01195       last_completion_items == echo_area_completion_items &&
01196       (strcmp (last_completion_request, request) == 0))
01197     {
01198       free (request);
01199       return;
01200     }
01201 
01202   maybe_free (last_completion_request);
01203   last_completion_request = request;
01204   last_completion_items = echo_area_completion_items;
01205 
01206   /* Always start at the beginning of the list. */
01207   completions_found_index = 0;
01208   LCD_completion = (REFERENCE *)NULL;
01209 
01210   for (i = 0; (entry = echo_area_completion_items[i]); i++)
01211     {
01212       if (strncasecmp (request, entry->label, len) == 0)
01213         add_pointer_to_array (entry, completions_found_index,
01214                               completions_found, completions_found_slots,
01215                               20, REFERENCE *);
01216 
01217       if (!informed_of_lengthy_job && completions_found_index > 100)
01218         {
01219           informed_of_lengthy_job = 1;
01220           window_message_in_echo_area ((char *) _("Building completions..."),
01221               NULL, NULL);
01222         }
01223     }
01224 
01225   if (!completions_found_index)
01226     return;
01227 
01228   /* Sort and prune duplicate entries from the completions array. */
01229   remove_completion_duplicates ();
01230 
01231   /* If there is only one completion, just return that. */
01232   if (completions_found_index == 1)
01233     {
01234       LCD_completion = completions_found[0];
01235       return;
01236     }
01237 
01238   /* Find the least common denominator. */
01239   {
01240     long shortest = 100000;
01241 
01242     for (i = 1; i < completions_found_index; i++)
01243       {
01244         register int j;
01245         int c1, c2;
01246 
01247         for (j = 0;
01248              (c1 = info_tolower (completions_found[i - 1]->label[j])) &&
01249              (c2 = info_tolower (completions_found[i]->label[j]));
01250              j++)
01251           if (c1 != c2)
01252             break;
01253 
01254         if (shortest > j)
01255           shortest = j;
01256       }
01257 
01258     maybe_free (LCD_reference.label);
01259     LCD_reference.label = (char *)xmalloc (1 + shortest);
01260     /* Since both the sorting done inside remove_completion_duplicates
01261        and all the comparisons above are case-insensitive, it's
01262        possible that the completion we are going to return is
01263        identical to what the user typed but for the letter-case.  This
01264        is confusing, since the user could type FOOBAR<TAB> and get her
01265        string change letter-case for no good reason.  So try to find a
01266        possible completion whose letter-case is identical, and if so,
01267        use that.  */
01268     if (completions_found_index > 1)
01269       {
01270        int req_len = strlen (request);
01271 
01272         for (i = 0; i < completions_found_index; i++)
01273           if (strncmp (request, completions_found[i]->label, req_len) == 0)
01274             break;
01275         /* If none of the candidates match exactly, use the first one.  */
01276         if (i >= completions_found_index)
01277           i = 0;
01278       }
01279     strncpy (LCD_reference.label, completions_found[i]->label, shortest);
01280     LCD_reference.label[shortest] = '\0';
01281     LCD_completion = &LCD_reference;
01282   }
01283 
01284   if (informed_of_lengthy_job)
01285     echo_area_initialize_node ();
01286 }
01287 
01288 /* Function called by qsort. */
01289 static int
01290 compare_references (const void *entry1, const void *entry2)
01291 {
01292   REFERENCE **e1 = (REFERENCE **) entry1;
01293   REFERENCE **e2 = (REFERENCE **) entry2;
01294 
01295   return (strcasecmp ((*e1)->label, (*e2)->label));
01296 }
01297 
01298 /* Prune duplicate entries from COMPLETIONS_FOUND. */
01299 static void
01300 remove_completion_duplicates (void)
01301 {
01302   register int i, j;
01303   REFERENCE **temp;
01304   int newlen;
01305 
01306   if (!completions_found_index)
01307     return;
01308 
01309   /* Sort the items. */
01310   qsort (completions_found, completions_found_index, sizeof (REFERENCE *),
01311          compare_references);
01312 
01313   for (i = 0, newlen = 1; i < completions_found_index - 1; i++)
01314     {
01315       if (strcmp (completions_found[i]->label,
01316                   completions_found[i + 1]->label) == 0)
01317         completions_found[i] = (REFERENCE *)NULL;
01318       else
01319         newlen++;
01320     }
01321 
01322   /* We have marked all the dead slots.  It is faster to copy the live slots
01323      twice than to prune the dead slots one by one. */
01324   temp = (REFERENCE **)xmalloc ((1 + newlen) * sizeof (REFERENCE *));
01325   for (i = 0, j = 0; i < completions_found_index; i++)
01326     if (completions_found[i])
01327       temp[j++] = completions_found[i];
01328 
01329   for (i = 0; i < newlen; i++)
01330     completions_found[i] = temp[i];
01331 
01332   completions_found[i] = (REFERENCE *)NULL;
01333   completions_found_index = newlen;
01334   free (temp);
01335 }
01336 
01337 /* Scroll the "other" window.  If there is a window showing completions, scroll
01338    that one, otherwise scroll the window which was active on entering the read
01339    function. */
01340 DECLARE_INFO_COMMAND (ea_scroll_completions_window, _("Scroll the completions window"))
01341 {
01342   WINDOW *compwin;
01343   int old_pagetop;
01344 
01345   compwin = get_internal_info_window (compwin_name);
01346 
01347   if (!compwin)
01348     compwin = calling_window;
01349 
01350   old_pagetop = compwin->pagetop;
01351 
01352   /* Let info_scroll_forward () do the work, and print any messages that
01353      need to be displayed. */
01354   info_scroll_forward (compwin, count, key);
01355 }
01356 
01357 /* Function which gets called when an Info window is deleted while the
01358    echo area is active.  WINDOW is the window which has just been deleted. */
01359 void
01360 echo_area_inform_of_deleted_window (WINDOW *window)
01361 {
01362   /* If this is the calling_window, forget what we remembered about it. */
01363   if (window == calling_window)
01364     {
01365       if (active_window != the_echo_area)
01366         remember_calling_window (active_window);
01367       else
01368         remember_calling_window (windows);
01369     }
01370 
01371   /* If this window was the echo_area_completions_window, then notice that
01372      the window has been deleted. */
01373   if (window == echo_area_completions_window)
01374     echo_area_completions_window = (WINDOW *)NULL;
01375 }
01376 
01377 /* **************************************************************** */
01378 /*                                                                  */
01379 /*                 Pushing and Popping the Echo Area                */
01380 /*                                                                  */
01381 /* **************************************************************** */
01382 
01383 /* Push and Pop the echo area. */
01384 typedef struct {
01385   char *line;
01386   char *prompt;
01387   REFERENCE **comp_items;
01388   int point, beg, end;
01389   int must_complete;
01390   NODE node;
01391   WINDOW *compwin;
01392 } PUSHED_EA;
01393 
01394 static PUSHED_EA **pushed_echo_areas = (PUSHED_EA **)NULL;
01395 static int pushed_echo_areas_index = 0;
01396 static int pushed_echo_areas_slots = 0;
01397 
01398 /* Pushing the echo_area has a side effect of zeroing the completion_items. */
01399 static void
01400 push_echo_area (void)
01401 {
01402   PUSHED_EA *pushed;
01403 
01404   pushed = (PUSHED_EA *)xmalloc (sizeof (PUSHED_EA));
01405   pushed->line = xstrdup (input_line);
01406   pushed->prompt = input_line_prompt;
01407   pushed->point = input_line_point;
01408   pushed->beg = input_line_beg;
01409   pushed->end = input_line_end;
01410   pushed->node = input_line_node;
01411   pushed->comp_items = echo_area_completion_items;
01412   pushed->must_complete = echo_area_must_complete_p;
01413   pushed->compwin = echo_area_completions_window;
01414 
01415   add_pointer_to_array (pushed, pushed_echo_areas_index, pushed_echo_areas,
01416                         pushed_echo_areas_slots, 4, PUSHED_EA *);
01417 
01418   echo_area_completion_items = (REFERENCE **)NULL;
01419 }
01420 
01421 static void
01422 pop_echo_area (void)
01423 {
01424   PUSHED_EA *popped;
01425 
01426   popped = pushed_echo_areas[--pushed_echo_areas_index];
01427 
01428   strcpy (input_line, popped->line);
01429   free (popped->line);
01430   input_line_prompt = popped->prompt;
01431   input_line_point = popped->point;
01432   input_line_beg = popped->beg;
01433   input_line_end = popped->end;
01434   input_line_node = popped->node;
01435   echo_area_completion_items = popped->comp_items;
01436   echo_area_must_complete_p = popped->must_complete;
01437   echo_area_completions_window = popped->compwin;
01438   completions_must_be_rebuilt ();
01439 
01440   /* If the completion window no longer exists, forget about it. */
01441   if (echo_area_completions_window)
01442     {
01443       register WINDOW *win;
01444 
01445       for (win = windows; win; win = win->next)
01446         if (echo_area_completions_window == win)
01447           break;
01448 
01449       /* If the window wasn't found, then it has already been deleted. */
01450       if (!win)
01451         echo_area_completions_window = (WINDOW *)NULL;
01452     }
01453 
01454   free (popped);
01455 }
01456 
01457 /* Returns non-zero if any of the prior stacked calls to read in the echo
01458    area produced a completions window. */
01459 static int
01460 echo_area_stack_contains_completions_p (void)
01461 {
01462   register int i;
01463 
01464   for (i = 0; i < pushed_echo_areas_index; i++)
01465     if (pushed_echo_areas[i]->compwin)
01466       return (1);
01467 
01468   return (0);
01469 }
01470 
01471 /* **************************************************************** */
01472 /*                                                                  */
01473 /*             Error Messages While Reading in Echo Area            */
01474 /*                                                                  */
01475 /* **************************************************************** */
01476 
01477 #if defined (HAVE_SYS_TIME_H)
01478 #  include <sys/time.h>
01479 #  define HAVE_STRUCT_TIMEVAL
01480 #endif /* HAVE_SYS_TIME_H */
01481 
01482 static void
01483 pause_or_input (void)
01484 {
01485 #ifdef FD_SET
01486   struct timeval timer;
01487   fd_set readfds;
01488   int ready;
01489 
01490   FD_ZERO (&readfds);
01491   FD_SET (fileno (stdin), &readfds);
01492   timer.tv_sec = 2;
01493   timer.tv_usec = 0;
01494   ready = select (fileno (stdin) + 1, &readfds, (fd_set *) NULL,
01495                   (fd_set *) NULL, &timer);
01496 #endif /* FD_SET */
01497 }
01498 
01499 /* Print MESSAGE right after the end of the current line, and wait
01500    for input or a couple of seconds, whichever comes first.  Then flush the
01501    informational message that was printed. */
01502 void
01503 inform_in_echo_area (const char *message)
01504 {
01505   int i;
01506   char *text;
01507   int avail = EA_MAX_INPUT + 1 - input_line_end;
01508 
01509   text = xstrdup (message);
01510   for (i = 0; text[i] && text[i] != '\n' && i < avail; i++)
01511     ;
01512   text[i] = 0;
01513 
01514   echo_area_initialize_node ();
01515   sprintf (&input_line[input_line_end], "%s[%s]\n",
01516            echo_area_is_active ? " ": "", text);
01517   free (text);
01518   the_echo_area->point = input_line_point;
01519   display_update_one_window (the_echo_area);
01520   display_cursor_at_point (active_window);
01521   fflush (stdout);
01522   pause_or_input ();
01523   echo_area_initialize_node ();
01524 }