Begin destabilisation in the wake of 0.53! This checkin contains the
[u/mdw/putty] / terminal.c
index 7f6f8bb..b286efe 100644 (file)
@@ -1,5 +1,3 @@
-#include <windows.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
@@ -59,7 +57,7 @@ static unsigned long *disptext;              /* buffer of text on real screen */
 static unsigned long *dispcurs;               /* location of cursor on real screen */
 static unsigned long curstype;        /* type of cursor on real screen */
 
-#define VBELL_TIMEOUT 100             /* millisecond len of visual bell */
+#define VBELL_TIMEOUT (TICKSPERSEC/10) /* visual bell lasts 1/10 sec */
 
 struct beeptime {
     struct beeptime *next;
@@ -99,7 +97,7 @@ static int wrap, wrapnext;           /* wrap flags */
 static int insert;                    /* insert-mode flag */
 static int cset;                      /* 0 or 1: which char set */
 static int save_cset, save_csattr;     /* saved with cursor position */
-static int save_utf;                  /* saved with cursor position */
+static int save_utf, save_wnext;       /* saved with cursor position */
 static int rvideo;                    /* global reverse video flag */
 static unsigned long rvbell_startpoint;/* for ESC[?5hESC[?5l vbell */
 static int cursor_on;                 /* cursor enabled flag */
@@ -115,6 +113,10 @@ static int vt52_bold;                     /* Force bold on non-bold colours */
 static int utf_state;                 /* Is there a pending UTF-8 character */
 static int utf_char;                  /* and what is it so far. */
 static int utf_size;                  /* The size of the UTF character. */
+static int printing, only_printing;    /* Are we doing ANSI printing? */
+static int print_state;                       /* state of print-end-sequence scan */
+static bufchain printer_buf;          /* buffered data for printer */
+static printer_job *print_job;
 
 static int xterm_mouse;                       /* send mouse messages to app */
 
@@ -206,10 +208,7 @@ static void erase_lots(int, int, int);
 static void swap_screen(int);
 static void update_sbar(void);
 static void deselect(void);
-/* log session to file stuff ... */
-static FILE *lgfp = NULL;
-static void logtraffic(unsigned char c, int logmode);
-static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm);
+static void term_print_finish(void);
 
 /*
  * Resize a line to make it `cols' columns wide.
@@ -306,6 +305,7 @@ static void power_on(void)
     blink_is_real = cfg.blinktext;
     erase_char = ERASE_CHAR;
     alt_which = 0;
+    term_print_finish();
     {
        int i;
        for (i = 0; i < 256; i++)
@@ -355,6 +355,30 @@ void term_pwron(void)
 }
 
 /*
+ * When the user reconfigures us, we need to check the forbidden-
+ * alternate-screen config option, disable raw mouse mode if the
+ * user has disabled mouse reporting, and abandon a print job if
+ * the user has disabled printing.
+ */
+void term_reconfig(void)
+{
+    if (cfg.no_alt_screen)
+       swap_screen(0);
+    if (cfg.no_mouse_rep) {
+       xterm_mouse = 0;
+       set_raw_mouse_mode(0);
+    }
+    if (cfg.no_remote_charset) {
+       cset_attr[0] = cset_attr[1] = ATTR_ASCII;
+       sco_acs = alt_sco_acs = 0;
+       utf = 0;
+    }
+    if (!*cfg.printer) {
+       term_print_finish();
+    }
+}
+
+/*
  * Clear the scrollback.
  */
 void term_clrsb(void)
@@ -750,6 +774,7 @@ static void save_cursor(int save)
        save_attr = curr_attr;
        save_cset = cset;
        save_utf = utf;
+       save_wnext = wrapnext;
        save_csattr = cset_attr[cset];
        save_sco_acs = sco_acs;
     } else {
@@ -763,6 +788,13 @@ static void save_cursor(int save)
        curr_attr = save_attr;
        cset = save_cset;
        utf = save_utf;
+       wrapnext = save_wnext;
+       /*
+        * wrapnext might reset to False if the x position is no
+        * longer at the rightmost edge.
+        */
+       if (wrapnext && curs.x < cols-1)
+           wrapnext = FALSE;
        cset_attr[cset] = save_csattr;
        sco_acs = save_sco_acs;
        fix_cpos;
@@ -873,7 +905,8 @@ static void toggle_mode(int mode, int query, int state)
            break;
          case 3:                      /* 80/132 columns */
            deselect();
-           request_resize(state ? 132 : 80, rows);
+           if (!cfg.no_remote_resize)
+               request_resize(state ? 132 : 80, rows);
            reset_132 = state;
            break;
          case 5:                      /* reverse video */
@@ -883,7 +916,7 @@ static void toggle_mode(int mode, int query, int state)
             * effective visual bell, so that ESC[?5hESC[?5l will
             * always be an actually _visible_ visual bell.
             */
-           ticks = GetTickCount();
+           ticks = GETTICKCOUNT();
            /* turn off a previous vbell to avoid inconsistencies */
            if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
                in_vbell = FALSE;
@@ -925,7 +958,7 @@ static void toggle_mode(int mode, int query, int state)
          case 47:                     /* alternate screen */
            compatibility(OTHER);
            deselect();
-           swap_screen(state);
+           swap_screen(cfg.no_alt_screen ? 0 : state);
            disptop = 0;
            break;
          case 1000:                   /* xterm mouse 1 */
@@ -968,19 +1001,65 @@ static void do_osc(void)
        switch (esc_args[0]) {
          case 0:
          case 1:
-           set_icon(osc_string);
+           if (!cfg.no_remote_wintitle)
+               set_icon(osc_string);
            if (esc_args[0] == 1)
                break;
            /* fall through: parameter 0 means set both */
          case 2:
          case 21:
-           set_title(osc_string);
+           if (!cfg.no_remote_wintitle)
+               set_title(osc_string);
            break;
        }
     }
 }
 
 /*
+ * ANSI printing routines.
+ */
+static void term_print_setup(void)
+{
+    bufchain_clear(&printer_buf);
+    print_job = printer_start_job(cfg.printer);
+}
+static void term_print_flush(void)
+{
+    void *data;
+    int len;
+    int size;
+    while ((size = bufchain_size(&printer_buf)) > 5) {
+       bufchain_prefix(&printer_buf, &data, &len);
+       if (len > size-5)
+           len = size-5;
+       printer_job_data(print_job, data, len);
+       bufchain_consume(&printer_buf, len);
+    }
+}
+static void term_print_finish(void)
+{
+    void *data;
+    int len, size;
+    char c;
+
+    term_print_flush();
+    while ((size = bufchain_size(&printer_buf)) > 0) {
+       bufchain_prefix(&printer_buf, &data, &len);
+       c = *(char *)data;
+       if (c == '\033' || c == '\233') {
+           bufchain_consume(&printer_buf, size);
+           break;
+       } else {
+           printer_job_data(print_job, &c, 1);
+           bufchain_consume(&printer_buf, 1);
+       }
+    }
+    printer_finish_job(print_job);
+    print_job = NULL;
+    printing = only_printing = FALSE;
+}
+
+/*
  * Remove everything currently in `inbuf' and stick it up on the
  * in-memory display. There's a big state machine in here to
  * process escape sequences...
@@ -1024,6 +1103,39 @@ void term_out(void)
         * of i18n.
         */
 
+       /*
+        * If we're printing, add the character to the printer
+        * buffer.
+        */
+       if (printing) {
+           bufchain_add(&printer_buf, &c, 1);
+
+           /*
+            * If we're in print-only mode, we use a much simpler
+            * state machine designed only to recognise the ESC[4i
+            * termination sequence.
+            */
+           if (only_printing) {
+               if (c == '\033')
+                   print_state = 1;
+               else if (c == (unsigned char)'\233')
+                   print_state = 2;
+               else if (c == '[' && print_state == 1)
+                   print_state = 2;
+               else if (c == '4' && print_state == 2)
+                   print_state = 3;
+               else if (c == 'i' && print_state == 3)
+                   print_state = 4;
+               else
+                   print_state = 0;
+               if (print_state == 4) {
+                   printing = only_printing = FALSE;
+                   term_print_finish();
+               }
+               continue;
+           }
+       }
+
        /* First see about all those translations. */
        if (termstate == TOPLEVEL) {
            if (in_utf)
@@ -1168,7 +1280,8 @@ void term_out(void)
                curs.x--;
            wrapnext = FALSE;
            fix_cpos;
-           *cpos = (' ' | curr_attr | ATTR_ASCII);
+           if (!cfg.no_dbackspace)    /* destructive bksp might be disabled */
+               *cpos = (' ' | curr_attr | ATTR_ASCII);
        } else
            /* Or normal C0 controls. */
        if ((c & -32) == 0 && termstate < DO_CTRLS) {
@@ -1201,7 +1314,7 @@ void term_out(void)
                        } else
                            *d++ = *s;
                    }
-                   lpage_send(CP_ACP, abuf, d - abuf, 0);
+                   lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
                }
                break;
              case '\007':
@@ -1209,7 +1322,7 @@ void term_out(void)
                    struct beeptime *newbeep;
                    unsigned long ticks;
 
-                   ticks = GetTickCount();
+                   ticks = GETTICKCOUNT();
 
                    if (!beep_overloaded) {
                        newbeep = smalloc(sizeof(struct beeptime));
@@ -1513,7 +1626,8 @@ void term_out(void)
                    compatibility(VT100);
                    power_on();
                    if (reset_132) {
-                       request_resize(80, rows);
+                       if (!cfg.no_remote_resize)
+                           request_resize(80, rows);
                        reset_132 = 0;
                    }
                    fix_cpos;
@@ -1577,46 +1691,56 @@ void term_out(void)
 
                  case ANSI('A', '('):
                    compatibility(VT100);
-                   cset_attr[0] = ATTR_GBCHR;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_GBCHR;
                    break;
                  case ANSI('B', '('):
                    compatibility(VT100);
-                   cset_attr[0] = ATTR_ASCII;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_ASCII;
                    break;
                  case ANSI('0', '('):
                    compatibility(VT100);
-                   cset_attr[0] = ATTR_LINEDRW;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_LINEDRW;
                    break;
                  case ANSI('U', '('): 
                    compatibility(OTHER);
-                   cset_attr[0] = ATTR_SCOACS; 
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_SCOACS; 
                    break;
 
                  case ANSI('A', ')'):
                    compatibility(VT100);
-                   cset_attr[1] = ATTR_GBCHR;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_GBCHR;
                    break;
                  case ANSI('B', ')'):
                    compatibility(VT100);
-                   cset_attr[1] = ATTR_ASCII;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_ASCII;
                    break;
                  case ANSI('0', ')'):
                    compatibility(VT100);
-                   cset_attr[1] = ATTR_LINEDRW;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_LINEDRW;
                    break;
                  case ANSI('U', ')'): 
                    compatibility(OTHER);
-                   cset_attr[1] = ATTR_SCOACS; 
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_SCOACS; 
                    break;
 
                  case ANSI('8', '%'):  /* Old Linux code */
                  case ANSI('G', '%'):
                    compatibility(OTHER);
-                   utf = 1;
+                   if (!cfg.no_remote_charset)
+                       utf = 1;
                    break;
                  case ANSI('@', '%'):
                    compatibility(OTHER);
-                   utf = 0;
+                   if (!cfg.no_remote_charset)
+                       utf = 0;
                    break;
                }
                break;
@@ -1776,6 +1900,23 @@ void term_out(void)
                                toggle_mode(esc_args[i], esc_query, TRUE);
                        }
                        break;
+                     case 'i':
+                     case ANSI_QUE('i'):
+                       compatibility(VT100);
+                       {
+                           if (esc_nargs != 1) break;
+                           if (esc_args[0] == 5 && *cfg.printer) {
+                               printing = TRUE;
+                               only_printing = !esc_query;
+                               print_state = 0;
+                               term_print_setup();
+                           } else if (esc_args[0] == 4 && printing) {
+                               printing = FALSE;
+                               only_printing = FALSE;
+                               term_print_finish();
+                           }
+                       }
+                       break;                  
                      case 'l':       /* toggle modes to low */
                      case ANSI_QUE('l'):
                        compatibility(VT100);
@@ -1876,12 +2017,15 @@ void term_out(void)
                                    break;
                                  case 10:      /* SCO acs off */
                                    compatibility(SCOANSI);
+                                   if (cfg.no_remote_charset) break;
                                    sco_acs = 0; break;
                                  case 11:      /* SCO acs on */
                                    compatibility(SCOANSI);
+                                   if (cfg.no_remote_charset) break;
                                    sco_acs = 1; break;
                                  case 12:      /* SCO acs on flipped */
                                    compatibility(SCOANSI);
+                                   if (cfg.no_remote_charset) break;
                                    sco_acs = 2; break;
                                  case 22:      /* disable bold */
                                    compatibility2(OTHER, VT220);
@@ -1955,11 +2099,110 @@ void term_out(void)
                         * illegal values (eg first arg 1..9) for window changing 
                         * and reports.
                         */
-                       compatibility(VT340TEXT);
                        if (esc_nargs <= 1
                            && (esc_args[0] < 1 || esc_args[0] >= 24)) {
-                           request_resize(cols, def(esc_args[0], 24));
+                           compatibility(VT340TEXT);
+                           if (!cfg.no_remote_resize)
+                               request_resize(cols, def(esc_args[0], 24));
                            deselect();
+                       } else if (esc_nargs >= 1 &&
+                                  esc_args[0] >= 1 &&
+                                  esc_args[0] < 24) {
+                           compatibility(OTHER);
+
+                           switch (esc_args[0]) {
+                               int x, y, len;
+                               char buf[80], *p;
+                             case 1:
+                               set_iconic(FALSE);
+                               break;
+                             case 2:
+                               set_iconic(TRUE);
+                               break;
+                             case 3:
+                               if (esc_nargs >= 3) {
+                                   if (!cfg.no_remote_resize)
+                                       move_window(def(esc_args[1], 0),
+                                                   def(esc_args[2], 0));
+                               }
+                               break;
+                             case 4:
+                               /* We should resize the window to a given
+                                * size in pixels here, but currently our
+                                * resizing code isn't healthy enough to
+                                * manage it. */
+                               break;
+                             case 5:
+                               set_zorder(TRUE);   /* move to top */
+                               break;
+                             case 6:
+                               set_zorder(FALSE);  /* move to bottom */
+                               break;
+                             case 7:
+                               refresh_window();
+                               break;
+                             case 8:
+                               if (esc_nargs >= 3) {
+                                   if (!cfg.no_remote_resize)
+                                       request_resize(def(esc_args[2], cfg.width),
+                                                      def(esc_args[1], cfg.height));
+                               }
+                               break;
+                             case 9:
+                               if (esc_nargs >= 2)
+                                   set_zoomed(esc_args[1] ? TRUE : FALSE);
+                               break;
+                             case 11:
+                               ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
+                                          4, 0);
+                               break;
+                             case 13:
+                               get_window_pos(&x, &y);
+                               len = sprintf(buf, "\033[3;%d;%dt", x, y);
+                               ldisc_send(buf, len, 0);
+                               break;
+                             case 14:
+                               get_window_pixels(&x, &y);
+                               len = sprintf(buf, "\033[4;%d;%dt", x, y);
+                               ldisc_send(buf, len, 0);
+                               break;
+                             case 18:
+                               len = sprintf(buf, "\033[8;%d;%dt",
+                                             rows, cols);
+                               ldisc_send(buf, len, 0);
+                               break;
+                             case 19:
+                               /*
+                                * Hmmm. Strictly speaking we
+                                * should return `the size of the
+                                * screen in characters', but
+                                * that's not easy: (a) window
+                                * furniture being what it is it's
+                                * hard to compute, and (b) in
+                                * resize-font mode maximising the
+                                * window wouldn't change the
+                                * number of characters. *shrug*. I
+                                * think we'll ignore it for the
+                                * moment and see if anyone
+                                * complains, and then ask them
+                                * what they would like it to do.
+                                */
+                               break;
+                             case 20:
+                               p = get_window_title(TRUE);
+                               len = strlen(p);
+                               ldisc_send("\033]L", 3, 0);
+                               ldisc_send(p, len, 0);
+                               ldisc_send("\033\\", 2, 0);
+                               break;
+                             case 21:
+                               p = get_window_title(FALSE);
+                               len = strlen(p);
+                               ldisc_send("\033]l", 3, 0);
+                               ldisc_send(p, len, 0);
+                               ldisc_send("\033\\", 2, 0);
+                               break;
+                           }
                        }
                        break;
                      case 'S':
@@ -1984,7 +2227,8 @@ void term_out(void)
                         */
                        compatibility(VT420);
                        if (esc_nargs == 1 && esc_args[0] > 0) {
-                           request_resize(cols, def(esc_args[0], cfg.height));
+                           if (!cfg.no_remote_resize)
+                               request_resize(cols, def(esc_args[0], cfg.height));
                            deselect();
                        }
                        break;
@@ -1995,7 +2239,8 @@ void term_out(void)
                         */
                        compatibility(VT340TEXT);
                        if (esc_nargs <= 1) {
-                           request_resize(def(esc_args[0], cfg.width), rows);
+                           if (!cfg.no_remote_resize)
+                               request_resize(def(esc_args[0], cfg.width), rows);
                            deselect();
                        }
                        break;
@@ -2122,10 +2367,12 @@ void term_out(void)
                         * Well we should do a soft reset at this point ...
                         */
                        if (!has_compat(VT420) && has_compat(VT100)) {
-                           if (reset_132)
-                               request_resize(132, 24);
-                           else
-                               request_resize(80, 24);
+                           if (!cfg.no_remote_resize) {
+                               if (reset_132)
+                                   request_resize(132, 24);
+                               else
+                                   request_resize(80, 24);
+                           }
                        }
 #endif
                        break;
@@ -2512,6 +2759,8 @@ void term_out(void)
            check_selection(curs, cursplus);
        }
     }
+
+    term_print_flush();
 }
 
 #if 0
@@ -2547,7 +2796,7 @@ static void do_paint(Context ctx, int may_optimise)
      * Check the visual bell state.
      */
     if (in_vbell) {
-       ticks = GetTickCount();
+       ticks = GETTICKCOUNT();
        if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
            in_vbell = FALSE; 
    }
@@ -2742,7 +2991,7 @@ void term_blink(int flg)
     static long last_tblink = 0;
     long now, blink_diff;
 
-    now = GetTickCount();
+    now = GETTICKCOUNT();
     blink_diff = now - last_tblink;
 
     /* Make sure the text blinks no more than 2Hz */
@@ -2759,8 +3008,8 @@ void term_blink(int flg)
 
     blink_diff = now - last_blink;
 
-    /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
-    if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
+    /* Make sure the cursor blinks no faster than system blink rate */
+    if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
        return;
 
     last_blink = now;
@@ -3247,7 +3496,9 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
 {
     pos selpoint;
     unsigned long *ldata;
-    int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
+    int raw_mouse = (xterm_mouse &&
+                    !cfg.no_mouse_rep &&
+                    !(cfg.mouse_override && shift));
     int default_seltype;
 
     if (y < 0) {
@@ -3459,7 +3710,7 @@ void term_paste()
 
     /* Don't wait forever to paste */
     if (paste_hold) {
-       now = GetTickCount();
+       now = GETTICKCOUNT();
        paste_diff = now - last_paste;
        if (paste_diff >= 0 && paste_diff < 450)
            return;
@@ -3511,6 +3762,8 @@ int term_ldisc(int option)
  */
 int from_backend(int is_stderr, char *data, int len)
 {
+    assert(len > 0);
+
     bufchain_add(&inbuf, data, len);
 
     /*
@@ -3534,138 +3787,3 @@ int from_backend(int is_stderr, char *data, int len)
      */
     return 0;
 }
-
-/*
- * Log session traffic.
- */
-void logtraffic(unsigned char c, int logmode)
-{
-    if (cfg.logtype > 0) {
-       if (cfg.logtype == logmode) {
-           /* deferred open file from pgm start? */
-           if (!lgfp)
-               logfopen();
-           if (lgfp)
-               fputc(c, lgfp);
-       }
-    }
-}
-
-void settimstr(char *ta, int no_sec);
-char *subslfcode(char *dest, char *src, char *dstrt);
-char *stpncpy(char *dst, const char *src, size_t maxlen);
-char timdatbuf[20];
-char currlogfilename[FILENAME_MAX];
-
-/* open log file append/overwrite mode */
-void logfopen(void)
-{
-    char buf[256];
-    time_t t;
-    struct tm tm;
-    char writemod[4];
-
-    if (!cfg.logtype)
-       return;
-    sprintf(writemod, "wb");          /* default to rewrite */
-
-    time(&t);
-    tm = *localtime(&t);
-
-    /* substitute special codes in file name */
-    xlatlognam(currlogfilename,cfg.logfilename,cfg.host, &tm);
-
-    lgfp = fopen(currlogfilename, "r");        /* file already present? */
-    if (lgfp) {
-       int i;
-       fclose(lgfp);
-       i = askappend(currlogfilename);
-       if (i == 1)
-           writemod[0] = 'a';         /* set append mode */
-       else if (i == 0) {             /* cancelled */
-           lgfp = NULL;
-           cfg.logtype = 0;           /* disable logging */
-           return;
-       }
-    }
-
-    lgfp = fopen(currlogfilename, writemod);
-    if (lgfp) {                               /* enter into event log */
-       sprintf(buf, "%s session log (%s mode) to file : ",
-               (writemod[0] == 'a') ? "Appending" : "Writing new",
-               (cfg.logtype == LGTYP_ASCII ? "ASCII" :
-                cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
-       /* Make sure we do not exceed the output buffer size */
-       strncat(buf, currlogfilename, 128);
-       buf[strlen(buf)] = '\0';
-       logevent(buf);
-
-       /* --- write header line into log file */
-       fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
-       strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
-       fputs(buf, lgfp);
-       fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
-    }
-}
-
-void logfclose(void)
-{
-    if (lgfp) {
-       fclose(lgfp);
-       lgfp = NULL;
-    }
-}
-
-/*
- * translate format codes into time/date strings
- * and insert them into log file name
- *
- * "&Y":YYYY   "&m":MM   "&d":DD   "&T":hhmm   "&h":<hostname>   "&&":&
- */
-static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm) {
-    char buf[10], *bufp;
-    int size;
-    char *ds = d; /* save start pos. */
-    int len = FILENAME_MAX-1;
-
-    while (*s) {
-       /* Let (bufp, len) be the string to append. */
-       bufp = buf;                    /* don't usually override this */
-       if (*s == '&') {
-           char c;
-           s++;
-           if (*s) switch (c = *s++, tolower(c)) {
-             case 'y':
-               size = strftime(buf, sizeof(buf), "%Y", tm);
-               break;
-             case 'm':
-               size = strftime(buf, sizeof(buf), "%m", tm);
-               break;
-             case 'd':
-               size = strftime(buf, sizeof(buf), "%d", tm);
-               break;
-             case 't':
-               size = strftime(buf, sizeof(buf), "%H%M%S", tm);
-               break;
-             case 'h':
-               bufp = hostname;
-               size = strlen(bufp);
-               break;
-             default:
-               buf[0] = '&';
-               size = 1;
-               if (c != '&')
-                   buf[size++] = c;
-           }
-       } else {
-           buf[0] = *s++;
-           size = 1;
-       }
-       if (size > len)
-           size = len;
-       memcpy(d, bufp, size);
-       d += size;
-       len -= size;
-    }
-    *d = '\0';
-}