1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * httxt2dbm.c: simple program for converting RewriteMap text files to DBM
19 * Rewrite databases for the Apache HTTP server
20 *
21 */
22
23#include "apr.h"
24#include "apr_lib.h"
25#include "apr_strings.h"
26#include "apr_file_io.h"
27#include "apr_file_info.h"
28#include "apr_pools.h"
29#include "apr_getopt.h"
30#include "apu.h"
31#include "apr_dbm.h"
32
33#if APR_HAVE_STDLIB_H
34#include <stdlib.h> /* for atexit() */
35#endif
36
37static const char *input;
38static const char *output;
39static const char *format;
40static const char *shortname;
41static apr_file_t *errfile;
42static char errbuf[120];
43static int verbose;
44
45/* From mod_rewrite.c */
46#ifndef REWRITE_MAX_TXT_MAP_LINE
47#define REWRITE_MAX_TXT_MAP_LINE 1024
48#endif
49
50#define NL APR_EOL_STR
51
52#define AVAIL "available"
53#define UNAVAIL "unavailable"
54
55static void usage(void)
56{
57    const char *have_sdbm;
58    const char *have_gdbm;
59    const char *have_ndbm;
60    const char *have_db;
61
62#if APU_HAVE_SDBM
63    have_sdbm = AVAIL;
64#else
65    have_sdbm = UNAVAIL;
66#endif
67#if APU_HAVE_GDBM
68    have_gdbm = AVAIL;
69#else
70    have_gdbm = UNAVAIL;
71#endif
72#if APU_HAVE_NDBM
73    have_ndbm = AVAIL;
74#else
75    have_ndbm = UNAVAIL;
76#endif
77#if APU_HAVE_DB
78    have_db = AVAIL;
79#else
80    have_db = UNAVAIL;
81#endif
82
83    apr_file_printf(errfile,
84    "%s -- Program to Create DBM Files for use by RewriteMap" NL
85    "Usage: %s [-v] [-f format] -i SOURCE_TXT -o OUTPUT_DBM" NL
86    NL
87    "Options: " NL
88    " -v    More verbose output"NL
89    NL
90    " -i    Source Text File. If '-', use stdin."NL
91    NL
92    " -o    Output DBM."NL
93    NL
94    " -f    DBM Format.  If not specified, will use the APR Default." NL
95    "           GDBM for GDBM files (%s)" NL
96    "           SDBM for SDBM files (%s)" NL
97    "           DB   for berkeley DB files (%s)" NL
98    "           NDBM for NDBM files (%s)" NL
99    "           default for the default DBM type" NL
100    NL,
101    shortname,
102    shortname,
103    have_gdbm,
104    have_sdbm,
105    have_db,
106    have_ndbm);
107}
108
109
110static apr_status_t to_dbm(apr_dbm_t *dbm, apr_file_t *fp, apr_pool_t *pool)
111{
112    apr_status_t rv = APR_SUCCESS;
113    char line[REWRITE_MAX_TXT_MAP_LINE + 1]; /* +1 for \0 */
114    apr_datum_t dbmkey;
115    apr_datum_t dbmval;
116    apr_pool_t* p;
117
118    apr_pool_create(&p, pool);
119
120    while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) {
121        char *c, *value;
122
123        if (*line == '#' || apr_isspace(*line)) {
124            continue;
125        }
126
127        c = line;
128
129        while (*c && !apr_isspace(*c)) {
130            ++c;
131        }
132
133        if (!*c) {
134            /* no value. solid line of data. */
135            continue;
136        }
137
138        dbmkey.dptr = apr_pstrmemdup(p, line,  c - line);
139        dbmkey.dsize = (c - line);
140
141        while (*c && apr_isspace(*c)) {
142            ++c;
143        }
144
145        if (!*c) {
146            apr_pool_clear(p);
147            continue;
148        }
149
150        value = c;
151
152        while (*c && !apr_isspace(*c)) {
153            ++c;
154        }
155
156        dbmval.dptr = apr_pstrmemdup(p, value,  c - value);
157        dbmval.dsize = (c - line);
158
159        if (verbose) {
160            apr_file_printf(errfile, "    '%s' -> '%s'"NL,
161                            dbmkey.dptr, dbmval.dptr);
162        }
163
164        rv = apr_dbm_store(dbm, dbmkey, dbmval);
165
166        apr_pool_clear(p);
167
168        if (rv != APR_SUCCESS) {
169            break;
170        }
171    }
172
173    return rv;
174}
175
176int main(int argc, const char *const argv[])
177{
178    apr_pool_t *pool;
179    apr_status_t rv = APR_SUCCESS;
180    apr_getopt_t *opt;
181    const char *optarg;
182    char ch;
183    apr_file_t *infile;
184    apr_dbm_t *outdbm;
185
186    apr_app_initialize(&argc, &argv, NULL);
187    atexit(apr_terminate);
188
189    verbose = 0;
190    format = NULL;
191    input = NULL;
192    output = NULL;
193
194    apr_pool_create(&pool, NULL);
195
196    if (argc) {
197        shortname = apr_filepath_name_get(argv[0]);
198    }
199    else {
200        shortname = "httxt2dbm";
201    }
202
203    apr_file_open_stderr(&errfile, pool);
204    rv = apr_getopt_init(&opt, pool, argc, argv);
205
206    if (rv != APR_SUCCESS) {
207        apr_file_printf(errfile, "Error: apr_getopt_init failed."NL NL);
208        return 1;
209    }
210
211    if (argc <= 1) {
212        usage();
213        return 1;
214    }
215
216    while ((rv = apr_getopt(opt, "vf::i::o::", &ch, &optarg)) == APR_SUCCESS) {
217        switch (ch) {
218        case 'v':
219            if (verbose) {
220                apr_file_printf(errfile, "Error: -v can only be passed once" NL NL);
221                usage();
222                return 1;
223            }
224            verbose = 1;
225            break;
226        case 'f':
227            if (format) {
228                apr_file_printf(errfile, "Error: -f can only be passed once" NL NL);
229                usage();
230                return 1;
231            }
232            format = apr_pstrdup(pool, optarg);
233            break;
234        case 'i':
235            if (input) {
236                apr_file_printf(errfile, "Error: -i can only be passed once" NL NL);
237                usage();
238                return 1;
239            }
240            input = apr_pstrdup(pool, optarg);
241            break;
242        case 'o':
243            if (output) {
244                apr_file_printf(errfile, "Error: -o can only be passed once" NL NL);
245                usage();
246                return 1;
247            }
248            output = apr_pstrdup(pool, optarg);
249            break;
250        }
251    }
252
253    if (rv != APR_EOF) {
254        apr_file_printf(errfile, "Error: Parsing Arguments Failed" NL NL);
255        usage();
256        return 1;
257    }
258
259    if (!input) {
260        apr_file_printf(errfile, "Error: No input file specified." NL NL);
261        usage();
262        return 1;
263    }
264
265    if (!output) {
266        apr_file_printf(errfile, "Error: No output DBM specified." NL NL);
267        usage();
268        return 1;
269    }
270
271    if (!format) {
272        format = "default";
273    }
274
275    if (verbose) {
276        apr_file_printf(errfile, "DBM Format: %s"NL, format);
277    }
278
279    if (!strcmp(input, "-")) {
280        rv = apr_file_open_stdin(&infile, pool);
281    }
282    else {
283        rv = apr_file_open(&infile, input, APR_READ|APR_BUFFERED,
284                           APR_OS_DEFAULT, pool);
285    }
286
287    if (rv != APR_SUCCESS) {
288        apr_file_printf(errfile,
289                        "Error: Cannot open input file '%s': (%d) %s" NL NL,
290                         input, rv, apr_strerror(rv, errbuf, sizeof(errbuf)));
291        return 1;
292    }
293
294    if (verbose) {
295        apr_file_printf(errfile, "Input File: %s"NL, input);
296    }
297
298    rv = apr_dbm_open_ex(&outdbm, format, output, APR_DBM_RWCREATE,
299                    APR_OS_DEFAULT, pool);
300
301    if (APR_STATUS_IS_ENOTIMPL(rv)) {
302        apr_file_printf(errfile,
303                        "Error: The requested DBM Format '%s' is not available." NL NL,
304                         format);
305        return 1;
306    }
307
308    if (rv != APR_SUCCESS) {
309        apr_file_printf(errfile,
310                        "Error: Cannot open output DBM '%s': (%d) %s" NL NL,
311                         output, rv, apr_strerror(rv, errbuf, sizeof(errbuf)));
312        return 1;
313    }
314
315    if (verbose) {
316        apr_file_printf(errfile, "DBM File: %s"NL, output);
317    }
318
319    rv = to_dbm(outdbm, infile, pool);
320
321    if (rv != APR_SUCCESS) {
322        apr_file_printf(errfile,
323                        "Error: Converting to DBM: (%d) %s" NL NL,
324                         rv, apr_strerror(rv, errbuf, sizeof(errbuf)));
325        return 1;
326    }
327
328    apr_dbm_close(outdbm);
329
330    if (verbose) {
331        apr_file_printf(errfile, "Conversion Complete." NL);
332    }
333
334    return 0;
335}
336
337