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().GetStringRef(),
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.GetStringRef(), 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.GetStringRef(), 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.GetStringRef(), 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(
245       exe_ctx, will_modify,
246       m_name_to_index.Find(name.GetStringRef(), SIZE_MAX));
247 }
248 
249 const Property *OptionValueProperties::GetPropertyAtIndex(
250     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
251   return ProtectedGetPropertyAtIndex(idx);
252 }
253 
254 lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex(
255     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
256   const Property *setting = GetPropertyAtIndex(exe_ctx, will_modify, idx);
257   if (setting)
258     return setting->GetValue();
259   return OptionValueSP();
260 }
261 
262 OptionValuePathMappings *
263 OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings(
264     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
265   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
266   if (value_sp)
267     return value_sp->GetAsPathMappings();
268   return nullptr;
269 }
270 
271 OptionValueFileSpecList *
272 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList(
273     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
274   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
275   if (value_sp)
276     return value_sp->GetAsFileSpecList();
277   return nullptr;
278 }
279 
280 OptionValueArch *OptionValueProperties::GetPropertyAtIndexAsOptionValueArch(
281     const ExecutionContext *exe_ctx, uint32_t idx) const {
282   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
283   if (property)
284     return property->GetValue()->GetAsArch();
285   return nullptr;
286 }
287 
288 OptionValueLanguage *
289 OptionValueProperties::GetPropertyAtIndexAsOptionValueLanguage(
290     const ExecutionContext *exe_ctx, uint32_t idx) const {
291   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
292   if (property)
293     return property->GetValue()->GetAsLanguage();
294   return nullptr;
295 }
296 
297 bool OptionValueProperties::GetPropertyAtIndexAsArgs(
298     const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const {
299   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
300   if (property) {
301     OptionValue *value = property->GetValue().get();
302     if (value) {
303       const OptionValueArray *array = value->GetAsArray();
304       if (array)
305         return array->GetArgs(args);
306       else {
307         const OptionValueDictionary *dict = value->GetAsDictionary();
308         if (dict)
309           return dict->GetArgs(args);
310       }
311     }
312   }
313   return false;
314 }
315 
316 bool OptionValueProperties::SetPropertyAtIndexFromArgs(
317     const ExecutionContext *exe_ctx, uint32_t idx, const Args &args) {
318   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
319   if (property) {
320     OptionValue *value = property->GetValue().get();
321     if (value) {
322       OptionValueArray *array = value->GetAsArray();
323       if (array)
324         return array->SetArgs(args, eVarSetOperationAssign).Success();
325       else {
326         OptionValueDictionary *dict = value->GetAsDictionary();
327         if (dict)
328           return dict->SetArgs(args, eVarSetOperationAssign).Success();
329       }
330     }
331   }
332   return false;
333 }
334 
335 bool OptionValueProperties::GetPropertyAtIndexAsBoolean(
336     const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const {
337   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
338   if (property) {
339     OptionValue *value = property->GetValue().get();
340     if (value)
341       return value->GetBooleanValue(fail_value);
342   }
343   return fail_value;
344 }
345 
346 bool OptionValueProperties::SetPropertyAtIndexAsBoolean(
347     const ExecutionContext *exe_ctx, uint32_t idx, bool new_value) {
348   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
349   if (property) {
350     OptionValue *value = property->GetValue().get();
351     if (value) {
352       value->SetBooleanValue(new_value);
353       return true;
354     }
355   }
356   return false;
357 }
358 
359 OptionValueDictionary *
360 OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary(
361     const ExecutionContext *exe_ctx, uint32_t idx) const {
362   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
363   if (property)
364     return property->GetValue()->GetAsDictionary();
365   return nullptr;
366 }
367 
368 int64_t OptionValueProperties::GetPropertyAtIndexAsEnumeration(
369     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
370   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
371   if (property) {
372     OptionValue *value = property->GetValue().get();
373     if (value)
374       return value->GetEnumerationValue(fail_value);
375   }
376   return fail_value;
377 }
378 
379 bool OptionValueProperties::SetPropertyAtIndexAsEnumeration(
380     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
381   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
382   if (property) {
383     OptionValue *value = property->GetValue().get();
384     if (value)
385       return value->SetEnumerationValue(new_value);
386   }
387   return false;
388 }
389 
390 const FormatEntity::Entry *
391 OptionValueProperties::GetPropertyAtIndexAsFormatEntity(
392     const ExecutionContext *exe_ctx, uint32_t idx) {
393   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
394   if (property) {
395     OptionValue *value = property->GetValue().get();
396     if (value)
397       return value->GetFormatEntity();
398   }
399   return nullptr;
400 }
401 
402 OptionValueFileSpec *
403 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec(
404     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
405   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
406   if (property) {
407     OptionValue *value = property->GetValue().get();
408     if (value)
409       return value->GetAsFileSpec();
410   }
411   return nullptr;
412 }
413 
414 FileSpec OptionValueProperties::GetPropertyAtIndexAsFileSpec(
415     const ExecutionContext *exe_ctx, uint32_t idx) const {
416   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
417   if (property) {
418     OptionValue *value = property->GetValue().get();
419     if (value)
420       return value->GetFileSpecValue();
421   }
422   return FileSpec();
423 }
424 
425 bool OptionValueProperties::SetPropertyAtIndexAsFileSpec(
426     const ExecutionContext *exe_ctx, uint32_t idx,
427     const FileSpec &new_file_spec) {
428   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
429   if (property) {
430     OptionValue *value = property->GetValue().get();
431     if (value)
432       return value->SetFileSpecValue(new_file_spec);
433   }
434   return false;
435 }
436 
437 const RegularExpression *
438 OptionValueProperties::GetPropertyAtIndexAsOptionValueRegex(
439     const ExecutionContext *exe_ctx, uint32_t idx) const {
440   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
441   if (property) {
442     OptionValue *value = property->GetValue().get();
443     if (value)
444       return value->GetRegexValue();
445   }
446   return nullptr;
447 }
448 
449 OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64(
450     const ExecutionContext *exe_ctx, uint32_t idx) const {
451   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
452   if (property) {
453     OptionValue *value = property->GetValue().get();
454     if (value)
455       return value->GetAsSInt64();
456   }
457   return nullptr;
458 }
459 
460 int64_t OptionValueProperties::GetPropertyAtIndexAsSInt64(
461     const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const {
462   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
463   if (property) {
464     OptionValue *value = property->GetValue().get();
465     if (value)
466       return value->GetSInt64Value(fail_value);
467   }
468   return fail_value;
469 }
470 
471 bool OptionValueProperties::SetPropertyAtIndexAsSInt64(
472     const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) {
473   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
474   if (property) {
475     OptionValue *value = property->GetValue().get();
476     if (value)
477       return value->SetSInt64Value(new_value);
478   }
479   return false;
480 }
481 
482 const char *OptionValueProperties::GetPropertyAtIndexAsString(
483     const ExecutionContext *exe_ctx, uint32_t idx,
484     const char *fail_value) const {
485   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
486   if (property) {
487     OptionValue *value = property->GetValue().get();
488     if (value)
489       return value->GetStringValue(fail_value);
490   }
491   return fail_value;
492 }
493 
494 bool OptionValueProperties::SetPropertyAtIndexAsString(
495     const ExecutionContext *exe_ctx, uint32_t idx, llvm::StringRef new_value) {
496   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
497   if (property) {
498     OptionValue *value = property->GetValue().get();
499     if (value)
500       return value->SetStringValue(new_value);
501   }
502   return false;
503 }
504 
505 OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString(
506     const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const {
507   OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx));
508   if (value_sp)
509     return value_sp->GetAsString();
510   return nullptr;
511 }
512 
513 uint64_t OptionValueProperties::GetPropertyAtIndexAsUInt64(
514     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const {
515   const Property *property = GetPropertyAtIndex(exe_ctx, false, idx);
516   if (property) {
517     OptionValue *value = property->GetValue().get();
518     if (value)
519       return value->GetUInt64Value(fail_value);
520   }
521   return fail_value;
522 }
523 
524 bool OptionValueProperties::SetPropertyAtIndexAsUInt64(
525     const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value) {
526   const Property *property = GetPropertyAtIndex(exe_ctx, true, idx);
527   if (property) {
528     OptionValue *value = property->GetValue().get();
529     if (value)
530       return value->SetUInt64Value(new_value);
531   }
532   return false;
533 }
534 
535 bool OptionValueProperties::Clear() {
536   const size_t num_properties = m_properties.size();
537   for (size_t i = 0; i < num_properties; ++i)
538     m_properties[i].GetValue()->Clear();
539   return true;
540 }
541 
542 Error OptionValueProperties::SetValueFromString(llvm::StringRef value,
543                                                 VarSetOperationType op) {
544   Error error;
545 
546   //    Args args(value_cstr);
547   //    const size_t argc = args.GetArgumentCount();
548   switch (op) {
549   case eVarSetOperationClear:
550     Clear();
551     break;
552 
553   case eVarSetOperationReplace:
554   case eVarSetOperationAssign:
555   case eVarSetOperationRemove:
556   case eVarSetOperationInsertBefore:
557   case eVarSetOperationInsertAfter:
558   case eVarSetOperationAppend:
559   case eVarSetOperationInvalid:
560     error = OptionValue::SetValueFromString(value, op);
561     break;
562   }
563 
564   return error;
565 }
566 
567 void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx,
568                                       Stream &strm, uint32_t dump_mask) {
569   const size_t num_properties = m_properties.size();
570   for (size_t i = 0; i < num_properties; ++i) {
571     const Property *property = GetPropertyAtIndex(exe_ctx, false, i);
572     if (property) {
573       OptionValue *option_value = property->GetValue().get();
574       assert(option_value);
575       const bool transparent_value = option_value->ValueIsTransparent();
576       property->Dump(exe_ctx, strm, dump_mask);
577       if (!transparent_value)
578         strm.EOL();
579     }
580   }
581 }
582 
583 Error OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx,
584                                                Stream &strm,
585                                                const char *property_path,
586                                                uint32_t dump_mask) {
587   Error error;
588   const bool will_modify = false;
589   lldb::OptionValueSP value_sp(
590       GetSubValue(exe_ctx, property_path, will_modify, error));
591   if (value_sp) {
592     if (!value_sp->ValueIsTransparent()) {
593       if (dump_mask & eDumpOptionName)
594         strm.PutCString(property_path);
595       if (dump_mask & ~eDumpOptionName)
596         strm.PutChar(' ');
597     }
598     value_sp->DumpValue(exe_ctx, strm, dump_mask);
599   }
600   return error;
601 }
602 
603 lldb::OptionValueSP OptionValueProperties::DeepCopy() const {
604   assert(!"this shouldn't happen");
605   return lldb::OptionValueSP();
606 }
607 
608 const Property *OptionValueProperties::GetPropertyAtPath(
609     const ExecutionContext *exe_ctx, bool will_modify, const char *name) const {
610   const Property *property = nullptr;
611   if (name && name[0]) {
612     const char *sub_name = nullptr;
613     ConstString key;
614     size_t key_len = ::strcspn(name, ".[{");
615 
616     if (name[key_len]) {
617       key.SetCStringWithLength(name, key_len);
618       sub_name = name + key_len;
619     } else
620       key.SetCString(name);
621 
622     property = GetProperty(exe_ctx, will_modify, key);
623     if (sub_name && property) {
624       if (sub_name[0] == '.') {
625         OptionValueProperties *sub_properties =
626             property->GetValue()->GetAsProperties();
627         if (sub_properties)
628           return sub_properties->GetPropertyAtPath(exe_ctx, will_modify,
629                                                    sub_name + 1);
630       }
631       property = nullptr;
632     }
633   }
634   return property;
635 }
636 
637 void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
638                                                 Stream &strm) const {
639   size_t max_name_len = 0;
640   const size_t num_properties = m_properties.size();
641   for (size_t i = 0; i < num_properties; ++i) {
642     const Property *property = ProtectedGetPropertyAtIndex(i);
643     if (property)
644       max_name_len =
645           std::max<size_t>(property->GetName().GetLength(), max_name_len);
646   }
647   for (size_t i = 0; i < num_properties; ++i) {
648     const Property *property = ProtectedGetPropertyAtIndex(i);
649     if (property)
650       property->DumpDescription(interpreter, strm, max_name_len, false);
651   }
652 }
653 
654 void OptionValueProperties::Apropos(
655     const char *keyword,
656     std::vector<const Property *> &matching_properties) const {
657   const size_t num_properties = m_properties.size();
658   StreamString strm;
659   for (size_t i = 0; i < num_properties; ++i) {
660     const Property *property = ProtectedGetPropertyAtIndex(i);
661     if (property) {
662       const OptionValueProperties *properties =
663           property->GetValue()->GetAsProperties();
664       if (properties) {
665         properties->Apropos(keyword, matching_properties);
666       } else {
667         bool match = false;
668         const char *name = property->GetName().GetCString();
669         if (name && ::strcasestr(name, keyword))
670           match = true;
671         else {
672           const char *desc = property->GetDescription();
673           if (desc && ::strcasestr(desc, keyword))
674             match = true;
675         }
676         if (match) {
677           matching_properties.push_back(property);
678         }
679       }
680     }
681   }
682 }
683 
684 lldb::OptionValuePropertiesSP
685 OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx,
686                                       const ConstString &name) {
687   lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name, false));
688   if (option_value_sp) {
689     OptionValueProperties *ov_properties = option_value_sp->GetAsProperties();
690     if (ov_properties)
691       return ov_properties->shared_from_this();
692   }
693   return lldb::OptionValuePropertiesSP();
694 }
695