source: azure_iot_hub_riscv/trunk/azure_iot_sdk/serializer/src/commanddecoder.c@ 453

Last change on this file since 453 was 453, checked in by coas-nagasima, 4 years ago

ファイルを追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 49.3 KB
Line 
1// Copyright (c) Microsoft. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4#include <stdlib.h>
5#include "azure_c_shared_utility/optimize_size.h"
6#include "azure_c_shared_utility/gballoc.h"
7
8#include <stddef.h>
9
10#include "commanddecoder.h"
11#include "multitree.h"
12#include "azure_c_shared_utility/crt_abstractions.h"
13#include "azure_c_shared_utility/xlogging.h"
14#include "schema.h"
15#include "codefirst.h"
16#include "jsondecoder.h"
17
18MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(COMMANDDECODER_RESULT, COMMANDDECODER_RESULT_VALUES);
19
20typedef struct COMMAND_DECODER_HANDLE_DATA_TAG
21{
22 METHOD_CALLBACK_FUNC methodCallback;
23 void* methodCallbackContext;
24 SCHEMA_MODEL_TYPE_HANDLE ModelHandle;
25 ACTION_CALLBACK_FUNC ActionCallback;
26 void* ActionCallbackContext;
27} COMMAND_DECODER_HANDLE_DATA;
28
29static int DecodeValueFromNode(SCHEMA_HANDLE schemaHandle, AGENT_DATA_TYPE* agentDataType, MULTITREE_HANDLE node, const char* edmTypeName)
30{
31 /* because "pottentially uninitialized variable on MS compiler" */
32 int result = 0;
33 const char* argStringValue;
34 AGENT_DATA_TYPE_TYPE primitiveType;
35
36 /* Codes_SRS_COMMAND_DECODER_99_029:[ If the argument type is complex then a complex type value shall be built from the child nodes.] */
37 if ((primitiveType = CodeFirst_GetPrimitiveType(edmTypeName)) == EDM_NO_TYPE)
38 {
39 SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle;
40 size_t propertyCount;
41
42 /* Codes_SRS_COMMAND_DECODER_99_033:[ In order to determine which are the members of a complex types, Schema APIs for structure types shall be used.] */
43 if (((structTypeHandle = Schema_GetStructTypeByName(schemaHandle, edmTypeName)) == NULL) ||
44 (Schema_GetStructTypePropertyCount(structTypeHandle, &propertyCount) != SCHEMA_OK))
45 {
46 /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
47 result = MU_FAILURE;
48 LogError("Getting Struct information failed.");
49 }
50 else
51 {
52 if (propertyCount == 0)
53 {
54 /* Codes_SRS_COMMAND_DECODER_99_034:[ If Schema APIs indicate that a complex type has 0 members then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
55 result = MU_FAILURE;
56 LogError("Struct type with 0 members is not allowed");
57 }
58 else
59 {
60 AGENT_DATA_TYPE* memberValues = (AGENT_DATA_TYPE*)calloc(1, (sizeof(AGENT_DATA_TYPE)* propertyCount));
61 if (memberValues == NULL)
62 {
63 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
64 result = MU_FAILURE;
65 LogError("Failed allocating member values for command argument");
66 }
67 else
68 {
69 const char** memberNames = (const char**)malloc(sizeof(const char*)* propertyCount);
70 if (memberNames == NULL)
71 {
72 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
73 result = MU_FAILURE;
74 LogError("Failed allocating member names for command argument.");
75 }
76 else
77 {
78 size_t j;
79 size_t k;
80
81 for (j = 0; j < propertyCount; j++)
82 {
83 SCHEMA_PROPERTY_HANDLE propertyHandle;
84 MULTITREE_HANDLE memberNode;
85 const char* propertyName;
86 const char* propertyType;
87
88 if ((propertyHandle = Schema_GetStructTypePropertyByIndex(structTypeHandle, j)) == NULL)
89 {
90 /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
91 result = MU_FAILURE;
92 LogError("Getting struct member failed.");
93 break;
94 }
95 else if (((propertyName = Schema_GetPropertyName(propertyHandle)) == NULL) ||
96 ((propertyType = Schema_GetPropertyType(propertyHandle)) == NULL))
97 {
98 /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
99 result = MU_FAILURE;
100 LogError("Getting the struct member information failed.");
101 break;
102 }
103 else
104 {
105 memberNames[j] = propertyName;
106
107 /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */
108 if (MultiTree_GetChildByName(node, memberNames[j], &memberNode) != MULTITREE_OK)
109 {
110 /* Codes_SRS_COMMAND_DECODER_99_028:[ If decoding the argument fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
111 result = MU_FAILURE;
112 LogError("Getting child %s failed", propertyName);
113 break;
114 }
115 /* Codes_SRS_COMMAND_DECODER_99_032:[ Nesting shall be supported for complex type.] */
116 else if ((result = DecodeValueFromNode(schemaHandle, &memberValues[j], memberNode, propertyType)) != 0)
117 {
118 break;
119 }
120 }
121 }
122
123 if (j == propertyCount)
124 {
125 /* Codes_SRS_COMMAND_DECODER_99_031:[ The complex type value that aggregates the children shall be built by using the Create_AGENT_DATA_TYPE_from_Members.] */
126 if (Create_AGENT_DATA_TYPE_from_Members(agentDataType, edmTypeName, propertyCount, (const char* const*)memberNames, memberValues) != AGENT_DATA_TYPES_OK)
127 {
128 /* Codes_SRS_COMMAND_DECODER_99_028:[ If decoding the argument fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
129 result = MU_FAILURE;
130 LogError("Creating the agent data type from members failed.");
131 }
132 else
133 {
134 result = 0;
135 }
136 }
137
138 for (k = 0; k < j; k++)
139 {
140 Destroy_AGENT_DATA_TYPE(&memberValues[k]);
141 }
142
143 free((void*)memberNames);
144 }
145
146 free(memberValues);
147 }
148 }
149 }
150 }
151 else
152 {
153 /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */
154 if (MultiTree_GetValue(node, (const void **)&argStringValue) != MULTITREE_OK)
155 {
156 /* Codes_SRS_COMMAND_DECODER_99_012:[ If any argument is missing in the command text then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
157 result = MU_FAILURE;
158 LogError("Getting the string from the multitree failed.");
159 }
160 /* Codes_SRS_COMMAND_DECODER_99_027:[ The value for an argument of primitive type shall be decoded by using the CreateAgentDataType_From_String API.] */
161 else if (CreateAgentDataType_From_String(argStringValue, primitiveType, agentDataType) != AGENT_DATA_TYPES_OK)
162 {
163 /* Codes_SRS_COMMAND_DECODER_99_028:[ If decoding the argument fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
164 result = MU_FAILURE;
165 LogError("Failed parsing node %s.", argStringValue);
166 }
167 }
168
169 return result;
170}
171
172static EXECUTE_COMMAND_RESULT DecodeAndExecuteModelAction(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, SCHEMA_MODEL_TYPE_HANDLE modelHandle, const char* relativeActionPath, const char* actionName, MULTITREE_HANDLE commandNode)
173{
174 EXECUTE_COMMAND_RESULT result;
175 char tempStr[128];
176 size_t strLength = strlen(actionName);
177
178 if (strLength <= 1)
179 {
180 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
181 LogError("Invalid action name");
182 result = EXECUTE_COMMAND_ERROR;
183 }
184 else if (strLength >= 128)
185 {
186 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
187 LogError("Invalid action name length");
188 result = EXECUTE_COMMAND_ERROR;
189 }
190 else
191 {
192 /* Codes_SRS_COMMAND_DECODER_99_006:[ The action name shall be decoded from the element "Name" of the command JSON.] */
193 SCHEMA_ACTION_HANDLE modelActionHandle;
194 size_t argCount;
195 MULTITREE_HANDLE parametersTreeNode;
196
197 if (memcpy(tempStr, actionName, strLength-1) == NULL)
198 {
199 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
200 LogError("Invalid action name.");
201 result = EXECUTE_COMMAND_ERROR;
202 }
203 /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */
204 else if (MultiTree_GetChildByName(commandNode, "Parameters", &parametersTreeNode) != MULTITREE_OK)
205 {
206 /* Codes_SRS_COMMAND_DECODER_01_015: [If any MultiTree API call fails then the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
207 LogError("Error getting Parameters node.");
208 result = EXECUTE_COMMAND_ERROR;
209 }
210 else
211 {
212 tempStr[strLength-1] = 0;
213 /* Codes_SRS_COMMAND_DECODER_99_009:[ CommandDecoder shall call Schema_GetModelActionByName to obtain the information about a specific action.] */
214 if (((modelActionHandle = Schema_GetModelActionByName(modelHandle, tempStr)) == NULL) ||
215 (Schema_GetModelActionArgumentCount(modelActionHandle, &argCount) != SCHEMA_OK))
216 {
217 /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
218 LogError("Failed reading action %s from the schema", tempStr);
219 result = EXECUTE_COMMAND_ERROR;
220 }
221 else
222 {
223 AGENT_DATA_TYPE* arguments = NULL;
224
225 if (argCount > 0)
226 {
227 arguments = (AGENT_DATA_TYPE*)calloc(1, (sizeof(AGENT_DATA_TYPE)* argCount));
228 }
229
230 if ((argCount > 0) &&
231 (arguments == NULL))
232 {
233 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
234 LogError("Failed allocating arguments array");
235 result = EXECUTE_COMMAND_ERROR;
236 }
237 else
238 {
239 size_t i;
240 size_t j;
241 result = EXECUTE_COMMAND_ERROR;
242
243 /* Codes_SRS_COMMAND_DECODER_99_011:[ CommandDecoder shall attempt to extract from the command text the value for each action argument.] */
244 for (i = 0; i < argCount; i++)
245 {
246 SCHEMA_ACTION_ARGUMENT_HANDLE actionArgumentHandle;
247 MULTITREE_HANDLE argumentNode;
248 const char* argName;
249 const char* argType;
250
251 if (((actionArgumentHandle = Schema_GetModelActionArgumentByIndex(modelActionHandle, i)) == NULL) ||
252 ((argName = Schema_GetActionArgumentName(actionArgumentHandle)) == NULL) ||
253 ((argType = Schema_GetActionArgumentType(actionArgumentHandle)) == NULL))
254 {
255 LogError("Failed getting the argument information from the schema");
256 result = EXECUTE_COMMAND_ERROR;
257 break;
258 }
259 /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */
260 /* Codes_SRS_COMMAND_DECODER_01_008: [Each argument shall be looked up as a field, member of the "Parameters" node.] */
261 else if (MultiTree_GetChildByName(parametersTreeNode, argName, &argumentNode) != MULTITREE_OK)
262 {
263 /* Codes_SRS_COMMAND_DECODER_99_012:[ If any argument is missing in the command text then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
264 LogError("Missing argument %s", argName);
265 result = EXECUTE_COMMAND_ERROR;
266 break;
267 }
268 else if (DecodeValueFromNode(schemaHandle, &arguments[i], argumentNode, argType) != 0)
269 {
270 result = EXECUTE_COMMAND_ERROR;
271 break;
272 }
273 }
274
275 if (i == argCount)
276 {
277 /* Codes_SRS_COMMAND_DECODER_99_005:[ If an Invoke Action is decoded successfully then the callback actionCallback shall be called, passing to it the callback action context, decoded name and arguments.] */
278 result = commandDecoderInstance->ActionCallback(commandDecoderInstance->ActionCallbackContext, relativeActionPath, tempStr, argCount, arguments);
279 }
280
281 for (j = 0; j < i; j++)
282 {
283 Destroy_AGENT_DATA_TYPE(&arguments[j]);
284 }
285
286 if (arguments != NULL)
287 {
288 free(arguments);
289 }
290 }
291 }
292 }
293 }
294 return result;
295}
296
297static METHODRETURN_HANDLE DecodeAndExecuteModelMethod(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, SCHEMA_MODEL_TYPE_HANDLE modelHandle, const char* relativeMethodPath, const char* methodName, MULTITREE_HANDLE methodTree)
298{
299 METHODRETURN_HANDLE result;
300 size_t strLength = strlen(methodName);
301
302 if (strLength == 0)
303 {
304 /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
305 LogError("Invalid method name");
306 result = NULL;
307 }
308 else
309 {
310 SCHEMA_METHOD_HANDLE modelMethodHandle;
311 size_t argCount;
312
313#ifdef _MSC_VER
314#pragma warning(suppress: 6324) /* We intentionally use here strncpy */
315#endif
316
317 /*Codes_SRS_COMMAND_DECODER_02_020: [ CommandDecoder_ExecuteMethod shall verify that the model has a method called methodName. ]*/
318 if (((modelMethodHandle = Schema_GetModelMethodByName(modelHandle, methodName)) == NULL) ||
319 (Schema_GetModelMethodArgumentCount(modelMethodHandle, &argCount) != SCHEMA_OK))
320 {
321 /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
322 LogError("Failed reading method %s from the schema", methodName);
323 result = NULL;
324 }
325 else
326 {
327 /*Codes_SRS_COMMAND_DECODER_02_021: [ For every argument of methodName, CommandDecoder_ExecuteMethod shall build an AGENT_DATA_TYPE from the node with the same name from the MULTITREE_HANDLE. ]*/
328
329 if (argCount == 0)
330 {
331 /*no need for any parameters*/
332 result = commandDecoderInstance->methodCallback(commandDecoderInstance->methodCallbackContext, relativeMethodPath, methodName, 0, NULL);
333 }
334 else
335 {
336 AGENT_DATA_TYPE* arguments;
337 arguments = (AGENT_DATA_TYPE*)calloc(1, (sizeof(AGENT_DATA_TYPE)* argCount));
338 if (arguments == NULL)
339 {
340 LogError("Failed allocating arguments array");
341 result = NULL;
342 }
343 else
344 {
345 size_t i;
346 size_t j;
347 result = NULL;
348
349 for (i = 0; i < argCount; i++)
350 {
351 SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle;
352 MULTITREE_HANDLE argumentNode;
353 const char* argName;
354 const char* argType;
355
356 if (((methodArgumentHandle = Schema_GetModelMethodArgumentByIndex(modelMethodHandle, i)) == NULL) ||
357 ((argName = Schema_GetMethodArgumentName(methodArgumentHandle)) == NULL) ||
358 ((argType = Schema_GetMethodArgumentType(methodArgumentHandle)) == NULL))
359 {
360 /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
361 LogError("Failed getting the argument information from the schema");
362 result = NULL;
363 break;
364 }
365 else if (MultiTree_GetChildByName(methodTree, argName, &argumentNode) != MULTITREE_OK)
366 {
367 /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
368 LogError("Missing argument %s", argName);
369 result = NULL;
370 break;
371 }
372 else if (DecodeValueFromNode(schemaHandle, &arguments[i], argumentNode, argType) != 0)
373 {
374 /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
375 LogError("failure in DecodeValueFromNode");
376 result = NULL;
377 break;
378 }
379 }
380
381 if (i == argCount)
382 {
383 /*Codes_SRS_COMMAND_DECODER_02_022: [ CommandDecoder_ExecuteMethod shall call methodCallback passing the context, the methodName, number of arguments and the AGENT_DATA_TYPE. ]*/
384 /*Codes_SRS_COMMAND_DECODER_02_024: [ Otherwise, CommandDecoder_ExecuteMethod shall return what methodCallback returns. ]*/
385 result = commandDecoderInstance->methodCallback(commandDecoderInstance->methodCallbackContext, relativeMethodPath, methodName, argCount, arguments);
386 }
387
388 for (j = 0; j < i; j++)
389 {
390 Destroy_AGENT_DATA_TYPE(&arguments[j]);
391 }
392
393 free(arguments);
394 }
395
396 }
397 }
398
399 }
400 return result;
401}
402
403
404static EXECUTE_COMMAND_RESULT ScanActionPathAndExecuteAction(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, const char* actionPath, MULTITREE_HANDLE commandNode)
405{
406 EXECUTE_COMMAND_RESULT result;
407 char* relativeActionPath;
408 const char* actionName = actionPath;
409 SCHEMA_MODEL_TYPE_HANDLE modelHandle = commandDecoderInstance->ModelHandle;
410
411 /* Codes_SRS_COMMAND_DECODER_99_035:[ CommandDecoder_ExecuteCommand shall support paths to actions that are in child models (i.e. ChildModel/SomeAction.] */
412 do
413 {
414 /* find the slash */
415 const char* slashPos = strchr(actionName, '/');
416 if (slashPos == NULL)
417 {
418 size_t relativeActionPathLength;
419
420 /* Codes_SRS_COMMAND_DECODER_99_037:[ The relative path passed to the actionCallback shall be in the format "childModel1/childModel2/.../childModelN".] */
421 if (actionName == actionPath)
422 {
423 relativeActionPathLength = 0;
424 }
425 else
426 {
427 relativeActionPathLength = actionName - actionPath - 1;
428 }
429
430 relativeActionPath = (char*)malloc(relativeActionPathLength + 1);
431 if (relativeActionPath == NULL)
432 {
433 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
434 LogError("Failed allocating relative action path");
435 result = EXECUTE_COMMAND_ERROR;
436 }
437 else
438 {
439 strncpy(relativeActionPath, actionPath, relativeActionPathLength);
440 relativeActionPath[relativeActionPathLength] = 0;
441
442 /* no slash found, this must be an action */
443 result = DecodeAndExecuteModelAction(commandDecoderInstance, schemaHandle, modelHandle, relativeActionPath, actionName, commandNode);
444
445 free(relativeActionPath);
446 actionName = NULL;
447 }
448 break;
449 }
450 else
451 {
452 /* found a slash, get the child model name */
453 size_t modelLength = slashPos - actionName;
454 char* childModelName = (char*)malloc(modelLength + 1);
455 if (childModelName == NULL)
456 {
457 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
458 LogError("Failed allocating child model name");
459 result = EXECUTE_COMMAND_ERROR;
460 break;
461 }
462 else
463 {
464 strncpy(childModelName, actionName, modelLength);
465 childModelName[modelLength] = 0;
466
467 /* find the model */
468 modelHandle = Schema_GetModelModelByName(modelHandle, childModelName);
469 if (modelHandle == NULL)
470 {
471 /* Codes_SRS_COMMAND_DECODER_99_036:[ If a child model cannot be found by using Schema APIs then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
472 LogError("Getting the model %s failed", childModelName);
473 free(childModelName);
474 result = EXECUTE_COMMAND_ERROR;
475 break;
476 }
477 else
478 {
479 free(childModelName);
480 actionName = slashPos + 1;
481 result = EXECUTE_COMMAND_ERROR; /*this only exists to quench a compiler warning about returning an uninitialized variable, which is not possible by design*/
482 }
483 }
484 }
485 } while (actionName != NULL);
486 return result;
487}
488
489static METHODRETURN_HANDLE ScanMethodPathAndExecuteMethod(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, const char* fullMethodName, MULTITREE_HANDLE methodTree)
490{
491 METHODRETURN_HANDLE result;
492 char* relativeMethodPath;
493 const char* methodName = fullMethodName;
494 SCHEMA_MODEL_TYPE_HANDLE modelHandle = commandDecoderInstance->ModelHandle;
495
496 /*Codes_SRS_COMMAND_DECODER_02_018: [ CommandDecoder_ExecuteMethod shall validate that consecutive segments of the fullMethodName exist in the model. ]*/
497 /*Codes_SRS_COMMAND_DECODER_02_019: [ CommandDecoder_ExecuteMethod shall locate the final model to which the methodName applies. ]*/
498 do
499 {
500 /* find the slash */
501 const char* slashPos = strchr(methodName, '/');
502 if (slashPos == NULL)
503 {
504 size_t relativeMethodPathLength;
505
506 if (methodName == fullMethodName)
507 {
508 relativeMethodPathLength = 0;
509 }
510 else
511 {
512 relativeMethodPathLength = methodName - fullMethodName - 1;
513 }
514
515 relativeMethodPath = (char*)malloc(relativeMethodPathLength + 1);
516 if (relativeMethodPath == NULL)
517 {
518 /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
519 LogError("Failed allocating relative method path");
520 result = NULL;
521 }
522 else
523 {
524 strncpy(relativeMethodPath, fullMethodName, relativeMethodPathLength);
525 relativeMethodPath[relativeMethodPathLength] = 0;
526
527 /* no slash found, this must be an method */
528 result = DecodeAndExecuteModelMethod(commandDecoderInstance, schemaHandle, modelHandle, relativeMethodPath, methodName, methodTree);
529
530 free(relativeMethodPath);
531 methodName = NULL;
532 }
533 break;
534 }
535 else
536 {
537 /* found a slash, get the child model name */
538 size_t modelLength = slashPos - methodName;
539 char* childModelName = (char*)malloc(modelLength + 1);
540 if (childModelName == NULL)
541 {
542 /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
543 LogError("Failed allocating child model name");
544 result = NULL;
545 break;
546 }
547 else
548 {
549 strncpy(childModelName, methodName, modelLength);
550 childModelName[modelLength] = 0;
551
552 /* find the model */
553 modelHandle = Schema_GetModelModelByName(modelHandle, childModelName);
554 if (modelHandle == NULL)
555 {
556 /*Codes_SRS_COMMAND_DECODER_02_023: [ If any of the previous operations fail, then CommandDecoder_ExecuteMethod shall return NULL. ]*/
557 LogError("Getting the model %s failed", childModelName);
558 free(childModelName);
559 result = NULL;
560 break;
561 }
562 else
563 {
564 free(childModelName);
565 methodName = slashPos + 1;
566 result = NULL; /*this only exists to quench a compiler warning about returning an uninitialized variable, which is not possible by design*/
567 }
568 }
569 }
570 } while (methodName != NULL);
571 return result;
572}
573
574static EXECUTE_COMMAND_RESULT DecodeCommand(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, MULTITREE_HANDLE commandNode)
575{
576 EXECUTE_COMMAND_RESULT result;
577 SCHEMA_HANDLE schemaHandle;
578
579 /* Codes_SRS_COMMAND_DECODER_99_022:[ CommandDecoder shall use the Schema APIs to obtain the information about the entity set name and namespace] */
580 if ((schemaHandle = Schema_GetSchemaForModelType(commandDecoderInstance->ModelHandle)) == NULL)
581 {
582 /* Codes_SRS_COMMAND_DECODER_99_010:[ If any Schema API fails then the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
583 LogError("Getting schema information failed");
584 result = EXECUTE_COMMAND_ERROR;
585 }
586 else
587 {
588 const char* actionName;
589 MULTITREE_HANDLE nameTreeNode;
590
591 /* Codes_SRS_COMMAND_DECODER_01_014: [CommandDecoder shall use the MultiTree APIs to extract a specific element from the command JSON.] */
592 /* Codes_SRS_COMMAND_DECODER_99_006:[ The action name shall be decoded from the element "name" of the command JSON.] */
593 if ((MultiTree_GetChildByName(commandNode, "Name", &nameTreeNode) != MULTITREE_OK) ||
594 (MultiTree_GetValue(nameTreeNode, (const void **)&actionName) != MULTITREE_OK))
595 {
596 /* Codes_SRS_COMMAND_DECODER_01_015: [If any MultiTree API call fails then the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
597 LogError("Getting action name failed.");
598 result = EXECUTE_COMMAND_ERROR;
599 }
600 else if (strlen(actionName) < 2)
601 {
602 /* Codes_SRS_COMMAND_DECODER_99_021:[ If the parsing of the command fails for any other reason the command shall not be dispatched.] */
603 LogError("Invalid action name.");
604 result = EXECUTE_COMMAND_ERROR;
605 }
606 else
607 {
608 actionName++;
609 result = ScanActionPathAndExecuteAction(commandDecoderInstance, schemaHandle, actionName, commandNode);
610 }
611 }
612 return result;
613}
614
615static METHODRETURN_HANDLE DecodeMethod(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, const char* fullMethodName, MULTITREE_HANDLE methodTree)
616{
617 METHODRETURN_HANDLE result;
618 SCHEMA_HANDLE schemaHandle;
619
620 /*Codes_SRS_COMMAND_DECODER_02_017: [ CommandDecoder_ExecuteMethod shall get the SCHEMA_HANDLE associated with the modelHandle passed at CommandDecoder_Create. ]*/
621 if ((schemaHandle = Schema_GetSchemaForModelType(commandDecoderInstance->ModelHandle)) == NULL)
622 {
623 LogError("Getting schema information failed");
624 result = NULL;
625 }
626 else
627 {
628 result = ScanMethodPathAndExecuteMethod(commandDecoderInstance, schemaHandle, fullMethodName, methodTree);
629
630 }
631 return result;
632}
633
634/*Codes_SRS_COMMAND_DECODER_01_009: [Whenever CommandDecoder_ExecuteCommand is the command shall be decoded and further dispatched to the actionCallback passed in CommandDecoder_Create.]*/
635EXECUTE_COMMAND_RESULT CommandDecoder_ExecuteCommand(COMMAND_DECODER_HANDLE handle, const char* command)
636{
637 EXECUTE_COMMAND_RESULT result;
638 COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle;
639 /*Codes_SRS_COMMAND_DECODER_01_010: [If either the buffer or the receiveCallbackContext argument is NULL, the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
640 if (
641 (command == NULL) ||
642 (commandDecoderInstance == NULL)
643 )
644 {
645 LogError("Invalid argument, COMMAND_DECODER_HANDLE handle=%p, const char* command=%p", handle, command);
646 result = EXECUTE_COMMAND_ERROR;
647 }
648 else
649 {
650 size_t size = strlen(command);
651 char* commandJSON;
652
653 /* Codes_SRS_COMMAND_DECODER_01_011: [If the size of the command is 0 then the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
654 if (size == 0)
655 {
656 LogError("Failed because command size is zero");
657 result = EXECUTE_COMMAND_ERROR;
658 }
659 /*Codes_SRS_COMMAND_DECODER_01_013: [If parsing the JSON to a multi tree fails, the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
660 else if ((commandJSON = (char*)malloc(size + 1)) == NULL)
661 {
662 LogError("Failed to allocate temporary storage for the commands JSON");
663 result = EXECUTE_COMMAND_ERROR;
664 }
665 else
666 {
667 MULTITREE_HANDLE commandsTree;
668
669 (void)memcpy(commandJSON, command, size);
670 commandJSON[size] = '\0';
671
672 /* Codes_SRS_COMMAND_DECODER_01_012: [CommandDecoder shall decode the command JSON contained in buffer to a multi-tree by using JSONDecoder_JSON_To_MultiTree.] */
673 if (JSONDecoder_JSON_To_MultiTree(commandJSON, &commandsTree) != JSON_DECODER_OK)
674 {
675 /* Codes_SRS_COMMAND_DECODER_01_013: [If parsing the JSON to a multi tree fails, the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.] */
676 LogError("Decoding JSON to a multi tree failed");
677 result = EXECUTE_COMMAND_ERROR;
678 }
679 else
680 {
681 result = DecodeCommand(commandDecoderInstance, commandsTree);
682
683 /* Codes_SRS_COMMAND_DECODER_01_016: [CommandDecoder shall ensure that the multi-tree resulting from JSONDecoder_JSON_To_MultiTree is freed after the commands are executed.] */
684 MultiTree_Destroy(commandsTree);
685 }
686
687 free(commandJSON);
688 }
689 }
690 return result;
691}
692
693METHODRETURN_HANDLE CommandDecoder_ExecuteMethod(COMMAND_DECODER_HANDLE handle, const char* fullMethodName, const char* methodPayload)
694{
695 METHODRETURN_HANDLE result;
696 /*Codes_SRS_COMMAND_DECODER_02_014: [ If handle is NULL then CommandDecoder_ExecuteMethod shall fail and return NULL. ]*/
697 /*Codes_SRS_COMMAND_DECODER_02_015: [ If fulMethodName is NULL then CommandDecoder_ExecuteMethod shall fail and return NULL. ]*/
698 if (
699 (handle == NULL) ||
700 (fullMethodName == NULL) /*methodPayload can be NULL*/
701 )
702 {
703 LogError("Invalid argument, COMMAND_DECODER_HANDLE handle=%p, const char* fullMethodName=%p", handle, fullMethodName);
704 result = NULL;
705 }
706 else
707 {
708 COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle;
709 /*Codes_SRS_COMMAND_DECODER_02_025: [ If methodCallback is NULL then CommandDecoder_ExecuteMethod shall fail and return NULL. ]*/
710 if (commandDecoderInstance->methodCallback == NULL)
711 {
712 LogError("unable to execute a method when the methodCallback passed in CommandDecoder_Create is NULL");
713 result = NULL;
714 }
715 else
716 {
717 /*Codes_SRS_COMMAND_DECODER_02_016: [ If methodPayload is not NULL then CommandDecoder_ExecuteMethod shall build a MULTITREE_HANDLE out of methodPayload. ]*/
718 if (methodPayload == NULL)
719 {
720 result = DecodeMethod(commandDecoderInstance, fullMethodName, NULL);
721 }
722 else
723 {
724 char* methodJSON;
725
726 if (mallocAndStrcpy_s(&methodJSON, methodPayload) != 0)
727 {
728 LogError("Failed to allocate temporary storage for the method JSON");
729 result = NULL;
730 }
731 else
732 {
733 MULTITREE_HANDLE methodTree;
734 if (JSONDecoder_JSON_To_MultiTree(methodJSON, &methodTree) != JSON_DECODER_OK)
735 {
736 LogError("Decoding JSON to a multi tree failed");
737 result = NULL;
738 }
739 else
740 {
741 result = DecodeMethod(commandDecoderInstance, fullMethodName, methodTree);
742 MultiTree_Destroy(methodTree);
743 }
744 free(methodJSON);
745 }
746 }
747 }
748 }
749 return result;
750}
751
752
753COMMAND_DECODER_HANDLE CommandDecoder_Create(SCHEMA_MODEL_TYPE_HANDLE modelHandle, ACTION_CALLBACK_FUNC actionCallback, void* actionCallbackContext, METHOD_CALLBACK_FUNC methodCallback, void* methodCallbackContext)
754{
755 COMMAND_DECODER_HANDLE_DATA* result;
756 /* Codes_SRS_COMMAND_DECODER_99_019:[ For all exposed APIs argument validity checks shall precede other checks.] */
757 /* Codes_SRS_COMMAND_DECODER_01_003: [ If modelHandle is NULL, CommandDecoder_Create shall return NULL. ]*/
758 if (modelHandle == NULL)
759 {
760 LogError("Invalid arguments: SCHEMA_MODEL_TYPE_HANDLE modelHandle=%p, ACTION_CALLBACK_FUNC actionCallback=%p, void* actionCallbackContext=%p, METHOD_CALLBACK_FUNC methodCallback=%p, void* methodCallbackContext=%p",
761 modelHandle, actionCallback, actionCallbackContext, methodCallback, methodCallbackContext);
762 result = NULL;
763 }
764 else
765 {
766 /* Codes_SRS_COMMAND_DECODER_01_001: [CommandDecoder_Create shall create a new instance of a CommandDecoder.] */
767 result = malloc(sizeof(COMMAND_DECODER_HANDLE_DATA));
768 if (result == NULL)
769 {
770 /* Codes_SRS_COMMAND_DECODER_01_004: [If any error is encountered during CommandDecoder_Create CommandDecoder_Create shall return NULL.] */
771 /*return as is*/
772 }
773 else
774 {
775 result->ModelHandle = modelHandle;
776 result->ActionCallback = actionCallback;
777 result->ActionCallbackContext = actionCallbackContext;
778 result->methodCallback = methodCallback;
779 result->methodCallbackContext = methodCallbackContext;
780 }
781 }
782
783 return result;
784}
785
786void CommandDecoder_Destroy(COMMAND_DECODER_HANDLE commandDecoderHandle)
787{
788 /* Codes_SRS_COMMAND_DECODER_01_007: [If CommandDecoder_Destroy is called with a NULL handle, CommandDecoder_Destroy shall do nothing.] */
789 if (commandDecoderHandle != NULL)
790 {
791 COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)commandDecoderHandle;
792
793 /* Codes_SRS_COMMAND_DECODER_01_005: [CommandDecoder_Destroy shall free all resources associated with the commandDecoderHandle instance.] */
794 free(commandDecoderInstance);
795 }
796}
797
798MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(AGENT_DATA_TYPE_TYPE, AGENT_DATA_TYPE_TYPE_VALUES);
799
800/*validates that the multitree (coming from a JSON) is actually a serialization of the model (complete or incomplete)*/
801/*if the serialization contains more than the model, then it fails.*/
802/*if the serialization does not contain mandatory items from the model, it fails*/
803static bool validateModel_vs_Multitree(void* startAddress, SCHEMA_MODEL_TYPE_HANDLE modelHandle, MULTITREE_HANDLE desiredPropertiesTree, size_t offset)
804{
805
806 bool result;
807 size_t nChildren;
808 size_t nProcessedChildren = 0;
809 (void)MultiTree_GetChildCount(desiredPropertiesTree, &nChildren);
810 for (size_t i = 0;i < nChildren;i++)
811 {
812 MULTITREE_HANDLE child;
813 if (MultiTree_GetChild(desiredPropertiesTree, i, &child) != MULTITREE_OK)
814 {
815 LogError("failure in MultiTree_GetChild");
816 i = nChildren;
817 }
818 else
819 {
820 STRING_HANDLE childName = STRING_new();
821 if (childName == NULL)
822 {
823 LogError("failure to STRING_new");
824 i = nChildren;
825 }
826 else
827 {
828 if (MultiTree_GetName(child, childName) != MULTITREE_OK)
829 {
830 LogError("failure to MultiTree_GetName");
831 i = nChildren;
832 }
833 else
834 {
835 const char *childName_str = STRING_c_str(childName);
836 SCHEMA_MODEL_ELEMENT elementType = Schema_GetModelElementByName(modelHandle, childName_str);
837 switch (elementType.elementType)
838 {
839 default:
840 {
841 LogError("INTERNAL ERROR: unexpected function return");
842 i = nChildren;
843 break;
844 }
845 case (SCHEMA_PROPERTY):
846 {
847 LogError("cannot ingest name (WITH_DATA instead of WITH_DESIRED_PROPERTY): %s", childName_str);
848 i = nChildren;
849 break;
850 }
851 case (SCHEMA_REPORTED_PROPERTY):
852 {
853 LogError("cannot ingest name (WITH_REPORTED_PROPERTY instead of WITH_DESIRED_PROPERTY): %s", childName_str);
854 i = nChildren;
855 break;
856 }
857 case (SCHEMA_DESIRED_PROPERTY):
858 {
859 /*Codes_SRS_COMMAND_DECODER_02_007: [ If the child name corresponds to a desired property then an AGENT_DATA_TYPE shall be constructed from the MULTITREE node. ]*/
860 SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle = elementType.elementHandle.desiredPropertyHandle;
861
862 const char* desiredPropertyType = Schema_GetModelDesiredPropertyType(desiredPropertyHandle);
863 AGENT_DATA_TYPE output;
864 if (DecodeValueFromNode(Schema_GetSchemaForModelType(modelHandle), &output, child, desiredPropertyType) != 0)
865 {
866 LogError("failure in DecodeValueFromNode");
867 i = nChildren;
868 }
869 else
870 {
871 /*Codes_SRS_COMMAND_DECODER_02_008: [ The desired property shall be constructed in memory by calling pfDesiredPropertyFromAGENT_DATA_TYPE. ]*/
872 pfDesiredPropertyFromAGENT_DATA_TYPE leFunction = Schema_GetModelDesiredProperty_pfDesiredPropertyFromAGENT_DATA_TYPE(desiredPropertyHandle);
873 if (leFunction(&output, (char*)startAddress + offset + Schema_GetModelDesiredProperty_offset(desiredPropertyHandle)) != 0)
874 {
875 LogError("failure in a function that converts from AGENT_DATA_TYPE to C data");
876 }
877 else
878 {
879 /*Codes_SRS_COMMAND_DECODER_02_013: [ If the desired property has a non-NULL pfOnDesiredProperty then it shall be called. ]*/
880 pfOnDesiredProperty onDesiredProperty = Schema_GetModelDesiredProperty_pfOnDesiredProperty(desiredPropertyHandle);
881 if (onDesiredProperty != NULL)
882 {
883 onDesiredProperty((char*)startAddress + offset);
884 }
885 nProcessedChildren++;
886 }
887 Destroy_AGENT_DATA_TYPE(&output);
888 }
889
890 break;
891 }
892 case(SCHEMA_MODEL_IN_MODEL):
893 {
894 SCHEMA_MODEL_TYPE_HANDLE modelModel = elementType.elementHandle.modelHandle;
895
896 /*Codes_SRS_COMMAND_DECODER_02_009: [ If the child name corresponds to a model in model then the function shall call itself recursively. ]*/
897 if (!validateModel_vs_Multitree(startAddress, modelModel, child, offset + Schema_GetModelModelByName_Offset(modelHandle, childName_str)))
898 {
899 LogError("failure in validateModel_vs_Multitree");
900 i = nChildren;
901 }
902 else
903 {
904 /*if the model in model so happened to be a WITH_DESIRED_PROPERTY... (only those has non_NULL pfOnDesiredProperty) */
905 /*Codes_SRS_COMMAND_DECODER_02_012: [ If the child model in model has a non-NULL pfOnDesiredProperty then pfOnDesiredProperty shall be called. ]*/
906 pfOnDesiredProperty onDesiredProperty = Schema_GetModelModelByName_OnDesiredProperty(modelHandle, childName_str);
907 if (onDesiredProperty != NULL)
908 {
909 onDesiredProperty((char*)startAddress + offset);
910 }
911
912 nProcessedChildren++;
913 }
914
915 break;
916 }
917
918 } /*switch*/
919 }
920 STRING_delete(childName);
921 }
922 }
923 }
924
925 if(nProcessedChildren == nChildren)
926 {
927 /*Codes_SRS_COMMAND_DECODER_02_010: [ If the complete MULTITREE has been parsed then CommandDecoder_IngestDesiredProperties shall succeed and return EXECUTE_COMMAND_SUCCESS. ]*/
928 result = true;
929 }
930 else
931 {
932 /*Codes_SRS_COMMAND_DECODER_02_011: [ Otherwise CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_FAILED. ]*/
933 LogError("not all constituents of the JSON have been ingested");
934 result = false;
935 }
936 return result;
937}
938
939static EXECUTE_COMMAND_RESULT DecodeDesiredProperties(void* startAddress, COMMAND_DECODER_HANDLE_DATA* handle, MULTITREE_HANDLE desiredPropertiesTree)
940{
941 /*Codes_SRS_COMMAND_DECODER_02_006: [ CommandDecoder_IngestDesiredProperties shall parse the MULTITREEE recursively. ]*/
942 return validateModel_vs_Multitree(startAddress, handle->ModelHandle, desiredPropertiesTree, 0 )?EXECUTE_COMMAND_SUCCESS:EXECUTE_COMMAND_FAILED;
943}
944
945/* Raw JSON has properties we don't need; potentially nodes other than "desired" for full TWIN as well as a $version we don't pass to callees */
946static bool RemoveUnneededTwinProperties(MULTITREE_HANDLE initialParsedTree, bool parseDesiredNode, MULTITREE_HANDLE *desiredPropertiesTree)
947{
948 MULTITREE_HANDLE updateTree;
949 bool result;
950
951 if (parseDesiredNode)
952 {
953 /*Codes_SRS_COMMAND_DECODER_02_014: [ If parseDesiredNode is TRUE, parse only the `desired` part of JSON tree ]*/
954 if (MultiTree_GetChildByName(initialParsedTree, "desired", &updateTree) != MULTITREE_OK)
955 {
956 LogError("Unable to find 'desired' in tree");
957 return false;
958 }
959 }
960 else
961 {
962 // Tree already starts on node we want so just use it.
963 updateTree = initialParsedTree;
964 }
965
966 /*Codes_COMMAND_DECODER_02_015: [ Remove '$version' string from node, if it is present. It not being present is not an error ]*/
967 MULTITREE_RESULT deleteChildResult = MultiTree_DeleteChild(updateTree, "$version");
968 if ((deleteChildResult == MULTITREE_OK) || (deleteChildResult == MULTITREE_CHILD_NOT_FOUND))
969 {
970 *desiredPropertiesTree = updateTree;
971 result = true;
972 }
973 else
974 {
975 *desiredPropertiesTree = NULL;
976 result = false;
977 }
978
979 return result;
980}
981
982EXECUTE_COMMAND_RESULT CommandDecoder_IngestDesiredProperties(void* startAddress, COMMAND_DECODER_HANDLE handle, const char* jsonPayload, bool parseDesiredNode)
983{
984 EXECUTE_COMMAND_RESULT result;
985
986 /*Codes_SRS_COMMAND_DECODER_02_001: [ If startAddress is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/
987 /*Codes_SRS_COMMAND_DECODER_02_002: [ If handle is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/
988 /*Codes_SRS_COMMAND_DECODER_02_003: [ If jsonPayload is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/
989 if(
990 (startAddress == NULL) ||
991 (handle == NULL) ||
992 (jsonPayload == NULL)
993 )
994 {
995 LogError("invalid argument COMMAND_DECODER_HANDLE handle=%p, const char* jsonPayload=%p", handle, jsonPayload);
996 result = EXECUTE_COMMAND_ERROR;
997 }
998 else
999 {
1000 /*Codes_SRS_COMMAND_DECODER_02_004: [ CommandDecoder_IngestDesiredProperties shall clone desiredProperties. ]*/
1001 char* copy;
1002 if (mallocAndStrcpy_s(&copy, jsonPayload) != 0)
1003 {
1004 LogError("failure in mallocAndStrcpy_s");
1005 result = EXECUTE_COMMAND_FAILED;
1006 }
1007 else
1008 {
1009 /*Codes_SRS_COMMAND_DECODER_02_005: [ CommandDecoder_IngestDesiredProperties shall create a MULTITREE_HANDLE ouf of the clone of desiredProperties. ]*/
1010 MULTITREE_HANDLE initialParsedTree;
1011 MULTITREE_HANDLE desiredPropertiesTree;
1012
1013 if (JSONDecoder_JSON_To_MultiTree(copy, &initialParsedTree) != JSON_DECODER_OK)
1014 {
1015 LogError("Decoding JSON to a multi tree failed");
1016 result = EXECUTE_COMMAND_ERROR;
1017 }
1018 else
1019 {
1020 if (RemoveUnneededTwinProperties(initialParsedTree, parseDesiredNode, &desiredPropertiesTree) == false)
1021 {
1022 LogError("Removing unneeded twin properties failed");
1023 result = EXECUTE_COMMAND_ERROR;
1024 }
1025 else
1026 {
1027 COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle;
1028
1029 /*Codes_SRS_COMMAND_DECODER_02_006: [ CommandDecoder_IngestDesiredProperties shall parse the MULTITREEE recursively. ]*/
1030 result = DecodeDesiredProperties(startAddress, commandDecoderInstance, desiredPropertiesTree);
1031
1032 // Do NOT free desiredPropertiesTree. It is only a pointer into initialParsedTree.
1033 MultiTree_Destroy(initialParsedTree);
1034 }
1035 }
1036 free(copy);
1037 }
1038 }
1039 return result;
1040}
Note: See TracBrowser for help on using the repository browser.