Index: Makefile =================================================================== --- Makefile (revision 302244) +++ Makefile (working copy) @@ -6,7 +6,7 @@ PROG= sed SRCS= compile.c main.c misc.c process.c -WARNS?= 2 +WARNS?= 5 .if ${MK_TESTS} != "no" SUBDIR+= tests Index: compile.c =================================================================== --- compile.c (revision 302244) +++ compile.c (working copy) @@ -64,21 +64,21 @@ int lh_ref; } *labels[LHSZ]; -static char *compile_addr(char *, struct s_addr *); -static char *compile_ccl(char **, char *); -static char *compile_delimited(char *, char *, int); -static char *compile_flags(char *, struct s_subst *); -static regex_t *compile_re(char *, int); -static char *compile_subst(char *, struct s_subst *); -static char *compile_text(void); -static char *compile_tr(char *, struct s_tr **); +static const char *compile_addr(const char *, struct s_addr *); +static char *compile_ccl(const char **, char *); +static const char *compile_delimited(const char *, char *, int); +static const char *compile_flags(const char *, struct s_subst *); +static regex_t *compile_re(const char *, int); +static const char *compile_subst(const char *, struct s_subst *); +static char *compile_text(size_t *); +static const char *compile_tr(const char *, struct s_tr **); static struct s_command **compile_stream(struct s_command **); -static char *duptoeol(char *, const char *); +static char *duptoeol(const char *, const char *, size_t *); static void enterlabel(struct s_command *); static struct s_command - *findlabel(char *); -static void fixuplabel(struct s_command *, struct s_command *); + *findlabel(const char *); +static void fixuplabel(struct s_command *, const struct s_command *); static void uselabel(void); /* @@ -144,40 +144,43 @@ err(1, "malloc"); } -#define EATSPACE() do { \ - if (p) \ - while (*p && isspace((unsigned char)*p)) \ - p++; \ +#define EATSPACE() do { \ + while (*p && isspace((unsigned char)*p)) \ + p++; \ } while (0) +#define EATSPACEN() do { \ + while (*p && *p != '\n' && isspace((unsigned char)*p)) \ + p++; \ + } while (0) + static struct s_command ** compile_stream(struct s_command **link) { - char *p; - static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ + const char *p; struct s_command *cmd, *cmd2, *stack; struct s_format *fp; char re[_POSIX2_LINE_MAX + 1]; int naddr; /* Number of addresses */ - stack = 0; + stack = NULL; for (;;) { - if ((p = cu_fgets(lbuf, sizeof(lbuf), NULL)) == NULL) { - if (stack != 0) + if ((p = cu_fgets(NULL)) == NULL) { + if (stack != NULL) errx(1, "%lu: %s: unexpected EOF (pending }'s)", linenum, fname); return (link); } -semicolon: EATSPACE(); - if (p) { - if (*p == '#' || *p == '\0') - continue; - else if (*p == ';') { - p++; - goto semicolon; - } +semicolon: EATSPACEN(); + switch (*p) { + case '#': case '\0': case '\n': + continue; /* to next command-unit */ + case ';': + p++; + goto semicolon; } + if ((*link = cmd = malloc(sizeof(struct s_command))) == NULL) err(1, "malloc"); link = &cmd->next; @@ -208,7 +211,7 @@ cmd->a1 = cmd->a2 = 0; nonsel: /* Now parse the command */ - if (!*p) + if (*p == '\0' || *p == '\n') errx(1, "%lu: %s: command expected", linenum, fname); cmd->code = *p; for (fp = cmd_fmts; fp->code; fp++) @@ -215,7 +218,7 @@ if (fp->code == *p) break; if (!fp->code) - errx(1, "%lu: %s: invalid command code %c", linenum, fname, *p); + errx(1, "%lu: %s: invalid command code %c (%s)", linenum, fname, *p, p); if (naddr > fp->naddr) errx(1, "%lu: %s: command %c expects up to %d address(es), found %d", @@ -228,11 +231,11 @@ goto nonsel; case GROUP: /* { */ p++; - EATSPACE(); + EATSPACEN(); cmd->next = stack; stack = cmd; link = &cmd->u.c; - if (*p) + if (*p != '\0' && *p != '\n') goto semicolon; break; case ENDGROUP: @@ -241,7 +244,7 @@ * group is really just a noop. */ cmd->nonsel = 1; - if (stack == 0) + if (stack == NULL) errx(1, "%lu: %s: unexpected }", linenum, fname); cmd2 = stack; stack = cmd2->next; @@ -249,13 +252,13 @@ /*FALLTHROUGH*/ case EMPTY: /* d D g G h H l n N p P q x = \0 */ p++; - EATSPACE(); + EATSPACEN(); if (*p == ';') { p++; link = &cmd->next; goto semicolon; } - if (*p) + if (*p != '\0' && *p != '\n') errx(1, "%lu: %s: extra characters at the end of %c command", linenum, fname, cmd->code); break; @@ -266,12 +269,12 @@ errx(1, "%lu: %s: command %c expects \\ followed by text", linenum, fname, cmd->code); p++; - EATSPACE(); - if (*p) + EATSPACEN(); + if (*p != '\n') errx(1, - "%lu: %s: extra characters after \\ at the end of %c command", - linenum, fname, cmd->code); - cmd->t = compile_text(); + "%lu: %s: extra characters (%c) after \\ at the end of %c command", + linenum, fname, *p, cmd->code); + cmd->t = compile_text(&cmd->tlen); break; case COMMENT: /* \0 # */ break; @@ -280,10 +283,10 @@ EATSPACE(); if (*p == '\0') errx(1, "%lu: %s: filename expected", linenum, fname); - cmd->t = duptoeol(p, "w command"); + cmd->t = duptoeol(p, "w command", &cmd->tlen); if (aflag) cmd->u.fd = -1; - else if ((cmd->u.fd = open(p, + else if ((cmd->u.fd = open(cmd->t, O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) err(1, "%s", p); @@ -294,27 +297,27 @@ if (*p == '\0') errx(1, "%lu: %s: filename expected", linenum, fname); else - cmd->t = duptoeol(p, "read command"); + cmd->t = duptoeol(p, "read command", &cmd->tlen); break; case BRANCH: /* b t */ p++; - EATSPACE(); - if (*p == '\0') + EATSPACEN(); + if (*p == '\0' || *p == '\n') cmd->t = NULL; else - cmd->t = duptoeol(p, "branch"); + cmd->t = duptoeol(p, "branch", &cmd->tlen); break; case LABEL: /* : */ p++; EATSPACE(); - cmd->t = duptoeol(p, "label"); - if (strlen(p) == 0) + cmd->t = duptoeol(p, "label", &cmd->tlen); + if (cmd->t[0] == '\0') errx(1, "%lu: %s: empty label", linenum, fname); enterlabel(cmd); break; case SUBST: /* s */ p++; - if (*p == '\0' || *p == '\\') + if (*p == '\0' || *p == '\\' || *p == '\n') errx(1, "%lu: %s: substitute pattern can not be delimited by newline or backslash", linenum, fname); @@ -325,21 +328,13 @@ errx(1, "%lu: %s: unterminated substitute pattern", linenum, fname); - /* Compile RE with no case sensitivity temporarily */ - if (*re == '\0') - cmd->u.s->re = NULL; - else - cmd->u.s->re = compile_re(re, 0); --p; p = compile_subst(p, cmd->u.s); p = compile_flags(p, cmd->u.s); - /* Recompile RE with case sensitivity from "I" flag if any */ - if (*re == '\0') - cmd->u.s->re = NULL; - else + if (*re != '\0') cmd->u.s->re = compile_re(re, cmd->u.s->icase); - EATSPACE(); + if (*p == ';') { p++; link = &cmd->next; @@ -372,8 +367,8 @@ * in the case of a non-terminated string. The character array d is filled * with the processed string. */ -static char * -compile_delimited(char *p, char *d, int is_tr) +static const char * +compile_delimited(const char *p, char *d, int is_tr) { char c; @@ -416,10 +411,10 @@ /* compile_ccl: expand a POSIX character class */ static char * -compile_ccl(char **sp, char *t) +compile_ccl(const char **sp, char *t) { int c, d; - char *s = *sp; + const char *s = *sp; *t++ = *s++; if (*s == '^') @@ -442,7 +437,7 @@ * Cflags are passed to regcomp. */ static regex_t * -compile_re(char *re, int case_insensitive) +compile_re(const char *re, int case_insensitive) { regex_t *rep; int eval, flags; @@ -466,14 +461,13 @@ * point to a saved copy of it. Nsub is the number of parenthesized regular * expressions. */ -static char * -compile_subst(char *p, struct s_subst *s) +static const char * +compile_subst(const char *p, struct s_subst *s) { - static char lbuf[_POSIX2_LINE_MAX + 1]; int asize, size; u_char ref; char c, *text, *op, *sp; - int more = 1, sawesc = 0; + int more = 0, sawesc = 0; c = *p++; /* Terminator character */ if (c == '\0') @@ -487,7 +481,7 @@ size = 0; do { op = sp = text + size; - for (; *p; p++) { + for (; *p != '\0' && *p != '\n'; p++) { if (*p == '\\' || sawesc) { /* * If this is a continuation from the last @@ -509,7 +503,10 @@ */ sawesc = 1; p--; - continue; + break; + } else if (*p == '\n') { + *sp++ = '\n'; + break; } else if (strchr("123456789", *p) != NULL) { *sp++ = '\\'; ref = *p - '0'; @@ -523,8 +520,11 @@ *sp++ = '\\'; } else if (*p == c) { if (*++p == '\0' && more) { - if (cu_fgets(lbuf, sizeof(lbuf), &more)) - p = lbuf; + const char *nextp; + + nextp = cu_fgets(&more); + if (nextp != NULL) + p = nextp; } *sp++ = '\0'; size += sp - op; @@ -544,7 +544,7 @@ if ((text = realloc(text, asize)) == NULL) err(1, "realloc"); } - } while (cu_fgets(p = lbuf, sizeof(lbuf), &more)); + } while ((p = cu_fgets(&more))); errx(1, "%lu: %s: unterminated substitute in regular expression", linenum, fname); /* NOTREACHED */ @@ -553,12 +553,12 @@ /* * Compile the flags of the s command */ -static char * -compile_flags(char *p, struct s_subst *s) +static const char * +compile_flags(const char *p, struct s_subst *s) { int gn; /* True if we have seen g or n */ unsigned long nval; - char wfile[_POSIX2_LINE_MAX + 1], *q, *eq; + char *q; s->n = 1; /* Default */ s->p = 0; @@ -566,7 +566,7 @@ s->wfd = -1; s->icase = 0; for (gn = 0;;) { - EATSPACE(); /* EXTENSION */ + EATSPACEN(); /* EXTENSION */ switch (*p) { case 'g': if (gn) @@ -594,12 +594,12 @@ "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); gn = 1; errno = 0; - nval = strtol(p, &p, 10); + nval = strtol(p, &q, 10); if (errno == ERANGE || nval > INT_MAX) errx(1, "%lu: %s: overflow in the 'N' substitute flag", linenum, fname); s->n = nval; - p--; + p = q; break; case 'w': p++; @@ -610,27 +610,15 @@ } #endif EATSPACE(); - q = wfile; - eq = wfile + sizeof(wfile) - 1; - while (*p) { - if (*p == '\n') - break; - if (q >= eq) - err(1, "wfile too long"); - *q++ = *p++; - } - *q = '\0'; - if (q == wfile) - errx(1, "%lu: %s: no wfile specified", linenum, fname); - s->wfile = strdup(wfile); - if (!aflag && (s->wfd = open(wfile, + s->wfile = duptoeol(p, "w flag", NULL); + if (!aflag && (s->wfd = open(s->wfile, O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) - err(1, "%s", wfile); + err(1, "%s", s->wfile); return (p); default: - errx(1, "%lu: %s: bad flag in substitute command: '%c'", - linenum, fname, *p); + errx(1, "%lu: %s: bad flag in substitute command: '%c' (%.10s)", + linenum, fname, *p, p); break; } p++; @@ -640,8 +628,8 @@ /* * Compile a translation set of strings into a lookup table. */ -static char * -compile_tr(char *p, struct s_tr **py) +static const char * +compile_tr(const char *p, struct s_tr **py) { struct s_tr *y; int i; @@ -652,7 +640,7 @@ mbstate_t mbs1, mbs2; if ((*py = y = malloc(sizeof(*y))) == NULL) - err(1, NULL); + err(1, "malloc"); y->multis = NULL; y->nmultis = 0; @@ -672,11 +660,11 @@ op = old; oldlen = mbsrtowcs(NULL, &op, 0, NULL); if (oldlen == (size_t)-1) - err(1, NULL); + err(1, "mbsrtowcs"); np = new; newlen = mbsrtowcs(NULL, &np, 0, NULL); if (newlen == (size_t)-1) - err(1, NULL); + err(1, "mbsrtowcs"); if (newlen != oldlen) errx(1, "%lu: %s: transform strings are not the same length", linenum, fname); @@ -715,7 +703,7 @@ y->multis = realloc(y->multis, (y->nmultis + 1) * sizeof(*y->multis)); if (y->multis == NULL) - err(1, NULL); + err(1, "realloc"); i = y->nmultis++; y->multis[i].fromlen = oclen; memcpy(y->multis[i].from, op, oclen); @@ -733,24 +721,25 @@ * Compile the text following an a or i command. */ static char * -compile_text(void) +compile_text(size_t *ptlen) { int asize, esc_nl, size; - char *text, *p, *op, *s; - char lbuf[_POSIX2_LINE_MAX + 1]; + char *text, *s; + const char *p, *op; asize = 2 * _POSIX2_LINE_MAX + 1; if ((text = malloc(asize)) == NULL) err(1, "malloc"); size = 0; - while (cu_fgets(lbuf, sizeof(lbuf), NULL)) { + while ((p = cu_fgets(NULL))) { op = s = text + size; - p = lbuf; - EATSPACE(); + EATSPACEN(); for (esc_nl = 0; *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0' && *++p == '\n') esc_nl = 1; *s++ = *p; + if (*p == '\n') + break; } size += s - op; if (!esc_nl) { @@ -764,9 +753,10 @@ } } text[size] = '\0'; - if ((p = realloc(text, size + 1)) == NULL) + if ((text = realloc(text, size + 1)) == NULL) err(1, "realloc"); - return (p); + *ptlen = size; + return (text); } /* @@ -773,8 +763,8 @@ * Get an address and return a pointer to the first character after * it. Fill the structure pointed to according to the address. */ -static char * -compile_addr(char *p, struct s_addr *a) +static const char * +compile_addr(const char *p, struct s_addr *a) { char *end, re[_POSIX2_LINE_MAX + 1]; int icase; @@ -828,22 +818,26 @@ * Return a copy of all the characters up to \n or \0. */ static char * -duptoeol(char *s, const char *ctype) +duptoeol(const char *s, const char *ctype, size_t *ptlen) { size_t len; int ws; - char *p, *start; + char *p; + const char *start; ws = 0; for (start = s; *s != '\0' && *s != '\n'; ++s) ws = isspace((unsigned char)*s); - *s = '\0'; if (ws) warnx("%lu: %s: whitespace after %s", linenum, fname, ctype); - len = s - start + 1; - if ((p = malloc(len)) == NULL) + len = s - start; + if ((p = malloc(len + 1)) == NULL) err(1, "malloc"); - return (memmove(p, start, len)); + memmove(p, start, len); + p[len] = '\0'; + if (ptlen != NULL) + *ptlen = len; + return p; } /* @@ -854,7 +848,7 @@ * TODO: Remove } nodes */ static void -fixuplabel(struct s_command *cp, struct s_command *end) +fixuplabel(struct s_command *cp, const struct s_command *end) { for (; cp != end; cp = cp->next) @@ -871,7 +865,7 @@ break; } if ((cp->u.c = findlabel(cp->t)) == NULL) - errx(1, "%lu: %s: undefined label '%s'", linenum, fname, cp->t); + errx(1, "%lu: %s: %c: undefined label '%s'", linenum, fname, cp->code, cp->t); free(cp->t); break; case '{': @@ -911,7 +905,7 @@ * list cp. L is excluded from the search. Return NULL if not found. */ static struct s_command * -findlabel(char *name) +findlabel(const char *name) { struct labhash *lh; u_char *p; Index: defs.h =================================================================== --- defs.h (revision 302244) +++ defs.h (working copy) @@ -94,6 +94,7 @@ struct s_addr *a1, *a2; /* Start and end address */ u_long startline; /* Start line number or zero */ char *t; /* Text for : a c i r w */ + size_t tlen; /* Length of the text */ union { struct s_command *c; /* Command(s) for b t { */ struct s_subst *s; /* Substitute command */ Index: extern.h =================================================================== --- extern.h (revision 302244) +++ extern.h (working copy) @@ -48,9 +48,9 @@ void cfclose(struct s_command *, struct s_command *); void compile(void); void cspace(SPACE *, const char *, size_t, enum e_spflag); -char *cu_fgets(char *, int, int *); +const char *cu_fgets(int *); int mf_fgets(SPACE *, enum e_spflag); int lastline(void); void process(void); void resetstate(void); -char *strregerror(int, regex_t *); +const char *strregerror(int, const regex_t *); Index: main.c =================================================================== --- main.c (revision 302244) +++ main.c (working copy) @@ -73,7 +73,7 @@ struct s_compunit { struct s_compunit *next; enum e_cut {CU_FILE, CU_STRING} type; - char *s; /* Pointer to string or fname */ + const char *s; /* Pointer to string or fname */ }; /* @@ -86,7 +86,7 @@ * Linked list of files to be processed */ struct s_flist { - char *fname; + const char *fname; struct s_flist *next; }; @@ -117,8 +117,8 @@ static const char *inplace; /* Inplace edit file extension. */ u_long linenum; -static void add_compunit(enum e_cut, char *); -static void add_file(char *); +static void add_compunit(enum e_cut, const char *); +static void add_file(const char *); static void usage(void); int @@ -125,7 +125,6 @@ main(int argc, char *argv[]) { int c, fflag; - char *temp_arg; (void) setlocale(LC_ALL, ""); @@ -147,11 +146,7 @@ break; case 'e': eflag = 1; - if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL) - err(1, "malloc"); - strcpy(temp_arg, optarg); - strcat(temp_arg, "\n"); - add_compunit(CU_STRING, temp_arg); + add_compunit(CU_STRING, optarg); break; case 'f': fflag = 1; @@ -214,14 +209,16 @@ * Like fgets, but go through the chain of compilation units chaining them * together. Empty strings and files are ignored. */ -char * -cu_fgets(char *buf, int n, int *more) +const char * +cu_fgets(int *more) { static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; static FILE *f; /* Current open file */ - static char *s; /* Current pointer inside string */ - static char string_ident[30]; + static const char *s; /* Current pointer inside string */ + static char string_ident[30], *lastresult; + static size_t lastsize; char *p; + const char *start; again: switch (state) { @@ -251,14 +248,16 @@ goto again; } case ST_FILE: - if ((p = fgets(buf, n, f)) != NULL) { + p = lastresult; + if (getline(&p, &lastsize, f) != -1) { linenum++; - if (linenum == 1 && buf[0] == '#' && buf[1] == 'n') + if (linenum == 1 && p[0] == '#' && p[1] == 'n') nflag = 1; if (more != NULL) *more = !feof(f); - return (p); - } + return (lastresult = p); + } else if (ferror(f)) + err(1, "%s", script->s); script = script->next; (void)fclose(f); state = ST_EOF; @@ -266,39 +265,26 @@ case ST_STRING: if (linenum == 0 && s[0] == '#' && s[1] == 'n') nflag = 1; - p = buf; + else if (s[0] == '\0') { + state = ST_EOF; + script = script->next; + goto again; + } + start = s; for (;;) { - if (n-- <= 1) { - *p = '\0'; - linenum++; - if (more != NULL) - *more = 1; - return (buf); - } switch (*s) { case '\0': state = ST_EOF; - if (s == script->s) { - script = script->next; - goto again; - } else { - script = script->next; - *p = '\0'; - linenum++; - if (more != NULL) - *more = 0; - return (buf); - } + script = script->next; + /* FALLTHROUGH */ case '\n': - *p++ = '\n'; - *p = '\0'; s++; linenum++; if (more != NULL) *more = 0; - return (buf); + return (start); default: - *p++ = *s++; + s++; } } } @@ -400,13 +386,15 @@ sizeof(oldfname)); len = strlcat(oldfname, inplace, sizeof(oldfname)); - if (len > sizeof(oldfname)) + if ((size_t)len > sizeof(oldfname)) errx(1, "%s: name too long", fname); } len = snprintf(tmpfname, sizeof(tmpfname), "%s/.!%ld!%s", dirname(fname), (long)getpid(), basename(fname)); - if (len >= sizeof(tmpfname)) + if (len < 0) + err(1, "snprintf"); + if ((size_t)len >= sizeof(tmpfname)) errx(1, "%s: name too long", fname); unlink(tmpfname); if (outfile != NULL && outfile != stdout) @@ -454,7 +442,7 @@ * Add a compilation unit to the linked list */ static void -add_compunit(enum e_cut type, char *s) +add_compunit(enum e_cut type, const char *s) { struct s_compunit *cu; @@ -471,7 +459,7 @@ * Add a file to the linked list */ static void -add_file(char *s) +add_file(const char *s) { struct s_flist *fp; Index: misc.c =================================================================== --- misc.c (revision 302244) +++ misc.c (working copy) @@ -55,8 +55,8 @@ * because of the silly semantics of regerror (we can never know the size of * the buffer). */ -char * -strregerror(int errcode, regex_t *preg) +const char * +strregerror(int errcode, const regex_t *preg) { static char *oe; size_t s; Index: process.c =================================================================== --- process.c (revision 302244) +++ process.c (working copy) @@ -67,13 +67,13 @@ #define hsl HS.len static inline int applies(struct s_command *); -static void do_tr(struct s_tr *); +static void do_tr(const struct s_tr *); static void flush_appends(void); -static void lputs(char *, size_t); -static int regexec_e(regex_t *, const char *, int, int, size_t, - size_t); -static void regsub(SPACE *, char *, char *); -static int substitute(struct s_command *); +static void lputs(const char *, size_t); +static int regexec_e(const regex_t *, const char *, int, int, + size_t start, size_t stop); +static void regsub(SPACE *, const char *, const char *); +static int substitute(const struct s_command *); struct s_appends *appends; /* Array of pointers to strings to append. */ static int appendx; /* Index into appends array. */ @@ -82,7 +82,7 @@ static int lastaddr; /* Set by applies if last address of a range. */ static int sdone; /* If any substitutes since last line input. */ /* Iov structure for 'w' commands. */ -static regex_t *defpreg; +static const regex_t *defpreg; size_t maxnsub; regmatch_t *match; @@ -120,7 +120,7 @@ err(1, "realloc"); appends[appendx].type = AP_STRING; appends[appendx].s = cp->t; - appends[appendx].len = strlen(cp->t); + appends[appendx].len = cp->tlen; appendx++; break; case 'b': @@ -210,7 +210,7 @@ err(1, "realloc"); appends[appendx].type = AP_FILE; appends[appendx].s = cp->t; - appends[appendx].len = strlen(cp->t); + appends[appendx].len = cp->tlen; appendx++; break; case 's': @@ -366,13 +366,13 @@ * and then swap them. */ static int -substitute(struct s_command *cp) +substitute(const struct s_command *cp) { SPACE tspace; regex_t *re; regoff_t slen; int lastempty, n; - size_t le = 0; + regoff_t le = 0; char *s; s = ps; @@ -477,7 +477,7 @@ * Perform translation ('y' command) in the pattern space. */ static void -do_tr(struct s_tr *y) +do_tr(const struct s_tr *y) { SPACE tmp; char c, *p; @@ -566,7 +566,7 @@ } static void -lputs(char *s, size_t len) +lputs(const char *s, size_t len) { static const char escapes[] = "\\\a\b\f\r\t\v"; int c, col, width; @@ -646,7 +646,7 @@ } static int -regexec_e(regex_t *preg, const char *string, int eflags, int nomatch, +regexec_e(const regex_t *preg, const char *string, int eflags, int nomatch, size_t start, size_t stop) { int eval; @@ -678,7 +678,7 @@ * Based on a routine by Henry Spencer */ static void -regsub(SPACE *sp, char *string, char *src) +regsub(SPACE *sp, const char *string, const char *src) { int len, no; char c, *dst;