140087Sbostic /* 240087Sbostic * Copyright (c) 1989 The Regents of the University of California. 340087Sbostic * All rights reserved. 440087Sbostic * 540087Sbostic * This code is derived from software contributed to Berkeley by 640087Sbostic * Guido van Rossum. 740087Sbostic * 842625Sbostic * %sccs.include.redist.c% 940087Sbostic */ 1040087Sbostic 1140087Sbostic #if defined(LIBC_SCCS) && !defined(lint) 12*56373Sbostic static char sccsid[] = "@(#)glob.c 5.16 (Berkeley) 10/01/92"; 1340087Sbostic #endif /* LIBC_SCCS and not lint */ 1440087Sbostic 1540087Sbostic /* 1650050Sbostic * glob(3) -- a superset of the one defined in POSIX 1003.2. 1740087Sbostic * 1840087Sbostic * The [!...] convention to negate a range is supported (SysV, Posix, ksh). 1940087Sbostic * 2040087Sbostic * Optional extra services, controlled by flags not defined by POSIX: 2147590Sbostic * 2247590Sbostic * GLOB_QUOTE: 2347590Sbostic * Escaping convention: \ inhibits any special meaning the following 2447590Sbostic * character might have (except \ at end of string is retained). 2547590Sbostic * GLOB_MAGCHAR: 2648540Sbostic * Set in gl_flags if pattern contained a globbing character. 2750420Sbostic * GLOB_NOMAGIC: 2850420Sbostic * Same as GLOB_NOCHECK, but it will only append pattern if it did 2950420Sbostic * not contain any magic characters. [Used in csh style globbing] 3047590Sbostic * gl_matchc: 3147590Sbostic * Number of matches in the current invocation of glob. 3240087Sbostic */ 3340087Sbostic 3440087Sbostic #include <sys/param.h> 3540087Sbostic #include <sys/stat.h> 3640087Sbostic #include <dirent.h> 3740087Sbostic #include <glob.h> 3840087Sbostic #include <ctype.h> 3940087Sbostic #include <errno.h> 4040087Sbostic #include <string.h> 4140087Sbostic #include <stdio.h> 4246597Sdonn #include <stdlib.h> 4340087Sbostic 4440087Sbostic #define DOLLAR '$' 4540087Sbostic #define DOT '.' 4640087Sbostic #define EOS '\0' 4740087Sbostic #define LBRACKET '[' 4840087Sbostic #define NOT '!' 4940087Sbostic #define QUESTION '?' 5040087Sbostic #define QUOTE '\\' 5140087Sbostic #define RANGE '-' 5240087Sbostic #define RBRACKET ']' 5340087Sbostic #define SEP '/' 5440087Sbostic #define STAR '*' 5540087Sbostic #define TILDE '~' 5640087Sbostic #define UNDERSCORE '_' 5740087Sbostic 5850049Sbostic #define M_QUOTE 0x8000 5950049Sbostic #define M_PROTECT 0x4000 6048540Sbostic #define M_MASK 0xffff 6150121Sbostic #define M_ASCII 0x00ff 6248540Sbostic 6350121Sbostic #define CHAR(c) ((c)&M_ASCII) 6450049Sbostic #define META(c) ((c)|M_QUOTE) 6540087Sbostic #define M_ALL META('*') 6640087Sbostic #define M_END META(']') 6740087Sbostic #define M_NOT META('!') 6840087Sbostic #define M_ONE META('?') 6940087Sbostic #define M_RNG META('-') 7040087Sbostic #define M_SET META('[') 7150049Sbostic #define ismeta(c) (((c)&M_QUOTE) != 0) 7240087Sbostic 7350050Sbostic typedef u_short Char; 7450049Sbostic 7550050Sbostic static int compare __P((const void *, const void *)); 7650050Sbostic static void g_Ctoc __P((Char *, char *)); 7750050Sbostic static int g_lstat __P((Char *, struct stat *)); 7850050Sbostic static DIR *g_opendir __P((Char *)); 7950050Sbostic static Char *g_strchr __P((Char *, int)); 8050050Sbostic static int g_stat __P((Char *, struct stat *)); 8150050Sbostic static int glob1 __P((Char *, glob_t *)); 8250050Sbostic static int glob2 __P((Char *, Char *, Char *, glob_t *)); 8350050Sbostic static int glob3 __P((Char *, Char *, Char *, Char *, glob_t *)); 8450050Sbostic static int globextend __P((Char *, glob_t *)); 8550050Sbostic static int match __P((Char *, Char *, Char *)); 8650049Sbostic #ifdef DEBUG 8750050Sbostic static void qprintf __P((Char *)); 8850049Sbostic #endif 8950049Sbostic 9040087Sbostic /* 9140087Sbostic * The main glob() routine: compiles the pattern (optionally processing 9240087Sbostic * quotes), calls glob1() to do the real pattern matching, and finally 9340087Sbostic * sorts the list (unless unsorted operation is requested). Returns 0 9440087Sbostic * if things went well, nonzero if errors occurred. It is not an error 9540087Sbostic * to find no matches. 9640087Sbostic */ 9740087Sbostic glob(pattern, flags, errfunc, pglob) 9846597Sdonn const char *pattern; 9950050Sbostic int flags, (*errfunc) __P((char *, int)); 10040087Sbostic glob_t *pglob; 10140087Sbostic { 10250121Sbostic const u_char *compilepat, *patnext; 10350117Sbostic int c, err, oldpathc; 10450050Sbostic Char *bufnext, *bufend, *compilebuf, *qpatnext, patbuf[MAXPATHLEN+1]; 10540087Sbostic 10650121Sbostic patnext = (u_char *) pattern; 10740087Sbostic if (!(flags & GLOB_APPEND)) { 10840087Sbostic pglob->gl_pathc = 0; 10940087Sbostic pglob->gl_pathv = NULL; 11040087Sbostic if (!(flags & GLOB_DOOFFS)) 11140087Sbostic pglob->gl_offs = 0; 11240087Sbostic } 11347590Sbostic pglob->gl_flags = flags & ~GLOB_MAGCHAR; 11440087Sbostic pglob->gl_errfunc = errfunc; 11540087Sbostic oldpathc = pglob->gl_pathc; 11647590Sbostic pglob->gl_matchc = 0; 11740087Sbostic 11840087Sbostic bufnext = patbuf; 11950049Sbostic bufend = bufnext + MAXPATHLEN; 12050050Sbostic compilebuf = bufnext; 12150050Sbostic compilepat = patnext; 12250049Sbostic if (flags & GLOB_QUOTE) { 12350050Sbostic /* Protect the quoted characters. */ 12450049Sbostic while (bufnext < bufend && (c = *patnext++) != EOS) 12550049Sbostic if (c == QUOTE) { 12650049Sbostic if ((c = *patnext++) == EOS) { 12750049Sbostic c = QUOTE; 12850049Sbostic --patnext; 12950049Sbostic } 13050049Sbostic *bufnext++ = c | M_PROTECT; 13150049Sbostic } 13250049Sbostic else 13350049Sbostic *bufnext++ = c; 13450049Sbostic } 13550049Sbostic else 13650049Sbostic while (bufnext < bufend && (c = *patnext++) != EOS) 13750049Sbostic *bufnext++ = c; 13850049Sbostic *bufnext = EOS; 13940087Sbostic 14050049Sbostic bufnext = patbuf; 14150049Sbostic qpatnext = patbuf; 14250050Sbostic /* We don't need to check for buffer overflow any more. */ 14350049Sbostic while ((c = *qpatnext++) != EOS) { 14440087Sbostic switch (c) { 14540087Sbostic case LBRACKET: 14650049Sbostic c = *qpatnext; 14740087Sbostic if (c == NOT) 14850049Sbostic ++qpatnext; 14950049Sbostic if (*qpatnext == EOS || 15050050Sbostic g_strchr(qpatnext+1, RBRACKET) == NULL) { 15140087Sbostic *bufnext++ = LBRACKET; 15240087Sbostic if (c == NOT) 15350049Sbostic --qpatnext; 15440087Sbostic break; 15540087Sbostic } 15640087Sbostic *bufnext++ = M_SET; 15740087Sbostic if (c == NOT) 15840087Sbostic *bufnext++ = M_NOT; 15950049Sbostic c = *qpatnext++; 16040087Sbostic do { 16150121Sbostic *bufnext++ = CHAR(c); 16250049Sbostic if (*qpatnext == RANGE && 16350049Sbostic (c = qpatnext[1]) != RBRACKET) { 16440087Sbostic *bufnext++ = M_RNG; 16550121Sbostic *bufnext++ = CHAR(c); 16650049Sbostic qpatnext += 2; 16740087Sbostic } 16850049Sbostic } while ((c = *qpatnext++) != RBRACKET); 16950420Sbostic pglob->gl_flags |= GLOB_MAGCHAR; 17040087Sbostic *bufnext++ = M_END; 17140087Sbostic break; 17240087Sbostic case QUESTION: 17347590Sbostic pglob->gl_flags |= GLOB_MAGCHAR; 17440087Sbostic *bufnext++ = M_ONE; 17540087Sbostic break; 17640087Sbostic case STAR: 17747590Sbostic pglob->gl_flags |= GLOB_MAGCHAR; 178*56373Sbostic /* collapse adjacent stars to one, 179*56373Sbostic * to avoid exponential behavior 180*56373Sbostic */ 181*56373Sbostic if (bufnext == patbuf || bufnext[-1] != M_ALL) 182*56373Sbostic *bufnext++ = M_ALL; 18340087Sbostic break; 18440087Sbostic default: 18550121Sbostic *bufnext++ = CHAR(c); 18640087Sbostic break; 18740087Sbostic } 18840087Sbostic } 18940087Sbostic *bufnext = EOS; 19050049Sbostic #ifdef DEBUG 19150049Sbostic qprintf(patbuf); 19250049Sbostic #endif 19340087Sbostic 19440087Sbostic if ((err = glob1(patbuf, pglob)) != 0) 19540087Sbostic return(err); 19640087Sbostic 19750420Sbostic /* 19850420Sbostic * If there was no match we are going to append the pattern 19950420Sbostic * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified 20050420Sbostic * and the pattern did not contain any magic characters 20150420Sbostic * GLOB_NOMAGIC is there just for compatibility with csh. 20250420Sbostic */ 20350420Sbostic if (pglob->gl_pathc == oldpathc && 20450420Sbostic ((flags & GLOB_NOCHECK) || 20550420Sbostic ((flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR)))) { 20648540Sbostic if (!(flags & GLOB_QUOTE)) { 20750050Sbostic Char *dp = compilebuf; 20850121Sbostic const u_char *sp = compilepat; 20950121Sbostic while (*dp++ = *sp++); 21048540Sbostic } 21140087Sbostic else { 21240087Sbostic /* 21350050Sbostic * Copy pattern, interpreting quotes; this is slightly 21440087Sbostic * different than the interpretation of quotes above 21540087Sbostic * -- which should prevail? 21640087Sbostic */ 21740087Sbostic while (*compilepat != EOS) { 21840087Sbostic if (*compilepat == QUOTE) { 21940087Sbostic if (*++compilepat == EOS) 22040087Sbostic --compilepat; 22140087Sbostic } 22248540Sbostic *compilebuf++ = (u_char)*compilepat++; 22340087Sbostic } 22440087Sbostic *compilebuf = EOS; 22540087Sbostic } 22640087Sbostic return(globextend(patbuf, pglob)); 22748540Sbostic } else if (!(flags & GLOB_NOSORT)) 22850050Sbostic qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, 22950050Sbostic pglob->gl_pathc - oldpathc, sizeof(char *), compare); 23040087Sbostic return(0); 23140087Sbostic } 23240087Sbostic 23350050Sbostic static int 23450050Sbostic compare(p, q) 23550050Sbostic const void *p, *q; 23650050Sbostic { 23750050Sbostic return(strcmp(*(char **)p, *(char **)q)); 23850050Sbostic } 23950050Sbostic 24040087Sbostic static 24140087Sbostic glob1(pattern, pglob) 24250050Sbostic Char *pattern; 24340087Sbostic glob_t *pglob; 24440087Sbostic { 24550050Sbostic Char pathbuf[MAXPATHLEN+1]; 24640087Sbostic 24750050Sbostic /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ 24840087Sbostic if (*pattern == EOS) 24940087Sbostic return(0); 25040087Sbostic return(glob2(pathbuf, pathbuf, pattern, pglob)); 25140087Sbostic } 25240087Sbostic 25340087Sbostic /* 25450050Sbostic * The functions glob2 and glob3 are mutually recursive; there is one level 25550050Sbostic * of recursion for each segment in the pattern that contains one or more 25650050Sbostic * meta characters. 25740087Sbostic */ 25840087Sbostic static 25940087Sbostic glob2(pathbuf, pathend, pattern, pglob) 26050050Sbostic Char *pathbuf, *pathend, *pattern; 26140087Sbostic glob_t *pglob; 26240087Sbostic { 26350050Sbostic struct stat sb; 26450050Sbostic Char *p, *q; 26548540Sbostic int anymeta; 26640087Sbostic 26740087Sbostic /* 26850050Sbostic * Loop over pattern segments until end of pattern or until 26940087Sbostic * segment with meta character found. 27040087Sbostic */ 27148357Sbostic for (anymeta = 0;;) { 27250050Sbostic if (*pattern == EOS) { /* End of pattern? */ 27340087Sbostic *pathend = EOS; 27452361Sbostic if (g_lstat(pathbuf, &sb)) 27548357Sbostic return(0); 27648540Sbostic 27748540Sbostic if (((pglob->gl_flags & GLOB_MARK) && 27850050Sbostic pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) 27950050Sbostic || (S_ISLNK(sb.st_mode) && 28050050Sbostic (g_stat(pathbuf, &sb) == 0) && 28150050Sbostic S_ISDIR(sb.st_mode)))) { 28240087Sbostic *pathend++ = SEP; 28340087Sbostic *pathend = EOS; 28440087Sbostic } 28547590Sbostic ++pglob->gl_matchc; 28640087Sbostic return(globextend(pathbuf, pglob)); 28740087Sbostic } 28840087Sbostic 28950050Sbostic /* Find end of next segment, copy tentatively to pathend. */ 29040087Sbostic q = pathend; 29140087Sbostic p = pattern; 29240087Sbostic while (*p != EOS && *p != SEP) { 29340087Sbostic if (ismeta(*p)) 29440087Sbostic anymeta = 1; 29540087Sbostic *q++ = *p++; 29640087Sbostic } 29740087Sbostic 29850050Sbostic if (!anymeta) { /* No expansion, do next segment. */ 29940087Sbostic pathend = q; 30040087Sbostic pattern = p; 30140087Sbostic while (*pattern == SEP) 30240087Sbostic *pathend++ = *pattern++; 30350050Sbostic } else /* Need expansion, recurse. */ 30440087Sbostic return(glob3(pathbuf, pathend, pattern, p, pglob)); 30540087Sbostic } 30640087Sbostic /* NOTREACHED */ 30740087Sbostic } 30840087Sbostic 30940087Sbostic static 31040087Sbostic glob3(pathbuf, pathend, pattern, restpattern, pglob) 31150050Sbostic Char *pathbuf, *pathend, *pattern, *restpattern; 31240087Sbostic glob_t *pglob; 31340087Sbostic { 31450050Sbostic register struct dirent *dp; 31540087Sbostic DIR *dirp; 31640087Sbostic int len, err; 31740087Sbostic 31840087Sbostic *pathend = EOS; 31940087Sbostic errno = 0; 32048540Sbostic 32150050Sbostic if (!(dirp = g_opendir(pathbuf))) 32250050Sbostic /* TODO: don't call for ENOENT or ENOTDIR? */ 32340087Sbostic if (pglob->gl_errfunc && 32440087Sbostic (*pglob->gl_errfunc)(pathbuf, errno) || 32540087Sbostic (pglob->gl_flags & GLOB_ERR)) 32640087Sbostic return(GLOB_ABEND); 32740087Sbostic else 32840087Sbostic return(0); 32940087Sbostic 33040087Sbostic err = 0; 33140087Sbostic 33250050Sbostic /* Search directory for matching names. */ 33340087Sbostic while ((dp = readdir(dirp))) { 33450121Sbostic register u_char *sc; 33550050Sbostic register Char *dc; 33650050Sbostic 33750050Sbostic /* Initial DOT must be matched literally. */ 33840087Sbostic if (dp->d_name[0] == DOT && *pattern != DOT) 33940087Sbostic continue; 34050121Sbostic for (sc = (u_char *) dp->d_name, dc = pathend; 34150121Sbostic *dc++ = *sc++;); 34248540Sbostic if (!match(pathend, pattern, restpattern)) { 34348540Sbostic *pathend = EOS; 34440087Sbostic continue; 34548540Sbostic } 34648540Sbostic err = glob2(pathbuf, --dc, restpattern, pglob); 34740087Sbostic if (err) 34840087Sbostic break; 34940087Sbostic } 35050050Sbostic 35150050Sbostic /* TODO: check error from readdir? */ 35240087Sbostic (void)closedir(dirp); 35340087Sbostic return(err); 35440087Sbostic } 35540087Sbostic 35640087Sbostic 35740087Sbostic /* 35840087Sbostic * Extend the gl_pathv member of a glob_t structure to accomodate a new item, 35940087Sbostic * add the new item, and update gl_pathc. 36040087Sbostic * 36140087Sbostic * This assumes the BSD realloc, which only copies the block when its size 36240087Sbostic * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic 36340087Sbostic * behavior. 36440087Sbostic * 36540087Sbostic * Return 0 if new item added, error code if memory couldn't be allocated. 36640087Sbostic * 36740087Sbostic * Invariant of the glob_t structure: 36840087Sbostic * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and 36950050Sbostic * gl_pathv points to (gl_offs + gl_pathc + 1) items. 37040087Sbostic */ 37148540Sbostic static int 37240087Sbostic globextend(path, pglob) 37350050Sbostic Char *path; 37440087Sbostic glob_t *pglob; 37540087Sbostic { 37640087Sbostic register char **pathv; 37740087Sbostic register int i; 37848540Sbostic u_int newsize; 37940087Sbostic char *copy; 38050050Sbostic Char *p; 38140087Sbostic 38240087Sbostic newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); 38348540Sbostic pathv = (char **)realloc((char *)pglob->gl_pathv, newsize); 38440087Sbostic if (pathv == NULL) 38540087Sbostic return(GLOB_NOSPACE); 38640087Sbostic 38740087Sbostic if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { 38840087Sbostic /* first time around -- clear initial gl_offs items */ 38940087Sbostic pathv += pglob->gl_offs; 39040087Sbostic for (i = pglob->gl_offs; --i >= 0; ) 39140087Sbostic *--pathv = NULL; 39240087Sbostic } 39340087Sbostic pglob->gl_pathv = pathv; 39440087Sbostic 39548540Sbostic for (p = path; *p++;); 39648540Sbostic if ((copy = malloc(p - path)) != NULL) { 39750050Sbostic g_Ctoc(path, copy); 39840087Sbostic pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; 39940087Sbostic } 40040087Sbostic pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; 40150050Sbostic return(copy == NULL ? GLOB_NOSPACE : 0); 40240087Sbostic } 40340087Sbostic 40440087Sbostic 40540087Sbostic /* 40640087Sbostic * pattern matching function for filenames. Each occurrence of the * 40740087Sbostic * pattern causes a recursion level. 40840087Sbostic */ 40948540Sbostic static 41040087Sbostic match(name, pat, patend) 41150050Sbostic register Char *name, *pat, *patend; 41240087Sbostic { 41348540Sbostic int ok, negate_range; 41450050Sbostic Char c, k; 41540087Sbostic 41640087Sbostic while (pat < patend) { 41740087Sbostic c = *pat++; 41848540Sbostic switch (c & M_MASK) { 41940087Sbostic case M_ALL: 42040087Sbostic if (pat == patend) 42140087Sbostic return(1); 422*56373Sbostic do 423*56373Sbostic if (match(name, pat, patend)) 424*56373Sbostic return(1); 425*56373Sbostic while (*name++ != EOS); 42640087Sbostic return(0); 42740087Sbostic case M_ONE: 42840087Sbostic if (*name++ == EOS) 42940087Sbostic return(0); 43040087Sbostic break; 43140087Sbostic case M_SET: 43240087Sbostic ok = 0; 43352339Sbostic if ((k = *name++) == EOS) 43452339Sbostic return(0); 43548540Sbostic if (negate_range = ((*pat & M_MASK) == M_NOT)) 43640087Sbostic ++pat; 43750050Sbostic while (((c = *pat++) & M_MASK) != M_END) 43848540Sbostic if ((*pat & M_MASK) == M_RNG) { 43940087Sbostic if (c <= k && k <= pat[1]) 44040087Sbostic ok = 1; 44140087Sbostic pat += 2; 44250050Sbostic } else if (c == k) 44340087Sbostic ok = 1; 44440087Sbostic if (ok == negate_range) 44540087Sbostic return(0); 44640087Sbostic break; 44740087Sbostic default: 44840087Sbostic if (*name++ != c) 44940087Sbostic return(0); 45040087Sbostic break; 45140087Sbostic } 45240087Sbostic } 45340087Sbostic return(*name == EOS); 45440087Sbostic } 45540087Sbostic 45650050Sbostic /* Free allocated data belonging to a glob_t structure. */ 45740087Sbostic void 45840087Sbostic globfree(pglob) 45940087Sbostic glob_t *pglob; 46040087Sbostic { 46140087Sbostic register int i; 46240087Sbostic register char **pp; 46340087Sbostic 46440087Sbostic if (pglob->gl_pathv != NULL) { 46540087Sbostic pp = pglob->gl_pathv + pglob->gl_offs; 46640087Sbostic for (i = pglob->gl_pathc; i--; ++pp) 46740087Sbostic if (*pp) 46850050Sbostic free(*pp); 46950049Sbostic free(pglob->gl_pathv); 47040087Sbostic } 47140087Sbostic } 47250050Sbostic 47350050Sbostic static DIR * 47450050Sbostic g_opendir(str) 47550050Sbostic register Char *str; 47650050Sbostic { 47750050Sbostic char buf[MAXPATHLEN]; 47850050Sbostic 47950050Sbostic if (!*str) 48050050Sbostic return(opendir(".")); 48150050Sbostic g_Ctoc(str, buf); 48250050Sbostic return(opendir(buf)); 48350050Sbostic } 48450050Sbostic 48550050Sbostic static int 48650050Sbostic g_lstat(fn, sb) 48750050Sbostic register Char *fn; 48850050Sbostic struct stat *sb; 48950050Sbostic { 49050050Sbostic char buf[MAXPATHLEN]; 49150050Sbostic 49250050Sbostic g_Ctoc(fn, buf); 49350050Sbostic return(lstat(buf, sb)); 49450050Sbostic } 49550050Sbostic 49650050Sbostic static int 49750050Sbostic g_stat(fn, sb) 49850050Sbostic register Char *fn; 49950050Sbostic struct stat *sb; 50050050Sbostic { 50150050Sbostic char buf[MAXPATHLEN]; 50250050Sbostic 50350050Sbostic g_Ctoc(fn, buf); 50450050Sbostic return(stat(buf, sb)); 50550050Sbostic } 50650050Sbostic 50750050Sbostic static Char * 50850050Sbostic g_strchr(str, ch) 50950050Sbostic Char *str; 51050050Sbostic int ch; 51150050Sbostic { 51250050Sbostic do { 51350050Sbostic if (*str == ch) 51450050Sbostic return (str); 51550050Sbostic } while (*str++); 51650050Sbostic return (NULL); 51750050Sbostic } 51850050Sbostic 51950050Sbostic static void 52050050Sbostic g_Ctoc(str, buf) 52150050Sbostic register Char *str; 52250050Sbostic char *buf; 52350050Sbostic { 52450050Sbostic register char *dc; 52550050Sbostic 52650050Sbostic for (dc = buf; *dc++ = *str++;); 52750050Sbostic } 52850050Sbostic 52950050Sbostic #ifdef DEBUG 53050050Sbostic static void 53150050Sbostic qprintf(s) 53250050Sbostic register Char *s; 53350050Sbostic { 53450050Sbostic register Char *p; 53550050Sbostic 53650050Sbostic for (p = s; *p; p++) 53750050Sbostic (void)printf("%c", *p & 0xff); 53850050Sbostic (void)printf("\n"); 53950050Sbostic for (p = s; *p; p++) 54050050Sbostic (void)printf("%c", *p & M_PROTECT ? '"' : ' '); 54150050Sbostic (void)printf("\n"); 54250050Sbostic for (p = s; *p; p++) 54350050Sbostic (void)printf("%c", *p & M_META ? '_' : ' '); 54450050Sbostic (void)printf("\n"); 54550050Sbostic } 54650050Sbostic #endif 547