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