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