root/releases/pkgcore/0.2.14/src/filter_env.c @ ferringb%2540gmail.com-20070317214430-an7m1ush57ag5qe6

Revision ferringb%2540gmail.com-20070317214430-an7m1ush57ag5qe6, 20.0 KB (checked in by Brian Harring <ferringb@…>, 22 months ago)

update copyright headers, correct spankys last name.

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