diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-10-01 10:02:25 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-10-01 10:02:25 +0000 |
commit | d65ea39ffc7503807fa95e8840c012a80c83e4f3 (patch) | |
tree | ff05ca5f98996c7bd3207c9499bbb2818505a6a2 /shell/hush.c | |
parent | ff0976248ac8759af1f1de32677887c4c9866865 (diff) | |
download | busybox-w32-d65ea39ffc7503807fa95e8840c012a80c83e4f3.tar.gz busybox-w32-d65ea39ffc7503807fa95e8840c012a80c83e4f3.tar.bz2 busybox-w32-d65ea39ffc7503807fa95e8840c012a80c83e4f3.zip |
hush: fix glob() abuse. Code was making unfounded assumptions how
glob() works, and it broke horribly on specific uclibc config.
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 189 |
1 files changed, 120 insertions, 69 deletions
diff --git a/shell/hush.c b/shell/hush.c index e73432a89..90ed15547 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -246,7 +246,7 @@ struct redir_struct { | |||
246 | redir_type type; /* type of redirection */ | 246 | redir_type type; /* type of redirection */ |
247 | int fd; /* file descriptor being redirected */ | 247 | int fd; /* file descriptor being redirected */ |
248 | int dup; /* -1, or file descriptor being duplicated */ | 248 | int dup; /* -1, or file descriptor being duplicated */ |
249 | glob_t glob_word; /* *word.gl_pathv is the filename */ | 249 | char **glob_word; /* *word.gl_pathv is the filename */ |
250 | }; | 250 | }; |
251 | 251 | ||
252 | struct child_prog { | 252 | struct child_prog { |
@@ -256,7 +256,7 @@ struct child_prog { | |||
256 | smallint subshell; /* flag, non-zero if group must be forked */ | 256 | smallint subshell; /* flag, non-zero if group must be forked */ |
257 | smallint is_stopped; /* is the program currently running? */ | 257 | smallint is_stopped; /* is the program currently running? */ |
258 | struct redir_struct *redirects; /* I/O redirections */ | 258 | struct redir_struct *redirects; /* I/O redirections */ |
259 | glob_t glob_result; /* result of parameter globbing */ | 259 | char **glob_result; /* result of parameter globbing */ |
260 | struct pipe *family; /* pointer back to the child's parent pipe */ | 260 | struct pipe *family; /* pointer back to the child's parent pipe */ |
261 | //sp counting seems to be broken... so commented out, grep for '//sp:' | 261 | //sp counting seems to be broken... so commented out, grep for '//sp:' |
262 | //sp: int sp; /* number of SPECIAL_VAR_SYMBOL */ | 262 | //sp: int sp; /* number of SPECIAL_VAR_SYMBOL */ |
@@ -503,9 +503,9 @@ static void pseudo_exec_argv(char **argv) ATTRIBUTE_NORETURN; | |||
503 | static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN; | 503 | static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN; |
504 | static int run_pipe_real(struct pipe *pi); | 504 | static int run_pipe_real(struct pipe *pi); |
505 | /* extended glob support: */ | 505 | /* extended glob support: */ |
506 | static int globhack(const char *src, int flags, glob_t *pglob); | 506 | static char **globhack(const char *src, char **strings); |
507 | static int glob_needed(const char *s); | 507 | static int glob_needed(const char *s); |
508 | static int xglob(o_string *dest, int flags, glob_t *pglob); | 508 | static int xglob(o_string *dest, char ***pglob); |
509 | /* variable assignment: */ | 509 | /* variable assignment: */ |
510 | static int is_assignment(const char *s); | 510 | static int is_assignment(const char *s); |
511 | /* data structure manipulation: */ | 511 | /* data structure manipulation: */ |
@@ -548,6 +548,58 @@ static struct variable *get_local_var(const char *name); | |||
548 | static int set_local_var(char *str, int flg_export); | 548 | static int set_local_var(char *str, int flg_export); |
549 | static void unset_local_var(const char *name); | 549 | static void unset_local_var(const char *name); |
550 | 550 | ||
551 | |||
552 | static char **add_strings_to_strings(int need_xstrdup, char **strings, char **add) | ||
553 | { | ||
554 | int i; | ||
555 | unsigned count1; | ||
556 | unsigned count2; | ||
557 | char **v; | ||
558 | |||
559 | v = strings; | ||
560 | count1 = 0; | ||
561 | if (v) { | ||
562 | while (*v) { | ||
563 | count1++; | ||
564 | v++; | ||
565 | } | ||
566 | } | ||
567 | count2 = 0; | ||
568 | v = add; | ||
569 | while (*v) { | ||
570 | count2++; | ||
571 | v++; | ||
572 | } | ||
573 | v = xrealloc(strings, (count1 + count2 + 1) * sizeof(char*)); | ||
574 | v[count1 + count2] = NULL; | ||
575 | i = count2; | ||
576 | while (--i >= 0) | ||
577 | v[count1 + i] = need_xstrdup ? xstrdup(add[i]) : add[i]; | ||
578 | return v; | ||
579 | } | ||
580 | |||
581 | /* 'add' should be a malloced pointer */ | ||
582 | static char **add_string_to_strings(char **strings, char *add) | ||
583 | { | ||
584 | char *v[2]; | ||
585 | |||
586 | v[0] = add; | ||
587 | v[1] = NULL; | ||
588 | |||
589 | return add_strings_to_strings(0, strings, v); | ||
590 | } | ||
591 | |||
592 | static void free_strings(char **strings) | ||
593 | { | ||
594 | if (strings) { | ||
595 | char **v = strings; | ||
596 | while (*v) | ||
597 | free(*v++); | ||
598 | free(strings); | ||
599 | } | ||
600 | } | ||
601 | |||
602 | |||
551 | /* Table of built-in functions. They can be forked or not, depending on | 603 | /* Table of built-in functions. They can be forked or not, depending on |
552 | * context: within pipes, they fork. As simple commands, they do not. | 604 | * context: within pipes, they fork. As simple commands, they do not. |
553 | * When used in non-forking context, they can change global variables | 605 | * When used in non-forking context, they can change global variables |
@@ -1067,16 +1119,14 @@ static void b_reset(o_string *o) | |||
1067 | { | 1119 | { |
1068 | o->length = 0; | 1120 | o->length = 0; |
1069 | o->nonnull = 0; | 1121 | o->nonnull = 0; |
1070 | if (o->data != NULL) | 1122 | if (o->data) |
1071 | *o->data = '\0'; | 1123 | o->data[0] = '\0'; |
1072 | } | 1124 | } |
1073 | 1125 | ||
1074 | static void b_free(o_string *o) | 1126 | static void b_free(o_string *o) |
1075 | { | 1127 | { |
1076 | b_reset(o); | ||
1077 | free(o->data); | 1128 | free(o->data); |
1078 | o->data = NULL; | 1129 | memset(o, 0, sizeof(*o)); |
1079 | o->maxlen = 0; | ||
1080 | } | 1130 | } |
1081 | 1131 | ||
1082 | /* My analysis of quoting semantics tells me that state information | 1132 | /* My analysis of quoting semantics tells me that state information |
@@ -1256,13 +1306,13 @@ static int setup_redirects(struct child_prog *prog, int squirrel[]) | |||
1256 | struct redir_struct *redir; | 1306 | struct redir_struct *redir; |
1257 | 1307 | ||
1258 | for (redir = prog->redirects; redir; redir = redir->next) { | 1308 | for (redir = prog->redirects; redir; redir = redir->next) { |
1259 | if (redir->dup == -1 && redir->glob_word.gl_pathv == NULL) { | 1309 | if (redir->dup == -1 && redir->glob_word == NULL) { |
1260 | /* something went wrong in the parse. Pretend it didn't happen */ | 1310 | /* something went wrong in the parse. Pretend it didn't happen */ |
1261 | continue; | 1311 | continue; |
1262 | } | 1312 | } |
1263 | if (redir->dup == -1) { | 1313 | if (redir->dup == -1) { |
1264 | mode = redir_table[redir->type].mode; | 1314 | mode = redir_table[redir->type].mode; |
1265 | openfd = open_or_warn(redir->glob_word.gl_pathv[0], mode); | 1315 | openfd = open_or_warn(redir->glob_word[0], mode); |
1266 | if (openfd < 0) { | 1316 | if (openfd < 0) { |
1267 | /* this could get lost if stderr has been redirected, but | 1317 | /* this could get lost if stderr has been redirected, but |
1268 | bash and ash both lose it as well (though zsh doesn't!) */ | 1318 | bash and ash both lose it as well (though zsh doesn't!) */ |
@@ -2024,6 +2074,7 @@ static int run_list_real(struct pipe *pi) | |||
2024 | #if ENABLE_HUSH_LOOPS | 2074 | #if ENABLE_HUSH_LOOPS |
2025 | if (rword == RES_FOR && pi->num_progs) { | 2075 | if (rword == RES_FOR && pi->num_progs) { |
2026 | if (!for_lcur) { | 2076 | if (!for_lcur) { |
2077 | /* first loop through for */ | ||
2027 | /* if no variable values after "in" we skip "for" */ | 2078 | /* if no variable values after "in" we skip "for" */ |
2028 | if (!pi->next->progs->argv) | 2079 | if (!pi->next->progs->argv) |
2029 | continue; | 2080 | continue; |
@@ -2036,17 +2087,16 @@ static int run_list_real(struct pipe *pi) | |||
2036 | } | 2087 | } |
2037 | free(pi->progs->argv[0]); | 2088 | free(pi->progs->argv[0]); |
2038 | if (!*for_lcur) { | 2089 | if (!*for_lcur) { |
2090 | /* for loop is over, clean up */ | ||
2039 | free(for_list); | 2091 | free(for_list); |
2040 | for_lcur = NULL; | 2092 | for_lcur = NULL; |
2041 | flag_rep = 0; | 2093 | flag_rep = 0; |
2042 | pi->progs->argv[0] = for_varname; | 2094 | pi->progs->argv[0] = for_varname; |
2043 | pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0]; | ||
2044 | continue; | 2095 | continue; |
2045 | } | 2096 | } |
2046 | /* insert next value from for_lcur */ | 2097 | /* insert next value from for_lcur */ |
2047 | /* vda: does it need escaping? */ | 2098 | /* vda: does it need escaping? */ |
2048 | pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); | 2099 | pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); |
2049 | pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0]; | ||
2050 | } | 2100 | } |
2051 | if (rword == RES_IN) | 2101 | if (rword == RES_IN) |
2052 | continue; | 2102 | continue; |
@@ -2149,8 +2199,8 @@ static int free_pipe(struct pipe *pi, int indent) | |||
2149 | for (a = 0, p = child->argv; *p; a++, p++) { | 2199 | for (a = 0, p = child->argv; *p; a++, p++) { |
2150 | debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p); | 2200 | debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p); |
2151 | } | 2201 | } |
2152 | globfree(&child->glob_result); | 2202 | free_strings(child->glob_result); |
2153 | child->argv = NULL; | 2203 | child->glob_result = NULL; |
2154 | } else if (child->group) { | 2204 | } else if (child->group) { |
2155 | debug_printf_clean("%s begin group (subshell:%d)\n", indenter(indent), child->subshell); | 2205 | debug_printf_clean("%s begin group (subshell:%d)\n", indenter(indent), child->subshell); |
2156 | ret_code = free_pipe_list(child->group, indent+3); | 2206 | ret_code = free_pipe_list(child->group, indent+3); |
@@ -2162,9 +2212,10 @@ static int free_pipe(struct pipe *pi, int indent) | |||
2162 | debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->type].descrip); | 2212 | debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->type].descrip); |
2163 | if (r->dup == -1) { | 2213 | if (r->dup == -1) { |
2164 | /* guard against the case >$FOO, where foo is unset or blank */ | 2214 | /* guard against the case >$FOO, where foo is unset or blank */ |
2165 | if (r->glob_word.gl_pathv) { | 2215 | if (r->glob_word) { |
2166 | debug_printf_clean(" %s\n", r->glob_word.gl_pathv[0]); | 2216 | debug_printf_clean(" %s\n", r->glob_word[0]); |
2167 | globfree(&r->glob_word); | 2217 | free_strings(r->glob_word); |
2218 | r->glob_word = NULL; | ||
2168 | } | 2219 | } |
2169 | } else { | 2220 | } else { |
2170 | debug_printf_clean("&%d\n", r->dup); | 2221 | debug_printf_clean("&%d\n", r->dup); |
@@ -2224,80 +2275,79 @@ static int run_list(struct pipe *pi) | |||
2224 | * string into the output structure, removing non-backslashed backslashes. | 2275 | * string into the output structure, removing non-backslashed backslashes. |
2225 | * If someone can prove me wrong, by performing this function within the | 2276 | * If someone can prove me wrong, by performing this function within the |
2226 | * original glob(3) api, feel free to rewrite this routine into oblivion. | 2277 | * original glob(3) api, feel free to rewrite this routine into oblivion. |
2227 | * Return code (0 vs. GLOB_NOSPACE) matches glob(3). | ||
2228 | * XXX broken if the last character is '\\', check that before calling. | 2278 | * XXX broken if the last character is '\\', check that before calling. |
2229 | */ | 2279 | */ |
2230 | static int globhack(const char *src, int flags, glob_t *pglob) | 2280 | static char **globhack(const char *src, char **strings) |
2231 | { | 2281 | { |
2232 | int cnt = 0, pathc; | 2282 | int cnt; |
2233 | const char *s; | 2283 | const char *s; |
2234 | char *dest; | 2284 | char *v, *dest; |
2285 | |||
2235 | for (cnt = 1, s = src; s && *s; s++) { | 2286 | for (cnt = 1, s = src; s && *s; s++) { |
2236 | if (*s == '\\') s++; | 2287 | if (*s == '\\') s++; |
2237 | cnt++; | 2288 | cnt++; |
2238 | } | 2289 | } |
2239 | dest = xmalloc(cnt); | 2290 | v = dest = xmalloc(cnt); |
2240 | if (!(flags & GLOB_APPEND)) { | ||
2241 | pglob->gl_pathv = NULL; | ||
2242 | pglob->gl_pathc = 0; | ||
2243 | pglob->gl_offs = 0; | ||
2244 | pglob->gl_offs = 0; | ||
2245 | } | ||
2246 | pathc = ++pglob->gl_pathc; | ||
2247 | pglob->gl_pathv = xrealloc(pglob->gl_pathv, (pathc+1) * sizeof(*pglob->gl_pathv)); | ||
2248 | pglob->gl_pathv[pathc-1] = dest; | ||
2249 | pglob->gl_pathv[pathc] = NULL; | ||
2250 | for (s = src; s && *s; s++, dest++) { | 2291 | for (s = src; s && *s; s++, dest++) { |
2251 | if (*s == '\\') s++; | 2292 | if (*s == '\\') s++; |
2252 | *dest = *s; | 2293 | *dest = *s; |
2253 | } | 2294 | } |
2254 | *dest = '\0'; | 2295 | *dest = '\0'; |
2255 | return 0; | 2296 | |
2297 | return add_string_to_strings(strings, v); | ||
2256 | } | 2298 | } |
2257 | 2299 | ||
2258 | /* XXX broken if the last character is '\\', check that before calling */ | 2300 | /* XXX broken if the last character is '\\', check that before calling */ |
2259 | static int glob_needed(const char *s) | 2301 | static int glob_needed(const char *s) |
2260 | { | 2302 | { |
2261 | for (; *s; s++) { | 2303 | for (; *s; s++) { |
2262 | if (*s == '\\') s++; | 2304 | if (*s == '\\') |
2263 | if (strchr("*[?", *s)) return 1; | 2305 | s++; |
2306 | if (strchr("*[?", *s)) | ||
2307 | return 1; | ||
2264 | } | 2308 | } |
2265 | return 0; | 2309 | return 0; |
2266 | } | 2310 | } |
2267 | 2311 | ||
2268 | static int xglob(o_string *dest, int flags, glob_t *pglob) | 2312 | static int xglob(o_string *dest, char ***pglob) |
2269 | { | 2313 | { |
2270 | int gr; | ||
2271 | |||
2272 | /* short-circuit for null word */ | 2314 | /* short-circuit for null word */ |
2273 | /* we can code this better when the debug_printf's are gone */ | 2315 | /* we can code this better when the debug_printf's are gone */ |
2274 | if (dest->length == 0) { | 2316 | if (dest->length == 0) { |
2275 | if (dest->nonnull) { | 2317 | if (dest->nonnull) { |
2276 | /* bash man page calls this an "explicit" null */ | 2318 | /* bash man page calls this an "explicit" null */ |
2277 | gr = globhack(dest->data, flags, pglob); | 2319 | *pglob = globhack(dest->data, *pglob); |
2278 | debug_printf("globhack returned %d\n", gr); | ||
2279 | } else { | ||
2280 | return 0; | ||
2281 | } | 2320 | } |
2282 | } else if (glob_needed(dest->data)) { | 2321 | return 0; |
2283 | gr = glob(dest->data, flags, NULL, pglob); | 2322 | } |
2323 | |||
2324 | if (glob_needed(dest->data)) { | ||
2325 | glob_t globdata; | ||
2326 | int gr; | ||
2327 | |||
2328 | memset(&globdata, 0, sizeof(globdata)); | ||
2329 | gr = glob(dest->data, 0, NULL, &globdata); | ||
2284 | debug_printf("glob returned %d\n", gr); | 2330 | debug_printf("glob returned %d\n", gr); |
2331 | if (gr == GLOB_NOSPACE) | ||
2332 | bb_error_msg_and_die("out of memory during glob"); | ||
2285 | if (gr == GLOB_NOMATCH) { | 2333 | if (gr == GLOB_NOMATCH) { |
2286 | /* quote removal, or more accurately, backslash removal */ | ||
2287 | gr = globhack(dest->data, flags, pglob); | ||
2288 | debug_printf("globhack returned %d\n", gr); | 2334 | debug_printf("globhack returned %d\n", gr); |
2335 | /* quote removal, or more accurately, backslash removal */ | ||
2336 | *pglob = globhack(dest->data, *pglob); | ||
2337 | return 0; | ||
2289 | } | 2338 | } |
2290 | } else { | 2339 | if (gr != 0) { /* GLOB_ABORTED ? */ |
2291 | gr = globhack(dest->data, flags, pglob); | 2340 | bb_error_msg("glob(3) error %d", gr); |
2292 | debug_printf("globhack returned %d\n", gr); | 2341 | } |
2293 | } | 2342 | if (globdata.gl_pathv && globdata.gl_pathv[0]) |
2294 | if (gr == GLOB_NOSPACE) | 2343 | *pglob = add_strings_to_strings(1, *pglob, globdata.gl_pathv); |
2295 | bb_error_msg_and_die("out of memory during glob"); | 2344 | /* globprint(glob_target); */ |
2296 | if (gr != 0) { /* GLOB_ABORTED ? */ | 2345 | globfree(&globdata); |
2297 | bb_error_msg("glob(3) error %d", gr); | 2346 | return gr; |
2298 | } | 2347 | } |
2299 | /* globprint(glob_target); */ | 2348 | |
2300 | return gr; | 2349 | *pglob = globhack(dest->data, *pglob); |
2350 | return 0; | ||
2301 | } | 2351 | } |
2302 | 2352 | ||
2303 | /* expand_strvec_to_strvec() takes a list of strings, expands | 2353 | /* expand_strvec_to_strvec() takes a list of strings, expands |
@@ -2775,7 +2825,7 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style, | |||
2775 | } | 2825 | } |
2776 | redir = xzalloc(sizeof(struct redir_struct)); | 2826 | redir = xzalloc(sizeof(struct redir_struct)); |
2777 | /* redir->next = NULL; */ | 2827 | /* redir->next = NULL; */ |
2778 | /* redir->glob_word.gl_pathv = NULL; */ | 2828 | /* redir->glob_word = NULL; */ |
2779 | if (last_redir) { | 2829 | if (last_redir) { |
2780 | last_redir->next = redir; | 2830 | last_redir->next = redir; |
2781 | } else { | 2831 | } else { |
@@ -2919,8 +2969,8 @@ static int reserved_word(o_string *dest, struct p_context *ctx) | |||
2919 | static int done_word(o_string *dest, struct p_context *ctx) | 2969 | static int done_word(o_string *dest, struct p_context *ctx) |
2920 | { | 2970 | { |
2921 | struct child_prog *child = ctx->child; | 2971 | struct child_prog *child = ctx->child; |
2922 | glob_t *glob_target; | 2972 | char ***glob_target; |
2923 | int gr, flags = 0; | 2973 | int gr; |
2924 | 2974 | ||
2925 | debug_printf_parse("done_word entered: '%s' %p\n", dest->data, child); | 2975 | debug_printf_parse("done_word entered: '%s' %p\n", dest->data, child); |
2926 | if (dest->length == 0 && !dest->nonnull) { | 2976 | if (dest->length == 0 && !dest->nonnull) { |
@@ -2942,11 +2992,9 @@ static int done_word(o_string *dest, struct p_context *ctx) | |||
2942 | return (ctx->res_w == RES_SNTX); | 2992 | return (ctx->res_w == RES_SNTX); |
2943 | } | 2993 | } |
2944 | } | 2994 | } |
2945 | glob_target = &child->glob_result; | 2995 | glob_target = &child->argv; |
2946 | if (child->argv) | ||
2947 | flags |= GLOB_APPEND; | ||
2948 | } | 2996 | } |
2949 | gr = xglob(dest, flags, glob_target); | 2997 | gr = xglob(dest, glob_target); |
2950 | if (gr != 0) { | 2998 | if (gr != 0) { |
2951 | debug_printf_parse("done_word return 1: xglob returned %d\n", gr); | 2999 | debug_printf_parse("done_word return 1: xglob returned %d\n", gr); |
2952 | return 1; | 3000 | return 1; |
@@ -2954,14 +3002,17 @@ static int done_word(o_string *dest, struct p_context *ctx) | |||
2954 | 3002 | ||
2955 | b_reset(dest); | 3003 | b_reset(dest); |
2956 | if (ctx->pending_redirect) { | 3004 | if (ctx->pending_redirect) { |
2957 | ctx->pending_redirect = NULL; | 3005 | if (ctx->pending_redirect->glob_word |
2958 | if (glob_target->gl_pathc != 1) { | 3006 | && ctx->pending_redirect->glob_word[0] |
3007 | && ctx->pending_redirect->glob_word[1] | ||
3008 | ) { | ||
3009 | /* more than one word resulted from globbing redir */ | ||
3010 | ctx->pending_redirect = NULL; | ||
2959 | bb_error_msg("ambiguous redirect"); | 3011 | bb_error_msg("ambiguous redirect"); |
2960 | debug_printf_parse("done_word return 1: ambiguous redirect\n"); | 3012 | debug_printf_parse("done_word return 1: ambiguous redirect\n"); |
2961 | return 1; | 3013 | return 1; |
2962 | } | 3014 | } |
2963 | } else { | 3015 | ctx->pending_redirect = NULL; |
2964 | child->argv = glob_target->gl_pathv; | ||
2965 | } | 3016 | } |
2966 | #if ENABLE_HUSH_LOOPS | 3017 | #if ENABLE_HUSH_LOOPS |
2967 | if (ctx->res_w == RES_FOR) { | 3018 | if (ctx->res_w == RES_FOR) { |
@@ -3006,7 +3057,7 @@ static int done_command(struct p_context *ctx) | |||
3006 | /*child->argv = NULL;*/ | 3057 | /*child->argv = NULL;*/ |
3007 | /*child->is_stopped = 0;*/ | 3058 | /*child->is_stopped = 0;*/ |
3008 | /*child->group = NULL;*/ | 3059 | /*child->group = NULL;*/ |
3009 | /*child->glob_result.gl_pathv = NULL;*/ | 3060 | /*child->glob_result = NULL;*/ |
3010 | child->family = pi; | 3061 | child->family = pi; |
3011 | //sp: /*child->sp = 0;*/ | 3062 | //sp: /*child->sp = 0;*/ |
3012 | //pt: child->parse_type = ctx->parse_type; | 3063 | //pt: child->parse_type = ctx->parse_type; |