--- glob.c-nomb Tue Nov 6 11:32:41 2012 +++ glob.c-mb Sun Dec 16 20:45:47 2012 @@ -94,6 +94,7 @@ #include #include #include +#include #include "charclass.h" @@ -114,39 +115,25 @@ #define RBRACE '}' #define SLASH '/' #define COMMA ',' +#define COLON ':' -#ifndef DEBUG +#define M_QUOTE 0x800000 +#define M_PROTECT 0x400000 -#define M_QUOTE 0x8000 -#define M_PROTECT 0x4000 -#define M_MASK 0xffff -#define M_ASCII 0x00ff +typedef struct Char_s { + wchar_t wc; + uint_t at; +} Char; -typedef ushort_t Char; +#define M_ALL '*' /* Plus M_QUOTE */ +#define M_END ']' /* Plus M_QUOTE */ +#define M_NOT '!' /* Plus M_QUOTE */ +#define M_ONE '?' /* Plus M_QUOTE */ +#define M_RNG '-' /* Plus M_QUOTE */ +#define M_SET '[' /* Plus M_QUOTE */ +#define M_CLASS ':' /* Plus M_QUOTE */ +#define ismeta(c) (((c).at&M_QUOTE) != 0) -#else - -#define M_QUOTE 0x80 -#define M_PROTECT 0x40 -#define M_MASK 0xff -#define M_ASCII 0x7f - -typedef char Char; - -#endif - - -#define CHAR(c) ((Char)((c)&M_ASCII)) -#define META(c) ((Char)((c)|M_QUOTE)) -#define M_ALL META('*') -#define M_END META(']') -#define M_NOT META('!') -#define M_ONE META('?') -#define M_RNG META('-') -#define M_SET META('[') -#define M_CLASS META(':') -#define ismeta(c) (((c)&M_QUOTE) != 0) - #define GLOB_LIMIT_MALLOC 65536 #define GLOB_LIMIT_STAT 2048 #define GLOB_LIMIT_READDIR 16384 @@ -170,8 +157,7 @@ static int g_Ctoc(const Char *, char *, uint_t); static int g_lstat(Char *, struct stat *, glob_t *); static DIR *g_opendir(Char *, glob_t *); -static Char *g_strchr(const Char *, int); -static int g_strncmp(const Char *, const char *, size_t); +static Char *g_strchr(const Char *, wchar_t); static int g_stat(Char *, struct stat *, glob_t *); static int glob0(const Char *, glob_t *, struct glob_lim *, int (*)(const char *, int)); @@ -200,8 +186,9 @@ glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob) { - const uchar_t *patnext; - int c; + const char *patnext; + size_t n; + wchar_t c; Char *bufnext, *bufend, patbuf[MAXPATHLEN]; struct glob_lim limit = { 0, 0, 0 }; @@ -208,7 +195,7 @@ if (strnlen(pattern, PATH_MAX) == PATH_MAX) return (GLOB_NOMATCH); - patnext = (uchar_t *)pattern; + patnext = pattern; if (!(flags & GLOB_APPEND)) { pglob->gl_pathc = 0; pglob->gl_pathv = NULL; @@ -227,22 +214,46 @@ bufnext = patbuf; bufend = bufnext + MAXPATHLEN - 1; - if (flags & GLOB_NOESCAPE) - while (bufnext < bufend && (c = *patnext++) != EOS) - *bufnext++ = c; - else { + if (flags & GLOB_NOESCAPE) { + while (bufnext < bufend) { + if ((n = mbtowc(&c, patnext, PATH_MAX)) > 0) { + patnext += n; + bufnext->at = 0; + (bufnext++)->wc = c; + } else if (n == 0) { + break; + } else { + return (GLOB_NOMATCH); + } + } + } else { /* Protect the quoted characters. */ - while (bufnext < bufend && (c = *patnext++) != EOS) - if (c == QUOTE) { - if ((c = *patnext++) == EOS) { - c = QUOTE; - --patnext; + while (bufnext < bufend) { + if ((n = mbtowc(&c, patnext, PATH_MAX)) > 0) { + patnext += n; + if (c == QUOTE) { + n = mbtowc(&c, patnext, PATH_MAX); + if (n < 0) + return (GLOB_NOMATCH); + if (n > 0) + patnext += n; + if (n == 0) + c = QUOTE; + bufnext->at = M_PROTECT; + (bufnext++)->wc = c; + } else { + bufnext->at = 0; + (bufnext++)->wc = c; } - *bufnext++ = c | M_PROTECT; - } else - *bufnext++ = c; + } else if (n == 0) { + break; + } else { + return (GLOB_NOMATCH); + } + } } - *bufnext = EOS; + bufnext->at = 0; + bufnext->wc = EOS; if (flags & GLOB_BRACE) return (globexp1(patbuf, pglob, &limit, errfunc)); @@ -262,7 +273,8 @@ const Char* ptr = pattern; /* Protect a single {}, for find(1), like csh */ - if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) + if (pattern[0].wc == LBRACE && pattern[1].wc == RBRACE && + pattern[2].wc == EOS) return (glob0(pattern, pglob, limitp, errfunc)); if ((ptr = (const Char *) g_strchr(ptr, LBRACE)) != NULL) @@ -289,16 +301,18 @@ /* copy part up to the brace */ for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++) ; - *lm = EOS; + lm->at = 0; + lm->wc = EOS; ls = lm; /* Find the balanced brace */ - for (i = 0, pe = ++ptr; *pe; pe++) - if (*pe == LBRACKET) { + for (i = 0, pe = ++ptr; pe->wc != EOS; pe++) + if (pe->wc == LBRACKET) { /* Ignore everything between [] */ - for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++) + for (pm = pe++; pe->wc != RBRACKET && + pe->wc != EOS; pe++) ; - if (*pe == EOS) { + if (pe->wc == EOS) { /* * We could not find a matching RBRACKET. * Ignore and just look for RBRACE @@ -305,9 +319,9 @@ */ pe = pm; } - } else if (*pe == LBRACE) + } else if (pe->wc == LBRACE) { i++; - else if (*pe == RBRACE) { + } else if (pe->wc == RBRACE) { if (i == 0) break; i--; @@ -314,16 +328,17 @@ } /* Non matching braces; just glob the pattern */ - if (i != 0 || *pe == EOS) + if (i != 0 || pe->wc == EOS) return (glob0(patbuf, pglob, limitp, errfunc)); for (i = 0, pl = pm = ptr; pm <= pe; pm++) { - switch (*pm) { + switch (pm->wc) { case LBRACKET: /* Ignore everything between [] */ - for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++) + for (pl = pm++; pm->wc != RBRACKET && pm->wc != EOS; + pm++) ; - if (*pm == EOS) { + if (pm->wc == EOS) { /* * We could not find a matching RBRACKET. * Ignore and just look for RBRACE @@ -343,7 +358,7 @@ } /* FALLTHROUGH */ case COMMA: - if (i && *pm == COMMA) + if (i && pm->wc == COMMA) break; else { /* Append the current string */ @@ -354,13 +369,11 @@ * Append the rest of the pattern after the * closing brace */ - for (pl = pe + 1; (*lm++ = *pl++) != EOS; ) + for (pl = pe + 1; + (*lm++ = *pl++).wc != EOS; /* */) ; /* Expand the current pattern */ -#ifdef DEBUG - qprintf("globexp2:", patbuf); -#endif rv = globexp1(patbuf, pglob, limitp, errfunc); if (rv && rv != GLOB_NOMATCH) return (rv); @@ -388,25 +401,25 @@ struct passwd *pwd; char *h; const Char *p; - Char *b, *eb; + Char *b, *eb, *q; + size_t n; + wchar_t c; - if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE)) + if (pattern->wc != TILDE || !(pglob->gl_flags & GLOB_TILDE)) return (pattern); /* Copy up to the end of the string or / */ eb = &patbuf[patbuf_len - 1]; - for (p = pattern + 1, h = (char *)patbuf; - h < (char *)eb && *p && *p != SLASH; *h++ = *p++) + for (p = pattern + 1, q = patbuf; + q < eb && p->wc != EOS && p->wc != SLASH; *q++ = *p++) ; - *h = EOS; + q->at = 0; + q->wc = EOS; -#if 0 - if (h == (char *)eb) - return (what); -#endif + /* What to do if patbuf is full? */ - if (((char *)patbuf)[0] == EOS) { + if (patbuf[0].wc == EOS) { /* * handle a plain ~ or ~/ by expanding $HOME * first and then trying the password file @@ -428,33 +441,26 @@ } /* Copy the home directory */ - for (b = patbuf; b < eb && *h; *b++ = *h++) - ; + for (b = patbuf; b < eb && *h != EOS; b++) { + if ((n = mbtowc(&c, h, PATH_MAX)) > 0) { + h += n; + b->at = 0; + b->wc = c; + } else { + break; + } + } /* Append the rest of the pattern */ - while (b < eb && (*b++ = *p++) != EOS) + while (b < eb && (*b++ = *p++).wc != EOS) ; - *b = EOS; + b->at = 0; + b->wc = EOS; return (patbuf); } static int -g_strncmp(const Char *s1, const char *s2, size_t n) -{ - int rv = 0; - - while (n--) { - rv = *(Char *)s1 - *(const unsigned char *)s2++; - if (rv) - break; - if (*s1++ == '\0') - break; - } - return (rv); -} - -static int g_charclass(const Char **patternp, Char **bufnextp) { const Char *pattern = *patternp + 1; @@ -463,18 +469,31 @@ struct cclass *cc; size_t len; - if ((colon = g_strchr(pattern, ':')) == NULL || colon[1] != ']') + if ((colon = g_strchr(pattern, COLON)) == NULL || + colon[1].wc != RBRACKET) return (1); /* not a character class */ len = (size_t)(colon - pattern); for (cc = cclasses; cc->name != NULL; cc++) { - if (!g_strncmp(pattern, cc->name, len) && cc->name[len] == '\0') + wchar_t w; + const Char *s1 = pattern; + const char *s2 = cc->name; + size_t n = len; + + /* Are the strings the same? */ + while (n > 0 && (w = (s1++)->wc) == *s2 && w != EOS) { + n--; + s2++; + } + if (n == 0 && *s2 == EOS) break; } if (cc->name == NULL) return (-1); /* invalid character class */ - *bufnext++ = M_CLASS; - *bufnext++ = (Char)(cc - &cclasses[0]); + bufnext->at = M_QUOTE; + (bufnext++)->wc = M_CLASS; + bufnext->at = (cc - &cclasses[0]); + (bufnext++)->wc = COLON; *bufnextp = bufnext; *patternp += len + 3; @@ -493,7 +512,9 @@ int (*errfunc)(const char *, int)) { const Char *qpatnext; - int c, err, oldpathc; + int err, oldpathc; + wchar_t c; + int a; Char *bufnext, patbuf[MAXPATHLEN]; qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob); @@ -501,71 +522,112 @@ bufnext = patbuf; /* We don't need to check for buffer overflow any more. */ - while ((c = *qpatnext++) != EOS) { + while ((a = qpatnext->at), (c = (qpatnext++)->wc) != EOS) { switch (c) { case LBRACKET: - c = *qpatnext; - if (c == NOT) + if (a != 0) { + bufnext->at = a; + (bufnext++)->wc = c; + break; + } + a = qpatnext->at; + c = qpatnext->wc; + if (a == 0 && c == NOT) ++qpatnext; - if (*qpatnext == EOS || + if (qpatnext->wc == EOS || g_strchr(qpatnext+1, RBRACKET) == NULL) { - *bufnext++ = LBRACKET; - if (c == NOT) + bufnext->at = 0; + (bufnext++)->wc = LBRACKET; + if (a == 0 && c == NOT) --qpatnext; break; } - *bufnext++ = M_SET; - if (c == NOT) - *bufnext++ = M_NOT; - c = *qpatnext++; + bufnext->at = M_QUOTE; + (bufnext++)->wc = M_SET; + if (a == 0 && c == NOT) { + bufnext->at = M_QUOTE; + (bufnext++)->wc = M_NOT; + } + a = qpatnext->at; + c = (qpatnext++)->wc; do { - if (c == LBRACKET && *qpatnext == ':') { + if (a == 0 && c == LBRACKET && + qpatnext->wc == COLON) { do { err = g_charclass(&qpatnext, &bufnext); if (err) break; - c = *qpatnext++; - } while (c == LBRACKET && - *qpatnext == ':'); + a = qpatnext->at; + c = (qpatnext++)->wc; + } while (a == 0 && c == LBRACKET && + qpatnext->wc == COLON); if (err == -1 && !(pglob->gl_flags & GLOB_NOCHECK)) return (GLOB_NOMATCH); - if (c == RBRACKET) + if (a == 0 && c == RBRACKET) break; } - *bufnext++ = CHAR(c); - if (*qpatnext == RANGE && - (c = qpatnext[1]) != RBRACKET) { - *bufnext++ = M_RNG; - *bufnext++ = CHAR(c); - qpatnext += 2; + bufnext->at = a; + (bufnext++)->wc = c; + if (qpatnext->at == 0 && + qpatnext->wc == RANGE) { + a = qpatnext[1].at; + c = qpatnext[1].wc; + if (qpatnext[1].at != 0 || + qpatnext[1].wc != RBRACKET) { + bufnext->at = M_QUOTE; + (bufnext++)->wc = M_RNG; + bufnext->at = a; + (bufnext++)->wc = c; + qpatnext += 2; + } } - } while ((c = *qpatnext++) != RBRACKET); + a = qpatnext->at; + c = (qpatnext++)->wc; + } while (a != 0 || c != RBRACKET); pglob->gl_flags |= GLOB_MAGCHAR; - *bufnext++ = M_END; + bufnext->at = M_QUOTE; + (bufnext++)->wc = M_END; break; case QUESTION: + if (a != 0) { + bufnext->at = a; + (bufnext++)->wc = c; + break; + } pglob->gl_flags |= GLOB_MAGCHAR; - *bufnext++ = M_ONE; + bufnext->at = M_QUOTE; + (bufnext++)->wc = M_ONE; break; case STAR: + if (a != 0) { + bufnext->at = a; + (bufnext++)->wc = c; + break; + } pglob->gl_flags |= GLOB_MAGCHAR; /* * collapse adjacent stars to one, * to avoid exponential behavior */ - if (bufnext == patbuf || bufnext[-1] != M_ALL) - *bufnext++ = M_ALL; + if (bufnext == patbuf || + bufnext[-1].at != M_QUOTE || + bufnext[-1].wc != M_ALL) { + bufnext->at = M_QUOTE; + (bufnext++)->wc = M_ALL; + } break; default: - *bufnext++ = CHAR(c); + bufnext->at = a; + (bufnext++)->wc = c; break; } } - *bufnext = EOS; + bufnext->at = 0; + bufnext->wc = EOS; #ifdef DEBUG - qprintf("glob0:", patbuf); + qprintf("glob0:glob1:patbuf", patbuf); #endif if ((err = glob1(patbuf, patbuf+MAXPATHLEN-1, pglob, limitp, errfunc)) @@ -638,7 +700,7 @@ Char pathbuf[MAXPATHLEN]; /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ - if (*pattern == EOS) + if (pattern->wc == EOS) return (0); return (glob2(pathbuf, pathbuf+MAXPATHLEN-1, pathbuf, pathbuf+MAXPATHLEN-1, @@ -664,14 +726,17 @@ * segment with meta character found. */ for (anymeta = 0; ; ) { - if (*pattern == EOS) { /* End of pattern? */ - *pathend = EOS; + if (pattern->wc == EOS) { /* End of pattern? */ + pathend->at = 0; + pathend->wc = EOS; if ((pglob->gl_flags & GLOB_LIMIT) && limitp->glim_stat++ >= GLOB_LIMIT_STAT) { errno = 0; - *pathend++ = SEP; - *pathend = EOS; + pathend->at = 0; + (pathend++)->wc = SEP; + pathend->at = 0; + pathend->wc = EOS; return (GLOB_NOSPACE); } if (g_lstat(pathbuf, &sb, pglob)) @@ -678,14 +743,18 @@ return (0); if (((pglob->gl_flags & GLOB_MARK) && - pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) || + (pathend[-1].at != 0 || + pathend[-1].wc != SEP)) && + (S_ISDIR(sb.st_mode) || (S_ISLNK(sb.st_mode) && (g_stat(pathbuf, &sb, pglob) == 0) && S_ISDIR(sb.st_mode)))) { if (pathend+1 > pathend_last) - return (1); - *pathend++ = SEP; - *pathend = EOS; + return (GLOB_NOSPACE); + pathend->at = 0; + (pathend++)->wc = SEP; + pathend->at = 0; + pathend->wc = EOS; } ++pglob->gl_matchc; return (globextend(pathbuf, pglob, limitp, &sb)); @@ -694,11 +763,11 @@ /* Find end of next segment, copy tentatively to pathend. */ q = pathend; p = pattern; - while (*p != EOS && *p != SEP) { + while (p->wc != EOS && p->wc != SEP) { if (ismeta(*p)) anymeta = 1; if (q+1 > pathend_last) - return (1); + return (GLOB_NOSPACE); *q++ = *p++; } @@ -705,16 +774,17 @@ if (!anymeta) { /* No expansion, do next segment. */ pathend = q; pattern = p; - while (*pattern == SEP) { + while (pattern->wc == SEP) { if (pathend+1 > pathend_last) - return (1); + return (GLOB_NOSPACE); *pathend++ = *pattern++; } - } else + } else { /* Need expansion, recurse. */ return (glob3(pathbuf, pathbuf_last, pathend, pathend_last, pattern, p, pattern_last, pglob, limitp, errfunc)); + } } /* NOTREACHED */ } @@ -738,8 +808,9 @@ struct dirent *(*readdirfunc)(void *); if (pathend > pathend_last) - return (1); - *pathend = EOS; + return (GLOB_NOSPACE); + pathend->at = 0; + pathend->wc = EOS; errno = 0; if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { @@ -762,33 +833,56 @@ else readdirfunc = (struct dirent *(*)(void *))readdir; while ((dp = (*readdirfunc)(dirp))) { - uchar_t *sc; + char *sc; Char *dc; + size_t n; + wchar_t w; if ((pglob->gl_flags & GLOB_LIMIT) && limitp->glim_readdir++ >= GLOB_LIMIT_READDIR) { errno = 0; - *pathend++ = SEP; - *pathend = EOS; + pathend->at = 0; + (pathend++)->wc = SEP; + pathend->at = 0; + pathend->wc = EOS; err = GLOB_NOSPACE; break; } /* Initial DOT must be matched literally. */ - if (dp->d_name[0] == DOT && *pattern != DOT) + if (dp->d_name[0] == DOT && pattern->wc != DOT) continue; dc = pathend; - sc = (uchar_t *)dp->d_name; - while (dc < pathend_last && (*dc++ = *sc++) != EOS) - ; + sc = dp->d_name; + while (dc < pathend_last) { + if ((n = mbtowc(&w, sc, MB_LEN_MAX)) <= 0) { + sc += 1; + dc->at = 0; + dc->wc = EOS; + } else { + sc += n; + dc->at = 0; + dc->wc = w; + } + dc++; + if (n <= 0) + break; + } if (dc >= pathend_last) { - *dc = EOS; - err = 1; + dc->at = 0; + dc->wc = EOS; + err = GLOB_NOSPACE; break; } + if (n < 0) { + dc->at = 0; + dc->wc = EOS; + continue; + } if (!match(pathend, pattern, restpattern, GLOB_LIMIT_RECUR)) { - *pathend = EOS; + pathend->at = 0; + pathend->wc = EOS; continue; } err = glob2(pathbuf, pathbuf_last, --dc, pathend_last, @@ -830,6 +924,8 @@ char *copy = NULL; const Char *p; struct stat **statv; + char junk[MB_LEN_MAX]; + int n; newn = 2 + pglob->gl_pathc + pglob->gl_offs; if (pglob->gl_offs >= INT_MAX || @@ -897,9 +993,14 @@ statv[pglob->gl_offs + pglob->gl_pathc + 1] = NULL; } - for (p = path; *p++; ) - ; - len = (size_t)(p - path); + len = MB_LEN_MAX; + p = path; + while ((n = wctomb(junk, p->wc)) > 0) { + len += n; + if ((p++)->wc == EOS) + break; + } + limitp->glim_malloc += len; if ((copy = malloc(len)) != NULL) { if (g_Ctoc(path, copy, len)) { @@ -932,13 +1033,20 @@ Char c, k; if (recur-- == 0) - return (GLOB_NOSPACE); + return (1); while (pat < patend) { c = *pat++; - switch (c & M_MASK) { + switch (c.wc) { case M_ALL: - while (pat < patend && (*pat & M_MASK) == M_ALL) + if (c.at != M_QUOTE) { + k = *name++; + if (k.at != c.at || k.wc != c.wc) + return (0); + break; + } + while (pat < patend && pat->at == M_QUOTE && + pat->wc == M_ALL) pat++; /* eat consecutive '*' */ if (pat == patend) return (1); @@ -945,31 +1053,47 @@ do { if (match(name, pat, patend, recur)) return (1); - } while (*name++ != EOS); + } while ((name++)->wc != EOS); return (0); case M_ONE: - if (*name++ == EOS) + if (c.at != M_QUOTE) { + k = *name++; + if (k.at != c.at || k.wc != c.wc) + return (0); + break; + } + if ((name++)->wc == EOS) return (0); break; case M_SET: + if (c.at != M_QUOTE) { + k = *name++; + if (k.at != c.at || k.wc != c.wc) + return (0); + break; + } ok = 0; - if ((k = *name++) == EOS) + if ((k = *name++).wc == EOS) return (0); - if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS) + if ((negate_range = (pat->at == M_QUOTE && + pat->wc == M_NOT)) != 0) ++pat; - while (((c = *pat++) & M_MASK) != M_END) { - if ((c & M_MASK) == M_CLASS) { - Char idx = *pat & M_MASK; - if (idx < NCCLASSES && - cclasses[idx].isctype(k)) + while (((c = *pat++).at != M_QUOTE) || c.wc != M_END) { + if (c.at == M_QUOTE && c.wc == M_CLASS) { + Char idx; + + idx.at = pat->at; + idx.wc = pat->wc; + if (idx.at < NCCLASSES && + cclasses[idx.at].isctype(k.wc)) ok = 1; ++pat; } - if ((*pat & M_MASK) == M_RNG) { - if (c <= k && k <= pat[1]) + if (pat->at == M_QUOTE && pat->wc == M_RNG) { + if (c.wc <= k.wc && k.wc <= pat[1].wc) ok = 1; pat += 2; - } else if (c == k) + } else if (c.wc == k.wc) ok = 1; } if (ok == negate_range) @@ -976,12 +1100,13 @@ return (0); break; default: - if (*name++ != c) + k = *name++; + if (k.at != c.at || k.wc != c.wc) return (0); break; } } - return (*name == EOS); + return (name->wc == EOS); } /* Free allocated data belonging to a glob_t structure. */ @@ -1015,7 +1140,7 @@ { char buf[MAXPATHLEN]; - if (!*str) + if (str->wc == EOS) strlcpy(buf, ".", sizeof (buf)); else { if (g_Ctoc(str, buf, sizeof (buf))) @@ -1053,12 +1178,12 @@ } static Char * -g_strchr(const Char *str, int ch) +g_strchr(const Char *str, wchar_t ch) { do { - if (*str == ch) + if (str->at == 0 && str->wc == ch) return ((Char *)str); - } while (*str++); + } while ((str++)->wc != EOS); return (NULL); } @@ -1065,9 +1190,18 @@ static int g_Ctoc(const Char *str, char *buf, uint_t len) { + int n; + wchar_t w; - while (len--) { - if ((*buf++ = *str++) == EOS) + while (len >= MB_LEN_MAX) { + w = (str++)->wc; + if ((n = wctomb(buf, w)) > 0) { + len -= n; + buf += n; + } + if (n < 0) + break; + if (w == EOS) return (0); } return (1); @@ -1080,13 +1214,13 @@ Char *p; (void) printf("%s:\n", str); - for (p = s; *p; p++) - (void) printf("%c", CHAR(*p)); + for (p = s; p->wc != EOS; p++) + (void) printf("%wc", p->wc); (void) printf("\n"); - for (p = s; *p; p++) - (void) printf("%c", *p & M_PROTECT ? '"' : ' '); + for (p = s; p->wc != EOS; p++) + (void) printf("%c", p->at & M_PROTECT ? '"' : ' '); (void) printf("\n"); - for (p = s; *p; p++) + for (p = s; p->wc != EOS; p++) (void) printf("%c", ismeta(*p) ? '_' : ' '); (void) printf("\n"); }