"Fossies" - the Fresh Open Source Software Archive

Member "tnftp-20200705/libedit/keymacro.c" (4 Jul 2020, 16934 Bytes) of package /linux/privat/tnftp-20200705.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "keymacro.c" see the Fossies "Dox" file reference documentation.

    1 /*  $NetBSD: keymacro.c,v 1.3 2020/07/04 13:43:21 lukem Exp $   */
    2 /*  from    NetBSD: keymacro.c,v 1.24 2019/07/23 10:18:52 christos Exp  */
    3 
    4 /*-
    5  * Copyright (c) 1992, 1993
    6  *  The Regents of the University of California.  All rights reserved.
    7  *
    8  * This code is derived from software contributed to Berkeley by
    9  * Christos Zoulas of Cornell University.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. Neither the name of the University nor the names of its contributors
   20  *    may be used to endorse or promote products derived from this software
   21  *    without specific prior written permission.
   22  *
   23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   33  * SUCH DAMAGE.
   34  */
   35 
   36 #include "config.h"
   37 
   38 #if 0 /* tnftp */
   39 #if !defined(lint) && !defined(SCCSID)
   40 #if 0
   41 static char sccsid[] = "@(#)key.c   8.1 (Berkeley) 6/4/93";
   42 #else
   43 __RCSID(" NetBSD: keymacro.c,v 1.24 2019/07/23 10:18:52 christos Exp  ");
   44 #endif
   45 #endif /* not lint && not SCCSID */
   46 #endif /* tnftp */
   47 
   48 /*
   49  * keymacro.c: This module contains the procedures for maintaining
   50  *         the extended-key map.
   51  *
   52  *      An extended-key (key) is a sequence of keystrokes introduced
   53  *  with a sequence introducer and consisting of an arbitrary
   54  *  number of characters.  This module maintains a map (the
   55  *  el->el_keymacro.map)
   56  *  to convert these extended-key sequences into input strs
   57  *  (XK_STR) or editor functions (XK_CMD).
   58  *
   59  *      Warning:
   60  *    If key is a substr of some other keys, then the longer
   61  *    keys are lost!!  That is, if the keys "abcd" and "abcef"
   62  *    are in el->el_keymacro.map, adding the key "abc" will cause
   63  *    the first two definitions to be lost.
   64  *
   65  *      Restrictions:
   66  *      -------------
   67  *      1) It is not possible to have one key that is a
   68  *     substr of another.
   69  */
   70 #if 0 /* tnftp */
   71 #include <stdlib.h>
   72 #include <string.h>
   73 #endif /* tnftp */
   74 
   75 #include "el.h"
   76 #include "fcns.h"
   77 
   78 /*
   79  * The Nodes of the el->el_keymacro.map.  The el->el_keymacro.map is a
   80  * linked list of these node elements
   81  */
   82 struct keymacro_node_t {
   83     wchar_t      ch;        /* single character of key   */
   84     int      type;      /* node type             */
   85     keymacro_value_t val;       /* command code or pointer to str,  */
   86                     /* if this is a leaf         */
   87     struct keymacro_node_t *next;   /* ptr to next char of this key  */
   88     struct keymacro_node_t *sibling;/* ptr to another key with same prefix*/
   89 };
   90 
   91 static int       node_trav(EditLine *, keymacro_node_t *, wchar_t *,
   92     keymacro_value_t *);
   93 static int       node__try(EditLine *, keymacro_node_t *,
   94     const wchar_t *, keymacro_value_t *, int);
   95 static keymacro_node_t  *node__get(wint_t);
   96 static void      node__free(keymacro_node_t *);
   97 static void      node__put(EditLine *, keymacro_node_t *);
   98 static int       node__delete(EditLine *, keymacro_node_t **,
   99     const wchar_t *);
  100 static int       node_lookup(EditLine *, const wchar_t *,
  101     keymacro_node_t *, size_t);
  102 static int       node_enum(EditLine *, keymacro_node_t *, size_t);
  103 
  104 #define KEY_BUFSIZ  EL_BUFSIZ
  105 
  106 
  107 /* keymacro_init():
  108  *  Initialize the key maps
  109  */
  110 libedit_private int
  111 keymacro_init(EditLine *el)
  112 {
  113 
  114     el->el_keymacro.buf = el_calloc(KEY_BUFSIZ,
  115         sizeof(*el->el_keymacro.buf));
  116     if (el->el_keymacro.buf == NULL)
  117         return -1;
  118     el->el_keymacro.map = NULL;
  119     keymacro_reset(el);
  120     return 0;
  121 }
  122 
  123 /* keymacro_end():
  124  *  Free the key maps
  125  */
  126 libedit_private void
  127 keymacro_end(EditLine *el)
  128 {
  129 
  130     el_free(el->el_keymacro.buf);
  131     el->el_keymacro.buf = NULL;
  132     node__free(el->el_keymacro.map);
  133 }
  134 
  135 
  136 /* keymacro_map_cmd():
  137  *  Associate cmd with a key value
  138  */
  139 libedit_private keymacro_value_t *
  140 keymacro_map_cmd(EditLine *el, int cmd)
  141 {
  142 
  143     el->el_keymacro.val.cmd = (el_action_t) cmd;
  144     return &el->el_keymacro.val;
  145 }
  146 
  147 
  148 /* keymacro_map_str():
  149  *  Associate str with a key value
  150  */
  151 libedit_private keymacro_value_t *
  152 keymacro_map_str(EditLine *el, wchar_t *str)
  153 {
  154 
  155     el->el_keymacro.val.str = str;
  156     return &el->el_keymacro.val;
  157 }
  158 
  159 
  160 /* keymacro_reset():
  161  *  Takes all nodes on el->el_keymacro.map and puts them on free list.
  162  *  Then initializes el->el_keymacro.map with arrow keys
  163  *  [Always bind the ansi arrow keys?]
  164  */
  165 libedit_private void
  166 keymacro_reset(EditLine *el)
  167 {
  168 
  169     node__put(el, el->el_keymacro.map);
  170     el->el_keymacro.map = NULL;
  171     return;
  172 }
  173 
  174 
  175 /* keymacro_get():
  176  *  Calls the recursive function with entry point el->el_keymacro.map
  177  *      Looks up *ch in map and then reads characters until a
  178  *      complete match is found or a mismatch occurs. Returns the
  179  *      type of the match found (XK_STR or XK_CMD).
  180  *      Returns NULL in val.str and XK_STR for no match.
  181  *      Returns XK_NOD for end of file or read error.
  182  *      The last character read is returned in *ch.
  183  */
  184 libedit_private int
  185 keymacro_get(EditLine *el, wchar_t *ch, keymacro_value_t *val)
  186 {
  187 
  188     return node_trav(el, el->el_keymacro.map, ch, val);
  189 }
  190 
  191 
  192 /* keymacro_add():
  193  *      Adds key to the el->el_keymacro.map and associates the value in
  194  *  val with it. If key is already is in el->el_keymacro.map, the new
  195  *  code is applied to the existing key. Ntype specifies if code is a
  196  *  command, an out str or a unix command.
  197  */
  198 libedit_private void
  199 keymacro_add(EditLine *el, const wchar_t *key, keymacro_value_t *val,
  200     int ntype)
  201 {
  202 
  203     if (key[0] == '\0') {
  204         (void) fprintf(el->el_errfile,
  205             "keymacro_add: Null extended-key not allowed.\n");
  206         return;
  207     }
  208     if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
  209         (void) fprintf(el->el_errfile,
  210             "keymacro_add: sequence-lead-in command not allowed\n");
  211         return;
  212     }
  213     if (el->el_keymacro.map == NULL)
  214         /* tree is initially empty.  Set up new node to match key[0] */
  215         el->el_keymacro.map = node__get(key[0]);
  216             /* it is properly initialized */
  217 
  218     /* Now recurse through el->el_keymacro.map */
  219     (void) node__try(el, el->el_keymacro.map, key, val, ntype);
  220     return;
  221 }
  222 
  223 
  224 /* keymacro_clear():
  225  *
  226  */
  227 libedit_private void
  228 keymacro_clear(EditLine *el, el_action_t *map, const wchar_t *in)
  229 {
  230         if (*in > N_KEYS) /* can't be in the map */
  231                 return;
  232     if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) &&
  233         ((map == el->el_map.key &&
  234         el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) ||
  235         (map == el->el_map.alt &&
  236         el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN)))
  237         (void) keymacro_delete(el, in);
  238 }
  239 
  240 
  241 /* keymacro_delete():
  242  *      Delete the key and all longer keys staring with key, if
  243  *      they exists.
  244  */
  245 libedit_private int
  246 keymacro_delete(EditLine *el, const wchar_t *key)
  247 {
  248 
  249     if (key[0] == '\0') {
  250         (void) fprintf(el->el_errfile,
  251             "keymacro_delete: Null extended-key not allowed.\n");
  252         return -1;
  253     }
  254     if (el->el_keymacro.map == NULL)
  255         return 0;
  256 
  257     (void) node__delete(el, &el->el_keymacro.map, key);
  258     return 0;
  259 }
  260 
  261 
  262 /* keymacro_print():
  263  *  Print the binding associated with key key.
  264  *  Print entire el->el_keymacro.map if null
  265  */
  266 libedit_private void
  267 keymacro_print(EditLine *el, const wchar_t *key)
  268 {
  269 
  270     /* do nothing if el->el_keymacro.map is empty and null key specified */
  271     if (el->el_keymacro.map == NULL && *key == 0)
  272         return;
  273 
  274     el->el_keymacro.buf[0] = '"';
  275     if (node_lookup(el, key, el->el_keymacro.map, (size_t)1) <= -1)
  276         /* key is not bound */
  277         (void) fprintf(el->el_errfile, "Unbound extended key \"%ls"
  278             "\"\n", key);
  279     return;
  280 }
  281 
  282 
  283 /* node_trav():
  284  *  recursively traverses node in tree until match or mismatch is
  285  *  found.  May read in more characters.
  286  */
  287 static int
  288 node_trav(EditLine *el, keymacro_node_t *ptr, wchar_t *ch,
  289     keymacro_value_t *val)
  290 {
  291 
  292     if (ptr->ch == *ch) {
  293         /* match found */
  294         if (ptr->next) {
  295             /* key not complete so get next char */
  296             if (el_wgetc(el, ch) != 1)
  297                 return XK_NOD;
  298             return node_trav(el, ptr->next, ch, val);
  299         } else {
  300             *val = ptr->val;
  301             if (ptr->type != XK_CMD)
  302                 *ch = '\0';
  303             return ptr->type;
  304         }
  305     } else {
  306         /* no match found here */
  307         if (ptr->sibling) {
  308             /* try next sibling */
  309             return node_trav(el, ptr->sibling, ch, val);
  310         } else {
  311             /* no next sibling -- mismatch */
  312             val->str = NULL;
  313             return XK_STR;
  314         }
  315     }
  316 }
  317 
  318 
  319 /* node__try():
  320  *  Find a node that matches *str or allocate a new one
  321  */
  322 static int
  323 node__try(EditLine *el, keymacro_node_t *ptr, const wchar_t *str,
  324     keymacro_value_t *val, int ntype)
  325 {
  326 
  327     if (ptr->ch != *str) {
  328         keymacro_node_t *xm;
  329 
  330         for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
  331             if (xm->sibling->ch == *str)
  332                 break;
  333         if (xm->sibling == NULL)
  334             xm->sibling = node__get(*str);  /* setup new node */
  335         ptr = xm->sibling;
  336     }
  337     if (*++str == '\0') {
  338         /* we're there */
  339         if (ptr->next != NULL) {
  340             node__put(el, ptr->next);
  341                 /* lose longer keys with this prefix */
  342             ptr->next = NULL;
  343         }
  344         switch (ptr->type) {
  345         case XK_CMD:
  346         case XK_NOD:
  347             break;
  348         case XK_STR:
  349             if (ptr->val.str)
  350                 el_free(ptr->val.str);
  351             break;
  352         default:
  353             EL_ABORT((el->el_errfile, "Bad XK_ type %d\n",
  354                 ptr->type));
  355             break;
  356         }
  357 
  358         switch (ptr->type = ntype) {
  359         case XK_CMD:
  360             ptr->val = *val;
  361             break;
  362         case XK_STR:
  363             if ((ptr->val.str = wcsdup(val->str)) == NULL)
  364                 return -1;
  365             break;
  366         default:
  367             EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
  368             break;
  369         }
  370     } else {
  371         /* still more chars to go */
  372         if (ptr->next == NULL)
  373             ptr->next = node__get(*str);    /* setup new node */
  374         (void) node__try(el, ptr->next, str, val, ntype);
  375     }
  376     return 0;
  377 }
  378 
  379 
  380 /* node__delete():
  381  *  Delete node that matches str
  382  */
  383 static int
  384 node__delete(EditLine *el, keymacro_node_t **inptr, const wchar_t *str)
  385 {
  386     keymacro_node_t *ptr;
  387     keymacro_node_t *prev_ptr = NULL;
  388 
  389     ptr = *inptr;
  390 
  391     if (ptr->ch != *str) {
  392         keymacro_node_t *xm;
  393 
  394         for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
  395             if (xm->sibling->ch == *str)
  396                 break;
  397         if (xm->sibling == NULL)
  398             return 0;
  399         prev_ptr = xm;
  400         ptr = xm->sibling;
  401     }
  402     if (*++str == '\0') {
  403         /* we're there */
  404         if (prev_ptr == NULL)
  405             *inptr = ptr->sibling;
  406         else
  407             prev_ptr->sibling = ptr->sibling;
  408         ptr->sibling = NULL;
  409         node__put(el, ptr);
  410         return 1;
  411     } else if (ptr->next != NULL &&
  412         node__delete(el, &ptr->next, str) == 1) {
  413         if (ptr->next != NULL)
  414             return 0;
  415         if (prev_ptr == NULL)
  416             *inptr = ptr->sibling;
  417         else
  418             prev_ptr->sibling = ptr->sibling;
  419         ptr->sibling = NULL;
  420         node__put(el, ptr);
  421         return 1;
  422     } else {
  423         return 0;
  424     }
  425 }
  426 
  427 
  428 /* node__put():
  429  *  Puts a tree of nodes onto free list using free(3).
  430  */
  431 static void
  432 node__put(EditLine *el, keymacro_node_t *ptr)
  433 {
  434     if (ptr == NULL)
  435         return;
  436 
  437     if (ptr->next != NULL) {
  438         node__put(el, ptr->next);
  439         ptr->next = NULL;
  440     }
  441     node__put(el, ptr->sibling);
  442 
  443     switch (ptr->type) {
  444     case XK_CMD:
  445     case XK_NOD:
  446         break;
  447     case XK_STR:
  448         if (ptr->val.str != NULL)
  449             el_free(ptr->val.str);
  450         break;
  451     default:
  452         EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type));
  453         break;
  454     }
  455     el_free(ptr);
  456 }
  457 
  458 
  459 /* node__get():
  460  *  Returns pointer to a keymacro_node_t for ch.
  461  */
  462 static keymacro_node_t *
  463 node__get(wint_t ch)
  464 {
  465     keymacro_node_t *ptr;
  466 
  467     ptr = el_malloc(sizeof(*ptr));
  468     if (ptr == NULL)
  469         return NULL;
  470     ptr->ch = ch;
  471     ptr->type = XK_NOD;
  472     ptr->val.str = NULL;
  473     ptr->next = NULL;
  474     ptr->sibling = NULL;
  475     return ptr;
  476 }
  477 
  478 static void
  479 node__free(keymacro_node_t *k)
  480 {
  481     if (k == NULL)
  482         return;
  483     node__free(k->sibling);
  484     node__free(k->next);
  485     el_free(k);
  486 }
  487 
  488 /* node_lookup():
  489  *  look for the str starting at node ptr.
  490  *  Print if last node
  491  */
  492 static int
  493 node_lookup(EditLine *el, const wchar_t *str, keymacro_node_t *ptr,
  494     size_t cnt)
  495 {
  496     ssize_t used;
  497 
  498     if (ptr == NULL)
  499         return -1;  /* cannot have null ptr */
  500 
  501     if (!str || *str == 0) {
  502         /* no more chars in str.  node_enum from here. */
  503         (void) node_enum(el, ptr, cnt);
  504         return 0;
  505     } else {
  506         /* If match put this char into el->el_keymacro.buf.  Recurse */
  507         if (ptr->ch == *str) {
  508             /* match found */
  509             used = ct_visual_char(el->el_keymacro.buf + cnt,
  510                 KEY_BUFSIZ - cnt, ptr->ch);
  511             if (used == -1)
  512                 return -1; /* ran out of buffer space */
  513             if (ptr->next != NULL)
  514                 /* not yet at leaf */
  515                 return (node_lookup(el, str + 1, ptr->next,
  516                     (size_t)used + cnt));
  517             else {
  518                 /* next node is null so key should be complete */
  519                 if (str[1] == 0) {
  520                     size_t px = cnt + (size_t)used;
  521                     el->el_keymacro.buf[px] = '"';
  522                     el->el_keymacro.buf[px + 1] = '\0';
  523                     keymacro_kprint(el, el->el_keymacro.buf,
  524                         &ptr->val, ptr->type);
  525                     return 0;
  526                 } else
  527                     return -1;
  528                     /* mismatch -- str still has chars */
  529             }
  530         } else {
  531             /* no match found try sibling */
  532             if (ptr->sibling)
  533                 return (node_lookup(el, str, ptr->sibling,
  534                     cnt));
  535             else
  536                 return -1;
  537         }
  538     }
  539 }
  540 
  541 
  542 /* node_enum():
  543  *  Traverse the node printing the characters it is bound in buffer
  544  */
  545 static int
  546 node_enum(EditLine *el, keymacro_node_t *ptr, size_t cnt)
  547 {
  548         ssize_t used;
  549 
  550     if (cnt >= KEY_BUFSIZ - 5) {    /* buffer too small */
  551         el->el_keymacro.buf[++cnt] = '"';
  552         el->el_keymacro.buf[++cnt] = '\0';
  553         (void) fprintf(el->el_errfile,
  554             "Some extended keys too long for internal print buffer");
  555         (void) fprintf(el->el_errfile, " \"%ls...\"\n",
  556             el->el_keymacro.buf);
  557         return 0;
  558     }
  559     if (ptr == NULL) {
  560 #ifdef DEBUG_EDIT
  561         (void) fprintf(el->el_errfile,
  562             "node_enum: BUG!! Null ptr passed\n!");
  563 #endif
  564         return -1;
  565     }
  566     /* put this char at end of str */
  567         used = ct_visual_char(el->el_keymacro.buf + cnt, KEY_BUFSIZ - cnt,
  568         ptr->ch);
  569     if (ptr->next == NULL) {
  570         /* print this key and function */
  571         el->el_keymacro.buf[cnt + (size_t)used   ] = '"';
  572         el->el_keymacro.buf[cnt + (size_t)used + 1] = '\0';
  573         keymacro_kprint(el, el->el_keymacro.buf, &ptr->val, ptr->type);
  574     } else
  575         (void) node_enum(el, ptr->next, cnt + (size_t)used);
  576 
  577     /* go to sibling if there is one */
  578     if (ptr->sibling)
  579         (void) node_enum(el, ptr->sibling, cnt);
  580     return 0;
  581 }
  582 
  583 
  584 /* keymacro_kprint():
  585  *  Print the specified key and its associated
  586  *  function specified by val
  587  */
  588 libedit_private void
  589 keymacro_kprint(EditLine *el, const wchar_t *key, keymacro_value_t *val,
  590     int ntype)
  591 {
  592     el_bindings_t *fp;
  593     char unparsbuf[EL_BUFSIZ];
  594     static const char fmt[] = "%-15s->  %s\n";
  595 
  596     if (val != NULL)
  597         switch (ntype) {
  598         case XK_STR:
  599             (void) keymacro__decode_str(val->str, unparsbuf,
  600                 sizeof(unparsbuf),
  601                 ntype == XK_STR ? "\"\"" : "[]");
  602             (void) fprintf(el->el_outfile, fmt,
  603                 ct_encode_string(key, &el->el_scratch), unparsbuf);
  604             break;
  605         case XK_CMD:
  606             for (fp = el->el_map.help; fp->name; fp++)
  607                 if (val->cmd == fp->func) {
  608                     wcstombs(unparsbuf, fp->name, sizeof(unparsbuf));
  609                     unparsbuf[sizeof(unparsbuf) -1] = '\0';
  610                     (void) fprintf(el->el_outfile, fmt,
  611                         ct_encode_string(key, &el->el_scratch), unparsbuf);
  612                     break;
  613                 }
  614 #ifdef DEBUG_KEY
  615             if (fp->name == NULL)
  616                 (void) fprintf(el->el_outfile,
  617                     "BUG! Command not found.\n");
  618 #endif
  619 
  620             break;
  621         default:
  622             EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
  623             break;
  624         }
  625     else
  626         (void) fprintf(el->el_outfile, fmt, ct_encode_string(key,
  627             &el->el_scratch), "no input");
  628 }
  629 
  630 
  631 #define ADDC(c) \
  632     if (b < eb) \
  633         *b++ = c; \
  634     else \
  635         b++
  636 /* keymacro__decode_str():
  637  *  Make a printable version of the ey
  638  */
  639 libedit_private size_t
  640 keymacro__decode_str(const wchar_t *str, char *buf, size_t len,
  641     const char *sep)
  642 {
  643     char *b = buf, *eb = b + len;
  644     const wchar_t *p;
  645 
  646     b = buf;
  647     if (sep[0] != '\0') {
  648         ADDC(sep[0]);
  649     }
  650     if (*str == '\0') {
  651         ADDC('^');
  652         ADDC('@');
  653         goto add_endsep;
  654     }
  655     for (p = str; *p != 0; p++) {
  656         wchar_t dbuf[VISUAL_WIDTH_MAX];
  657         wchar_t *p2 = dbuf;
  658         ssize_t l = ct_visual_char(dbuf, VISUAL_WIDTH_MAX, *p);
  659         while (l-- > 0) {
  660             ssize_t n = ct_encode_char(b, (size_t)(eb - b), *p2++);
  661             if (n == -1) /* ran out of space */
  662                 goto add_endsep;
  663             else
  664                 b += n;
  665         }
  666     }
  667 add_endsep:
  668     if (sep[0] != '\0' && sep[1] != '\0') {
  669         ADDC(sep[1]);
  670     }
  671     ADDC('\0');
  672     if ((size_t)(b - buf) >= len)
  673         buf[len - 1] = '\0';
  674     return (size_t)(b - buf);
  675 }