1
/* skel.c --- parsing and unparsing skeletons
3
* ====================================================================
4
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
6
* This software is licensed as described in the file COPYING, which
7
* you should have received as part of this distribution. The terms
8
* are also available at http://subversion.tigris.org/license-1.html.
9
* If newer versions of this license are posted there, you may use a
10
* newer version instead, at your option.
12
* This software consists of voluntary contributions made by many
13
* individuals. For exact contribution history, see the revision
14
* history and logs, available at http://subversion.tigris.org/.
15
* ====================================================================
19
#include "svn_string.h"
21
#include "../key-gen.h"
24
/* Parsing skeletons. */
35
/* We can't use the <ctype.h> macros here, because they are locale-
36
dependent. The syntax of a skel is specified directly in terms of
37
byte values, and is independent of locale. */
39
static const enum char_type skel_char_type[256] = {
40
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
41
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
42
1, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0,
43
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
46
0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
47
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 0, 3, 0, 0,
48
0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
49
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0,
52
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
54
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
58
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65
static skel_t *parse (const char *data, apr_size_t len,
67
static skel_t *list (const char *data, apr_size_t len,
69
static skel_t *implicit_atom (const char *data, apr_size_t len,
71
static skel_t *explicit_atom (const char *data, apr_size_t len,
76
svn_fs_base__parse_skel (const char *data,
80
return parse (data, len, pool);
84
/* Parse any kind of skel object --- atom, or list. */
86
parse (const char *data,
92
/* The empty string isn't a valid skel. */
98
/* Is it a list, or an atom? */
100
return list (data, len, pool);
102
/* Is it a string with an implicit length? */
103
if (skel_char_type[(unsigned char) c] == type_name)
104
return implicit_atom (data, len, pool);
106
/* Otherwise, we assume it's a string with an explicit length;
107
svn_fs_base__getsize will catch the error. */
109
return explicit_atom (data, len, pool);
114
list (const char *data,
118
const char *end = data + len;
119
const char *list_start;
121
/* Verify that the list starts with an opening paren. At the
122
moment, all callers have checked this already, but it's more
124
if (data >= end || *data != '(')
127
/* Mark where the list starts. */
130
/* Skip the opening paren. */
133
/* Parse the children. */
135
skel_t *children = 0;
136
skel_t **tail = &children;
142
/* Skip any whitespace. */
144
&& skel_char_type[(unsigned char) *data] == type_space)
147
/* End of data, but no closing paren? */
158
/* Parse the next element in the list. */
159
element = parse (data, end - data, pool);
163
/* Link that element into our list. */
166
tail = &element->next;
168
/* Advance past that element. */
169
data = element->data + element->len;
172
/* Construct the return value. */
174
skel_t *s = apr_pcalloc (pool, sizeof (*s));
177
s->data = list_start;
178
s->len = data - list_start;
179
s->children = children;
187
/* Parse an atom with implicit length --- one that starts with a name
188
character, terminated by whitespace, '(', ')', or end-of-data. */
190
implicit_atom (const char *data,
194
const char *start = data;
195
const char *end = data + len;
198
/* Verify that the atom starts with a name character. At the
199
moment, all callers have checked this already, but it's more
201
if (data >= end || skel_char_type[(unsigned char) *data] != type_name)
204
/* Find the end of the string. */
206
&& skel_char_type[(unsigned char) *data] != type_space
207
&& skel_char_type[(unsigned char) *data] != type_paren)
210
/* Allocate the skel representing this string. */
211
s = apr_pcalloc (pool, sizeof (*s));
214
s->len = data - start;
220
/* Parse an atom with explicit length --- one that starts with a byte
221
length, as a decimal ASCII number. */
223
explicit_atom (const char *data,
227
const char *end = data + len;
232
/* Parse the length. */
233
size = svn_fs_base__getsize (data, end - data, &next, end - data);
236
/* Exit if we overflowed, or there wasn't a valid number there. */
240
/* Skip the whitespace character after the length. */
241
if (data >= end || skel_char_type[(unsigned char) *data] != type_space)
245
/* Check the length. */
246
if (data + size > end)
249
/* Allocate the skel representing this string. */
250
s = apr_pcalloc (pool, sizeof (skel_t));
260
/* Unparsing skeletons. */
262
static apr_size_t estimate_unparsed_size (skel_t *);
263
static svn_stringbuf_t *unparse (skel_t *, svn_stringbuf_t *, apr_pool_t *);
267
svn_fs_base__unparse_skel (skel_t *skel, apr_pool_t *pool)
269
svn_stringbuf_t *str;
271
/* Allocate a string to hold the data. */
272
str = apr_palloc (pool, sizeof (*str));
274
str->blocksize = estimate_unparsed_size (skel) + 200;
275
str->data = apr_palloc (pool, str->blocksize);
278
return unparse (skel, str, pool);
282
/* Return an estimate of the number of bytes that the external
283
representation of SKEL will occupy. Since reallocing is expensive
284
in pools, it's worth trying to get the buffer size right the first
287
estimate_unparsed_size (skel_t *skel)
292
/* If we have to use the explicit-length form, that'll be
293
two bytes for the length, one byte for the space, and
295
return skel->len + 3;
297
return skel->len + 30;
304
/* Allow space for opening and closing parens, and a space
305
between each pair of elements. */
307
for (child = skel->children; child; child = child->next)
308
total_len += estimate_unparsed_size (child) + 1;
315
/* Return non-zero iff we should use the implicit-length form for SKEL.
316
Assume that SKEL is an atom. */
318
use_implicit (skel_t *skel)
320
/* If it's null, or long, we should use explicit-length form. */
325
/* If it doesn't start with a name character, we must use
326
explicit-length form. */
327
if (skel_char_type[(unsigned char) skel->data[0]] != type_name)
330
/* If it contains any whitespace or parens, then we must use
331
explicit-length form. */
335
for (i = 1; i < skel->len; i++)
336
if (skel_char_type[(unsigned char) skel->data[i]] == type_space
337
|| skel_char_type[(unsigned char) skel->data[i]] == type_paren)
341
/* If we can't reject it for any of the above reasons, then we can
342
use implicit-length form. */
347
/* Append the concrete representation of SKEL to the string STR.
348
Grow S with new space from POOL as necessary. */
349
static svn_stringbuf_t *
350
unparse (skel_t *skel, svn_stringbuf_t *str, apr_pool_t *pool)
354
/* Append an atom to STR. */
355
if (use_implicit (skel))
356
svn_stringbuf_appendbytes (str, skel->data, skel->len);
359
/* Append the length to STR. */
363
length_len = svn_fs_base__putsize (buf, sizeof (buf), skel->len);
367
/* Make sure we have room for the length, the space, and the
369
svn_stringbuf_ensure (str, str->len + length_len + 1 + skel->len);
370
svn_stringbuf_appendbytes (str, buf, length_len);
371
str->data[str->len++] = ' ';
372
svn_stringbuf_appendbytes (str, skel->data, skel->len);
377
/* Append a list to STR. */
380
/* Emit an opening parenthesis. */
381
svn_stringbuf_ensure (str, str->len + 1);
382
str->data[str->len++] = '(';
384
/* Append each element. Emit a space between each pair of elements. */
385
for (child = skel->children; child; child = child->next)
387
unparse (child, str, pool);
390
svn_stringbuf_ensure (str, str->len + 1);
391
str->data[str->len++] = ' ';
395
/* Emit a closing parenthesis. */
396
svn_stringbuf_appendbytes (str, ")", 1);
404
/* Building skels. */
408
svn_fs_base__str_atom (const char *str, apr_pool_t *pool)
410
skel_t *skel = apr_pcalloc (pool, sizeof (*skel));
411
skel->is_atom = TRUE;
413
skel->len = strlen (str);
420
svn_fs_base__mem_atom (const void *addr,
424
skel_t *skel = apr_pcalloc (pool, sizeof (*skel));
425
skel->is_atom = TRUE;
434
svn_fs_base__make_empty_list (apr_pool_t *pool)
436
skel_t *skel = apr_pcalloc (pool, sizeof (*skel));
442
svn_fs_base__prepend (skel_t *skel, skel_t *list_skel)
444
/* If list_skel isn't even a list, somebody's not using this
445
function properly. */
446
if (list_skel->is_atom)
449
skel->next = list_skel->children;
450
list_skel->children = skel;
455
svn_fs_base__append (skel_t *skel, skel_t *list_skel)
457
/* If list_skel isn't even a list, somebody's not using this
458
function properly. */
459
if (list_skel->is_atom)
462
/* No kids? Let's make one. */
463
if (! list_skel->children)
465
list_skel->children = skel;
469
skel_t *tmp = list_skel->children;
471
/* Find the last child... */
476
/* ...and then give her a sister. */
483
/* Examining skels. */
487
svn_fs_base__matches_atom (skel_t *skel, const char *str)
489
if (skel && skel->is_atom)
491
apr_size_t len = strlen (str);
493
return ((skel->len == len
494
&& ! memcmp (skel->data, str, len)) ? TRUE : FALSE);
501
svn_fs_base__atom_matches_string (skel_t *skel, const svn_string_t *str)
503
if (skel && skel->is_atom)
505
return ((skel->len == str->len
506
&& ! memcmp (skel->data, str->data, skel->len)) ? TRUE : FALSE);
513
svn_fs_base__list_length (skel_t *skel)
518
if ((! skel) || skel->is_atom)
521
for (child = skel->children; child; child = child->next)
529
/* Comparing skels. */
532
svn_fs_base__skels_are_equal (skel_t *skel1, skel_t *skel2)
537
/* Else not `eq', but might still be `equal'. */
539
if (skel1->is_atom && skel2->is_atom)
541
if ((skel1->len == skel2->len)
542
&& (! strncmp (skel1->data, skel2->data, skel1->len)))
547
else if (((! skel1->is_atom) && (! skel2->is_atom))
548
&& ((svn_fs_base__list_length (skel1))
549
== (svn_fs_base__list_length (skel2))))
551
int len = svn_fs_base__list_length (skel1);
554
for (i = 0; i < len; i++)
555
if (! svn_fs_base__skels_are_equal ((skel1->children) + i,
556
(skel2->children) + i))
571
svn_fs_base__copy_skel (skel_t *skel, apr_pool_t *pool)
573
skel_t *copy = apr_pcalloc (pool, sizeof (*copy));
577
apr_size_t len = skel->len;
578
char *s = apr_palloc (pool, len);
580
memcpy (s, skel->data, len);
581
copy->is_atom = TRUE;
587
skel_t *skel_child, **copy_child_ptr;
589
copy->is_atom = FALSE;
593
copy_child_ptr = ©->children;
594
for (skel_child = skel->children;
596
skel_child = skel_child->next)
598
*copy_child_ptr = svn_fs_base__copy_skel (skel_child, pool);
599
copy_child_ptr = &(*copy_child_ptr)->next;