1 /*
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Guido van Rossum.
7 *
8 * %sccs.include.redist.c%
9 */
10
11 #if defined(LIBC_SCCS) && !defined(lint)
12 static char sccsid[] = "@(#)glob.c 8.3 (Berkeley) 10/13/93";
13 #endif /* LIBC_SCCS and not lint */
14
15 /*
16 * glob(3) -- a superset of the one defined in POSIX 1003.2.
17 *
18 * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
19 *
20 * Optional extra services, controlled by flags not defined by POSIX:
21 *
22 * GLOB_QUOTE:
23 * Escaping convention: \ inhibits any special meaning the following
24 * character might have (except \ at end of string is retained).
25 * GLOB_MAGCHAR:
26 * Set in gl_flags if pattern contained a globbing character.
27 * GLOB_NOMAGIC:
28 * Same as GLOB_NOCHECK, but it will only append pattern if it did
29 * not contain any magic characters. [Used in csh style globbing]
30 * GLOB_ALTDIRFUNC:
31 * Use alternately specified directory access functions.
32 * GLOB_TILDE:
33 * expand ~user/foo to the /home/dir/of/user/foo
34 * GLOB_BRACE:
35 * expand {1,2}{a,b} to 1a 1b 2a 2b
36 * gl_matchc:
37 * Number of matches in the current invocation of glob.
38 */
39
40 #include <sys/param.h>
41 #include <sys/stat.h>
42
43 #include <ctype.h>
44 #include <dirent.h>
45 #include <errno.h>
46 #include <glob.h>
47 #include <pwd.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52
53 #define DOLLAR '$'
54 #define DOT '.'
55 #define EOS '\0'
56 #define LBRACKET '['
57 #define NOT '!'
58 #define QUESTION '?'
59 #define QUOTE '\\'
60 #define RANGE '-'
61 #define RBRACKET ']'
62 #define SEP '/'
63 #define STAR '*'
64 #define TILDE '~'
65 #define UNDERSCORE '_'
66 #define LBRACE '{'
67 #define RBRACE '}'
68 #define SLASH '/'
69 #define COMMA ','
70
71 #ifndef DEBUG
72
73 #define M_QUOTE 0x8000
74 #define M_PROTECT 0x4000
75 #define M_MASK 0xffff
76 #define M_ASCII 0x00ff
77
78 typedef u_short Char;
79
80 #else
81
82 #define M_QUOTE 0x80
83 #define M_PROTECT 0x40
84 #define M_MASK 0xff
85 #define M_ASCII 0x7f
86
87 typedef char Char;
88
89 #endif
90
91
92 #define CHAR(c) ((Char)((c)&M_ASCII))
93 #define META(c) ((Char)((c)|M_QUOTE))
94 #define M_ALL META('*')
95 #define M_END META(']')
96 #define M_NOT META('!')
97 #define M_ONE META('?')
98 #define M_RNG META('-')
99 #define M_SET META('[')
100 #define ismeta(c) (((c)&M_QUOTE) != 0)
101
102
103 static int compare __P((const void *, const void *));
104 static void g_Ctoc __P((const Char *, char *));
105 static int g_lstat __P((Char *, struct stat *, glob_t *));
106 static DIR *g_opendir __P((Char *, glob_t *));
107 static Char *g_strchr __P((Char *, int));
108 #ifdef notdef
109 static Char *g_strcat __P((Char *, const Char *));
110 #endif
111 static int g_stat __P((Char *, struct stat *, glob_t *));
112 static int glob0 __P((const Char *, glob_t *));
113 static int glob1 __P((Char *, glob_t *));
114 static int glob2 __P((Char *, Char *, Char *, glob_t *));
115 static int glob3 __P((Char *, Char *, Char *, Char *, glob_t *));
116 static int globextend __P((const Char *, glob_t *));
117 static const Char * globtilde __P((const Char *, Char *, glob_t *));
118 static int globexp1 __P((const Char *, glob_t *));
119 static int globexp2 __P((const Char *, const Char *, glob_t *, int *));
120 static int match __P((Char *, Char *, Char *));
121 #ifdef DEBUG
122 static void qprintf __P((const char *, Char *));
123 #endif
124
125 int
glob(pattern,flags,errfunc,pglob)126 glob(pattern, flags, errfunc, pglob)
127 const char *pattern;
128 int flags, (*errfunc) __P((const char *, int));
129 glob_t *pglob;
130 {
131 const u_char *patnext;
132 int c;
133 Char *bufnext, *bufend, patbuf[MAXPATHLEN+1];
134
135 patnext = (u_char *) pattern;
136 if (!(flags & GLOB_APPEND)) {
137 pglob->gl_pathc = 0;
138 pglob->gl_pathv = NULL;
139 if (!(flags & GLOB_DOOFFS))
140 pglob->gl_offs = 0;
141 }
142 pglob->gl_flags = flags & ~GLOB_MAGCHAR;
143 pglob->gl_errfunc = errfunc;
144 pglob->gl_matchc = 0;
145
146 bufnext = patbuf;
147 bufend = bufnext + MAXPATHLEN;
148 if (flags & GLOB_QUOTE) {
149 /* Protect the quoted characters. */
150 while (bufnext < bufend && (c = *patnext++) != EOS)
151 if (c == QUOTE) {
152 if ((c = *patnext++) == EOS) {
153 c = QUOTE;
154 --patnext;
155 }
156 *bufnext++ = c | M_PROTECT;
157 }
158 else
159 *bufnext++ = c;
160 }
161 else
162 while (bufnext < bufend && (c = *patnext++) != EOS)
163 *bufnext++ = c;
164 *bufnext = EOS;
165
166 if (flags & GLOB_BRACE)
167 return globexp1(patbuf, pglob);
168 else
169 return glob0(patbuf, pglob);
170 }
171
172 /*
173 * Expand recursively a glob {} pattern. When there is no more expansion
174 * invoke the standard globbing routine to glob the rest of the magic
175 * characters
176 */
globexp1(pattern,pglob)177 static int globexp1(pattern, pglob)
178 const Char *pattern;
179 glob_t *pglob;
180 {
181 const Char* ptr = pattern;
182 int rv;
183
184 /* Protect a single {}, for find(1), like csh */
185 if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
186 return glob0(pattern, pglob);
187
188 while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL)
189 if (!globexp2(ptr, pattern, pglob, &rv))
190 return rv;
191
192 return glob0(pattern, pglob);
193 }
194
195
196 /*
197 * Recursive brace globbing helper. Tries to expand a single brace.
198 * If it succeeds then it invokes globexp1 with the new pattern.
199 * If it fails then it tries to glob the rest of the pattern and returns.
200 */
globexp2(ptr,pattern,pglob,rv)201 static int globexp2(ptr, pattern, pglob, rv)
202 const Char *ptr, *pattern;
203 glob_t *pglob;
204 int *rv;
205 {
206 int i;
207 Char *lm, *ls;
208 const Char *pe, *pm, *pl;
209 Char patbuf[MAXPATHLEN + 1];
210
211 /* copy part up to the brace */
212 for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
213 continue;
214 ls = lm;
215
216 /* Find the balanced brace */
217 for (i = 0, pe = ++ptr; *pe; pe++)
218 if (*pe == LBRACKET) {
219 /* Ignore everything between [] */
220 for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
221 continue;
222 if (*pe == EOS) {
223 /*
224 * We could not find a matching RBRACKET.
225 * Ignore and just look for RBRACE
226 */
227 pe = pm;
228 }
229 }
230 else if (*pe == LBRACE)
231 i++;
232 else if (*pe == RBRACE) {
233 if (i == 0)
234 break;
235 i--;
236 }
237
238 /* Non matching braces; just glob the pattern */
239 if (i != 0 || *pe == EOS) {
240 *rv = glob0(patbuf, pglob);
241 return 0;
242 }
243
244 for (i = 0, pl = pm = ptr; pm <= pe; pm++)
245 switch (*pm) {
246 case LBRACKET:
247 /* Ignore everything between [] */
248 for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
249 continue;
250 if (*pm == EOS) {
251 /*
252 * We could not find a matching RBRACKET.
253 * Ignore and just look for RBRACE
254 */
255 pm = pl;
256 }
257 break;
258
259 case LBRACE:
260 i++;
261 break;
262
263 case RBRACE:
264 if (i) {
265 i--;
266 break;
267 }
268 /* FALLTHROUGH */
269 case COMMA:
270 if (i && *pm == COMMA)
271 break;
272 else {
273 /* Append the current string */
274 for (lm = ls; (pl < pm); *lm++ = *pl++)
275 continue;
276 /*
277 * Append the rest of the pattern after the
278 * closing brace
279 */
280 for (pl = pe + 1; (*lm++ = *pl++) != EOS;)
281 continue;
282
283 /* Expand the current pattern */
284 #ifdef DEBUG
285 qprintf("globexp2:", patbuf);
286 #endif
287 *rv = globexp1(patbuf, pglob);
288
289 /* move after the comma, to the next string */
290 pl = pm + 1;
291 }
292 break;
293
294 default:
295 break;
296 }
297 *rv = 0;
298 return 0;
299 }
300
301
302
303 /*
304 * expand tilde from the passwd file.
305 */
306 static const Char *
globtilde(pattern,patbuf,pglob)307 globtilde(pattern, patbuf, pglob)
308 const Char *pattern;
309 Char *patbuf;
310 glob_t *pglob;
311 {
312 struct passwd *pwd;
313 char *h;
314 const Char *p;
315 Char *b;
316
317 if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE))
318 return pattern;
319
320 /* Copy up to the end of the string or / */
321 for (p = pattern + 1, h = (char *) patbuf; *p && *p != SLASH;
322 *h++ = *p++)
323 continue;
324
325 *h = EOS;
326
327 if (((char *) patbuf)[0] == EOS) {
328 /*
329 * handle a plain ~ or ~/ by expanding $HOME
330 * first and then trying the password file
331 */
332 if ((h = getenv("HOME")) == NULL) {
333 if ((pwd = getpwuid(getuid())) == NULL)
334 return pattern;
335 else
336 h = pwd->pw_dir;
337 }
338 }
339 else {
340 /*
341 * Expand a ~user
342 */
343 if ((pwd = getpwnam((char*) patbuf)) == NULL)
344 return pattern;
345 else
346 h = pwd->pw_dir;
347 }
348
349 /* Copy the home directory */
350 for (b = patbuf; *h; *b++ = *h++)
351 continue;
352
353 /* Append the rest of the pattern */
354 while ((*b++ = *p++) != EOS)
355 continue;
356
357 return patbuf;
358 }
359
360
361 /*
362 * The main glob() routine: compiles the pattern (optionally processing
363 * quotes), calls glob1() to do the real pattern matching, and finally
364 * sorts the list (unless unsorted operation is requested). Returns 0
365 * if things went well, nonzero if errors occurred. It is not an error
366 * to find no matches.
367 */
368 static int
glob0(pattern,pglob)369 glob0(pattern, pglob)
370 const Char *pattern;
371 glob_t *pglob;
372 {
373 const Char *qpatnext;
374 int c, err, oldpathc;
375 Char *bufnext, patbuf[MAXPATHLEN+1];
376
377 qpatnext = globtilde(pattern, patbuf, pglob);
378 oldpathc = pglob->gl_pathc;
379 bufnext = patbuf;
380
381 /* We don't need to check for buffer overflow any more. */
382 while ((c = *qpatnext++) != EOS) {
383 switch (c) {
384 case LBRACKET:
385 c = *qpatnext;
386 if (c == NOT)
387 ++qpatnext;
388 if (*qpatnext == EOS ||
389 g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) {
390 *bufnext++ = LBRACKET;
391 if (c == NOT)
392 --qpatnext;
393 break;
394 }
395 *bufnext++ = M_SET;
396 if (c == NOT)
397 *bufnext++ = M_NOT;
398 c = *qpatnext++;
399 do {
400 *bufnext++ = CHAR(c);
401 if (*qpatnext == RANGE &&
402 (c = qpatnext[1]) != RBRACKET) {
403 *bufnext++ = M_RNG;
404 *bufnext++ = CHAR(c);
405 qpatnext += 2;
406 }
407 } while ((c = *qpatnext++) != RBRACKET);
408 pglob->gl_flags |= GLOB_MAGCHAR;
409 *bufnext++ = M_END;
410 break;
411 case QUESTION:
412 pglob->gl_flags |= GLOB_MAGCHAR;
413 *bufnext++ = M_ONE;
414 break;
415 case STAR:
416 pglob->gl_flags |= GLOB_MAGCHAR;
417 /* collapse adjacent stars to one,
418 * to avoid exponential behavior
419 */
420 if (bufnext == patbuf || bufnext[-1] != M_ALL)
421 *bufnext++ = M_ALL;
422 break;
423 default:
424 *bufnext++ = CHAR(c);
425 break;
426 }
427 }
428 *bufnext = EOS;
429 #ifdef DEBUG
430 qprintf("glob0:", patbuf);
431 #endif
432
433 if ((err = glob1(patbuf, pglob)) != 0)
434 return(err);
435
436 /*
437 * If there was no match we are going to append the pattern
438 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
439 * and the pattern did not contain any magic characters
440 * GLOB_NOMAGIC is there just for compatibility with csh.
441 */
442 if (pglob->gl_pathc == oldpathc &&
443 ((pglob->gl_flags & GLOB_NOCHECK) ||
444 ((pglob->gl_flags & GLOB_NOMAGIC) &&
445 !(pglob->gl_flags & GLOB_MAGCHAR))))
446 return(globextend(pattern, pglob));
447 else if (!(pglob->gl_flags & GLOB_NOSORT))
448 qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
449 pglob->gl_pathc - oldpathc, sizeof(char *), compare);
450 return(0);
451 }
452
453 static int
compare(p,q)454 compare(p, q)
455 const void *p, *q;
456 {
457 return(strcmp(*(char **)p, *(char **)q));
458 }
459
460 static int
glob1(pattern,pglob)461 glob1(pattern, pglob)
462 Char *pattern;
463 glob_t *pglob;
464 {
465 Char pathbuf[MAXPATHLEN+1];
466
467 /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
468 if (*pattern == EOS)
469 return(0);
470 return(glob2(pathbuf, pathbuf, pattern, pglob));
471 }
472
473 /*
474 * The functions glob2 and glob3 are mutually recursive; there is one level
475 * of recursion for each segment in the pattern that contains one or more
476 * meta characters.
477 */
478 static int
glob2(pathbuf,pathend,pattern,pglob)479 glob2(pathbuf, pathend, pattern, pglob)
480 Char *pathbuf, *pathend, *pattern;
481 glob_t *pglob;
482 {
483 struct stat sb;
484 Char *p, *q;
485 int anymeta;
486
487 /*
488 * Loop over pattern segments until end of pattern or until
489 * segment with meta character found.
490 */
491 for (anymeta = 0;;) {
492 if (*pattern == EOS) { /* End of pattern? */
493 *pathend = EOS;
494 if (g_lstat(pathbuf, &sb, pglob))
495 return(0);
496
497 if (((pglob->gl_flags & GLOB_MARK) &&
498 pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
499 || (S_ISLNK(sb.st_mode) &&
500 (g_stat(pathbuf, &sb, pglob) == 0) &&
501 S_ISDIR(sb.st_mode)))) {
502 *pathend++ = SEP;
503 *pathend = EOS;
504 }
505 ++pglob->gl_matchc;
506 return(globextend(pathbuf, pglob));
507 }
508
509 /* Find end of next segment, copy tentatively to pathend. */
510 q = pathend;
511 p = pattern;
512 while (*p != EOS && *p != SEP) {
513 if (ismeta(*p))
514 anymeta = 1;
515 *q++ = *p++;
516 }
517
518 if (!anymeta) { /* No expansion, do next segment. */
519 pathend = q;
520 pattern = p;
521 while (*pattern == SEP)
522 *pathend++ = *pattern++;
523 } else /* Need expansion, recurse. */
524 return(glob3(pathbuf, pathend, pattern, p, pglob));
525 }
526 /* NOTREACHED */
527 }
528
529 static int
glob3(pathbuf,pathend,pattern,restpattern,pglob)530 glob3(pathbuf, pathend, pattern, restpattern, pglob)
531 Char *pathbuf, *pathend, *pattern, *restpattern;
532 glob_t *pglob;
533 {
534 register struct dirent *dp;
535 DIR *dirp;
536 int err;
537 char buf[MAXPATHLEN];
538
539 /*
540 * The readdirfunc declaration can't be prototyped, because it is
541 * assigned, below, to two functions which are prototyped in glob.h
542 * and dirent.h as taking pointers to differently typed opaque
543 * structures.
544 */
545 struct dirent *(*readdirfunc)();
546
547 *pathend = EOS;
548 errno = 0;
549
550 if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
551 /* TODO: don't call for ENOENT or ENOTDIR? */
552 if (pglob->gl_errfunc) {
553 g_Ctoc(pathbuf, buf);
554 if (pglob->gl_errfunc(buf, errno) ||
555 pglob->gl_flags & GLOB_ERR)
556 return (GLOB_ABEND);
557 }
558 return(0);
559 }
560
561 err = 0;
562
563 /* Search directory for matching names. */
564 if (pglob->gl_flags & GLOB_ALTDIRFUNC)
565 readdirfunc = pglob->gl_readdir;
566 else
567 readdirfunc = readdir;
568 while ((dp = (*readdirfunc)(dirp))) {
569 register u_char *sc;
570 register Char *dc;
571
572 /* Initial DOT must be matched literally. */
573 if (dp->d_name[0] == DOT && *pattern != DOT)
574 continue;
575 for (sc = (u_char *) dp->d_name, dc = pathend;
576 (*dc++ = *sc++) != EOS;)
577 continue;
578 if (!match(pathend, pattern, restpattern)) {
579 *pathend = EOS;
580 continue;
581 }
582 err = glob2(pathbuf, --dc, restpattern, pglob);
583 if (err)
584 break;
585 }
586
587 if (pglob->gl_flags & GLOB_ALTDIRFUNC)
588 (*pglob->gl_closedir)(dirp);
589 else
590 closedir(dirp);
591 return(err);
592 }
593
594
595 /*
596 * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
597 * add the new item, and update gl_pathc.
598 *
599 * This assumes the BSD realloc, which only copies the block when its size
600 * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
601 * behavior.
602 *
603 * Return 0 if new item added, error code if memory couldn't be allocated.
604 *
605 * Invariant of the glob_t structure:
606 * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
607 * gl_pathv points to (gl_offs + gl_pathc + 1) items.
608 */
609 static int
globextend(path,pglob)610 globextend(path, pglob)
611 const Char *path;
612 glob_t *pglob;
613 {
614 register char **pathv;
615 register int i;
616 u_int newsize;
617 char *copy;
618 const Char *p;
619
620 newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
621 pathv = pglob->gl_pathv ?
622 realloc((char *)pglob->gl_pathv, newsize) :
623 malloc(newsize);
624 if (pathv == NULL)
625 return(GLOB_NOSPACE);
626
627 if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
628 /* first time around -- clear initial gl_offs items */
629 pathv += pglob->gl_offs;
630 for (i = pglob->gl_offs; --i >= 0; )
631 *--pathv = NULL;
632 }
633 pglob->gl_pathv = pathv;
634
635 for (p = path; *p++;)
636 continue;
637 if ((copy = malloc(p - path)) != NULL) {
638 g_Ctoc(path, copy);
639 pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
640 }
641 pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
642 return(copy == NULL ? GLOB_NOSPACE : 0);
643 }
644
645
646 /*
647 * pattern matching function for filenames. Each occurrence of the *
648 * pattern causes a recursion level.
649 */
650 static int
match(name,pat,patend)651 match(name, pat, patend)
652 register Char *name, *pat, *patend;
653 {
654 int ok, negate_range;
655 Char c, k;
656
657 while (pat < patend) {
658 c = *pat++;
659 switch (c & M_MASK) {
660 case M_ALL:
661 if (pat == patend)
662 return(1);
663 do
664 if (match(name, pat, patend))
665 return(1);
666 while (*name++ != EOS);
667 return(0);
668 case M_ONE:
669 if (*name++ == EOS)
670 return(0);
671 break;
672 case M_SET:
673 ok = 0;
674 if ((k = *name++) == EOS)
675 return(0);
676 if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
677 ++pat;
678 while (((c = *pat++) & M_MASK) != M_END)
679 if ((*pat & M_MASK) == M_RNG) {
680 if (c <= k && k <= pat[1])
681 ok = 1;
682 pat += 2;
683 } else if (c == k)
684 ok = 1;
685 if (ok == negate_range)
686 return(0);
687 break;
688 default:
689 if (*name++ != c)
690 return(0);
691 break;
692 }
693 }
694 return(*name == EOS);
695 }
696
697 /* Free allocated data belonging to a glob_t structure. */
698 void
globfree(pglob)699 globfree(pglob)
700 glob_t *pglob;
701 {
702 register int i;
703 register char **pp;
704
705 if (pglob->gl_pathv != NULL) {
706 pp = pglob->gl_pathv + pglob->gl_offs;
707 for (i = pglob->gl_pathc; i--; ++pp)
708 if (*pp)
709 free(*pp);
710 free(pglob->gl_pathv);
711 }
712 }
713
714 static DIR *
g_opendir(str,pglob)715 g_opendir(str, pglob)
716 register Char *str;
717 glob_t *pglob;
718 {
719 char buf[MAXPATHLEN];
720
721 if (!*str)
722 strcpy(buf, ".");
723 else
724 g_Ctoc(str, buf);
725
726 if (pglob->gl_flags & GLOB_ALTDIRFUNC)
727 return((*pglob->gl_opendir)(buf));
728
729 return(opendir(buf));
730 }
731
732 static int
g_lstat(fn,sb,pglob)733 g_lstat(fn, sb, pglob)
734 register Char *fn;
735 struct stat *sb;
736 glob_t *pglob;
737 {
738 char buf[MAXPATHLEN];
739
740 g_Ctoc(fn, buf);
741 if (pglob->gl_flags & GLOB_ALTDIRFUNC)
742 return((*pglob->gl_lstat)(buf, sb));
743 return(lstat(buf, sb));
744 }
745
746 static int
g_stat(fn,sb,pglob)747 g_stat(fn, sb, pglob)
748 register Char *fn;
749 struct stat *sb;
750 glob_t *pglob;
751 {
752 char buf[MAXPATHLEN];
753
754 g_Ctoc(fn, buf);
755 if (pglob->gl_flags & GLOB_ALTDIRFUNC)
756 return((*pglob->gl_stat)(buf, sb));
757 return(stat(buf, sb));
758 }
759
760 static Char *
g_strchr(str,ch)761 g_strchr(str, ch)
762 Char *str;
763 int ch;
764 {
765 do {
766 if (*str == ch)
767 return (str);
768 } while (*str++);
769 return (NULL);
770 }
771
772 #ifdef notdef
773 static Char *
g_strcat(dst,src)774 g_strcat(dst, src)
775 Char *dst;
776 const Char* src;
777 {
778 Char *sdst = dst;
779
780 while (*dst++)
781 continue;
782 --dst;
783 while((*dst++ = *src++) != EOS)
784 continue;
785
786 return (sdst);
787 }
788 #endif
789
790 static void
g_Ctoc(str,buf)791 g_Ctoc(str, buf)
792 register const Char *str;
793 char *buf;
794 {
795 register char *dc;
796
797 for (dc = buf; (*dc++ = *str++) != EOS;)
798 continue;
799 }
800
801 #ifdef DEBUG
802 static void
qprintf(str,s)803 qprintf(str, s)
804 const char *str;
805 register Char *s;
806 {
807 register Char *p;
808
809 (void)printf("%s:\n", str);
810 for (p = s; *p; p++)
811 (void)printf("%c", CHAR(*p));
812 (void)printf("\n");
813 for (p = s; *p; p++)
814 (void)printf("%c", *p & M_PROTECT ? '"' : ' ');
815 (void)printf("\n");
816 for (p = s; *p; p++)
817 (void)printf("%c", ismeta(*p) ? '_' : ' ');
818 (void)printf("\n");
819 }
820 #endif
821