1 /*
2 *
3 * Copyright (c) 2016, Masatake YAMATO
4 * Copyright (c) 2016, Red Hat, K.K.
5 *
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License version 2 or (at your option) any later version.
8 *
9 */
10
11 #include "general.h" /* must always come first */
12 #include "debug.h"
13 #include "entry.h"
14 #include "kind.h"
15 #include "yaml.h"
16 #include "parse.h"
17 #include "subparser.h"
18
19 #include <stdio.h>
20
21 typedef enum {
22 K_PLAY
23 } ansiblePlaybookKind;
24
25 static kindDefinition AnsiblePlaybookKinds [] = {
26 { true, 'p', "play", "plays" },
27 };
28
29 struct yamlBlockTypeStack {
30 yaml_token_type_t type;
31 int associatedCorkIndex;
32 struct yamlBlockTypeStack *next;
33 };
34
35 /* - name: "THE NAME" */
36 enum ansiblePlaybookPlayDetectingState {
37 DSTAT_PLAY_NAME_INITIAL,
38 DSTAT_PLAY_NAME_KEY,
39 DSTAT_PLAY_NAME_KEY_SCALAR,
40 DSTAT_PLAY_NAME_VALUE,
41 };
42
43
44 struct sAnsiblePlaybookSubparser {
45 yamlSubparser yaml;
46 struct yamlBlockTypeStack *type_stack;
47 enum ansiblePlaybookPlayDetectingState play_detection_state;
48 };
49
pushBlockType(struct sAnsiblePlaybookSubparser * ansible,yaml_token_type_t t)50 static void pushBlockType (struct sAnsiblePlaybookSubparser *ansible, yaml_token_type_t t)
51 {
52 struct yamlBlockTypeStack *s;
53
54 s = xMalloc (1, struct yamlBlockTypeStack);
55
56 s->next = ansible->type_stack;
57 ansible->type_stack = s;
58
59 s->type = t;
60 s->associatedCorkIndex = CORK_NIL;
61 }
62
popBlockType(struct sAnsiblePlaybookSubparser * ansible,yaml_token_t * token)63 static void popBlockType (struct sAnsiblePlaybookSubparser *ansible,
64 yaml_token_t *token)
65 {
66 struct yamlBlockTypeStack *s;
67
68 s = ansible->type_stack;
69 ansible->type_stack = s->next;
70
71 s->next = NULL;
72 tagEntryInfo *tag = getEntryInCorkQueue (s->associatedCorkIndex);
73 if (tag && token)
74 attachYamlPosition (tag, token, true);
75
76 eFree (s);
77 }
78
popAllBlockType(struct sAnsiblePlaybookSubparser * ansible,yaml_token_t * token)79 static void popAllBlockType (struct sAnsiblePlaybookSubparser *ansible,
80 yaml_token_t *token)
81 {
82 while (ansible->type_stack)
83 popBlockType (ansible, token);
84 }
85
stateStackMatch(struct yamlBlockTypeStack * stack,yaml_token_type_t * expectation,unsigned int length,bool partial)86 static bool stateStackMatch (struct yamlBlockTypeStack *stack,
87 yaml_token_type_t *expectation,
88 unsigned int length,
89 bool partial)
90 {
91 if (length == 0)
92 {
93 if (stack == NULL)
94 return true;
95 else if (partial)
96 return true;
97 else
98 return false;
99 }
100
101 if (stack == NULL)
102 return false;
103
104 if (stack->type == expectation[0])
105 return stateStackMatch (stack->next, expectation + 1, length - 1, partial);
106 else
107 return false;
108 }
109
scalarNeq(yaml_token_t * token,unsigned int len,const char * val)110 static bool scalarNeq (yaml_token_t *token, unsigned int len, const char* val)
111 {
112 if ((token->data.scalar.length == len)
113 && (strncmp (val, (char *)token->data.scalar.value, len) == 0))
114 return true;
115 else
116 return false;
117 }
118
ansiblePlaybookPlayStateMachine(struct sAnsiblePlaybookSubparser * ansible,yaml_token_t * token)119 static void ansiblePlaybookPlayStateMachine (struct sAnsiblePlaybookSubparser *ansible,
120 yaml_token_t *token)
121 {
122 yaml_token_type_t play_expect[] = {
123 YAML_BLOCK_MAPPING_START_TOKEN,
124 YAML_BLOCK_SEQUENCE_START_TOKEN,
125 };
126
127 switch (token->type)
128 {
129 case YAML_KEY_TOKEN:
130 if (stateStackMatch (ansible->type_stack,
131 play_expect, ARRAY_SIZE (play_expect),
132 false))
133 ansible->play_detection_state = DSTAT_PLAY_NAME_KEY;
134 else
135 ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
136 break;
137 case YAML_SCALAR_TOKEN:
138 if ((ansible->play_detection_state == DSTAT_PLAY_NAME_KEY)
139 && scalarNeq (token, 4, "name"))
140 ansible->play_detection_state = DSTAT_PLAY_NAME_KEY_SCALAR;
141 else if (ansible->play_detection_state == DSTAT_PLAY_NAME_VALUE)
142 {
143 tagEntryInfo tag;
144 initTagEntry (&tag, (char *)token->data.scalar.value,
145 K_PLAY);
146 attachYamlPosition (&tag, token, false);
147
148 Assert (ansible->type_stack->associatedCorkIndex == CORK_NIL);
149 ansible->type_stack->associatedCorkIndex = makeTagEntry (&tag);
150 ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
151 }
152 else
153 ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
154 break;
155 case YAML_VALUE_TOKEN:
156 if (ansible->play_detection_state == DSTAT_PLAY_NAME_KEY_SCALAR)
157 ansible->play_detection_state = DSTAT_PLAY_NAME_VALUE;
158 else
159 ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
160 break;
161 default:
162 ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
163 break;
164 }
165 }
166
newTokenCallback(yamlSubparser * s,yaml_token_t * token)167 static void newTokenCallback (yamlSubparser *s, yaml_token_t *token)
168 {
169 if (token->type == YAML_BLOCK_SEQUENCE_START_TOKEN
170 || token->type == YAML_BLOCK_MAPPING_START_TOKEN)
171 pushBlockType ((struct sAnsiblePlaybookSubparser *)s, token->type);
172
173 ansiblePlaybookPlayStateMachine ((struct sAnsiblePlaybookSubparser *)s, token);
174
175 if (token->type == YAML_BLOCK_END_TOKEN)
176 popBlockType ((struct sAnsiblePlaybookSubparser *)s, token);
177 else if (token->type == YAML_STREAM_END_TOKEN)
178 popAllBlockType ((struct sAnsiblePlaybookSubparser *)s, token);
179 }
180
inputStart(subparser * s)181 static void inputStart(subparser *s)
182 {
183 ((struct sAnsiblePlaybookSubparser*)s)->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
184 ((struct sAnsiblePlaybookSubparser*)s)->type_stack = NULL;
185 }
186
inputEnd(subparser * s)187 static void inputEnd(subparser *s)
188 {
189 popAllBlockType ((struct sAnsiblePlaybookSubparser *)s, NULL);
190 Assert (((struct sAnsiblePlaybookSubparser*)s)->type_stack == NULL);
191 }
192
193 static void
findAnsiblePlaybookTags(void)194 findAnsiblePlaybookTags (void)
195 {
196 scheduleRunningBaseparser (0);
197 }
198
AnsiblePlaybookParser(void)199 extern parserDefinition* AnsiblePlaybookParser (void)
200 {
201 static struct sAnsiblePlaybookSubparser ansiblePlaybookSubparser = {
202 .yaml = {
203 .subparser = {
204 .direction = SUBPARSER_BI_DIRECTION,
205 .inputStart = inputStart,
206 .inputEnd = inputEnd,
207 },
208 .newTokenNotfify = newTokenCallback
209 },
210 };
211 static parserDependency dependencies [] = {
212 { DEPTYPE_SUBPARSER, "Yaml", &ansiblePlaybookSubparser },
213 };
214
215 parserDefinition* const def = parserNew ("AnsiblePlaybook");
216
217
218 def->dependencies = dependencies;
219 def->dependencyCount = ARRAY_SIZE (dependencies);
220
221 def->kindTable = AnsiblePlaybookKinds;
222 def->kindCount = ARRAY_SIZE (AnsiblePlaybookKinds);
223 def->parser = findAnsiblePlaybookTags;
224 def->useCork = CORK_QUEUE;
225 return def;
226 }
227
228 /* vi:set tabstop=4 shiftwidth=4: */
229