1 //===-- OptionValueDictionary.cpp -------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Interpreter/OptionValueDictionary.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 #include "llvm/ADT/StringRef.h"
16 // Project includes
17 #include "lldb/Core/State.h"
18 #include "lldb/DataFormatters/FormatManager.h"
19 #include "lldb/Interpreter/Args.h"
20 #include "lldb/Interpreter/OptionValueString.h"
21 
22 using namespace lldb;
23 using namespace lldb_private;
24 
25 void
26 OptionValueDictionary::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask)
27 {
28     const Type dict_type = ConvertTypeMaskToType (m_type_mask);
29     if (dump_mask & eDumpOptionType)
30     {
31         if (m_type_mask != eTypeInvalid)
32             strm.Printf ("(%s of %ss)", GetTypeAsCString(), GetBuiltinTypeAsCString(dict_type));
33         else
34             strm.Printf ("(%s)", GetTypeAsCString());
35     }
36     if (dump_mask & eDumpOptionValue)
37     {
38         if (dump_mask & eDumpOptionType)
39             strm.PutCString (" =");
40 
41         collection::iterator pos, end = m_values.end();
42 
43         strm.IndentMore();
44 
45         for (pos = m_values.begin(); pos != end; ++pos)
46         {
47             OptionValue *option_value = pos->second.get();
48             strm.EOL();
49             strm.Indent(pos->first.GetCString());
50 
51             const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
52             switch (dict_type)
53             {
54                 default:
55                 case eTypeArray:
56                 case eTypeDictionary:
57                 case eTypeProperties:
58                 case eTypeFileSpecList:
59                 case eTypePathMap:
60                     strm.PutChar (' ');
61                     option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options);
62                     break;
63 
64                 case eTypeBoolean:
65                 case eTypeChar:
66                 case eTypeEnum:
67                 case eTypeFileSpec:
68                 case eTypeFormat:
69                 case eTypeSInt64:
70                 case eTypeString:
71                 case eTypeUInt64:
72                 case eTypeUUID:
73                     // No need to show the type for dictionaries of simple items
74                     strm.PutCString("=");
75                     option_value->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) | extra_dump_options);
76                     break;
77             }
78         }
79         strm.IndentLess();
80     }
81 
82 }
83 
84 size_t
85 OptionValueDictionary::GetArgs (Args &args) const
86 {
87     args.Clear();
88     collection::const_iterator pos, end = m_values.end();
89     for (pos = m_values.begin(); pos != end; ++pos)
90     {
91         StreamString strm;
92         strm.Printf("%s=", pos->first.GetCString());
93         pos->second->DumpValue(nullptr, strm, eDumpOptionValue|eDumpOptionRaw);
94         args.AppendArgument(strm.GetString().c_str());
95     }
96     return args.GetArgumentCount();
97 }
98 
99 Error
100 OptionValueDictionary::SetArgs (const Args &args, VarSetOperationType op)
101 {
102     Error error;
103     const size_t argc = args.GetArgumentCount();
104     switch (op)
105     {
106     case eVarSetOperationClear:
107         Clear();
108         break;
109 
110     case eVarSetOperationAppend:
111     case eVarSetOperationReplace:
112     case eVarSetOperationAssign:
113         if (argc > 0)
114         {
115             for (size_t i=0; i<argc; ++i)
116             {
117                 llvm::StringRef key_and_value(args.GetArgumentAtIndex(i));
118                 if (!key_and_value.empty())
119                 {
120                     if (key_and_value.find('=') == llvm::StringRef::npos)
121                     {
122                         error.SetErrorString("assign operation takes one or more key=value arguments");
123                         return error;
124                     }
125 
126                     std::pair<llvm::StringRef, llvm::StringRef> kvp(key_and_value.split('='));
127                     llvm::StringRef key = kvp.first;
128                     bool key_valid = false;
129                     if (!key.empty())
130                     {
131                         if (key.front() == '[')
132                         {
133                             // Key name starts with '[', so the key value must be in single or double quotes like:
134                             // ['<key>']
135                             // ["<key>"]
136                             if ((key.size() > 2) && (key.back() == ']'))
137                             {
138                                 // Strip leading '[' and trailing ']'
139                                 key = key.substr(1, key.size()-2);
140                                 const char quote_char = key.front();
141                                 if ((quote_char == '\'') || (quote_char == '"'))
142                                 {
143                                     if ((key.size() > 2) && (key.back() == quote_char))
144                                     {
145                                         // Strip the quotes
146                                         key = key.substr(1, key.size()-2);
147                                         key_valid = true;
148                                     }
149                                 }
150                                 else
151                                 {
152                                     // square brackets, no quotes
153                                     key_valid = true;
154                                 }
155                             }
156                         }
157                         else
158                         {
159                             // No square brackets or quotes
160                             key_valid = true;
161                         }
162                     }
163                     if (!key_valid)
164                     {
165                         error.SetErrorStringWithFormat("invalid key \"%s\", the key must be a bare string or surrounded by brackets with optional quotes: [<key>] or ['<key>'] or [\"<key>\"]", kvp.first.str().c_str());
166                         return error;
167                     }
168 
169                     lldb::OptionValueSP value_sp (CreateValueFromCStringForTypeMask (kvp.second.data(),
170                                                                                      m_type_mask,
171                                                                                      error));
172                     if (value_sp)
173                     {
174                         if (error.Fail())
175                             return error;
176                         m_value_was_set = true;
177                         SetValueForKey (ConstString(key), value_sp, true);
178                     }
179                     else
180                     {
181                         error.SetErrorString("dictionaries that can contain multiple types must subclass OptionValueArray");
182                     }
183                 }
184                 else
185                 {
186                     error.SetErrorString("empty argument");
187                 }
188             }
189         }
190         else
191         {
192             error.SetErrorString("assign operation takes one or more key=value arguments");
193         }
194         break;
195 
196     case eVarSetOperationRemove:
197         if (argc > 0)
198         {
199             for (size_t i=0; i<argc; ++i)
200             {
201                 ConstString key(args.GetArgumentAtIndex(i));
202                 if (!DeleteValueForKey(key))
203                 {
204                     error.SetErrorStringWithFormat("no value found named '%s', aborting remove operation", key.GetCString());
205                     break;
206                 }
207             }
208         }
209         else
210         {
211             error.SetErrorString("remove operation takes one or more key arguments");
212         }
213         break;
214 
215     case eVarSetOperationInsertBefore:
216     case eVarSetOperationInsertAfter:
217     case eVarSetOperationInvalid:
218         error = OptionValue::SetValueFromString (llvm::StringRef(), op);
219         break;
220     }
221     return error;
222 }
223 
224 Error
225 OptionValueDictionary::SetValueFromString (llvm::StringRef value, VarSetOperationType op)
226 {
227     Args args(value.str().c_str());
228     Error error = SetArgs (args, op);
229     if (error.Success())
230         NotifyValueChanged();
231     return error;
232 }
233 
234 lldb::OptionValueSP
235 OptionValueDictionary::GetSubValue (const ExecutionContext *exe_ctx, const char *name, bool will_modify, Error &error) const
236 {
237     lldb::OptionValueSP value_sp;
238 
239     if (name && name[0])
240     {
241         const char *sub_name = nullptr;
242         ConstString key;
243         const char *open_bracket = ::strchr (name, '[');
244 
245         if (open_bracket)
246         {
247             const char *key_start = open_bracket + 1;
248             const char *key_end = nullptr;
249             switch (open_bracket[1])
250             {
251                 case '\'':
252                     ++key_start;
253                     key_end = strchr(key_start, '\'');
254                     if (key_end)
255                     {
256                         if (key_end[1] == ']')
257                         {
258                             if (key_end[2])
259                                 sub_name = key_end + 2;
260                         }
261                         else
262                         {
263                             error.SetErrorStringWithFormat ("invalid value path '%s', single quoted key names must be formatted as ['<key>'] where <key> is a string that doesn't contain quotes", name);
264                             return value_sp;
265                         }
266                     }
267                     else
268                     {
269                         error.SetErrorString ("missing '] key name terminator, key name started with ['");
270                         return value_sp;
271                     }
272                     break;
273                 case '"':
274                     ++key_start;
275                     key_end = strchr(key_start, '"');
276                     if (key_end)
277                     {
278                         if (key_end[1] == ']')
279                         {
280                             if (key_end[2])
281                                 sub_name = key_end + 2;
282                             break;
283                         }
284                         error.SetErrorStringWithFormat ("invalid value path '%s', double quoted key names must be formatted as [\"<key>\"] where <key> is a string that doesn't contain quotes", name);
285                         return value_sp;
286                     }
287                     else
288                     {
289                         error.SetErrorString ("missing \"] key name terminator, key name started with [\"");
290                         return value_sp;
291                     }
292                     break;
293 
294                 default:
295                     key_end = strchr(key_start, ']');
296                     if (key_end)
297                     {
298                         if (key_end[1])
299                             sub_name = key_end + 1;
300                     }
301                     else
302                     {
303                         error.SetErrorString ("missing ] key name terminator, key name started with [");
304                         return value_sp;
305                     }
306                     break;
307             }
308 
309             if (key_start && key_end)
310             {
311                 key.SetCStringWithLength (key_start, key_end - key_start);
312 
313                 value_sp = GetValueForKey (key);
314                 if (value_sp)
315                 {
316                     if (sub_name)
317                         return value_sp->GetSubValue (exe_ctx, sub_name, will_modify, error);
318                 }
319                 else
320                 {
321                     error.SetErrorStringWithFormat("dictionary does not contain a value for the key name '%s'", key.GetCString());
322                 }
323             }
324         }
325         if (!value_sp && error.AsCString() == nullptr)
326         {
327             error.SetErrorStringWithFormat ("invalid value path '%s', %s values only support '[<key>]' subvalues where <key> a string value optionally delimited by single or double quotes",
328                                             name,
329                                             GetTypeAsCString());
330         }
331     }
332     return value_sp;
333 }
334 
335 Error
336 OptionValueDictionary::SetSubValue (const ExecutionContext *exe_ctx, VarSetOperationType op, const char *name, const char *value)
337 {
338     Error error;
339     const bool will_modify = true;
340     lldb::OptionValueSP value_sp (GetSubValue (exe_ctx, name, will_modify, error));
341     if (value_sp)
342         error = value_sp->SetValueFromString(value, op);
343     else
344     {
345         if (error.AsCString() == nullptr)
346             error.SetErrorStringWithFormat("invalid value path '%s'", name);
347     }
348     return error;
349 }
350 
351 
352 lldb::OptionValueSP
353 OptionValueDictionary::GetValueForKey (const ConstString &key) const
354 {
355     lldb::OptionValueSP value_sp;
356     collection::const_iterator pos = m_values.find (key);
357     if (pos != m_values.end())
358         value_sp = pos->second;
359     return value_sp;
360 }
361 
362 const char *
363 OptionValueDictionary::GetStringValueForKey (const ConstString &key)
364 {
365     collection::const_iterator pos = m_values.find (key);
366     if (pos != m_values.end())
367     {
368         OptionValueString *string_value = pos->second->GetAsString();
369         if (string_value)
370             return string_value->GetCurrentValue();
371     }
372     return nullptr;
373 }
374 
375 
376 bool
377 OptionValueDictionary::SetStringValueForKey (const ConstString &key,
378                                              const char *value,
379                                              bool can_replace)
380 {
381     collection::const_iterator pos = m_values.find (key);
382     if (pos != m_values.end())
383     {
384         if (!can_replace)
385             return false;
386         if (pos->second->GetType() == OptionValue::eTypeString)
387         {
388             pos->second->SetValueFromString(value);
389             return true;
390         }
391     }
392     m_values[key] = OptionValueSP (new OptionValueString (value));
393     return true;
394 
395 }
396 
397 bool
398 OptionValueDictionary::SetValueForKey (const ConstString &key,
399                                        const lldb::OptionValueSP &value_sp,
400                                        bool can_replace)
401 {
402     // Make sure the value_sp object is allowed to contain
403     // values of the type passed in...
404     if (value_sp && (m_type_mask & value_sp->GetTypeAsMask()))
405     {
406         if (!can_replace)
407         {
408             collection::const_iterator pos = m_values.find (key);
409             if (pos != m_values.end())
410                 return false;
411         }
412         m_values[key] = value_sp;
413         return true;
414     }
415     return false;
416 }
417 
418 bool
419 OptionValueDictionary::DeleteValueForKey (const ConstString &key)
420 {
421     collection::iterator pos = m_values.find (key);
422     if (pos != m_values.end())
423     {
424         m_values.erase(pos);
425         return true;
426     }
427     return false;
428 }
429 
430 lldb::OptionValueSP
431 OptionValueDictionary::DeepCopy () const
432 {
433     OptionValueDictionary *copied_dict = new OptionValueDictionary (m_type_mask, m_raw_value_dump);
434     lldb::OptionValueSP copied_value_sp(copied_dict);
435     collection::const_iterator pos, end = m_values.end();
436     for (pos = m_values.begin(); pos != end; ++pos)
437     {
438         StreamString strm;
439         strm.Printf("%s=", pos->first.GetCString());
440         copied_dict->SetValueForKey (pos->first, pos->second->DeepCopy(), true);
441     }
442     return copied_value_sp;
443 }
444 
445