1 //===-- OptionValueProperties.cpp ---------------------------------*- C++
2 //-*-===//
3 //
4 //                     The LLVM Compiler Infrastructure
5 //
6 // This file is distributed under the University of Illinois Open Source
7 // License. See LICENSE.TXT for details.
8 //
9 //===----------------------------------------------------------------------===//
10 
11 #include "lldb/Interpreter/OptionValueProperties.h"
12 
13 // C Includes
14 // C++ Includes
15 // Other libraries and framework includes
16 // Project includes
17 #include "lldb/Core/Flags.h"
18 #include "lldb/Core/Stream.h"
19 #include "lldb/Core/StringList.h"
20 #include "lldb/Core/UserSettingsController.h"
21 #include "lldb/Interpreter/Args.h"
22 #include "lldb/Interpreter/OptionValues.h"
23 #include "lldb/Interpreter/Property.h"
24 
25 using namespace lldb;
26 using namespace lldb_private;
27 
28 OptionValueProperties::OptionValueProperties(const ConstString &name)
29     : OptionValue(), m_name(name), m_properties(), m_name_to_index() {}
30 
31 OptionValueProperties::OptionValueProperties(
32     const OptionValueProperties &global_properties)
33     : OptionValue(global_properties),
34       std::enable_shared_from_this<OptionValueProperties>(),
35       m_name(global_properties.m_name),
36       m_properties(global_properties.m_properties),
37       m_name_to_index(global_properties.m_name_to_index) {
38   // We now have an exact copy of "global_properties". We need to now
39   // find all non-global settings and copy the property values so that
40   // all non-global settings get new OptionValue instances created for
41   // them.
42   const size_t num_properties = m_properties.size();
43   for (size_t i = 0; i < num_properties; ++i) {
44     // Duplicate any values that are not global when constructing properties
45     // from
46     // a global copy
47     if (m_properties[i].IsGlobal() == false) {
48       lldb::OptionValueSP new_value_sp(m_properties[i].GetValue()->DeepCopy());
49       m_properties[i].SetOptionValue(new_value_sp);
50     }
51   }
52 }
53 
54 size_t OptionValueProperties::GetNumProperties() const {
55   return m_properties.size();
56 }
57 
58 void OptionValueProperties::Initialize(const PropertyDefinition *defs) {
59   for (size_t i = 0; defs[i].name; ++i) {
60     Property property(defs[i]);
61     assert(property.IsValid());
62     m_name_to_index.Append(property.GetName().GetCString(),
63                            m_properties.size());
64     property.GetValue()->SetParent(shared_from_this());
65     m_properties.push_back(property);
66   }
67   m_name_to_index.Sort();
68 }
69 
70 void OptionValueProperties::SetValueChangedCallback(
71     uint32_t property_idx, OptionValueChangedCallback callback, void *baton) {
72   Property *property = ProtectedGetPropertyAtIndex(property_idx);
73   if (property)
74     property->SetValueChangedCallback(callback, baton);
75 }
76 
77 void OptionValueProperties::AppendProperty(const ConstString &name,
78                                            const ConstString &desc,
79                                            bool is_global,
80                                            const OptionValueSP &value_sp) {
81   Property property(name, desc, is_global, value_sp);
82   m_name_to_index.Append(name.GetCString(), m_properties.size());
83   m_properties.push_back(property);
84   value_sp->SetParent(shared_from_this());
85   m_name_to_index.Sort();
86 }
87 
88 // bool
89 // OptionValueProperties::GetQualifiedName (Stream &strm)
90 //{
91 //    bool dumped_something = false;
92 ////    lldb::OptionValuePropertiesSP parent_sp(GetParent ());
93 ////    if (parent_sp)
94 ////    {
95 ////        parent_sp->GetQualifiedName (strm);
96 ////        strm.PutChar('.');
97 ////        dumped_something = true;
98 ////    }
99 //    if (m_name)
100 //    {
101 //        strm << m_name;
102 //        dumped_something = true;
103 //    }
104 //    return dumped_something;
105 //}
106 //
107 lldb::OptionValueSP
108 OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx,
109                                       const ConstString &key,
110                                       bool will_modify) const {
111   lldb::OptionValueSP value_sp;
112   size_t idx = m_name_to_index.Find(key.GetCString(), SIZE_MAX);
113   if (idx < m_properties.size())
114     value_sp = GetPropertyAtIndex(exe_ctx, will_modify, idx)->GetValue();
115   return value_sp;
116 }
117 
118 lldb::OptionValueSP
119 OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx,
120                                    const char *name, bool will_modify,
121                                    Error &error) const {
122   lldb::OptionValueSP value_sp;
123 
124   if (name && name[0]) {
125     const char *sub_name = nullptr;
126     ConstString key;
127     size_t key_len = ::strcspn(name, ".[{");
128 
129     if (name[key_len]) {
130       key.SetCStringWithLength(name, key_len);
131       sub_name = name + key_len;
132     } else
133       key.SetCString(name);
134 
135     value_sp = GetValueForKey(exe_ctx, key, will_modify);
136     if (sub_name && value_sp) {
137       switch (sub_name[0]) {
138       case '.': {
139         lldb::OptionValueSP return_val_sp;
140         return_val_sp =
141             value_sp->GetSubValue(exe_ctx, sub_name + 1, will_modify, error);
142         if (!return_val_sp) {
143           if (Properties::IsSettingExperimental(sub_name + 1)) {
144             size_t experimental_len =
145                 strlen(Properties::GetExperimentalSettingsName());
146             if (*(sub_name + experimental_len + 1) == '.')
147               return_val_sp = value_sp->GetSubValue(
148                   exe_ctx, sub_name + experimental_len + 2, will_modify, error);
149             // It isn't an error if an experimental setting is not present.
150             if (!return_val_sp)
151               error.Clear();
152           }
153         }
154         return return_val_sp;
155       }
156       case '{':
157         // Predicate matching for predicates like
158         // "<setting-name>{<predicate>}"
159         // strings are parsed by the current OptionValueProperties subclass
160         // to mean whatever they want to. For instance a subclass of
161         // OptionValueProperties for a lldb_private::Target might implement:
162         // "target.run-args{arch==i386}"   -- only set run args if the arch is
163         // i386
164         // "target.run-args{path=/tmp/a/b/c/a.out}" -- only set run args if the
165         // path matches
166         // "target.run-args{basename==test&&arch==x86_64}" -- only set run args
167         // if executable basename is "test" and arch is "x86_64"
168         if (sub_name[1]) {
169           const char *predicate_start = sub_name + 1;
170           const char *predicate_end = strchr(predicate_start, '}');
171           if (predicate_end) {
172             std::string predicate(predicate_start, predicate_end);
173             if (PredicateMatches(exe_ctx, predicate.c_str())) {
174               if (predicate_end[1]) {
175                 // Still more subvalue string to evaluate
176                 return value_sp->GetSubValue(exe_ctx, predicate_end + 1,
177                                              will_modify, error);
178               } else {
179                 // We have a match!
180                 break;
181               }
182             }
183           }
184         }
185         // Predicate didn't match or wasn't correctly formed
186         value_sp.reset();
187         break;
188 
189       case '[':
190         // Array or dictionary access for subvalues like:
191         // "[12]"       -- access 12th array element
192         // "['hello']"  -- dictionary access of key named hello
193         return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
194 
195       default:
196         value_sp.reset();
197         break;
198       }
199     }
200   }
201   return value_sp;
202 }
203 
204 Error OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx,
205                                          VarSetOperationType op,
206                                          const char *name, const char *value) {
207   Error error;
208   const bool will_modify = true;
209   lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error));
210   if (value_sp)
211     error = value_sp->SetValueFromString(
212         value ? llvm::StringRef(value) : llvm::StringRef(), op);
213   else {
214     if (error.AsCString() == nullptr)
215       error.SetErrorStringWithFormat("invalid value path '%s'", name);
216   }
217   return error;
218 }
219 
220 ConstString OptionValueProperties::GetPropertyNameAtIndex(uint32_t idx) const {
221   const Property *property = GetPropertyAtIndex(nullptr, false, idx);
222   if (property)
223     return property->GetName();
224   return ConstString();
225 }
226 
227 const char *
228 OptionValueProperties::GetPropertyDescriptionAtIndex(uint32_t idx) const {
229   const Property *property = GetPropertyAtIndex(nullptr, false, idx);
230   if (property)
231     return property->GetDescription();
232   return nullptr;
233 }
234 
235 uint32_t
236 OptionValueProperties::GetPropertyIndex(const ConstString &name) const {
237   return m_name_to_index.Find(name.GetCString(), SIZE_MAX);
238 }
239 
240 const Property *
241 OptionValueProperties::GetProperty(const ExecutionContext *exe_ctx,
242                                    bool will_modify,
243                                    const ConstString &name) const {
244   return GetPropertyAtIndex(exe_ctx, will_modify,
245                             m_name_to_index.Find(name.GetCString(), SIZE_MAX));
246 }
247 
248 const Property *OptionValueProperties::GetPropertyAtIndex(
249     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
250   return ProtectedGetPropertyAtIndex(idx);
251 }
252 
253 lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex(
254     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
255   const Property *setting = GetPropertyAtIndex(exe_ctx, will_modify, idx);
256   if (setting)
257     return setting->GetValue();
258   return OptionValueSP();
259 }
260 
261 OptionValuePathMappings *
262 OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings(
263     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
264   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
265   if (value_sp)
266     return value_sp->GetAsPathMappings();
267   return nullptr;
268 }
269 
270 OptionValueFileSpecList *
271 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList(
272     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
273   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
274   if (value_sp)
275     return value_sp->GetAsFileSpecList();
276   return nullptr;
277 }
278 
279 OptionValueArch *OptionValueProperties::GetPropertyAtIndexAsOptionValueArch(
280     const ExecutionContext *exe_ctx, uint32_t idx) const {
281   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
282   if (property)
283     return property->GetValue()->GetAsArch();
284   return nullptr;
285 }
286 
287 OptionValueLanguage *
288 OptionValueProperties::GetPropertyAtIndexAsOptionValueLanguage(
289     const ExecutionContext *exe_ctx, uint32_t idx) const {
290   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
291   if (property)
292     return property->GetValue()->GetAsLanguage();
293   return nullptr;
294 }
295 
296 bool OptionValueProperties::GetPropertyAtIndexAsArgs(
297     const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const {
298   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
299   if (property) {
300     OptionValue *value = property->GetValue().get();
301     if (value) {
302       const OptionValueArray *array = value->GetAsArray();
303       if (array)
304         return array->GetArgs(args);
305       else {
306         const OptionValueDictionary *dict = value->GetAsDictionary();
307         if (dict)
308           return dict->GetArgs(args);
309       }
310     }
311   }
312   return false;
313 }
314 
315 bool OptionValueProperties::SetPropertyAtIndexFromArgs(
316     const ExecutionContext *exe_ctx, uint32_t idx, const Args &args) {
317   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
318   if (property) {
319     OptionValue *value = property->GetValue().get();
320     if (value) {
321       OptionValueArray *array = value->GetAsArray();
322       if (array)
323         return array->SetArgs(args, eVarSetOperationAssign).Success();
324       else {
325         OptionValueDictionary *dict = value->GetAsDictionary();
326         if (dict)
327           return dict->SetArgs(args, eVarSetOperationAssign).Success();
328       }
329     }
330   }
331   return false;
332 }
333 
334 bool OptionValueProperties::GetPropertyAtIndexAsBoolean(
335     const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const {
336   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
337   if (property) {
338     OptionValue *value = property->GetValue().get();
339     if (value)
340       return value->GetBooleanValue(fail_value);
341   }
342   return fail_value;
343 }
344 
345 bool OptionValueProperties::SetPropertyAtIndexAsBoolean(
346     const ExecutionContext *exe_ctx, uint32_t idx, bool new_value) {
347   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
348   if (property) {
349     OptionValue *value = property->GetValue().get();
350     if (value) {
351       value->SetBooleanValue(new_value);
352       return true;
353     }
354   }
355   return false;
356 }
357 
358 OptionValueDictionary *
359 OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary(
360     const ExecutionContext *exe_ctx, uint32_t idx) const {
361   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
362   if (property)
363     return property->GetValue()->GetAsDictionary();
364   return nullptr;
365 }
366 
367 int64_t OptionValueProperties::GetPropertyAtIndexAsEnumeration(
368     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
369   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
370   if (property) {
371     OptionValue *value = property->GetValue().get();
372     if (value)
373       return value->GetEnumerationValue(fail_value);
374   }
375   return fail_value;
376 }
377 
378 bool OptionValueProperties::SetPropertyAtIndexAsEnumeration(
379     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
380   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
381   if (property) {
382     OptionValue *value = property->GetValue().get();
383     if (value)
384       return value->SetEnumerationValue(new_value);
385   }
386   return false;
387 }
388 
389 const FormatEntity::Entry *
390 OptionValueProperties::GetPropertyAtIndexAsFormatEntity(
391     const ExecutionContext *exe_ctx, uint32_t idx) {
392   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
393   if (property) {
394     OptionValue *value = property->GetValue().get();
395     if (value)
396       return value->GetFormatEntity();
397   }
398   return nullptr;
399 }
400 
401 OptionValueFileSpec *
402 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec(
403     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
404   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
405   if (property) {
406     OptionValue *value = property->GetValue().get();
407     if (value)
408       return value->GetAsFileSpec();
409   }
410   return nullptr;
411 }
412 
413 FileSpec OptionValueProperties::GetPropertyAtIndexAsFileSpec(
414     const ExecutionContext *exe_ctx, uint32_t idx) const {
415   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
416   if (property) {
417     OptionValue *value = property->GetValue().get();
418     if (value)
419       return value->GetFileSpecValue();
420   }
421   return FileSpec();
422 }
423 
424 bool OptionValueProperties::SetPropertyAtIndexAsFileSpec(
425     const ExecutionContext *exe_ctx, uint32_t idx,
426     const FileSpec &new_file_spec) {
427   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
428   if (property) {
429     OptionValue *value = property->GetValue().get();
430     if (value)
431       return value->SetFileSpecValue(new_file_spec);
432   }
433   return false;
434 }
435 
436 const RegularExpression *
437 OptionValueProperties::GetPropertyAtIndexAsOptionValueRegex(
438     const ExecutionContext *exe_ctx, uint32_t idx) const {
439   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
440   if (property) {
441     OptionValue *value = property->GetValue().get();
442     if (value)
443       return value->GetRegexValue();
444   }
445   return nullptr;
446 }
447 
448 OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64(
449     const ExecutionContext *exe_ctx, uint32_t idx) const {
450   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
451   if (property) {
452     OptionValue *value = property->GetValue().get();
453     if (value)
454       return value->GetAsSInt64();
455   }
456   return nullptr;
457 }
458 
459 int64_t OptionValueProperties::GetPropertyAtIndexAsSInt64(
460     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
461   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
462   if (property) {
463     OptionValue *value = property->GetValue().get();
464     if (value)
465       return value->GetSInt64Value(fail_value);
466   }
467   return fail_value;
468 }
469 
470 bool OptionValueProperties::SetPropertyAtIndexAsSInt64(
471     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
472   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
473   if (property) {
474     OptionValue *value = property->GetValue().get();
475     if (value)
476       return value->SetSInt64Value(new_value);
477   }
478   return false;
479 }
480 
481 const char *OptionValueProperties::GetPropertyAtIndexAsString(
482     const ExecutionContext *exe_ctx, uint32_t idx,
483     const char *fail_value) const {
484   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
485   if (property) {
486     OptionValue *value = property->GetValue().get();
487     if (value)
488       return value->GetStringValue(fail_value);
489   }
490   return fail_value;
491 }
492 
493 bool OptionValueProperties::SetPropertyAtIndexAsString(
494     const ExecutionContext *exe_ctx, uint32_t idx, llvm::StringRef new_value) {
495   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
496   if (property) {
497     OptionValue *value = property->GetValue().get();
498     if (value)
499       return value->SetStringValue(new_value);
500   }
501   return false;
502 }
503 
504 OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString(
505     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
506   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
507   if (value_sp)
508     return value_sp->GetAsString();
509   return nullptr;
510 }
511 
512 uint64_t OptionValueProperties::GetPropertyAtIndexAsUInt64(
513     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const {
514   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
515   if (property) {
516     OptionValue *value = property->GetValue().get();
517     if (value)
518       return value->GetUInt64Value(fail_value);
519   }
520   return fail_value;
521 }
522 
523 bool OptionValueProperties::SetPropertyAtIndexAsUInt64(
524     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value) {
525   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
526   if (property) {
527     OptionValue *value = property->GetValue().get();
528     if (value)
529       return value->SetUInt64Value(new_value);
530   }
531   return false;
532 }
533 
534 bool OptionValueProperties::Clear() {
535   const size_t num_properties = m_properties.size();
536   for (size_t i = 0; i < num_properties; ++i)
537     m_properties[i].GetValue()->Clear();
538   return true;
539 }
540 
541 Error OptionValueProperties::SetValueFromString(llvm::StringRef value,
542                                                 VarSetOperationType op) {
543   Error error;
544 
545   //    Args args(value_cstr);
546   //    const size_t argc = args.GetArgumentCount();
547   switch (op) {
548   case eVarSetOperationClear:
549     Clear();
550     break;
551 
552   case eVarSetOperationReplace:
553   case eVarSetOperationAssign:
554   case eVarSetOperationRemove:
555   case eVarSetOperationInsertBefore:
556   case eVarSetOperationInsertAfter:
557   case eVarSetOperationAppend:
558   case eVarSetOperationInvalid:
559     error = OptionValue::SetValueFromString(value, op);
560     break;
561   }
562 
563   return error;
564 }
565 
566 void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx,
567                                       Stream &strm, uint32_t dump_mask) {
568   const size_t num_properties = m_properties.size();
569   for (size_t i = 0; i < num_properties; ++i) {
570     const Property *property = GetPropertyAtIndex(exe_ctx, false, i);
571     if (property) {
572       OptionValue *option_value = property->GetValue().get();
573       assert(option_value);
574       const bool transparent_value = option_value->ValueIsTransparent();
575       property->Dump(exe_ctx, strm, dump_mask);
576       if (!transparent_value)
577         strm.EOL();
578     }
579   }
580 }
581 
582 Error OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx,
583                                                Stream &strm,
584                                                const char *property_path,
585                                                uint32_t dump_mask) {
586   Error error;
587   const bool will_modify = false;
588   lldb::OptionValueSP value_sp(
589       GetSubValue(exe_ctx, property_path, will_modify, error));
590   if (value_sp) {
591     if (!value_sp->ValueIsTransparent()) {
592       if (dump_mask & eDumpOptionName)
593         strm.PutCString(property_path);
594       if (dump_mask & ~eDumpOptionName)
595         strm.PutChar(' ');
596     }
597     value_sp->DumpValue(exe_ctx, strm, dump_mask);
598   }
599   return error;
600 }
601 
602 lldb::OptionValueSP OptionValueProperties::DeepCopy() const {
603   assert(!"this shouldn't happen");
604   return lldb::OptionValueSP();
605 }
606 
607 const Property *OptionValueProperties::GetPropertyAtPath(
608     const ExecutionContext *exe_ctx, bool will_modify, const char *name) const {
609   const Property *property = nullptr;
610   if (name && name[0]) {
611     const char *sub_name = nullptr;
612     ConstString key;
613     size_t key_len = ::strcspn(name, ".[{");
614 
615     if (name[key_len]) {
616       key.SetCStringWithLength(name, key_len);
617       sub_name = name + key_len;
618     } else
619       key.SetCString(name);
620 
621     property = GetProperty(exe_ctx, will_modify, key);
622     if (sub_name && property) {
623       if (sub_name[0] == '.') {
624         OptionValueProperties *sub_properties =
625             property->GetValue()->GetAsProperties();
626         if (sub_properties)
627           return sub_properties->GetPropertyAtPath(exe_ctx, will_modify,
628                                                    sub_name + 1);
629       }
630       property = nullptr;
631     }
632   }
633   return property;
634 }
635 
636 void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
637                                                 Stream &strm) const {
638   size_t max_name_len = 0;
639   const size_t num_properties = m_properties.size();
640   for (size_t i = 0; i < num_properties; ++i) {
641     const Property *property = ProtectedGetPropertyAtIndex(i);
642     if (property)
643       max_name_len =
644           std::max<size_t>(property->GetName().GetLength(), max_name_len);
645   }
646   for (size_t i = 0; i < num_properties; ++i) {
647     const Property *property = ProtectedGetPropertyAtIndex(i);
648     if (property)
649       property->DumpDescription(interpreter, strm, max_name_len, false);
650   }
651 }
652 
653 void OptionValueProperties::Apropos(
654     const char *keyword,
655     std::vector<const Property *> &matching_properties) const {
656   const size_t num_properties = m_properties.size();
657   StreamString strm;
658   for (size_t i = 0; i < num_properties; ++i) {
659     const Property *property = ProtectedGetPropertyAtIndex(i);
660     if (property) {
661       const OptionValueProperties *properties =
662           property->GetValue()->GetAsProperties();
663       if (properties) {
664         properties->Apropos(keyword, matching_properties);
665       } else {
666         bool match = false;
667         const char *name = property->GetName().GetCString();
668         if (name && ::strcasestr(name, keyword))
669           match = true;
670         else {
671           const char *desc = property->GetDescription();
672           if (desc && ::strcasestr(desc, keyword))
673             match = true;
674         }
675         if (match) {
676           matching_properties.push_back(property);
677         }
678       }
679     }
680   }
681 }
682 
683 lldb::OptionValuePropertiesSP
684 OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx,
685                                       const ConstString &name) {
686   lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name, false));
687   if (option_value_sp) {
688     OptionValueProperties *ov_properties = option_value_sp->GetAsProperties();
689     if (ov_properties)
690       return ov_properties->shared_from_this();
691   }
692   return lldb::OptionValuePropertiesSP();
693 }
694