lynx-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

lynx-dev [dev17] patch with new TEXTAREA commands, etc.


From: Kim DeVaughn
Subject: lynx-dev [dev17] patch with new TEXTAREA commands, etc.
Date: Wed, 24 Feb 1999 09:25:04 -0800

Here is the latest TEXTAREA patch for -dev.17.  It provides:

 1. A new command named GROWTEXTAREA, which adds 5 new, blank lines to
    the bottom of a TEXTAREA when executed.  It does not have a default
    binding, so you need to add a KEYMAP for it in your lynx.cfg.  The
    number of lines added is fixed by a #define, but 5 seems to be a
    "reasonable" increment to use, IMO.

 2. A new command named INSERTFILE, which (obviously) inserts a file's
    contents into a TEXTAREA, just above the line the cursor is on when
    invoked (which means you cannot insert a file *below* the final
    existing TEXTAREA line ... but see 1) and 3) for easy ways to over-
    come this limitation).  It too, has no default binding, so a KEYMAP
    entry is also needed for it.

 3. By default, when the cursor is on the last line of a TEXTAREA, if
    you hit the ENTER key, a new line will be added to the TEXTAREA, with
    the cursor positioned on it.  If that should happen at the bottom of
    the screen, a DOWN_HALF scroll operation is (effectively) performed.

    Since at least one person objected to this automatic behavior when it
    was discussed awhile back (BL, IIRC), I've ifdef'd this feature for
    easy disabling (and to make adding a ./configure option easier).  The
    symbol is AUTOGROW is #define'd in LYMainLoop.h.

 4. I added an ifdef for the code that causes the external editor to be
    invoked "automatically" on the TEXTAREA contents when ^Ve (or whatever
    the EDIT binding is), rather than attempting to invoke it on the .html
    source file.  This should allow adding a ./configure option to control
    this behavior, until we come up with a different binding/mechanism to
    do the job, that we can all agree on.  The symbol AUTOEXTEDIT is also
    #define'd in LYMainLoop.h.

    The command EDITTEXTAREA is still available for seperate KEYMAP binding,
    per KW's suggestion.

 5. The range of chars between 0x80 and LYlowest_eightbit[current_char_set]
    is now checked for validity against the charset in use.  If such chars
    are detected in edited or inserted data, a "." char is substituted for
    them, as with normal control chars.  Suggested by LP.

 6. Added KW's LYCloseTempFP bugfix.

 7. The function GetFileName() used by INSERTFILE is fairly general purpose,
    and can be used whenever the name of an existing file needs to be input.
    It uses the same mechanism for filename expansion and "history" as does
    the PRINT function.

 8. Reorganized alot of the code into subroutines to provide for 1) and 2);
    still need to do a bit more cleanup of some other (nearly) common code.


On my 2do list:

 a. I *still* want to fix the cursor positioning when returning from using
    the external editor (EDITTEXTAREA).

 b. Probably should add some documentation here and there ...

 c. Isolate and fix a bloody nasty intermittent bug that exists in this (as
    well as in the earlier versions of the) TEXTAREA editing-related code.

    I'll describe this pest in a seperate post, as I'll be damned if I can
    find it.  It only happens if you're numbering links *and* form anchors,
    and then intermittently on only one page that I've come across.  There
    is a crude trap in the code for this SOB, but nastiness is still likely
    to happen.  More on this later.

/kim


Against a clean 2.8.2dev.17:

diff -uNr lynx-2.8.2-dev.17+kd.orig/LYMessages_en.h 
lynx-2.8.2-dev.17+kd/LYMessages_en.h
--- lynx-2.8.2-dev.17+kd.orig/LYMessages_en.h   Wed Feb 17 06:29:33 1999
+++ lynx-2.8.2-dev.17+kd/LYMessages_en.h        Sun Feb 21 13:23:40 1999
@@ -486,6 +486,12 @@
 #define HAVE_MAIL_MSG gettext("*** You have mail. ***")
 #endif /* VMS */
 #define HAVE_NEW_MAIL_MSG gettext("*** You have new mail. ***")
+#define FILE_INSERT_CANCELLED gettext("File insert cancelled!!!")
+#define FILE_DOES_NOT_EXIST gettext("File does not exist.")
+#define FILE_DOES_NOT_EXIST_RE gettext("File does not exist - reenter or 
cancel:")
+#define FILE_NOT_READABLE gettext("File is not readable.")
+#define FILE_NOT_READABLE_RE gettext("File is not readable - reenter or 
cancel:")
+#define FILE_INSERT_0_LENGTH gettext("Nothing to insert - file is 0-length.")
 #define SAVE_REQUEST_CANCELLED gettext("Save request cancelled!!!")
 #define MAIL_REQUEST_CANCELLED gettext("Mail request cancelled!!!")
 #define CONFIRM_MAIL_SOURCE_PREPARSED \
diff -uNr lynx-2.8.2-dev.17+kd.orig/src/GridText.c 
lynx-2.8.2-dev.17+kd/src/GridText.c
--- lynx-2.8.2-dev.17+kd.orig/src/GridText.c    Wed Feb 24 07:00:04 1999
+++ lynx-2.8.2-dev.17+kd/src/GridText.c Wed Feb 24 05:15:10 1999
@@ -36,6 +36,7 @@
 #include <LYCharUtils.h>       /* LYUCTranslateBack... */
 #include <UCMap.h>
 #include <LYEdit.h>
+#include <LYPrint.h>
 #ifdef EXP_CHARTRANS_AUTOSWITCH
 #include <UCAuto.h>
 #endif /* EXP_CHARTRANS_AUTOSWITCH */
@@ -8960,6 +8961,104 @@
 
 
 /*
+ *  Cleanup new lines coming into a TEXTAREA from an external editor, or a
+ *  file, such that they are in a suitable format for TEXTAREA rendering,
+ *  display, and manipulation.  That means trimming off trailing whitespace
+ *  from the line, expanding TABS into SPACES, and substituting a printable
+ *  character for control chars, and the like.
+ *
+ *  --KED  02/24/99
+ */
+PRIVATE void cleanup_line_for_textarea ARGS2(
+             char *,  line,
+            int,     len)
+{
+    char  tbuf[MAX_LINE];
+
+    char *cp;
+    char *p;
+    char *s;
+    int   i;
+    int   n;
+
+
+    /*
+     *  Whack off trailing whitespace from the line.
+     */
+    for (i = len, p = line + (len - 1); i != 0; p--, i--) {
+        if (isspace (*p))
+          *p = '\0';
+       else
+          break;
+    }
+
+    if (strlen (line) != 0) {
+       /*
+       *  Expand any tab's, since they won't render properly in a TEXTAREA.
+       *
+       *  [Is that "by spec", or just a "lynxism"?  As may be, it seems that
+       *   such chars may cause other problems, too ... with cursor movement,
+       *   submit'ing, etc.  Probably needs looking into more deeply.]
+       */
+       p = line;
+       s = tbuf;
+
+       while (*p) {
+          if ((cp = strchr (p, '\t')) != 0) {
+             i  = cp - p;
+             s  = (strncpy (s, p, i))      + i;
+             n  = TABSTOP - (i % TABSTOP);
+             s  = (strncpy (s, SPACES, n)) + n;
+             p += (i + 1);
+
+          } else {
+
+             strcpy (s, p);
+             break;
+          }
+       }
+
+       /*
+       *  Replace control chars with something printable.  Note that char
+       *  substitution above 0x7f is dependent on the charset being used,
+       *  and then only applies to the contiguous run of char values that
+       *  are between 0x80, and the 1st real high-order-bit-set character,
+       *  as specified by the charset.  In general (ie, for many character
+       *  sets), that usually means the so-called "C1 control chars" that
+       *  range from 0x80 thru 0x9f.  For EBCDIC machines, we only trim the
+       *  (control) chars below a space (x'40').
+       *
+       *  The assumption in all this is that the charset used in the editor,
+       *  is compatible with the charset specified in lynx.
+       *
+       *  [At some point in time, when/if lynx ever supports multibyte chars
+       *   internally (eg, UCS-2, UCS-4, UTF-16, etc), this kind of thing may
+       *   well cause problems.  But then, supporting such char sets will
+       *   require massive changes in (most) all parts of the lynx code, so
+       *   until then, we do the rational thing with char values that would
+       *   otherwise foul the display, if left alone.  If you're implementing
+       *   multibyte character set support, consider yourself to have been
+       *   warned.]
+       */
+       for (p = line, s = tbuf; *s != '\0'; p++, s++)
+#ifndef EBCDIC
+          *p = (((unsigned char)*s  < (unsigned char)' ')       ||
+                ((unsigned char)*s == (unsigned char)'\177')    ||
+                (((unsigned char)*s > (unsigned char)'\177') &&
+                 ((unsigned char)*s <
+                  (unsigned char)LYlowest_eightbit[current_char_set])))
+               ? SPLAT : *s;
+#else
+           *p = ((unsigned char)*s < (unsigned char)' ') ? SPLAT : *s;
+#endif
+       *p = '\0';
+    }
+
+    return;
+}
+
+
+/*
  *  Re-render the text of a tagged ("[123]") HTLine (arg1), with the tag
  *  number incremented by some value (arg5).  The re-rendered string may
  *  be allowed to expand in the event of a tag width change (eg, 99 -> 100)
@@ -8967,7 +9066,7 @@
  *  of) a value which must match, in order for the tag to be incremented,
  *  or (the address of) a 0-value, which will match any value, and cause
  *  any valid tag to be incremented.  Arg2 is a pointer to the first/only
- *  Anchor that exists on the line; we may need to adjust their position(s)
+ *  anchor that exists on the line; we may need to adjust their position(s)
  *  on the line.  Arg3 when non-0 indicates the number of new digits that
  *  were added to the 2nd line in a line crossing pair.
  *
@@ -8977,7 +9076,9 @@
  *  Untagged or improperly tagged lines are not altered.
  *
  *  Returns the number of chars added to the original string's length, if
- *  any.   KED  02/03/99
+ *  any.
+ *
+ *  --KED  02/03/99
  */
 PRIVATE int increment_tagged_htline ARGS6(
        HTLine *,       ht,
@@ -9021,14 +9122,14 @@
      *  [see comments below on line crosser caused problems]
      */
     if (*lx_val != 0) {
-       nxt_anchor = st_anchor;
-       while ((nxt_anchor) && (nxt_anchor->line_num == a->line_num)) {
-           nxt_anchor->line_pos += *lx_val;
-           nxt_anchor = nxt_anchor->next;
-       }
-       fixup  = *lx_val;
-       *lx_val = 0;
-       if (st_anchor) st_anchor = st_anchor->next;
+       nxt_anchor = st_anchor;
+       while ((nxt_anchor) && (nxt_anchor->line_num == a->line_num)) {
+          nxt_anchor->line_pos += *lx_val;
+          nxt_anchor = nxt_anchor->next;
+       }
+       fixup  = *lx_val;
+      *lx_val = 0;
+       if (st_anchor) st_anchor = st_anchor->next;
     }
 
 
@@ -9041,156 +9142,156 @@
           continue;
 
        } else {
-           *s++ = *p++;
-           t = p;
-           n = 0;
-           valid = TRUE;   /* p = t = byte after '[' */
+          *s++  = *p++;
+          t     = p;
+          n     = 0;
+          valid = TRUE;   /* p = t = byte after '[' */
+
+          /*
+           *  Make sure there are only digits between "[" and "]".
+           */
+          while  (*t != ']')  {
+              if (*t == '\0') { /* uhoh - we have a potential line crosser */
+                 valid = FALSE;
+                 plx   = TRUE;
+                 break;
+              }
+              if (isdigit (*t++) != 0) {
+                 n++;
+                 continue;
+              } else {
+                 valid = FALSE;
+                 break;
+              }
+          }
 
-           /*
-            *  Make sure there are only digits between "[" and "]".
-            */
-           while  (*t != ']')  {
-               if (*t == '\0') {  /* uhoh - we have a potential line crosser */
-                   valid = FALSE;
-                   plx   = TRUE;
-                   break;
-               }
-               if (isdigit (*t++) != 0) {
-                   n++;
-                   continue;
-               } else {
-                   valid = FALSE;
-                   break;
-               }
-           }
+          /*
+           *  If the format is OK, we check to see if the value is what
+           *  we expect.  If not, we have a random [nn] string in the text,
+           *  and leave it alone.
+           *
+           *  [It is *possible* to have a false match here, *if* there are
+           *   two identical [nn] strings (including the numeric value of
+           *   nn), one of which is the [tag], and the other being part of
+           *   a document.  In such a case, the 1st [nn] string will get
+           *   incremented; the 2nd one won't, which makes it a 50-50 chance
+           *   of being correct, if and when such an unlikely juxitposition
+           *   of text ever occurs.  Further validation tests of the [nnn]
+           *   string are probably not possible, since little of the actual
+           *   anchor-associated-text is retained in the TextAnchor or the
+           *   FormInfo structs.  Fortunately, I think the current method is
+           *   more than adequate to weed out 99.999% of any possible false
+           *   matches, just as it stands.  Caveat emptor.]
+           */
+          if ((valid) && (n > 0)) {
+             val = atoi (p);
+             if ((val == *old_val) || (*old_val == 0)) { /* 0 matches all */
+                if (*old_val != 0)
+                   (*old_val)++;
+                val += incr;
+                sprintf (s, "%d", val);
+                new_n = strlen (s);
+                s += new_n;
+                p += n;
 
-           /*
-            *  If the format is OK, we check to see if the value is what
-            *  we expect.  If not, we have a random [nn] string in the text,
-            *  and leave it alone.
-            *
-            *  [It is *possible* to have a false match here, *if* there are
-            *   two identical [nn] strings (including the numeric value of
-            *   nn), one of which is the [tag], and the other being part of
-            *   a document.  In such a case, the 1st [nn] string will get
-            *   incremented; the 2nd one won't, which makes it a 50-50 chance
-            *   of being correct, if and when such an unlikely juxitposition
-            *   of text ever occurs.  Further validation tests of the [nnn]
-            *   string are probably not possible, since little of the actual
-            *   anchor-associated-text is retained in the TextAnchor or the
-            *   FormInfo structs.  Fortunately, I think the current method is
-            *   more than adequate to weed out 99.999% of any possible false
-            *   matches, just as it stands.  Caveat emptor.]
-            */
-           if ((valid) && (n > 0)) {
-               val = atoi (p);
-               if ((val == *old_val) || (*old_val == 0)) {  /* 0 matches all */
+                /*
+                 *  If the number of digits in an existing [tag] increased
+                 *  (eg, [99] --> [100], etc), we need to "adjust" its
+                 *  horizontal position, and that of all subsequant tags
+                 *  that may be on the same line.  PITA.
+                 *
+                 *  [This seems to work as long as a tag isn't a line
+                 *   crosser; when it is, the position of anchors on either
+                 *   side of the split tag, seem to "float" and try to be
+                 *   as "centered" as possible.  Which means that simply
+                 *   incrementing the line_pos by the fixed value of the
+                 *   number of digits that got added to some tag in either
+                 *   line doesn't work quite right, and the text for (say)
+                 *   a button may get stomped on by another copy of itself,
+                 *   but offset by a few chars, when it is selected (eg,
+                 *   "Box Office" may end up looking like "BoBox Office" or
+                 *   "Box Officece", etc.
+                 *
+                 *   Dunno how to fix that behavior ATT, but at least the
+                 *   tag numbers themselves are correct.  -KED  /\oo/\ ]
+                 */
+                if (new_n -= n) {
+                   nxt_anchor = st_anchor;
+                   while ((nxt_anchor)                       &&
+                          (nxt_anchor->line_num == a->line_num)) {
+                         nxt_anchor->line_pos += new_n;
+                         nxt_anchor = nxt_anchor->next;
+                   }
+                   if (st_anchor) st_anchor = st_anchor->next;
+                }
+             }
+          }
+
+          /*
+           *  Unfortunately, valid [tag] strings *can* be split across two
+           *  lines.  Perhaps it would be best to just prevent that from
+           *  happening, but a look into that code, makes me wonder.  Anyway,
+           *  we can handle such tags without *too* much trouble in here [I
+           *  think], though since such animals are rather rare, it makes it
+           *  a bit difficult to test thoroughly (ie, Beyond here, there be
+           *  Dragons).
+           *
+           *  We use lxbuf[] to deal with the two lines involved.
+           */
+          if (plx) {
+             strcpy (lx, p);     /* <- 1st part of a possible lx'ing tag  */
+             pre_n = strlen (p); /* count of 1st part chars in this line  */
+             strcat (lx, ht->next->data);  /* tack on NEXT line           */
+
+             t     = lx;
+             n     = 0;
+             valid = TRUE;
+
+             /*
+              *  Go hunting again for just digits, followed by a tag end ']'.
+              */
+             while  (*t != ']') {
+                 if (isdigit (*t++) != 0) {
+                    n++;
+                    continue;
+                 } else {
+                    valid = FALSE;
+                    break;
+                 }
+             }
+
+             /*
+              *  It *looks* like a line crosser; now we value test it to
+              *  find out for sure [but see the "false match" warning,
+              *  above], and if it matches, increment it into the buffer,
+              *  along with the 2nd line's text.
+              */
+             if ((valid) && (n > 0)) {
+                val = atoi (lx);
+                if ((val == *old_val) || (*old_val == 0)) {
                    if (*old_val != 0)
-                       (*old_val)++;
+                      (*old_val)++;
                    val += incr;
-                   sprintf (s, "%d", val);
-                   new_n = strlen (s);
-                   s += new_n;
-                   p += n;
+                   sprintf (lx, "%d", val);
+                   new_n = strlen (lx);
+                   strcat (lx, strchr (ht->next->data, ']'));
 
                    /*
-                    *  If the number of digits in an existing [tag] increased
-                    *  (eg, [99] --> [100], etc), we need to "adjust" its
-                    *  horizontal position, and that of all subsequant tags
-                    *  that may be on the same line.  PITA.
+                    *  We keep the the same number of chars from the
+                    *  adjusted tag number in the current line; any
+                    *  extra chars due to a digits increase, will be
+                    *  stuffed into the next line.
                     *
-                    *  [This seems to work as long as a tag isn't a line
-                    *   crosser; when it is, the position of anchors on either
-                    *   side of the split tag, seem to "float" and try to be
-                    *   as "centered" as possible.  Which means that simply
-                    *   incrementing the line_pos by the fixed value of the
-                    *   number of digits that got added to some tag in either
-                    *   line doesn't work quite right, and the text for (say)
-                    *   a button may get stomped on by another copy of itself,
-                    *   but offset by a few chars, when it is selected (eg,
-                    *   "Box Office" may end up looking like "BoBox Office" or
-                    *   "Box Officece", etc.
-                    *
-                    *   Dunno how to fix that behavior ATT, but at least the
-                    *   tag numbers themselves are correct.  -KED  /\oo/\ ]
+                    *  Keep track of any digits added, for the next
+                    *  pass through.
                     */
-                   if (new_n -= n) {
-                       nxt_anchor = st_anchor;
-                       while ((nxt_anchor)                           &&
-                           (nxt_anchor->line_num == a->line_num)) {
-                           nxt_anchor->line_pos += new_n;
-                           nxt_anchor = nxt_anchor->next;
-                       }
-                       if (st_anchor) st_anchor = st_anchor->next;
-                   }
-               }
-           }
-
-           /*
-            *  Unfortunately, valid [tag] strings *can* be split across two
-            *  lines.  Perhaps it would be best to just prevent that from
-            *  happening, but a look into that code, makes me wonder.  Anyway,
-            *  we can handle such tags without *too* much trouble in here [I
-            *  think], though since such animals are rather rare, it makes it
-            *  a bit difficult to test thoroughly (ie, Beyond here, there be
-            *  Dragons).
-            *
-            *  We use lxbuf[] to deal with the two lines involved.
-            */
-           if (plx) {
-               strcpy (lx, p);      /* <- 1st part of a possible lx'ing tag  */
-               pre_n = strlen (p);  /* count of 1st part chars in this line  */
-               strcat (lx, ht->next->data);  /* tack on NEXT line          */
-
-               t = lx;
-               n = 0;
-               valid = TRUE;
-
-               /*
-                *  Go hunting again for just digits, followed by a tag end ']'.
-                */
-               while (*t != ']') {
-                   if (isdigit (*t++) != 0) {
-                       n++;
-                       continue;
-                   } else {
-                       valid = FALSE;
-                       break;
-                   }
-               }
-
-               /*
-                *  It *looks* like a line crosser; now we value test it to
-                *  find out for sure [but see the "false match" warning,
-                *  above], and if it matches, increment it into the buffer,
-                *  along with the 2nd line's text.
-                */
-               if ((valid) && (n > 0)) {
-                   val = atoi (lx);
-                   if ((val == *old_val) || (*old_val == 0)) {
-                       if (*old_val != 0)
-                           (*old_val)++;
-                       val += incr;
-                       sprintf (lx, "%d", val);
-                       new_n = strlen (lx);
-                       strcat (lx, strchr (ht->next->data, ']'));
+                   s   = strncpy (s, lx, pre_n) + pre_n;
+                   lx += pre_n;
+                   strcpy (ht->next->data, lx);
 
-                       /*
-                        *  We keep the the same number of chars from the
-                        *  adjusted tag number in the current line; any
-                        *  extra chars due to a digits increase, will be
-                        *  stuffed into the next line.
-                        *
-                        *  Keep track of any digits added, for the next
-                        *  pass through.
-                        */
-                       s   = strncpy (s, lx, pre_n) + pre_n;
-                       lx += pre_n;
-                       strcpy (ht->next->data, lx);
-
-                       *lx_val = new_n - n;
-                   }
-               }
+                   *lx_val = new_n - n;
+                }
+             }
              break;  /* had an lx'er, so we're done with this line */
           }
        }
@@ -9200,7 +9301,7 @@
 
     n = strlen (ht->data);
     if (mode == CHOP)
-       *(buf + n) = '\0';
+       *(buf + n) = '\0';
     strcpy (ht->data, buf);
 
     return (strlen (buf) - n + fixup);
@@ -9208,6 +9309,283 @@
 
 
 /*
+ *  Creates a new anchor and associated struct's appropriate for a form
+ *  TEXTAREA, and links them into the lists following the current anchor
+ *  position (as specified by arg1).
+ *
+ *  Exits with arg1 now pointing at the new TextAnchor, and arg2 pointing
+ *  at the new, associated HTLine.
+ *
+ *  --KED  02/13/99
+ */
+PRIVATE void insert_new_textarea_anchor ARGS2(
+       TextAnchor **,   curr_anchor,
+       HTLine **,       exit_htline)
+{
+    TextAnchor *anchor = *curr_anchor;
+    HTLine     *htline;
+
+    TextAnchor *a = 0;
+    FormInfo   *f = 0;
+    HTLine     *l = 0;
+
+    int curr_tag  = 0;   /* 0 ==> match any [tag] number */
+    int lx        = 0;   /* 0 ==> no line crossing [tag]; it's a new line */
+    int i;
+
+
+    /*
+     *  Find line in the text that matches ending anchorline of
+     *  the TEXTAREA.
+     *
+     *  [Yes, Virginia ... we *do* have to go thru this for each
+     *   anchor being added, since there is NOT a 1-to-1 mapping
+     *   between anchors and htlines.  I suppose we could create
+     *   YAS (Yet Another Struct), but there are too many structs{}
+     *   floating around in here, as it is.  IMNSHO.]
+     */
+    for (htline = HTMainText->last_line->next, i = 0;
+        anchor->line_num != i;                i++) {
+        htline = htline->next;
+       if (htline == HTMainText->last_line)
+          break;
+    }
+
+    /*
+     *  Clone and initialize the struct's needed to add a new TEXTAREA
+     *  anchor.
+     */
+    if (((a = (TextAnchor *) calloc (1, sizeof(*a)))          == 0)  ||
+       ((f = (FormInfo   *) calloc (1, sizeof(*f)))          == 0)  ||
+       ((l = (HTLine     *) calloc (1, LINE_SIZE(MAX_LINE))) == 0))
+       outofmem(__FILE__, "insert_new_textarea_anchor");
+
+    /*  Init all the fields in the new TextAnchor.                 */
+    /*  [anything "special" needed based on ->show_anchor value ?] */
+    a->next           = anchor->next;
+    a->number         = anchor->number;
+    a->start          = anchor->start + anchor->input_field->size + 1;
+    a->line_pos               = anchor->line_pos;
+    a->extent         = anchor->extent;
+    a->line_num               = anchor->line_num + 1;
+    StrAllocCopy (a->hightext,  anchor->hightext);
+    StrAllocCopy (a->hightext2, anchor->hightext2);
+    a->hightext2offset = anchor->hightext2offset;
+    a->link_type       = anchor->link_type;
+    a->input_field     = f;
+    a->show_anchor     = anchor->show_anchor;
+    a->inUnderline     = anchor->inUnderline;
+    a->expansion_anch  = TRUE;
+    a->anchor         = NULL;
+
+    /*  Just the (seemingly) relevant fields in the new FormInfo.  */
+    /*  [do we need to do anything "special" based on ->disabled]  */
+    StrAllocCopy (f->name, anchor->input_field->name);
+    f->number         = anchor->input_field->number;
+    f->type           = anchor->input_field->type;
+    StrAllocCopy (f->orig_value, "");
+    f->size           = anchor->input_field->size;
+    f->maxlength       = anchor->input_field->maxlength;
+    f->no_cache        = anchor->input_field->no_cache;
+    f->disabled        = anchor->input_field->disabled;
+
+    /*  Init all the fields in the new HTLine (but see the #if).   */
+    l->next           = htline->next;
+    l->prev           = htline;
+    l->offset         = htline->offset;
+    l->size           = htline->size;
+    l->split_after     = htline->split_after;
+    l->bullet         = htline->bullet;
+    l->expansion_line  = TRUE;
+#if defined(USE_COLOR_STYLE)
+    /* dup styles[] if needed [no need in TEXTAREA (?); leave 0's] */
+    l->numstyles       = htline->numstyles;
+#endif
+    strcpy (l->data,     htline->data);
+    if (keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED) {
+       a->number++;
+       increment_tagged_htline (l, a, &lx, &curr_tag, 1, CHOP);
+    }
+
+    /*
+     *  If we're at the tail end of the TextAnchor or HTLine list(s),
+     *  the new node becomes the last node.
+     */
+    if (anchor == HTMainText->last_anchor)
+       HTMainText->last_anchor = a;
+    if (htline == HTMainText->last_line)
+       HTMainText->last_line = l;
+
+    /*
+     *  Link in the new TextAnchor and point the entry anchor arg at it;
+     *  link in the new HTLine and point the entry htline arg at it, too.
+     */
+    anchor->next = a;
+   *curr_anchor  = a;
+
+    htline->next->prev = l;
+    htline->next       = l;
+   *exit_htline        = l->next;
+
+    return;
+}
+
+
+/*
+ *  If new anchors were added to expand a TEXTAREA, we need to ripple the
+ *  new line numbers [and char counts ?] thru the subsequent anchors.
+ *
+ *  If form lines are getting [nnn] tagged, we need to update the displayed
+ *  tag values to match (which means rerendering them ... sigh).
+ *
+ *  Finally, we need to update various HTMainText and other counts, etc.
+ *
+ *  [dunno if the char counts really *need* to be done, or if we're using
+ *   the exactly proper values/algorithms ... seems to be OK though ...]
+ *
+ *  --KED  02/13/99
+ */
+PRIVATE void update_subsequent_anchors ARGS4(
+        int,             n,
+        TextAnchor *,    start_anchor,
+       HTLine *,        start_htline,
+        int,             start_tag)
+{
+    TextAnchor *anchor;
+    HTLine     *htline = start_htline;
+
+    int form_chars_added = (start_anchor->input_field->size + 1) * n;
+    int         line_adj = 0;
+    int         tag_adj  = 0;
+    int         lx       = 0;
+    int      hang        = 0;  /* for HANG detection of a nasty intermittent */
+    int      hang_detect = 100000;  /* ditto */
+
+
+    CTRACE(tfp, "GridText: adjusting struct's to add %d new line(s)\n", n);
+
+    /*
+     *  Update numeric fields of the rest of the anchors.
+     *
+     *  [We bypass bumping ->number if it has a value of 0, which takes care
+     *   of the ->input_field->type == F_HIDDEN_TYPE (as well as any other
+     *   "hidden" anchors (if such things exist).  Seems like the "right
+     *   thing" to do.  I think.
+     */
+    anchor = start_anchor->next;   /* begin updating with the NEXT anchor */
+    while (anchor) {
+        if ((keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED) &&
+            (anchor->number != 0))
+          anchor->number += n;
+       anchor->line_num  += n;
+       anchor->start     += form_chars_added;
+       anchor = anchor->next;
+    }
+
+    /*
+     *  Update/rerender anchor [tags], if they are being numbered.
+     *
+     *  [If a number tag (eg, "[177]") is itself broken across a line
+     *   boundary, this fixup only partially works.  While the tag
+     *   numbering is done properly across the pair of lines, the
+     *   horizontal positioning on *either* side of the split, can get
+     *   out of sync by a char or two when it gets selected.  See [com
+     *   ments] in  increment_tagged_htline()  for some more detail.
+     *
+     *   I suppose THE fix is to prevent such tag-breaking in the first
+     *   place (dunno where yet, though).  Ah well ... at least the tag
+     *   numbers themselves are correct from top to bottom now.
+     *
+     *   All that said, about the only time this will be a problem in
+     *   *practice*, is when a page has near 1000 links or more (possibly
+     *   after a TEXTAREA expansion), and has line crossing tag(s), and
+     *   the tag numbers in a line crosser go from initially all 3 digit
+     *   numbers, to some mix of 3 and 4 digits (or all 4 digits) as a
+     *   result of the expansion process.  Oh, you also need a "clump" of
+     *   anchors all on the same lines.
+     *
+     *   Yes, it *can* happen, but in real life, it probably won't be
+     *   seen very much ...]
+     *
+     *  [This may also be an artifact of bumping into the right hand
+     *   screen edge (or RHS margin), since we don't even *think* about
+     *   relocating an anchor to the following line, when [tag] digits
+     *   expansion pushes things too far in that direction.]
+     */
+    if (keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED) {
+       anchor = start_anchor->next;
+       while (htline != HTMainText->last_line->next) {
+
+          while (anchor) {
+              if ((anchor->number - n) == start_tag)
+                 break;
+
+              /*** A HANG (infinite loop) *has* occurred here, with */
+              /*** the values of anchor and anchor->next being the  */
+              /*** the same, OR with anchor->number "magically" and */
+              /*** suddenly taking on an anchor-pointer-like value. */
+              /***                                                  */
+              /*** The same code and same doc have both passed and  */
+              /*** failed at different times, which indicates some  */
+              /*** sort of content/html dependency, or some kind of */
+              /*** a "race" condition, but I'll be damned if I can  */
+              /*** find it after tons of CTRACE's, printf()'s, gdb  */
+              /*** breakpoints and watchpoints, etc.                */
+              /***                                                  */
+              /*** I have added a hang detector (with error msg and */
+              /*** beep) here, to break the loop and warn the user, */
+              /*** until it can be isolated and fixed.              */
+              /***                                                  */
+              /*** [One UGLY intermittent .. gak ..!  02/22/99 KED] */
+
+              hang++;
+              if ((anchor == anchor->next) || (hang >= hang_detect))
+                 goto hang_detected;
+
+              anchor = anchor->next;
+          }
+
+          if (anchor) {
+             line_adj = increment_tagged_htline (htline,  anchor,  &lx,
+                                                 &start_tag, n,  NOCHOP);
+             htline->size += line_adj;
+             tag_adj      += line_adj;
+
+          } else {
+
+             break;  /* out of anchors ... we're done */
+          }
+
+          htline = htline->next;
+       }
+    }
+
+exit:
+    /*
+     *  Fixup various global variables.
+     */
+    nlinks                         += n;
+    HTMainText->Lines              += n;
+    HTMainText->last_anchor_number += n;
+    HTMainText->chars              += (form_chars_added + tag_adj);
+
+    more = HText_canScrollDown();
+
+    CTRACE(tfp, "GridText: TextAnchor and HTLine struct's adjusted\n");
+
+    return;
+
+hang_detected:  /* uglyness has happened; inform user and do the best we can */
+
+    printf ("\007");  /* beep the user */
+    fflush (NULL);
+    HTAlert ("Hang Detect: TextAnchor struct corrupted - suggest aborting!");
+    HTAlert ("Hang Detect: TextAnchor struct corrupted - suggest aborting!");
+    goto exit;
+}
+
+
+/*
  *  Transfer the initial contents of a TEXTAREA to a temp file, invoke the
  *  user's editor on that file, then transfer the contents of the resultant
  *  edited file back into the TEXTAREA (expanding the size of the area, if
@@ -9223,6 +9601,7 @@
            struct link *,  form_link)
 {
     struct stat stat_info;
+    size_t     size;
 
     char       *ed_temp;
     FILE       *fp;
@@ -9245,26 +9624,14 @@
 
     HTLine     *htline  = NULL;
 
-    TextAnchor *a = 0;
-    FormInfo   *f = 0;
-    HTLine     *l = 0;
-
     char       *ebuf;
     char       *tbuf = NULL;
     char       *line;
     char       *lp;
     char       *cp;
-    char       *p;
-    char       *s;
-    int         curr_tag;
-    int         line_adj = 0;
-    int         tag_adj  = 0;
-    int         newlines = 0;
+    int         match_tag = 0;
+    int         newlines  = 0;
     int        len;
-    int        i;
-    int        n;
-    int        lx;
-    size_t     size;
 
 
     CTRACE(tfp, "GridText: entered HText_ExtEditForm()\n");
@@ -9320,7 +9687,7 @@
         }
         anchor_ptr = anchor_ptr->next;
     }
-    fclose (fp);
+    LYCloseTempFP (fp);
 
     CTRACE(tfp, "GridText: TEXTAREA name=|%s| dumped to tempfile\n", areaname);
     CTRACE(tfp, "GridText: invoking editor (%s) on tempfile\n", editor);
@@ -9349,7 +9716,7 @@
     if (stat (tbuf, &stat_info) == 0)
        remove (tbuf);
 #endif
-    FREE (tbuf);
+    FREE(tbuf);
 
     CTRACE(tfp, "GridText: returned from editor (%s)\n", editor);
 
@@ -9379,14 +9746,11 @@
 
     /*
      * Copy each line from the temp file into the corresponding anchor
-     *  struct, removing any trailing whitespace, expanding any embedded
-     *  tab's, and substituting a printable char for any other embedded
-     *  control chars.  Add new lines to the TEXTAREA if needed.  (Always
-     *  leave the user with a blank line at the end of the TEXTAREA.)
+     *  struct.  Add new lines to the TEXTAREA if needed.  (Always leave
+     *  the user with a blank line at the end of the TEXTAREA.)
      */
-    if (((line = (char *) malloc (MAX_LINE)) == 0) ||
-       ((tbuf = (char *) malloc (MAX_LINE)) == 0))
-       outofmem(__FILE__, "HText_ExtEditForm");
+    if ((line = (char *) malloc (MAX_LINE)) == 0)
+       outofmem(__FILE__, "HText_ExtEditForm");
 
     anchor_ptr = start_anchor;
 
@@ -9403,184 +9767,24 @@
        strncpy (line, lp, len);
        *(line + len) = '\0';
 
-       /*
-        *  Whack off trailing whitespace from the line.
-        */
-       for (i = len, p = line + (len - 1); i != 0; p--, i--) {
-           if (isspace (*p))
-               *p = '\0';
-           else
-               break;
-       }
-
-       if (strlen (line) != 0) {
-           /*
-            *  Expand any tab's, since they won't render properly in a
-            *  TEXTAREA.
-            *
-            *  [is that "by spec", or just a "lynxism" ? ... as may be, it
-            *   seems they may cause other problems, too ... haven't really
-            *   looked into that very deeply though]
-            */
-           p = line;
-           s = tbuf;
-
-           while (*p) {
-               if ((cp = strchr (p, '\t')) != 0) {
-                   i  = cp - p;
-                   s  = (strncpy (s, p, i))      + i;
-                   n  = TABSTOP - (i % TABSTOP);
-                   s  = (strncpy (s, SPACES, n)) + n;
-                   p += (i + 1);
-
-               } else {
-
-                   strcpy (s, p);
-                   break;
-               }
-           }
-
-           /*
-            *  Replace control chars with something printable.  Note that
-            *  we leave any chars above 0x7f alone (ie, no translation is
-            *  performed ... the assumption being that the charset used in
-            *  the editor is compatible with the charset rendered by lynx).
-            *
-            *  [At some point in time, when/if lynx ever supports multibyte
-            *   characters internally (eg, UCS-2, UCS-4, UTF-16, etc), this
-            *   kind of thing may well cause problems.  But then, supporting
-            *   such char sets will require massive changes in (most) all
-            *   parts of the lynx code, so until then, we do the rational
-            *   thing with chars that would otherwise foul the display, if
-            *   left alone.  If you're implementing multibyte character set
-            *   support, consider yourself to have been warned.]
-            */
-           for (p = line, s = tbuf; *s != '\0'; p++, s++)
-#ifndef EBCDIC
-               *p = (((unsigned char)*s  < ' ')    ||
-                     ((unsigned char)*s == '\177')) ? SPLAT : *s;
-#else
-               *p = ((unsigned char)*s  < ' ') ? SPLAT : *s;
-#endif
-           *p = '\0';
-       }
-
+       cleanup_line_for_textarea (line, len);
 
        /*
         *  If there are more lines in the edit buffer than were in the
-        *  original TEXTAREA, we need to add some new lines, continuing
-        *  until the edit buffer is empty.
-        *
-        *  [cloning structs could be moved to a seperate fn(), or three]
+        *  original TEXTAREA, we need to add a new line/anchor, continuing
+        *  on until the edit buffer is empty.
         */
        if (line_cnt > orig_cnt) {
-
-           /*
-            *  Find line in the text that matches ending anchorline of
-            *  the TEXTAREA.
-            *
-            *  [Yes, Virginia ... we *do* have to go thru this for each
-            *   anchor being added, since there is NOT a 1-to-1 mapping
-            *   between anchors and htlines.  I suppose we could create
-            *   YAS (Yet Another Struct), but there are too many structs{}
-            *   floating around in here, as it is.  IMNSHO.]
-            */
-           for (htline = HTMainText->last_line->next, i = 0;
-                end_anchor->line_num != i;            i++) {
-               htline = htline->next;
-               if (htline == HTMainText->last_line)
-                   break;
-           }
-
-           /*
-            *  Clone and initialize the structs needed to add a new
-            *  TEXTAREA anchor.
-            */
-           if (((a = (TextAnchor *) calloc (1, sizeof(*a)))          == 0)  ||
-               ((f = (FormInfo   *) calloc (1, sizeof(*f)))          == 0)  ||
-               ((l = (HTLine     *) calloc (1, LINE_SIZE(MAX_LINE))) == 0))
-               outofmem(__FILE__, "HText_ExtEditForm");
-
-           /*  Init all the fields in the new TextAnchor.                 */
-           /*  [anything "special" needed based on ->show_anchor value ?] */
-           a->next             = end_anchor->next;
-           a->number           = end_anchor->number;
-           a->start            = end_anchor->start +
-                                 end_anchor->input_field->size + 1;
-           a->line_pos         = end_anchor->line_pos;
-           a->extent           = end_anchor->extent;
-           a->line_num         = end_anchor->line_num + 1;
-           StrAllocCopy (a->hightext,  end_anchor->hightext);
-           StrAllocCopy (a->hightext2, end_anchor->hightext2);
-           a->hightext2offset  = end_anchor->hightext2offset;
-           a->link_type        = end_anchor->link_type;
-           a->input_field      = f;
-           a->show_anchor      = end_anchor->show_anchor;
-           a->inUnderline      = end_anchor->inUnderline;
-           a->expansion_anch   = TRUE;
-           a->anchor           = NULL;
-
-           /*  Just the (seemingly) relevant fields in the new FormInfo.  */
-           /*  [do we need to do anything "special" based on ->disabled]  */
-           StrAllocCopy (f->name, end_anchor->input_field->name);
-           f->number           = end_anchor->input_field->number;
-           f->type             = end_anchor->input_field->type;
-           StrAllocCopy (f->orig_value, "");
-           f->size             = end_anchor->input_field->size;
-           f->maxlength        = end_anchor->input_field->maxlength;
-           f->no_cache         = end_anchor->input_field->no_cache;
-           f->disabled         = end_anchor->input_field->disabled;
-
-           /*  Init all the fields in the new HTLine (but see the #if).   */
-           l->next             = htline->next;
-           l->prev             = htline;
-           l->offset           = htline->offset;
-           l->size             = htline->size;
-           l->split_after      = htline->split_after;
-           l->bullet           = htline->bullet;
-           l->expansion_line   = TRUE;
-#if defined(USE_COLOR_STYLE)
-          /* dup styles[] if needed [no need in TEXTAREA (?); leave 0's] */
-           l->numstyles        = htline->numstyles;
-#endif
-           strcpy (l->data,      htline->data);
-           if (keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED) {
-               a->number++;
-               curr_tag = 0;  /* 0 matches any [tag] number */
-               lx       = 0;
-               increment_tagged_htline (l, a, &lx, &curr_tag, 1, CHOP);
-           }
-
-           /*
-            *  If we're at the tail end of the TextAnchor or HTLine list(s),
-            *  the new node becomes the last node.
-            */
-           if (end_anchor == HTMainText->last_anchor)
-               HTMainText->last_anchor = a;
-           if (htline == HTMainText->last_line)
-               HTMainText->last_line = l;
-
-           /*
-            *  Link in the new TextAnchor and make it current; link in
-            *  the new HTLine.
-            */
-           end_anchor->next = a;
-           anchor_ptr = a;
-
-           htline->next->prev = l;
-           htline->next = l;
-           htline = l->next;
-
-           newlines++;
+          insert_new_textarea_anchor (&end_anchor, &htline);
+          anchor_ptr = end_anchor;   /* make the new anchor current */
+          newlines++;
        }
 
-
        /*
         *  Finally copy the new line from the edit buffer into the anchor.
         */
        StrAllocCopy(anchor_ptr->input_field->value, line);
 
-
        /*
         *  Keep track of 1st blank line in any trailing blank lines, for
         *  later cursor repositioning.
@@ -9600,7 +9804,7 @@
        anchor_ptr = anchor_ptr->next;
 
        if (anchor_ptr)
-          curr_tag = anchor_ptr->number;
+          match_tag = anchor_ptr->number;
 
        line_cnt++;
     }
@@ -9608,118 +9812,403 @@
     CTRACE(tfp, "GridText: edited text inserted into lynx struct's\n");
 
     /*
-     * If new anchors were added, we need to ripple the new line numbers
-     * (and char counts ?) thru the subsequent anchors.  If form lines
-     *  are getting tagged, we need to update the displayed tag values
-     *  to match (which means re-rendering them ...sigh).  Finally update
-     *  the HText counts.
+     * If we've added any new lines/anchors, we need to adjust various
+     *  things in all anchor-bearing lines following the last newly added
+     *  line/anchor.  The fun stuff starts here ...
+     */
+    if (newlines > 0)
+       update_subsequent_anchors (newlines, end_anchor, htline, match_tag);
+
+    /*
+     *  Cleanup time.
+     */
+    FREE(line);
+    FREE(ebuf);
+    LYRemoveTemp (ed_temp);
+    FREE(ed_temp);
+
+    CTRACE(tfp, "GridText: exiting HText_ExtEditForm()\n");
+
+    /*
+     *  Return the offset needed to move the cursor from its current
+     *  (on entry) line number, to the 1st blank line of the trailing
+     *  (group of) blank line(s), which is where we want to be.  Let
+     *  the caller deal with moving us there, however ... :-) ...
+     */
+    return (exit_line - entry_line);
+}
+
+
+/*
+ *  Expand the size of a TEXTAREA by a fixed number of lines (as specified
+ *  by arg2).
+ *
+ *  --KED  02/14/99
+ */
+PUBLIC void HText_ExpandTextarea ARGS2(
+            struct link *,  form_link,
+           int,            newlines)
+{
+    TextAnchor *anchor_ptr;
+    TextAnchor *end_anchor    = NULL;
+    BOOLEAN    firstanchor   = TRUE;
+
+    FormInfo *form      = form_link->form;
+    char     *areaname  = form->name;
+    int       form_num  = form->number;
+
+    HTLine   *htline    = NULL;
+
+    int       match_tag = 0;
+    int       i;
+
+
+    CTRACE(tfp, "GridText: entered HText_ExpandTextarea()\n");
+
+    if (newlines < 1) return;
+
+    /*
+     * Begin at the beginning, to find the TEXTAREA, then on to find
+     *  the last line (anchor) in it.
+     *
+     * [Finding the TEXTAREA we're actually *in* with these attributes
+     *  isn't foolproof.  The form_num isn't unique to a given TEXTAREA,
+     *  and there *could* be TEXTAREA's with the same "name".  If that
+     *  should ever be true, we'll actually expand the *1st* TEXTAREA
+     *  in the page that matches.  We should probably assign a unique
+     *  id to each TEXTAREA in a page, and match on that, to avoid this
+     *  (potential) problem.
      *
-     * [dunno if the char counts really need to be done, or if we're using
-     *  the exactly proper values ... seems to be OK though ...]
+     *  Since the odds of "false matches" *actually* happening in real
+     *  life seem rather small though, we'll hold off doing this, for a
+     *  rainy day ...]
      */
-    if (newlines > 0) {
+    anchor_ptr = HTMainText->first_anchor;
 
-       CTRACE(tfp, "GridText: adjusting struct's to add %d new lines\n",
-                   newlines);
+    while (anchor_ptr) {
+
+       if ((anchor_ptr->link_type           == INPUT_ANCHOR)    &&
+           (anchor_ptr->input_field->type   == F_TEXTAREA_TYPE) &&
+           (anchor_ptr->input_field->number == form_num)        &&
+           !strcmp (anchor_ptr->input_field->name, areaname))   {
 
-       i = (end_anchor->input_field->size + 1) * newlines;
+           if (firstanchor)
+               firstanchor = FALSE;
+
+           end_anchor = anchor_ptr;
+
+        } else {
+
+           if (!firstanchor)
+               break;
+        }
+        anchor_ptr = anchor_ptr->next;
+    }
+
+    for (i = 1; i <= newlines; i++) {
+        insert_new_textarea_anchor (&end_anchor, &htline);
 
        /*
-        *  [*may* need to bypass bumping ->number if ->input_field->type
-        *  is of F_HIDDEN_TYPE ... need to investigate further]
+        *  Make the new line blank.
         */
-       while (anchor_ptr) {
-           if (keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED)
-               anchor_ptr->number += newlines;
-           anchor_ptr->line_num  += newlines;
-           anchor_ptr->start     += i;
-           anchor_ptr             = anchor_ptr->next;
-       }
-       anchor_ptr = end_anchor;
+       StrAllocCopy(end_anchor->input_field->value, "");
 
        /*
-        *  If a number tag (eg, "[177]") is itself broken across a line
-        *  boundary, this fixup only partially works.  While the tag
-        *  numbering is done properly across the pair of lines, the
-        *  horizontal positioning on *either* side of the split, can get
-        *  out of sync by a char or two when it gets selected.  See [com
-        *  ments] in  increment_tagged_htline()  for some more detail.
-        *
-        *  I suppose THE fix is to prevent such tag-breaking in the first
-        *  place (dunno where yet, though).  Ah well ... at least the tag
-        *  numbers are correct from top to bottom now.
-        *
-        *  All that said, about the only time this will be a problem in
-        *  *practice*, is when a page has near 1000 links or more (possibly
-        *  after a TEXTAREA expansion), and has line crossing tag(s), and
-        *  the tag numbers in a line crosser go from initially all 3 digit
-        *  numbers, to some mix of 3 and 4 digits (or all 4 digits) as a
-        *  result of the expansion process.  Oh, and you need a "clump" of
-        *  anchors all on the same lines.
-        *
-        *  Yes, it *can* happen, but in real life, it probably won't be
-        *  seen very much ...
-        *
-        *  [This may also be an artifact of bumping into the right hand
-        *   screen edge (or RHS margin), since we don't even *think* about
-        *   relocating an anchor to the following line, when [tag] digits
-        *   expansion pushes things too far in that direction.]
-        */
-       if (keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED) {
-           lx = 0;
-           while (htline != HTMainText->last_line->next) {
+        *  And go add another line ...
+        */
+       if (end_anchor->next)
+          match_tag = end_anchor->next->number;
+    }
 
-               while (anchor_ptr) {
-                   if ((anchor_ptr->number - newlines) == curr_tag)
-                       break;
-                   else
-                       anchor_ptr = anchor_ptr->next;
-               }
+    CTRACE(tfp, "GridText: %d blank line(s) added to TEXTAREA name=|%s|\n",
+               newlines, areaname);
 
-               if (anchor_ptr) {
-                   line_adj = increment_tagged_htline (htline,   anchor_ptr,
-                                                       &lx,    &curr_tag,
-                                                       newlines, NOCHOP);
-                   htline->size += line_adj;
-                   tag_adj      += line_adj;
+    /*
+     * We need to adjust various things in all anchor bearing lines
+     *  following the last newly added line/anchor.  Fun stuff.
+     */
+    update_subsequent_anchors (newlines, end_anchor, htline, match_tag);
 
-               } else {
+    CTRACE(tfp, "GridText: exiting HText_ExpandTextarea()\n");
 
-                   break;  /* out of anchors ... we're done */
-               }
+    return;
+}
 
-               htline = htline->next;
-           }
+
+/*
+ *  Insert the contents of a file into a TEXTAREA between the cursorline,
+ *  and the line preceeding it.
+ *
+ *  Returns the number of lines that the cursor should be moved so that it
+ *  will end up on the 1st line in the TEXTAREA following the inserted file
+ *  (if we decide to do that).
+ *
+ *  --KED  02/21/99
+ */
+PUBLIC int HText_InsertFile ARGS1(
+           struct link *,  form_link)
+{
+    struct stat stat_info;
+    size_t     size;
+
+    FILE       *fp;
+    char       *fn;
+
+    TextAnchor *anchor_ptr;
+    TextAnchor *prev_anchor = NULL;
+    TextAnchor *end_anchor  = NULL;
+    BOOLEAN    firstanchor = TRUE;
+
+    FormInfo   *form    = form_link->form;
+    char       *areaname = form->name;
+    int        form_num = form->number;
+
+    HTLine     *htline  = NULL;
+
+    TextAnchor *a = 0;
+    FormInfo   *f = 0;
+    HTLine     *l = 0;
+
+    char       *fbuf;
+    char       *line;
+    char       *lp;
+    char       *cp;
+    int        entry_line = form_link->anchor_line_num;
+    int         match_tag  = 0;
+    int         newlines   = 0;
+    int        len;
+    int        i;
+
+
+    CTRACE(tfp, "GridText: entered HText_InsertFile()\n");
+
+    /*
+     * Get the filename of the insert file.
+     */
+    if (!(fn = GetFileName())) {
+       HTInfoMsg (FILE_INSERT_CANCELLED);
+       CTRACE(tfp, "GridText: file insert cancelled - no filename provided\n");
+       return (0);
+    }
+
+    /*
+     * Read it into our buffer (abort on 0-length file).
+     */
+    if ((stat (fn, &stat_info) < 0)        ||
+       ((size = stat_info.st_size) == 0)) {
+       HTInfoMsg (FILE_INSERT_0_LENGTH);
+       CTRACE(tfp, "GridText: file insert aborted - file=|%s|- was 0-length\n",
+                  fn);
+       FREE(fn);
+       return (0);
+
+    } else {
+
+       if ((fbuf = (char *) calloc (size + 1, (sizeof(char)))) == NULL)
+         outofmem(__FILE__, "HText_InsertFile");
+
+       fp   = fopen (fn, "r");
+       size = fread (fbuf, 1, size, fp);
+       fclose (fp);
+       FREE(fn);
+    }
+
+
+    /*
+     * Begin at the beginning, to find the TEXTAREA we're in, then
+     * the current cursorline.
+     *
+     * [Finding the TEXTAREA we're actually *in* with these attributes
+     *  isn't foolproof.  The form_num isn't unique to a given TEXTAREA,
+     *  and there *could* be TEXTAREA's with the same "name".  If that
+     *  should ever be true, we'll actually insert data into the *1st*
+     *  TEXTAREA in the page that matches.  We should probably assign
+     *  a unique id to each TEXTAREA in a page, and match on that, to
+     *  avoid this (potential) problem.
+     *
+     *  Since the odds of "false matches" *actually* happening in real
+     *  life seem rather small though, we'll hold off doing this, for a
+     *  rainy day ...]
+     */
+    anchor_ptr = HTMainText->first_anchor;
+
+    while (anchor_ptr) {
+
+       if ((anchor_ptr->link_type           == INPUT_ANCHOR)    &&
+           (anchor_ptr->input_field->type   == F_TEXTAREA_TYPE) &&
+           (anchor_ptr->input_field->number == form_num)        &&
+           !strcmp (anchor_ptr->input_field->name, areaname))   {
+
+          if (anchor_ptr->line_num == entry_line)
+             break;
+       }
+       prev_anchor = anchor_ptr;
+       anchor_ptr  = anchor_ptr->next;
+    }
+
+
+    /*
+     *  Clone a new TEXTAREA line/anchor using the cursorline anchor as
+     *  a template, but link it in BEFORE the cursorline anchor/htline.
+     *
+     *  [We can probably combine this with insert_new_textarea_anchor()
+     *   along with a flag to indicate "insert before" as we do here,
+     *   or the "normal" mode of operation (add after "current" anchor/
+     *   line).  Beware of the differences ... some are a bit subtle to
+     *   notice.
+     */
+    for (htline = HTMainText->last_line->next, i = 0;
+        anchor_ptr->line_num != i;            i++) {
+        htline = htline->next;
+       if (htline == HTMainText->last_line)
+          break;
+    }
+
+    if (((a = (TextAnchor *) calloc (1, sizeof(*a)))          == 0)  ||
+       ((f = (FormInfo   *) calloc (1, sizeof(*f)))          == 0)  ||
+       ((l = (HTLine     *) calloc (1, LINE_SIZE(MAX_LINE))) == 0))
+       outofmem(__FILE__, "HText_InsertFile");
+
+    /*  Init all the fields in the new TextAnchor.                 */
+    /*  [anything "special" needed based on ->show_anchor value ?] */
+    a->next           = anchor_ptr;
+    a->number         = anchor_ptr->number;
+    a->start          = anchor_ptr->start;
+    a->line_pos               = anchor_ptr->line_pos;
+    a->extent         = anchor_ptr->extent;
+    a->line_num               = anchor_ptr->line_num;
+    StrAllocCopy (a->hightext,  anchor_ptr->hightext);
+    StrAllocCopy (a->hightext2, anchor_ptr->hightext2);
+    a->hightext2offset = anchor_ptr->hightext2offset;
+    a->link_type       = anchor_ptr->link_type;
+    a->input_field     = f;
+    a->show_anchor     = anchor_ptr->show_anchor;
+    a->inUnderline     = anchor_ptr->inUnderline;
+    a->expansion_anch  = TRUE;
+    a->anchor         = NULL;
+
+    /*  Just the (seemingly) relevant fields in the new FormInfo.  */
+    /*  [do we need to do anything "special" based on ->disabled]  */
+    StrAllocCopy (f->name, anchor_ptr->input_field->name);
+    f->number         = anchor_ptr->input_field->number;
+    f->type           = anchor_ptr->input_field->type;
+    StrAllocCopy (f->orig_value, "");
+    f->size           = anchor_ptr->input_field->size;
+    f->maxlength       = anchor_ptr->input_field->maxlength;
+    f->no_cache        = anchor_ptr->input_field->no_cache;
+    f->disabled        = anchor_ptr->input_field->disabled;
+
+    /*  Init all the fields in the new HTLine (but see the #if).   */
+    l->offset         = htline->offset;
+    l->size           = htline->size;
+    l->split_after     = htline->split_after;
+    l->bullet         = htline->bullet;
+    l->expansion_line  = TRUE;
+#if defined(USE_COLOR_STYLE)
+    /* dup styles[] if needed [no need in TEXTAREA (?); leave 0's] */
+    l->numstyles       = htline->numstyles;
+#endif
+    strcpy (l->data,     htline->data);
+
+    /*
+     *  If we're at the head of the TextAnchor list, the new node becomes
+     *  the first node.
+     */
+    if (anchor_ptr == HTMainText->first_anchor)
+       HTMainText->first_anchor = a;
+
+    /*
+     *  Link in the new TextAnchor, and corresponding HTLine.
+     */
+    if (prev_anchor)
+       prev_anchor->next = a;
+
+    htline             = htline->prev;
+    l->next           = htline->next;
+    l->prev           = htline;
+    htline->next->prev = l;
+    htline->next       = l;
+
+    /*
+     *  update_subsequent_anchors() expects htline to point to 1st potential
+     *  line needing fixup; we need to do this just in case the inserted file
+     *  was only a single line (yes, it's pathological ... ).
+     */
+    htline = htline->next; /* ->new (current) htline, for 1st inserted line  */
+    htline = htline->next; /* ->1st potential (following) [tag] fixup htline */
+
+    anchor_ptr = a;
+    newlines++;
+
+
+    /*
+     * Copy each line from the insert file into the corresponding anchor
+     *  struct.
+     *
+     *  Begin with the new line/anchor we just added (above the cursorline).
+     */
+    if ((line = (char *) malloc (MAX_LINE)) == 0)
+       outofmem(__FILE__, "HText_InsertFile");
+
+    match_tag = anchor_ptr->number;
+
+    len = 0;
+    lp  = fbuf;
+
+    while (*lp) {
+
+       if ((cp = strchr (lp, '\n')) != 0)
+          len = cp - lp;
+       else
+          len = strlen (lp);
+
+       strncpy (line, lp, len);
+       *(line + len) = '\0';
+
+       cleanup_line_for_textarea (line, len);
+
+       /*
+        *  If not the first line from the insert file, we need to add
+        *  a new line/anchor, continuing on until the buffer is empty.
+        */
+       if (!firstanchor) {
+          insert_new_textarea_anchor (&end_anchor, &htline);
+          anchor_ptr = end_anchor;   /* make the new anchor current */
+          newlines++;
        }
 
        /*
-        *  Fixup various global variables.
+        *  Copy the new line from the buffer into the anchor.
         */
-       nlinks                         += newlines;
-       HTMainText->Lines              += newlines;
-       HTMainText->last_anchor_number += newlines;
-       HTMainText->chars              += (i + tag_adj);
+       StrAllocCopy(anchor_ptr->input_field->value, line);
 
-       more = TRUE;
+       /*
+        *  And do the next line of insert text, for the next anchor ...
+        */
+       lp += len;
+       if (*lp) lp++;
 
-       CTRACE(tfp, "GridText: TextAnchor and HTLine struct's adjusted\n");
+       firstanchor = FALSE;
+       end_anchor  = anchor_ptr;
+       anchor_ptr  = anchor_ptr->next;
     }
 
-    CTRACE(tfp, "GridText: struct's adjusted - exiting HText_ExtEditForm()\n");
+    CTRACE(tfp, "GridText: file inserted into lynx struct's\n");
 
-    FREE (line);
-    FREE (ebuf);
-    FREE (tbuf);
-    LYRemoveTemp (ed_temp);
-    FREE (ed_temp);
+    /*
+     * Now adjust various things in all anchor-bearing lines following the
+     *  last newly added line/anchor.  Some say this is the fun part ...
+     */
+    update_subsequent_anchors (newlines, end_anchor, htline, match_tag);
 
-    CTRACE(tfp, "GridText: exiting HText_ExtEditForm()\n");
 
     /*
-     *  Return the offset needed to move the cursor from its current
-     *  (on entry) line number, to the 1st blank line of the trailing
-     *  (group of) blank line(s), which is where we want to be.  Let
-     *  the caller deal with moving us there, however ... :-) ...
+     *  Cleanup time.
      */
-    return (exit_line - entry_line);
+    FREE(line);
+    FREE(fbuf);
+
+    CTRACE(tfp, "GridText: exiting HText_InsertFile()\n");
+
+    return (newlines);
 }
diff -uNr lynx-2.8.2-dev.17+kd.orig/src/GridText.h 
lynx-2.8.2-dev.17+kd/src/GridText.h
--- lynx-2.8.2-dev.17+kd.orig/src/GridText.h    Wed Feb 24 07:00:04 1999
+++ lynx-2.8.2-dev.17+kd/src/GridText.h Mon Feb 22 20:10:40 1999
@@ -264,5 +264,10 @@
 
 extern int HText_ExtEditForm PARAMS((
        struct link *   form_link));
+extern void HText_ExpandTextarea PARAMS((
+       struct link *   form_link,
+       int             newlines));
+extern int HText_InsertFile PARAMS((
+       struct link *   form_link));
 
 #endif /* LYGRIDTEXT_H */
diff -uNr lynx-2.8.2-dev.17+kd.orig/src/LYKeymap.c 
lynx-2.8.2-dev.17+kd/src/LYKeymap.c
--- lynx-2.8.2-dev.17+kd.orig/src/LYKeymap.c    Wed Feb 24 07:00:04 1999
+++ lynx-2.8.2-dev.17+kd/src/LYKeymap.c Fri Feb 19 14:23:48 1999
@@ -711,6 +711,8 @@
 { "ELGOTO",            "edit the current link's URL or ACTION and go to it" },
 { "CHANGE_LINK",       "force reset of the current link on the page" },
 { "EDITTEXTAREA",      "use an external editor to edit a form's textarea" },
+{ "GROWTEXTAREA",      "add 5 new blank lines to the bottom of a textarea" },
+{ "INSERTFILE",                "insert file into a textarea (just above 
cursorline)" },
 #ifdef USE_EXTERNALS
 { "EXTERN",            "run external program with url" },
 #endif
diff -uNr lynx-2.8.2-dev.17+kd.orig/src/LYKeymap.h 
lynx-2.8.2-dev.17+kd/src/LYKeymap.h
--- lynx-2.8.2-dev.17+kd.orig/src/LYKeymap.h    Wed Feb 24 07:00:05 1999
+++ lynx-2.8.2-dev.17+kd/src/LYKeymap.h Fri Feb 19 14:26:26 1999
@@ -108,15 +108,17 @@
 #define       LYK_ELGOTO        75
 #define       LYK_CHANGE_LINK   76
 #define       LYK_EDIT_TEXTAREA 77
+#define       LYK_GROW_TEXTAREA 78
+#define       LYK_INSERT_FILE   79
 
 #ifdef USE_EXTERNALS
-#define       LYK_EXTERN        78
+#define       LYK_EXTERN        80
 #if defined(VMS) || defined(DIRED_SUPPORT)
-#define       LYK_DIRED_MENU    79
+#define       LYK_DIRED_MENU    81
 #endif /* VMS || DIRED_SUPPORT */
 #else  /* USE_EXTERNALS */
 #if defined(VMS) || defined(DIRED_SUPPORT)
-#define       LYK_DIRED_MENU    78
+#define       LYK_DIRED_MENU    80
 #endif /* VMS || DIRED_SUPPORT */
 #endif /* !defined(USE_EXTERNALS) */
 
diff -uNr lynx-2.8.2-dev.17+kd.orig/src/LYMainLoop.c 
lynx-2.8.2-dev.17+kd/src/LYMainLoop.c
--- lynx-2.8.2-dev.17+kd.orig/src/LYMainLoop.c  Wed Feb 24 07:00:05 1999
+++ lynx-2.8.2-dev.17+kd/src/LYMainLoop.c       Wed Feb 24 06:29:10 1999
@@ -1586,17 +1586,72 @@
                                     links[curdoc.link].form->name,
                                     links[curdoc.link].form->value);
 
-               if (c == '\n' || c == '\r')
+               if (c == '\n' || c == '\r') {
+#ifdef AUTOGROW
+                 /*
+                  *  If on the bottom line of a TEXTAREA, and the user hit
+                  *  the ENTER key, we add a new line/anchor automatically,
+                  *  positioning the cursor on it.
+                  *
+                  *  If at the bottom of the screen, we effectively perform
+                  *  an LYK_DOWN_HALF-like operation, then move down to the
+                  *  new line we just added.  --KED  02/14/99
+                  *
+                  *  [There is some redundancy and non-standard indentation
+                  *   in the monster-if() below.  This is intentional ... to
+                  *   try and improve the "readability" (such as it is).
+                  *   Caveat emptor to anyone trying to change it.]
+                  */
+                  if ((links[curdoc.link].type       == WWW_FORM_LINK_TYPE &&
+                       links[curdoc.link].form->type == F_TEXTAREA_TYPE)
+                       &&
+                       ((curdoc.link == nlinks-1)
+                        ||
+                        ((curdoc.link <  nlinks-1) &&
+                        !(links[curdoc.link+1].type == WWW_FORM_LINK_TYPE  &&
+                          links[curdoc.link+1].form->type == F_TEXTAREA_TYPE))
+                        ||
+                        ((curdoc.link <  nlinks-1) &&
+                        ((links[curdoc.link+1].type == WWW_FORM_LINK_TYPE  &&
+                          links[curdoc.link+1].form->type == F_TEXTAREA_TYPE)
+                           &&
+                           ((links[curdoc.link].form->number          !=
+                                     links[curdoc.link+1].form->number)     ||
+                            (strcmp (links[curdoc.link].form->name,
+                                     links[curdoc.link+1].form->name) != 
0)))))) {
+
+                     HText_ExpandTextarea (&links[curdoc.link], 1);
+                     lines_in_file = HText_getNumOfLines();
+
+                     if (links[curdoc.link].ly < display_lines) {
+                        refresh_screen = TRUE;
+
+                     } else {
+
+                        Newline += (display_lines/2);
+                        if (nlinks > 0 && curdoc.link > -1 &&
+                            links[curdoc.link].ly > display_lines/2) {
+                           newdoc.link = curdoc.link;
+                           for (i = 0; links[i].ly <= (display_lines/2); i++)
+                               --newdoc.link;
+                           newdoc.link++;
+                        }
+                     }
+                  }
+#endif /* AUTOGROW */
+
 #ifdef FASTTAB
-                   /*
-                    *  Make return act like down-arrow.
-                    */
-                   c = DNARROW;
+                  /*
+                   *   Make return act like down-arrow.
+                   */
+                  c = DNARROW;
+               }
 #else
-                   /*
-                    *  Make return act like tab.
-                    */
-                   c = '\t';
+                  /*
+                   *   Make return act like tab.
+                   */
+                  c = '\t';
+               }
 #endif /* FASTTAB */
            } else {
                /*
@@ -4330,9 +4385,11 @@
                }
                break;
            }
-
+#ifdef AUTOEXTEDIT
            /*
-            *  If we're in a forms TEXTAREA, invoke the editor on it.
+            *  If we're in a forms TEXTAREA, invoke the editor on *its*
+            *  contents, rather than attempting to edit the html source
+            *  document.  KED
             */
            if (links[curdoc.link].type       == WWW_FORM_LINK_TYPE &&
                links[curdoc.link].form->type == F_TEXTAREA_TYPE)   {
@@ -4346,12 +4403,17 @@
             *  document with the TEXT field is local, the editor *would*
             *  be invoked on the source .html file; eg, the o(ptions)
             *  form tempfile).
+            *
+            *  [This is done to avoid possible user confusion, due to
+            *   auto invocation of the editor on the TEXTAREA's contents
+            *   via the above if() statement.]
             */
            if (links[curdoc.link].type       == WWW_FORM_LINK_TYPE &&
                links[curdoc.link].form->type == F_TEXT_TYPE)       {
-              HTUserMsg(CANNOT_EDIT_FIELD);
+              HTUserMsg (CANNOT_EDIT_FIELD);
               break;
            }
+#endif /* AUTOEXTEDIT */
 
 #ifdef DIRED_SUPPORT
            /*
@@ -4521,32 +4583,82 @@
            if (links[curdoc.link].type       == WWW_FORM_LINK_TYPE &&
                links[curdoc.link].form->type == F_TEXTAREA_TYPE)   {
 
-               /* stop screen */
-               stop_curses();
+              /* stop screen */
+              stop_curses();
 
-               n = HText_ExtEditForm (&links[curdoc.link]);
+              n = HText_ExtEditForm (&links[curdoc.link]);
 
-               lines_in_file = HText_getNumOfLines();
+              lines_in_file = HText_getNumOfLines();
 
-               /*
-                *  TODO: Move cursor "n" lines from the current line to
-                *        position it on the 1st trailing blank line in
-                *        the now edited TEXTAREA.  If the target line/
-                *        anchor requires us to scroll up/down, position
-                *        the target in the approximate center of the
-                *        screen.
-                */
+              /*
+               *  TODO: Move cursor "n" lines from the current line to
+               *        position it on the 1st trailing blank line in
+               *        the now edited TEXTAREA.  If the target line/
+               *        anchor requires us to scroll up/down, position
+               *        the target in the approximate center of the
+               *        screen.
+               */
 
-               /* curdoc.link += n;*/  /* works, except for page crossing, */
+              /* curdoc.link += n;*/   /* works, except for page crossing, */
                                        /* damnit; why is nothing ever easy */
 
-               /* start screen */
-               start_curses();
-               refresh_screen = TRUE;
+              /* start screen */
+              start_curses();
+              refresh_screen = TRUE;
+
+           } else {
+
+              HTInfoMsg (NOT_IN_TEXTAREA);
+           }
+           break;
+
+       case LYK_GROW_TEXTAREA: /* add new lines to bottom of TEXTAREA - KED */
+           /*
+            *  See if the current link is in a form TEXTAREA.
+            */
+           if (links[curdoc.link].type       == WWW_FORM_LINK_TYPE &&
+               links[curdoc.link].form->type == F_TEXTAREA_TYPE)   {
+
+             HText_ExpandTextarea (&links[curdoc.link], TEXTAREA_EXPAND_SIZE);
+
+             lines_in_file  = HText_getNumOfLines();
+             refresh_screen = TRUE;
+
+           } else {
+
+             HTInfoMsg (NOT_IN_TEXTAREA);
+           }
+           break;
+
+       case LYK_INSERT_FILE: /* insert file in TEXTAREA, above cursor - KED */
+           /*
+            *  See if the current link is in a form TEXTAREA.
+            */
+           if (links[curdoc.link].type       == WWW_FORM_LINK_TYPE &&
+               links[curdoc.link].form->type == F_TEXTAREA_TYPE)   {
+
+              n = HText_InsertFile (&links[curdoc.link]);
+
+              lines_in_file = HText_getNumOfLines();
+
+              /*
+               *  TODO: Move cursor "n" lines from the current line to
+               *        position it on the 1st line following the text
+               *        that was inserted.  If the target line/anchor
+               *        requires us to scroll up/down, position the
+               *        target in the approximate center of the screen.
+               *
+               *  [Current behavior leaves cursor on the same line relative
+               *   to the start of the TEXTAREA that it was on before the
+               *   insertion.  This is the same behavior that occurs with
+               *   (my) editor, so this TODO will stay unimplemented.]
+               */
+
+              refresh_screen = TRUE;
 
            } else {
 
-               HTInfoMsg (NOT_IN_TEXTAREA);
+              HTInfoMsg (NOT_IN_TEXTAREA);
            }
            break;
 
diff -uNr lynx-2.8.2-dev.17+kd.orig/src/LYMainLoop.h 
lynx-2.8.2-dev.17+kd/src/LYMainLoop.h
--- lynx-2.8.2-dev.17+kd.orig/src/LYMainLoop.h  Wed Feb 24 07:00:05 1999
+++ lynx-2.8.2-dev.17+kd/src/LYMainLoop.h       Wed Feb 24 06:15:45 1999
@@ -3,6 +3,10 @@
 
 #include <HTUtils.h>
 
+#define TEXTAREA_EXPAND_SIZE  5
+#define AUTOGROW
+#define AUTOEXTEDIT
+
 extern BOOLEAN LYOpenTraceLog NOPARAMS;
 extern void LYCloseTracelog NOPARAMS;
 extern int mainloop NOPARAMS;
diff -uNr lynx-2.8.2-dev.17+kd.orig/src/LYPrint.c 
lynx-2.8.2-dev.17+kd/src/LYPrint.c
--- lynx-2.8.2-dev.17+kd.orig/src/LYPrint.c     Wed Feb 17 06:29:33 1999
+++ lynx-2.8.2-dev.17+kd/src/LYPrint.c  Sun Feb 21 02:23:21 1999
@@ -166,11 +166,15 @@
 #define FN_DONE 2
 #define FN_QUIT 3
 
-PRIVATE int RecallFilename ARGS4(
+#define PRINT_FLAG   0
+#define GENERIC_FLAG 1
+
+PRIVATE int RecallFilename ARGS5(
        char *,         filename,
        BOOLEAN *,      first,
        int *,          now,
-       int *,          total)
+       int *,          total,
+       int,            flag)
 {
     int ch;
     char *cp;
@@ -255,9 +259,13 @@
        }
 
        /*
-        * Save cancelled.
+        * Operation cancelled.
         */
-       HTInfoMsg(SAVE_REQUEST_CANCELLED);
+       if (flag == PRINT_FLAG)
+          HTInfoMsg(SAVE_REQUEST_CANCELLED);
+       else if (flag == GENERIC_FLAG)
+          return FN_QUIT;
+
        return FN_QUIT;
     }
     return FN_DONE;
@@ -280,7 +288,7 @@
 
        HTSprintf0(&msg, prompt, pages);
        _statusline(msg);
-       FREE(msg); 
+       FREE(msg);
 
        c = LYgetch();
 #ifdef VMS
@@ -325,7 +333,8 @@
        strcpy(filename, buffer);
     }
 check_recall:
-    switch (RecallFilename(filename, &FirstRecall, &FnameNum, &FnameTotal)) {
+    switch (RecallFilename(filename,    &FirstRecall, &FnameNum,
+                          &FnameTotal, PRINT_FLAG))  {
        case FN_INIT:
            goto retry;
        case FN_READ:
@@ -857,7 +866,8 @@
 again:
        SetupFilename(my_file, sug_filename);
 check_again:
-       switch (RecallFilename(my_file, &FirstRecall, &FnameNum, &FnameTotal)) {
+       switch (RecallFilename(my_file,     &FirstRecall, &FnameNum,
+                              &FnameTotal, PRINT_FLAG))  {
            case FN_INIT:
                goto again;
            case FN_READ:
@@ -1322,4 +1332,127 @@
 
     LYforce_no_cache = TRUE;
     return(0);
+}
+
+
+/*
+ *  General purpose filename getter.
+ *
+ *  Returns a pointer to an absolute filename string, if the input
+ *  filename exists, and is readable.  Returns NULL if the input
+ *  was cancelled (^G, or CR on empty input).
+ *
+ *  The pointer to the filename string needs to be free()'d by the
+ *  caller (when non-NULL).
+ *
+ *  --KED  02/21/99
+ */
+PUBLIC char * GetFileName NOARGS
+{
+    struct stat stat_info;
+
+    FILE *fp;
+
+    char  fbuf[LY_MAXPATH];
+    char  tbuf[LY_MAXPATH];
+    char *fn;
+
+    BOOLEAN FirstRecall = TRUE;
+    int     FnameNum    = -1;
+    int     FnameTotal;
+
+
+    _statusline(FILENAME_PROMPT);
+
+retry:
+    /*
+     *  No initial filename.
+     */
+    SetupFilename (fbuf, "");
+
+check_recall:
+    /*
+     *  Go get a filename (it would be nice to do TAB == filename-completion
+     *  as the name is entered, but we'll save doing that for another time.
+     */
+    switch (RecallFilename (fbuf,        &FirstRecall,  &FnameNum,
+                           &FnameTotal, GENERIC_FLAG)) {
+       case FN_INIT:
+           goto retry;
+       case FN_READ:
+           goto check_recall;
+       case FN_QUIT:
+           goto quit;
+       default:
+           break;
+    }
+
+    /*
+     *  Add raw input form to list ... we may want to reuse/edit it on a
+     *  subsequent call, etc.
+     */
+#ifdef VMS
+    if (0 == strncasecomp (fbuf, "sys$disk:", 9)) {
+       if (0 == strncmp ((fbuf+9), "[]", 2)) {
+         HTAddSugFilename (fbuf+11);
+       } else {
+         HTAddSugFilename (fbuf+9);
+       }
+    } else {
+       HTAddSugFilename (fbuf);
+    }
+#else
+    HTAddSugFilename (fbuf);
+#endif /* VMS */
+
+    /*
+     *  Expand tilde's, make filename absolute, etc.
+     */
+    if (!LYValidateFilename (tbuf, fbuf))
+       goto quit;
+
+    /*
+     *  Check for file existence; readability.
+     */
+    if ((stat (tbuf, &stat_info) < 0) ||
+       (!(S_ISREG(stat_info.st_mode) || S_ISLNK(stat_info.st_mode)))) {
+       HTInfoMsg (FILE_DOES_NOT_EXIST);
+       _statusline(FILE_DOES_NOT_EXIST_RE);
+       FirstRecall = TRUE;
+       FnameNum    = FnameTotal;
+       goto retry;
+       goto check_recall;
+    }
+
+    if ((fp = fopen (tbuf, "r")) == NULL) {
+       HTInfoMsg (FILE_NOT_READABLE);
+       _statusline(FILE_NOT_READABLE_RE);
+       FirstRecall = TRUE;
+       FnameNum    = FnameTotal;
+       goto retry;
+       goto check_recall;
+    } else {
+       fclose (fp);
+    }
+
+    /*
+     *  We have a valid filename, and readable file.  Return it to the
+     *  caller.
+     *
+     *  The returned pointer should be free()'d by the caller.
+     *
+     *  [For some silly reason, if we use StrAllocCopy() here, we get an
+     *   "invalid pointer" reported in the Lynx.leaks file (if compiled
+     *   with  --enable-find-leaks  turned on.  Dumb.]
+     */
+    if ((fn = (char *) calloc (1, (strlen (tbuf) + 1))) == NULL)
+       outofmem(__FILE__, "GetFileName");
+    return (strcpy (fn, tbuf));
+
+
+quit:
+    /*
+     *  The user cancelled the input (^G, or CR on empty input field).
+     */
+    return (NULL);
 }
diff -uNr lynx-2.8.2-dev.17+kd.orig/src/LYPrint.h 
lynx-2.8.2-dev.17+kd/src/LYPrint.h
--- lynx-2.8.2-dev.17+kd.orig/src/LYPrint.h     Sun Sep 13 07:35:55 1998
+++ lynx-2.8.2-dev.17+kd/src/LYPrint.h  Fri Feb 19 14:17:25 1999
@@ -8,6 +8,7 @@
 extern int printfile PARAMS((document *newdoc));
 extern int print_options PARAMS((char **newfile,
                                 char **printed_url, int lines_in_file));
+extern char * GetFileName NOPARAMS;
 
 #endif /* LYPRINT_H */
 
diff -uNr lynx-2.8.2-dev.17+kd.orig/src/structdump.h 
lynx-2.8.2-dev.17+kd/src/structdump.h
--- lynx-2.8.2-dev.17+kd.orig/src/structdump.h  Wed Feb 17 06:29:33 1999
+++ lynx-2.8.2-dev.17+kd/src/structdump.h       Tue Feb 23 02:51:03 1999
@@ -8,6 +8,7 @@
 
 /* usage: DUMPSTRUCT_LINK(link_ptr, "message"); */
 #define   DUMPSTRUCT_LINK(L,X) \
+if ((L)) { \
 CTRACE(tfp, "\n" \
             "KED:     link_ptr=0x%08x  sizeof=%d  ["X"]\n" \
             "link       struct {\n"      \
@@ -33,11 +34,16 @@
             (L)->hightext, (L)->hightext, (L)->hightext2, (L)->hightext2, \
             (L)->hightext2_offset, (L)->inUnderline, (L)->lx, (L)->ly, \
             (L)->type, (L)->anchor_number, (L)->anchor_line_num, (L)->form); \
+}else{ \
+CTRACE(tfp, "\n" \
+            "KED:     link_ptr=0x00000000  (NULL)     ["X"]\n"); \
+} \
 CTRACE_FLUSH(tfp);
 
 
 /* usage: DUMPSTRUCT_ANCHOR(anchor_ptr, "message"); */
 #define   DUMPSTRUCT_ANCHOR(A,X) \
+if ((A)) { \
 CTRACE(tfp, "\n" \
             "KED:   anchor_ptr=0x%08x  sizeof=%d  ["X"]\n" \
             "TextAnchor struct {\n"      \
@@ -67,11 +73,16 @@
             (A)->hightext2offset, (A)->link_type, \
             (A)->input_field, (A)->input_field, (A)->show_anchor, \
             (A)->inUnderline, (A)->expansion_anch, (A)->anchor); \
+}else{ \
+CTRACE(tfp, "\n" \
+            "KED:   anchor_ptr=0x00000000  (NULL)     ["X"]\n"); \
+} \
 CTRACE_FLUSH(tfp);
 
 
 /* usage: DUMPSTRUCT_FORM(forminfo_ptr, "message"); */
 #define   DUMPSTRUCT_FORMINFO(F,X) \
+if ((F)) { \
 CTRACE(tfp, "\n" \
             "KED: forminfo_ptr=0x%08x  sizeof=%d  ["X"]\n" \
             "FormInfo   struct {\n"      \
@@ -112,11 +123,16 @@
             (F)->no_cache, (F)->cp_submit_value, (F)->orig_submit_value, \
             (F)->size_l, (F)->disabled, (F)->name_cs, (F)->value_cs, \
             (F)->accept_cs); \
+}else{ \
+CTRACE(tfp, "\n" \
+            "KED: forminfo_ptr=0x00000000  (NULL)     ["X"]\n"); \
+} \
 CTRACE_FLUSH(tfp);
 
 
 /* usage: DUMPSTRUCT_LINE(htline_ptr, "message"); */
 #define   DUMPSTRUCT_LINE(L,X) \
+if ((L)) { \
 CTRACE(tfp, "\n" \
             "KED: htline_ptr=0x%08x  sizeof=%d  ["X"]\n" \
             "HTLine  struct {\n"      \
@@ -134,6 +150,10 @@
             (L), sizeof(*((L))), \
             (L)->next, (L)->prev, (L)->offset, (L)->size, (L)->split_after, \
             (L)->bullet, (L)->expansion_line, (L)->data, (L)->data); \
+}else{ \
+CTRACE(tfp, "\n" \
+            "KED: htline_ptr=0x00000000  (NULL)     ["X"]\n"); \
+} \
 CTRACE_FLUSH(tfp);
 
 #endif /* STRUCTDUMP_H */
##--eof--##

reply via email to

[Prev in Thread] Current Thread [Next in Thread]