~vorlon/ubuntu/raring/upstart/lp.1199778

« back to all changes in this revision

Viewing changes to init/cfgfile.c

  • Committer: Scott James Remnant
  • Date: 2006-08-22 09:46:01 UTC
  • Revision ID: scott@netsplit.com-20060822094601-9b2346ad0b35158a
* init/cfgfile.c (cfg_read_script): Function to parse a script
fragment ("foo script\n....end script\n") from the job file, which
is the most complex form we can find.  Write it assuming the file is
in a character array which may not be NULL terminated (ie. a mmap'd
file).
(cfg_script_end): Used by the above to detect the end of the
fragment.
* init/cfgfile.h: Empty header file.
* init/Makefile.am (init_SOURCES): Build and link cfgfile.c
using the cfgfile.h header
(TESTS): Build and run the config file test cases.
(test_cfgfile_SOURCES, test_cfgfile_LDFLAGS, test_cfgfile_LDADD):
Details for config file test case binary.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* upstart
 
2
 *
 
3
 * cfgfile.c - configuration and job file parsing
 
4
 *
 
5
 * Copyright © 2006 Canonical Ltd.
 
6
 * Author: Scott James Remnant <scott@ubuntu.com>.
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License as published by
 
10
 * the Free Software Foundation; either version 2 of the License, or
 
11
 * (at your option) any later version.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 * GNU General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License
 
19
 * along with this program; if not, write to the Free Software
 
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
21
 */
 
22
 
 
23
#ifdef HAVE_CONFIG_H
 
24
# include <config.h>
 
25
#endif /* HAVE_CONFIG_H */
 
26
 
 
27
 
 
28
#include <string.h>
 
29
 
 
30
#include <nih/macros.h>
 
31
#include <nih/alloc.h>
 
32
#include <nih/logging.h>
 
33
#include <nih/error.h>
 
34
 
 
35
#include "cfgfile.h"
 
36
 
 
37
 
 
38
/**
 
39
 * WS:
 
40
 *
 
41
 * Definition of what characters we consider whitespace.
 
42
 **/
 
43
#define WS " \t\r"
 
44
 
 
45
 
 
46
/* Prototypes for static functions */
 
47
static char *  cfg_read_script (void *parent, const char *file,
 
48
                                ssize_t len, ssize_t *pos)
 
49
static ssize_t cfg_script_end  (const char *file, ssize_t len, ssize_t *pos);
 
50
 
 
51
 
 
52
/**
 
53
 * cfg_read_script:
 
54
 * @parent: parent of returned string,
 
55
 * @file: memory mapped copy of file, or string buffer,
 
56
 * @len: length of @file,
 
57
 * @pos: position to read from.
 
58
 *
 
59
 * @pos should point to the start of the entire script fragment, including
 
60
 * the opening line.
 
61
 *
 
62
 * @pos is updated to point to the next line in the configuration or will be
 
63
 * past the end of the file.
 
64
 *
 
65
 * Returns: the script contained in the fragment or %NULL if the end could
 
66
 * not be found before the end of file.
 
67
 **/
 
68
static char *
 
69
cfg_read_script (void       *parent,
 
70
                 const char *file,
 
71
                 ssize_t     len,
 
72
                 ssize_t    *pos)
 
73
{
 
74
        char    *script;
 
75
        ssize_t  sh_start, sh_end, sh_len, ws, p;
 
76
        int      lines;
 
77
 
 
78
        nih_assert (file != NULL);
 
79
        nih_assert (len > 0);
 
80
        nih_assert (pos != NULL);
 
81
 
 
82
        /* Find the start of the script proper */
 
83
        while ((*pos < len) && (file[*pos] != '\n'))
 
84
                (*pos)++;
 
85
 
 
86
        /* Step over the newline */
 
87
        if (*pos < len) {
 
88
                (*pos)++;
 
89
        } else {
 
90
                return NULL;
 
91
        }
 
92
 
 
93
        /* Ok, we found the start of the script.  We now need to find the end
 
94
         * of the script which is a line that looks like:
 
95
         *
 
96
         *      WS end WS script (WS COMMENT?)?
 
97
         *
 
98
         * Just to make things more difficult for ourselves, we work out the
 
99
         * common whitespace on the start of the script lines and remember
 
100
         * not to copy those out later
 
101
         */
 
102
        sh_start = *pos;
 
103
        sh_end = -1;
 
104
        ws = -1;
 
105
        lines = 0;
 
106
 
 
107
        while ((sh_end = cfg_script_end (file, len, pos)) < 0) {
 
108
                ssize_t line_start;
 
109
 
 
110
                lines++;
 
111
                line_start = *pos;
 
112
                if (ws < 0) {
 
113
                        /* Count initial whitespace */
 
114
                        while ((*pos < len) && strchr (WS, file[*pos]))
 
115
                                (*pos)++;
 
116
 
 
117
                        ws = *pos - line_start;
 
118
                } else {
 
119
                        /* Compare how much whitespace matches the
 
120
                         * first line; and decrease the count if it's
 
121
                         * not as much.
 
122
                         */
 
123
                        while ((*pos < len) && (*pos - line_start < ws)
 
124
                               && (file[sh_start + *pos - line_start]
 
125
                                   == file[*pos]))
 
126
                                (*pos)++;
 
127
 
 
128
                        if (*pos - line_start < ws)
 
129
                                ws = *pos - line_start;
 
130
                }
 
131
 
 
132
                /* Find the end of the line */
 
133
                while ((*pos < len) && (file[*pos] != '\n'))
 
134
                        (*pos)++;
 
135
 
 
136
                /* Step over the newline */
 
137
                if (*pos < len) {
 
138
                        (*pos)++;
 
139
                } else {
 
140
                        return NULL;
 
141
                }
 
142
 
 
143
        }
 
144
 
 
145
        /*
 
146
         * Copy the fragment into a string, removing common whitespace from
 
147
         * the start.  We can be less strict here because we already know
 
148
         * the contents, etc.
 
149
         */
 
150
 
 
151
        sh_len = sh_end - sh_start - (ws * lines);
 
152
        NIH_MUST (script = nih_alloc (parent, sh_len + 1));
 
153
        script[0] = '\0';
 
154
 
 
155
        p = sh_start;
 
156
        while (p < sh_end) {
 
157
                size_t line_start;
 
158
 
 
159
                p += ws;
 
160
                line_start = p;
 
161
 
 
162
                while (file[p++] != '\n')
 
163
                        ;
 
164
 
 
165
                strncat (script, file + line_start, p - line_start);
 
166
        }
 
167
 
 
168
        return script;
 
169
}
 
170
 
 
171
/**
 
172
 * cfg_script_end:
 
173
 * @file: memory mapped copy of file, or string buffer,
 
174
 * @len: length of @file,
 
175
 * @pos: position to read from.
 
176
 *
 
177
 * Determines whether the current line is an end-of-script marker.
 
178
 *
 
179
 * @pos is updated to point to the next line in configuration or past the
 
180
 * end of file.
 
181
 *
 
182
 * Returns: index of script end (always the value of @pos at the time this
 
183
 * function was called) or -1 if it is not on this line.
 
184
 **/
 
185
static ssize_t
 
186
cfg_script_end (const char *file,
 
187
                ssize_t     len,
 
188
                ssize_t    *pos)
 
189
{
 
190
        ssize_t p, end;
 
191
 
 
192
        nih_assert (file != NULL);
 
193
        nih_assert (len > 0);
 
194
        nih_assert (pos != NULL);
 
195
 
 
196
        p = *pos;
 
197
 
 
198
        /* Skip initial whitespace */
 
199
        while ((p < len) && strchr (WS, file[p]))
 
200
                p++;
 
201
 
 
202
        /* Check the first word (check we have at least 4 chars because of
 
203
         * the need for whitespace immediately after)
 
204
         */
 
205
        if ((len - p < 4) || strncmp (file + p, "end", 3))
 
206
                return -1;
 
207
 
 
208
        /* Must be whitespace after */
 
209
        if (! strchr (WS, file[p + 3]))
 
210
                return -1;
 
211
 
 
212
        /* Find the second word */
 
213
        p += 3;
 
214
        while ((p < len) && strchr (WS, file[p]))
 
215
                p++;
 
216
 
 
217
        /* Check the second word */
 
218
        if ((len - p < 6) || strncmp (file + p, "script", 6))
 
219
                return -1;
 
220
 
 
221
        /* May be followed by whitespace */
 
222
        p += 6;
 
223
        while ((p < len) && strchr (WS, file[p]))
 
224
                p++;
 
225
 
 
226
        /* May be a comment, in which case eat up to the
 
227
         * newline
 
228
         */
 
229
        if ((p < len) && (file[p] == '#')) {
 
230
                while ((p < len) && (file[p] != '\n'))
 
231
                        p++;
 
232
        }
 
233
 
 
234
        /* Should be end of string, or a newline */
 
235
        if ((p < len) && (file[p] != '\n'))
 
236
                return -1;
 
237
 
 
238
        /* Point past the new line */
 
239
        if (p < len)
 
240
                p++;
 
241
 
 
242
        /* Return the beginning of the line (which is the end of the script)
 
243
         * but update pos to point past this line.
 
244
         */
 
245
        end = *pos;
 
246
        *pos = p;
 
247
 
 
248
        return end;
 
249
}