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