root/releases/pkgcore/0.2.4/src/filter_env.c @ ferringb%2540gmail.com-20070122063034-enckh8kecd0ajtob

Revision ferringb%2540gmail.com-20070122063034-enckh8kecd0ajtob, 18.4 KB (checked in by Brian Harring <ferringb@…>, 2 years ago)

add missing NULL termination; thanks to rej@… for smoking it out via hppa.

Line 
1/*
2 * Copyright: 2004-2006 Brian Harring
3 * Copyright: 2005 Mike Vapier
4 * Copyright: 2006 Marien Zwart
5 * License: GPL2
6 */
7
8#define PY_SSIZE_T_CLEAN
9
10#include <Python.h>
11#include "py24-compatibility.h"
12
13PyDoc_STRVAR(
14    module_doc,
15    "Filter a bash env dump.\n"
16    );
17
18#include <regex.h>
19#include "bmh_search.h"
20
21#define SPACE_PARSING            2
22#define COMMAND_PARSING          1
23
24static inline const char *raw_walk_command_escaped_parsing(const char *p,
25    const char *end, const char endchar, char var_expansion);
26static inline const char *walk_command_pound(const char *p);
27static const char *walk_command_complex(const char *p, const char *end,
28    char endchar, const char interpret_level);
29static inline const char *walk_command_no_parsing(const char *p,
30    const char *end, const char endchar);
31static inline const char *walk_command_dollared_parsing(const char *p,
32    const char *end, const char endchar);
33
34#define walk_command_escaped_parsing(start, end, endchar) \
35    raw_walk_command_escaped_parsing((start), (end), (endchar), 0)
36
37
38static PyObject *log_info = NULL;
39static PyObject *log_debug = NULL;
40static PyObject *write_str = NULL;
41
42/* Log a message. Returns -1 on error, 0 on success. */
43static int
44debug_print(PyObject *logfunc, const char *format, ...)
45{
46    /* Sanity check. Should not happen. */
47    if (!logfunc)
48        return -1;
49    va_list vargs;
50    va_start(vargs, format);
51    PyObject *message = PyString_FromFormatV(format, vargs);
52    va_end(vargs);
53    if (!message)
54        return -1;
55    PyObject *result = PyObject_CallFunctionObjArgs(logfunc, message, NULL);
56    Py_DECREF(message);
57    return result ? 0 : -1;
58}
59
60#define INFO(fmt, args...) debug_print(log_info, fmt, ## args)
61#define DEBUG(fmt, args...) debug_print(log_info, fmt, ## args)
62
63
64static const inline char *
65is_function(const char *p, char **start, char **end)
66{
67    #define SKIP_SPACES(p) while('\0' != *(p) && \
68        (' ' == *(p) || '\t' == *(p))) ++p;
69    #define FUNC_LEN 8
70    SKIP_SPACES(p);
71    if(strncmp(p, "function", FUNC_LEN) == 0)
72        p += FUNC_LEN;
73    while('\0' != *p && isspace(*p))
74        ++p;
75    *start = (char *)p;
76    while('\0' != *p && ' ' != *p && '\t' != *p && '\n' != *p &&
77        '=' != *p && '"' != *p && '\'' != *p && '(' != *p && ')' != *p)
78        ++p;
79    *end = (char *)p;
80    if(*end == *start)
81        return NULL;
82    SKIP_SPACES(p);
83    if('\0' == *p || '(' != *p)
84        return NULL;
85    ++p;
86    SKIP_SPACES(p);
87    if('\0' == *p || ')' != *p)
88        return NULL;
89    ++p;
90    while('\0' != *p && isspace(*p))
91        ++p;
92    if('\0' == *p || '{' != *p)
93        return NULL;
94    return ++p;
95}
96
97
98static inline const char *
99is_envvar(const char *p, char **start, char **end)
100{
101    SKIP_SPACES(p);
102    *start = (char *)p;
103    for(;;) {
104        switch(*p) {
105        case '\0':
106        case '"':
107        case '\'':
108        case '(':
109        case ')':
110        case '-':
111        case ' ':
112        case '\t':
113        case'\n':
114            return NULL;
115        case '=':
116            if(p == *start)
117                return NULL;
118            *end = (char *)p;
119            return ++p;
120        default:
121            ++p;
122        }
123    }
124}
125
126// zero for doesn't match, !0 for matches.
127static int
128regex_matches(regex_t *re, const char *buff, int desired_value)
129{
130    INFO("match %s, desired %d", buff, desired_value);
131    regmatch_t match[1];
132    match[0].rm_so = match[0].rm_eo = -1;
133    assert(buff != NULL);
134    assert(re != NULL);
135    regexec(re, buff, 1, match, 0);
136/*    fprintf(stderr,"result was %i for %s, returning %i\n", match[0].rm_so,
137        buff,i);
138*/
139    INFO("got %d", match[0].rm_so);
140    return match[0].rm_so != desired_value ? 1 : 0;
141}
142
143static const char *
144process_scope(PyObject *out, const char *buff, const char *end,
145              regex_t *var_re, regex_t *func_re, int desired_var_match,
146              int desired_func_match, char endchar)
147{
148    const char *p = NULL;
149    const char *window_start = NULL, *window_end = NULL;
150    const char *new_p = NULL;
151    const char *com_start = NULL;
152    char *s = NULL;
153    char *e = NULL;
154    char *temp_string = NULL;
155
156    regmatch_t matches[3];
157    p = buff;
158    matches[0].rm_so = matches[1].rm_so = matches[2].rm_so = -1;
159
160    window_start = buff;
161    window_end = NULL;
162    while (p < end && *p != endchar) {
163
164        /* wander forward to the next non space */
165        if (window_end != NULL) {
166            if (out) {
167                PyObject *string = PyString_FromStringAndSize(
168                    window_start, window_end - window_start);
169                if (!string)
170                    return NULL;
171                PyObject *result = PyObject_CallMethodObjArgs(
172                    out, write_str, string, NULL);
173                Py_DECREF(string);
174                if (!result)
175                    return NULL;
176                Py_DECREF(result);
177            }
178            window_start = p;
179            window_end = NULL;
180        }
181        com_start = p;
182        if (isspace(*p)) {
183            ++p;
184            continue;
185        }
186
187        /* ignore comments */
188        if (*p == '#') {
189            p = walk_command_pound(p);
190            continue;
191        }
192
193        if(NULL != (new_p = is_function(p, &s, &e))) {
194            asprintf(&temp_string, "%.*s", (int)(e - s), s);
195            INFO("matched func name '%s'", temp_string);
196            /* output it if it doesn't match */
197
198            new_p = process_scope(NULL, new_p, end, NULL, NULL, 0, 0, '}');
199            INFO("ended processing  '%s'", temp_string);
200            if (func_re != NULL && regex_matches(func_re, temp_string,
201                desired_func_match)) {
202               
203                /* well, it matched.  so it gets skipped. */
204                INFO("filtering func '%s'", temp_string);
205                window_end = com_start;
206            }
207
208            p = new_p;
209            free(temp_string);
210
211            ++p;
212        } else {
213            // check for env assignment
214            if (NULL == (new_p = is_envvar(p, &s, &e))) {
215                //exactly as it sounds, non env assignment.
216                p = walk_command_complex(p, end, endchar, COMMAND_PARSING);
217                if (!p)
218                    return NULL;
219                ++p;
220            } else {
221                //env assignment
222                asprintf(&temp_string, "%.*s", (int)(e - s), s);
223                p = new_p;
224                INFO("matched env assign '%s'", temp_string);
225
226                if (p >= end)
227                    return p;
228
229                while(p < end && !isspace(*p) && ';' != *p) {
230                    if ('\'' == *p)
231                        p = walk_command_no_parsing(p + 1, end, *p);
232                    else if ('"' == *p || '`' == *p)
233                        p = walk_command_escaped_parsing(p + 1, end, *p);
234                    else if ('(' == *p)
235                        p = walk_command_escaped_parsing(p + 1, end, ')');
236                    else if (isspace(*p)) {
237                        while (p < end && isspace(*p) && *p != '\n')
238                        ++p;
239                    } else if ('$' == *p) {
240                        if (p + 1 >= end) {
241                            ++p;
242                            continue;
243                        }
244                        if ('(' == p[1]) {
245                            p = walk_command_escaped_parsing(p + 2, end, ')');
246                        } else if ('\'' == p[1]) {
247                            p = walk_command_dollared_parsing(p + 2, end, '\'');
248                        } else if ('{' == p[1]) {
249                            p = raw_walk_command_escaped_parsing(p + 2, end,
250                                '}', 1);
251                        } else {
252                            while (p < end && !isspace(*p)) {
253                                if ('\\' == *p)
254                                    ++p;
255                                ++p;
256                            }
257                        }
258                    } else {
259                        // blah=cah ; single word.
260                        p = walk_command_complex(p, end, ' ', SPACE_PARSING);
261                        if (!p)
262                            return NULL;
263                    }
264                    if(isspace(*p)) {
265                        ++p;
266                        break;
267                    }
268                    ++p;
269                }
270                if (var_re && regex_matches(var_re, temp_string,
271                    desired_var_match)) {
272                    //this would be filtered.
273                    INFO("filtering var '%s'", temp_string);
274                    window_end = com_start;
275                }
276                free(temp_string);
277            }
278        }
279    }
280
281    if (out) {
282        if (window_end == NULL)
283            window_end = p;
284        if (window_end > end)
285            window_end = end;
286        PyObject *string = PyString_FromStringAndSize(
287            window_start, window_end - window_start);
288        if (!string)
289            return NULL;
290        PyObject *result = PyObject_CallMethodObjArgs(
291            out, write_str, string, NULL);
292        Py_DECREF(string);
293        if (!result)
294            return NULL;
295        Py_DECREF(result);
296    }
297
298    return p;
299}
300
301
302static inline const char *
303walk_command_no_parsing(const char *p, const char *end, const char endchar)
304{
305    while (p < end) {
306        if (*p == endchar)
307            return p;
308        ++p;
309    }
310    return p;
311}
312
313static inline const char *
314walk_command_dollared_parsing(const char *p, const char *end,
315    const char endchar)
316{
317    while (p < end) {
318        if (*p == endchar) {
319            return p;
320        } else if ('\\' == *p) {
321            ++p;
322        }
323        ++p;
324    }
325    return p;
326}
327
328/* Sets an exception and returns NULL if out of memory. */
329static const char *
330walk_here_command(const char *p, const char *end)
331{
332    char *end_here, *temp_string;
333    ++p;
334    /* DEBUG("starting here processing for COMMAND and l2 at p == '%.10s'",
335     * p); */
336    if (p >= end) {
337        fprintf(stderr, "bailing\n");
338        return p;
339    }
340    if ('<' == *p) {
341        /* d2printf("correction, it's a third level here.  Handing back to "
342         * "command parsing\n"); */
343        return ++p;
344    }
345    while (p < end && (isspace(*p) || '-' == *p))
346        ++p;
347    if ('\'' == *p || '"' == *p) {
348        end_here = (char *)walk_command_no_parsing(p + 1, end, *p);
349        ++p;
350    } else {
351        end_here = (char *)walk_command_complex(p, end, ' ',SPACE_PARSING);
352        if (!end_here)
353            return NULL;
354    }
355    /* INFO("end_here=%.5s",end_here); */
356    temp_string = malloc(end_here -p + 1);
357    if (!temp_string) {
358        PyErr_NoMemory();
359        return NULL;
360    }
361    memcpy(temp_string, p, end_here - p);
362    temp_string[end_here - p] = '\0';
363    /* d2printf("matched len('%zi')/'%s' for a here word\n", end_here - p,
364     * temp_string); */
365    /* XXX watch this.  potential for horkage.  need to do the quote
366        removal thing.
367        this sucks.
368    */
369    ++end_here;
370    if (end_here >= end) {
371        free(temp_string);
372        return end_here;
373    }
374    end_here = (char *)bmh_search((unsigned char*)temp_string,
375                                  (unsigned char*)end_here, end - end_here);
376    INFO("bmh returned %p", end_here);
377    if (end_here) {
378        /* d2printf("bmh = %.10s\n", end_here); */
379        p = end_here + strlen(temp_string) -1;
380        /* d2printf("continuing on at %.10s\n", p); */
381    } else {
382        p = end;
383    }
384    free(temp_string);
385    return p;
386}
387
388static const char *
389walk_command_pound(const char *p)
390{
391    while('\0' != *p) {
392        if('\n' == *p)
393            return p;
394        ++p;
395    }
396    return p;
397}
398
399/* Sets an exception and returns NULL if out of memory. */
400static const char *
401walk_command_complex(const char *p, const char *end, char endchar,
402    const char interpret_level)
403{
404    const char *start = p;
405    while (p < end) {
406        if (*p == endchar ||
407            (interpret_level == COMMAND_PARSING && (';'==*p || '\n'==*p)) ||
408            (interpret_level == SPACE_PARSING && isspace(*p))) {
409            return p;
410        } else if ('\\' == *p) {
411            ++p;
412        } else if ('<' == *p) {
413            if(end - 1 != p && '<' == p[1] && interpret_level == COMMAND_PARSING) {
414                p++;
415                p = walk_here_command(p, end);
416                if (!p)
417                    return NULL;
418            } else {
419                DEBUG("noticed '<', interpret_level=%i\n", interpret_level);
420            }
421        } else if ('#' == *p) {
422            /* echo x#y == x#y, echo x;#a == x */
423            if (start == p || isspace(p[-1]) || p[-1] == ';')
424                p = walk_command_pound(p);
425            else
426                ++p;
427            continue;
428        } else if ('{' == *p) {
429            //process_scope.  this gets fun.
430            p = walk_command_escaped_parsing(p + 1, end, '}');
431        } else if ('(' == *p && interpret_level == COMMAND_PARSING) {
432            p = walk_command_escaped_parsing(p + 1, end, ')');
433        } else if ('`' == *p || '"' == *p) {
434            p = walk_command_escaped_parsing(p + 1, end, *p);
435        } else if ('\'' == *p && '"' != endchar) {
436            p = walk_command_no_parsing(p + 1, end, '\'');
437        }
438        ++p;
439    }
440    return p;
441}
442
443
444/* Set a sensible exception for a failed regex compilation. */
445static void
446regex_exc(regex_t* reg, int result)
447{
448    ssize_t len = regerror(result, reg, NULL, 0);
449    char *buffer = malloc(len);
450    if (!buffer) {
451        PyErr_NoMemory();
452        return;
453    }
454    regerror(result, reg, buffer, len);
455    PyErr_SetString(PyExc_ValueError, buffer);
456    free(buffer);
457}
458
459
460PyDoc_STRVAR(
461    run_docstring,
462    "Print a filtered environment.\n"
463    "\n"
464    "@param out: file-like object to write to.\n"
465    "@param file_buff: string containing the environment to filter.\n"
466    "    Should end in '\0'.\n"
467    "@param vsr: result of build_regex_string or C{None}, for variables.\n"
468    "@param vsr: result of build_regex_string or C{None}, for functions.\n"
469    "@param desired_var_match: boolean indicating vsr should match or not.\n"
470    "@param desired_func_match: boolean indicating fsr should match or not.\n"
471    );
472
473static PyObject *
474pkgcore_filter_env_run(PyObject *self, PyObject *args, PyObject *kwargs)
475{
476    /* Arguments. */
477    PyObject *out, *desired_var_match_obj, *desired_func_match_obj;
478    const char *file_buff, *vsr, *fsr;
479    Py_ssize_t file_size;
480
481    /* Other vars. */
482
483    regex_t vre, *pvre, fre, *pfre;
484    int result, desired_func_match, desired_var_match;
485
486    static char *kwlist[] = {"out", "file_buff", "vsr", "fsr",
487                             "desired_var_match", "desired_func_match", NULL};
488    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os#zzOO", kwlist,
489                                     &out, &file_buff, &file_size, &vsr, &fsr,
490                                     &desired_var_match_obj,
491                                     &desired_func_match_obj))
492        return NULL;
493
494    desired_func_match = PyObject_IsTrue(desired_func_match_obj);
495    if (desired_func_match < 0)
496        return NULL;
497    if (desired_func_match)
498        desired_func_match = -1;
499
500    desired_var_match = PyObject_IsTrue(desired_var_match_obj);
501    if (desired_var_match < 0)
502        return NULL;
503    if (desired_var_match)
504        desired_var_match = -1;
505
506    if (file_buff[file_size] != '\0') {
507        PyErr_SetString(PyExc_ValueError, "file_buff should end in NULL");
508        return NULL;
509    }
510
511    if (fsr) {
512        result = regcomp(&fre, fsr, REG_EXTENDED);
513        if (result) {
514            regex_exc(&fre, result);
515            regfree(&fre);
516            return NULL;
517        }
518        pfre = &fre;
519    } else
520        pfre = NULL;
521
522    if (vsr) {
523        result = regcomp(&vre, vsr, REG_EXTENDED);
524        if (result) {
525            if (pfre)
526                regfree(pfre);
527            regex_exc(&vre, result);
528            regfree(&vre);
529            return NULL;
530        }
531        pvre = &vre;
532    } else
533        pvre = NULL;
534
535    const char *res_p = process_scope(
536        out, file_buff, file_buff + file_size, pvre, pfre,
537        desired_var_match, desired_func_match, '\0');
538
539    if (pvre)
540        regfree(pvre);
541    if (pfre)
542        regfree(pfre);
543
544    if (!res_p) {
545        PyErr_SetString(PyExc_ValueError, "Parsing failed");
546        return NULL;
547    }
548    Py_RETURN_NONE;
549}
550
551static const char *
552raw_walk_command_escaped_parsing(const char *p, const char *end, char endchar,
553    char var_expansion)
554{
555    int dollared = 0;
556    while (p < end) {
557        if (*p == endchar) {
558            return p;
559        } else if ('\\' == *p) {
560            ++p;
561        } else if ('{' == *p) {
562            // if double quote parsing, must be ${, else can be either
563            if('"' != endchar || dollared) {
564                //process_scope.  this gets fun.
565                p = raw_walk_command_escaped_parsing(p + 1, end, '}',
566                    dollared ? 1 : var_expansion);
567            }
568        } else if ('(' == *p) {
569            // if double quote parsing, must be $(, else can be either
570            if(('"' != endchar || dollared) && !var_expansion) {
571                p = raw_walk_command_escaped_parsing(p + 1, end, ')',
572                    0);
573            }
574        } else if ('`' == *p || '"' == *p) {
575            p = raw_walk_command_escaped_parsing(p + 1, end, *p, var_expansion);
576        } else if ('\'' == *p && '"' != endchar) {
577            p = walk_command_no_parsing(p + 1, end, '\'');
578        } else if('$' == *p) {
579            // if dollared, disable, else enable
580            dollared ^= 1;
581        } else {
582            dollared = 0;
583        }
584        ++p;
585    }
586    return p;
587}
588
589static PyMethodDef pkgcore_filter_env_methods[] = {
590    {"run", (PyCFunction)pkgcore_filter_env_run, METH_VARARGS | METH_KEYWORDS,
591     run_docstring},
592    {NULL}
593};
594
595PyMODINIT_FUNC
596init_filter_env()
597{
598    /* External objects. */
599    PyObject *s = PyString_FromString("pkgcore.log");
600    if (!s)
601        return;
602    PyObject *log = PyImport_Import(s);
603    Py_DECREF(s);
604    if (!log)
605        return;
606    PyObject *logger = PyObject_GetAttrString(log, "logger");
607    Py_DECREF(log);
608    if (!logger)
609        return;
610    log_debug = PyObject_GetAttrString(logger, "debug");
611    if (!log_debug) {
612        Py_DECREF(logger);
613        return;
614    }
615    log_info = PyObject_GetAttrString(logger, "info");
616    Py_DECREF(logger);
617    if (!log_info) {
618        Py_CLEAR(log_debug);
619        return;
620    }
621
622    /* String constants. */
623    write_str = PyString_FromString("write");
624    if (!write_str) {
625        Py_CLEAR(log_info);
626        Py_CLEAR(log_debug);
627        return;
628    }
629
630    /* XXX the returns above this point trigger SystemErrors. */
631    Py_InitModule3("_filter_env", pkgcore_filter_env_methods, module_doc);
632}
Note: See TracBrowser for help on using the browser.