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(*s
));
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
;