1 /*
2 ** cdecl -- C gibberish translator
3 ** src/c_sglob.c
4 **
5 ** Copyright (C) 2021 Paul J. Lucas
6 **
7 ** This program is free software: you can redistribute it and/or modify
8 ** it under the terms of the GNU General Public License as published by
9 ** the Free Software Foundation, either version 3 of the License, or
10 ** (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /**
22 * @file
23 * Defines functions for dealing with "sglob" (C++ scoped name glob) objects,
24 * e.g., `S::T::*`, that are used to match snames (C++ scoped names).
25 */
26
27 // local
28 #include "pjl_config.h" /* must go first */
29 /// @cond DOXYGEN_IGNORE
30 #define C_SGLOB_INLINE _GL_EXTERN_INLINE
31 /// @endcond
32 #include "c_sglob.h"
33
34 // standard
35 #include <assert.h>
36 #include <stdlib.h> /* for free(3) */
37
38 ////////// extern functions ///////////////////////////////////////////////////
39
c_sglob_cleanup(c_sglob_t * sglob)40 void c_sglob_cleanup( c_sglob_t *sglob ) {
41 if ( sglob != NULL ) {
42 for ( size_t i = 0; i < sglob->count; ++i )
43 free( sglob->pattern[i] );
44 free( sglob->pattern );
45 c_sglob_init( sglob );
46 }
47 }
48
c_sglob_parse(char const * s,c_sglob_t * sglob)49 void c_sglob_parse( char const *s, c_sglob_t *sglob ) {
50 assert( sglob != NULL );
51
52 if ( s == NULL )
53 return;
54 SKIP_WS( s );
55
56 //
57 // Scan through the scoped glob to count the number of scopes which is the
58 // number of occurrences of `::` plus 1, e.g., `a::b::c` yields 3.
59 //
60 sglob->count = 1;
61 for ( char const *t = s; *t != '\0'; ++t ) {
62 if ( *t == ':' ) {
63 ++t;
64 assert( *t == ':' );
65 ++sglob->count;
66 }
67 } // for
68
69 //
70 // Special case: if the scoped glob starts with `**`, match in any scope.
71 // Skip past `**::` and decrement scope count.
72 //
73 sglob->match_in_any_scope = s[0] == '*' && s[1] == '*';
74 if ( sglob->match_in_any_scope ) {
75 s += 2 /* "**" */;
76 SKIP_WS( s );
77 assert( s[0] == ':' && s[1] == ':' );
78 s += 2 /* "::" */;
79 --sglob->count;
80 }
81
82 sglob->pattern = MALLOC( char*, sglob->count );
83
84 //
85 // Break up scoped glob into array of globs.
86 //
87 char const *glob_begin = SKIP_WS( s );
88 size_t glob_index = 0;
89 for (;;) {
90 switch ( *s ) {
91 case ':':
92 case '\0': { // found end of glob
93 size_t const glob_len = STATIC_CAST( size_t, s - glob_begin );
94 assert( glob_len > 0 );
95 assert( glob_index < sglob->count );
96 sglob->pattern[ glob_index ] = check_strndup( glob_begin, glob_len );
97 if ( *s == '\0' )
98 return;
99 assert( *s == ':' );
100 ++s;
101 assert( *s == ':' );
102 ++s;
103 SKIP_WS( s );
104 assert( is_ident( *s ) || *s == '*' );
105 glob_begin = s;
106 ++glob_index;
107 break;
108 }
109
110 default:
111 assert( is_ident( *s ) );
112 PJL_FALLTHROUGH;
113 case '*':
114 ++s;
115 break;
116 } // switch
117 } // for
118 }
119
120 ///////////////////////////////////////////////////////////////////////////////
121 /* vim:set et sw=2 ts=2: */
122