Coverage Report

Created: 2024-05-20 07:14

/src/skia/tools/flags/CommandLineFlags.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2013 Google Inc.
3
 *
4
 * Use of this source code is governed by a BSD-style license that can be
5
 * found in the LICENSE file.
6
 */
7
8
#include "include/private/base/SkTDArray.h"
9
#include "src/base/SkTSort.h"
10
#include "tools/flags/CommandLineFlags.h"
11
12
#include <stdlib.h>
13
14
0
template <typename T> static void ignore_result(const T&) {}
15
16
bool SkFlagInfo::CreateStringFlag(const char*                    name,
17
                                  const char*                    shortName,
18
                                  CommandLineFlags::StringArray* pStrings,
19
                                  const char*                    defaultValue,
20
                                  const char*                    helpString,
21
148
                                  const char*                    extendedHelpString) {
22
148
    SkFlagInfo* info =
23
148
            new SkFlagInfo(name, shortName, kString_FlagType, helpString, extendedHelpString);
24
148
    info->fDefaultString.set(defaultValue);
25
26
148
    info->fStrings = pStrings;
27
148
    SetDefaultStrings(pStrings, defaultValue);
28
148
    return true;
29
148
}
30
31
void SkFlagInfo::SetDefaultStrings(CommandLineFlags::StringArray* pStrings,
32
148
                                   const char*                    defaultValue) {
33
148
    pStrings->reset();
34
148
    if (nullptr == defaultValue) {
35
0
        return;
36
0
    }
37
    // If default is "", leave the array empty.
38
148
    size_t defaultLength = strlen(defaultValue);
39
148
    if (defaultLength > 0) {
40
74
        const char* const defaultEnd = defaultValue + defaultLength;
41
74
        const char*       begin      = defaultValue;
42
148
        while (true) {
43
148
            while (begin < defaultEnd && ' ' == *begin) {
44
0
                begin++;
45
0
            }
46
148
            if (begin < defaultEnd) {
47
74
                const char* end = begin + 1;
48
666
                while (end < defaultEnd && ' ' != *end) {
49
592
                    end++;
50
592
                }
51
74
                size_t length = end - begin;
52
74
                pStrings->append(begin, length);
53
74
                begin = end + 1;
54
74
            } else {
55
74
                break;
56
74
            }
57
148
        }
58
74
    }
59
148
}
60
61
0
static bool string_is_in(const char* target, const char* set[], size_t len) {
62
0
    for (size_t i = 0; i < len; i++) {
63
0
        if (0 == strcmp(target, set[i])) {
64
0
            return true;
65
0
        }
66
0
    }
67
0
    return false;
68
0
}
69
70
/**
71
 *  Check to see whether string represents a boolean value.
72
 *  @param string C style string to parse.
73
 *  @param result Pointer to a boolean which will be set to the value in the string, if the
74
 *      string represents a boolean.
75
 *  @param boolean True if the string represents a boolean, false otherwise.
76
 */
77
0
static bool parse_bool_arg(const char* string, bool* result) {
78
0
    static const char* trueValues[] = {"1", "TRUE", "true"};
79
0
    if (string_is_in(string, trueValues, std::size(trueValues))) {
80
0
        *result = true;
81
0
        return true;
82
0
    }
83
0
    static const char* falseValues[] = {"0", "FALSE", "false"};
84
0
    if (string_is_in(string, falseValues, std::size(falseValues))) {
85
0
        *result = false;
86
0
        return true;
87
0
    }
88
0
    SkDebugf("Parameter \"%s\" not supported.\n", string);
89
0
    return false;
90
0
}
91
92
0
bool SkFlagInfo::match(const char* string) {
93
0
    if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
94
0
        string++;
95
0
        const SkString* compareName;
96
0
        if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
97
0
            string++;
98
            // There were two dashes. Compare against full name.
99
0
            compareName = &fName;
100
0
        } else {
101
            // One dash. Compare against the short name.
102
0
            compareName = &fShortName;
103
0
        }
104
0
        if (kBool_FlagType == fFlagType) {
105
            // In this case, go ahead and set the value.
106
0
            if (compareName->equals(string)) {
107
0
                *fBoolValue = true;
108
0
                return true;
109
0
            }
110
0
            if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
111
0
                string += 2;
112
                // Only allow "no" to be prepended to the full name.
113
0
                if (fName.equals(string)) {
114
0
                    *fBoolValue = false;
115
0
                    return true;
116
0
                }
117
0
                return false;
118
0
            }
119
0
            int equalIndex = SkStrFind(string, "=");
120
0
            if (equalIndex > 0) {
121
                // The string has an equal sign. Check to see if the string matches.
122
0
                SkString flag(string, equalIndex);
123
0
                if (flag.equals(*compareName)) {
124
                    // Check to see if the remainder beyond the equal sign is true or false:
125
0
                    string += equalIndex + 1;
126
0
                    parse_bool_arg(string, fBoolValue);
127
0
                    return true;
128
0
                } else {
129
0
                    return false;
130
0
                }
131
0
            }
132
0
        }
133
0
        return compareName->equals(string);
134
0
    }
135
136
    // Has no dash
137
0
    return false;
138
0
}
139
140
SkFlagInfo* CommandLineFlags::gHead;
141
SkString    CommandLineFlags::gUsage;
142
143
0
void CommandLineFlags::SetUsage(const char* usage) { gUsage.set(usage); }
144
145
0
void CommandLineFlags::PrintUsage() { SkDebugf("%s", gUsage.c_str()); }
146
147
// Maximum line length for the help message.
148
0
#define LINE_LENGTH 72
149
150
0
static void print_indented(const SkString& text) {
151
0
    size_t      length   = text.size();
152
0
    const char* currLine = text.c_str();
153
0
    const char* stop     = currLine + length;
154
0
    while (currLine < stop) {
155
0
        int lineBreak = SkStrFind(currLine, "\n");
156
0
        if (lineBreak < 0) {
157
0
            lineBreak = static_cast<int>(strlen(currLine));
158
0
        }
159
0
        if (lineBreak > LINE_LENGTH) {
160
            // No line break within line length. Will need to insert one.
161
            // Find a space before the line break.
162
0
            int spaceIndex = LINE_LENGTH - 1;
163
0
            while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
164
0
                spaceIndex--;
165
0
            }
166
0
            int gap;
167
0
            if (0 == spaceIndex) {
168
                // No spaces on the entire line. Go ahead and break mid word.
169
0
                spaceIndex = LINE_LENGTH;
170
0
                gap        = 0;
171
0
            } else {
172
                // Skip the space on the next line
173
0
                gap = 1;
174
0
            }
175
0
            SkDebugf("        %.*s\n", spaceIndex, currLine);
176
0
            currLine += spaceIndex + gap;
177
0
        } else {
178
            // the line break is within the limit. Break there.
179
0
            lineBreak++;
180
0
            SkDebugf("        %.*s", lineBreak, currLine);
181
0
            currLine += lineBreak;
182
0
        }
183
0
    }
184
0
}
185
186
0
static void print_help_for_flag(const SkFlagInfo* flag) {
187
0
    SkDebugf("    --%s", flag->name().c_str());
188
0
    const SkString& shortName = flag->shortName();
189
0
    if (shortName.size() > 0) {
190
0
        SkDebugf(" or -%s", shortName.c_str());
191
0
    }
192
0
    SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
193
0
    if (flag->defaultValue().size() > 0) {
194
0
        SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
195
0
    }
196
0
    SkDebugf("\n");
197
0
    const SkString& help = flag->help();
198
0
    print_indented(help);
199
0
    SkDebugf("\n");
200
0
}
201
0
static void print_extended_help_for_flag(const SkFlagInfo* flag) {
202
0
    print_help_for_flag(flag);
203
0
    print_indented(flag->extendedHelp());
204
0
    SkDebugf("\n");
205
0
}
206
207
namespace {
208
struct CompareFlagsByName {
209
0
    bool operator()(SkFlagInfo* a, SkFlagInfo* b) const {
210
0
        return strcmp(a->name().c_str(), b->name().c_str()) < 0;
211
0
    }
212
};
213
}  // namespace
214
215
0
void CommandLineFlags::Parse(int argc, const char* const* argv) {
216
    // Only allow calling this function once.
217
0
    static bool gOnce;
218
0
    if (gOnce) {
219
0
        SkDebugf("Parse should only be called once at the beginning of main!\n");
220
0
        SkASSERT(false);
221
0
        return;
222
0
    }
223
0
    gOnce = true;
224
225
0
    bool helpPrinted  = false;
226
0
    bool flagsPrinted = false;
227
    // Loop over argv, starting with 1, since the first is just the name of the program.
228
0
    for (int i = 1; i < argc; i++) {
229
0
        if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
230
            // Print help message.
231
0
            SkTDArray<const char*> helpFlags;
232
0
            for (int j = i + 1; j < argc; j++) {
233
0
                if (SkStrStartsWith(argv[j], '-')) {
234
0
                    break;
235
0
                }
236
0
                helpFlags.append(1, &argv[j]);
237
0
            }
238
0
            if (0 == helpFlags.size()) {
239
                // Only print general help message if help for specific flags is not requested.
240
0
                SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
241
0
            }
242
0
            if (!flagsPrinted) {
243
0
                SkDebugf("Flags:\n");
244
0
                flagsPrinted = true;
245
0
            }
246
0
            if (0 == helpFlags.size()) {
247
                // If no flags followed --help, print them all
248
0
                SkTDArray<SkFlagInfo*> allFlags;
249
0
                for (SkFlagInfo* flag = CommandLineFlags::gHead; flag; flag = flag->next()) {
250
0
                    allFlags.push_back(flag);
251
0
                }
252
0
                SkTQSort(allFlags.begin(), allFlags.end(), CompareFlagsByName());
253
0
                for (SkFlagInfo* flag : allFlags) {
254
0
                    print_help_for_flag(flag);
255
0
                    if (flag->extendedHelp().size() > 0) {
256
0
                        SkDebugf("        Use '--help %s' for more information.\n",
257
0
                                 flag->name().c_str());
258
0
                    }
259
0
                }
260
0
            } else {
261
0
                for (SkFlagInfo* flag = CommandLineFlags::gHead; flag; flag = flag->next()) {
262
0
                    for (int k = 0; k < helpFlags.size(); k++) {
263
0
                        if (flag->name().equals(helpFlags[k]) ||
264
0
                            flag->shortName().equals(helpFlags[k])) {
265
0
                            print_extended_help_for_flag(flag);
266
0
                            helpFlags.remove(k);
267
0
                            break;
268
0
                        }
269
0
                    }
270
0
                }
271
0
            }
272
0
            if (helpFlags.size() > 0) {
273
0
                SkDebugf("Requested help for unrecognized flags:\n");
274
0
                for (int k = 0; k < helpFlags.size(); k++) {
275
0
                    SkDebugf("    --%s\n", helpFlags[k]);
276
0
                }
277
0
            }
278
0
            helpPrinted = true;
279
0
        }
280
0
        if (!helpPrinted) {
281
0
            SkFlagInfo* matchedFlag = nullptr;
282
0
            SkFlagInfo* flag        = gHead;
283
0
            int         startI      = i;
284
0
            while (flag != nullptr) {
285
0
                if (flag->match(argv[startI])) {
286
0
                    i = startI;
287
0
                    if (matchedFlag) {
288
                        // Don't redefine the same flag with different types.
289
0
                        SkASSERT(matchedFlag->getFlagType() == flag->getFlagType());
290
0
                    } else {
291
0
                        matchedFlag = flag;
292
0
                    }
293
0
                    switch (flag->getFlagType()) {
294
0
                        case SkFlagInfo::kBool_FlagType:
295
                            // Can be handled by match, above, but can also be set by the next
296
                            // string.
297
0
                            if (i + 1 < argc && !SkStrStartsWith(argv[i + 1], '-')) {
298
0
                                i++;
299
0
                                bool value;
300
0
                                if (parse_bool_arg(argv[i], &value)) {
301
0
                                    flag->setBool(value);
302
0
                                }
303
0
                            }
304
0
                            break;
305
0
                        case SkFlagInfo::kString_FlagType:
306
0
                            flag->resetStrings();
307
                            // Add all arguments until another flag is reached.
308
0
                            while (i + 1 < argc) {
309
0
                                char* end = nullptr;
310
                                // Negative numbers aren't flags.
311
0
                                ignore_result(strtod(argv[i + 1], &end));
312
0
                                if (end == argv[i + 1] && SkStrStartsWith(argv[i + 1], '-')) {
313
0
                                    break;
314
0
                                }
315
0
                                i++;
316
0
                                flag->append(argv[i]);
317
0
                            }
318
0
                            break;
319
0
                        case SkFlagInfo::kInt_FlagType:
320
0
                            i++;
321
0
                            flag->setInt(atoi(argv[i]));
322
0
                            break;
323
0
                        case SkFlagInfo::kDouble_FlagType:
324
0
                            i++;
325
0
                            flag->setDouble(atof(argv[i]));
326
0
                            break;
327
0
                        default: SkDEBUGFAIL("Invalid flag type");
328
0
                    }
329
0
                }
330
0
                flag = flag->next();
331
0
            }
332
0
            if (!matchedFlag) {
333
#if defined(SK_BUILD_FOR_MAC)
334
                if (SkStrStartsWith(argv[i], "NSDocumentRevisions") ||
335
                    SkStrStartsWith(argv[i], "-NSDocumentRevisions")) {
336
                    i++;  // skip YES
337
                } else
338
#endif
339
0
                    SkDebugf("Got unknown flag '%s'. Exiting.\n", argv[i]);
340
0
                exit(-1);
341
0
            }
342
0
        }
343
0
    }
344
    // Since all of the flags have been set, release the memory used by each
345
    // flag. FLAGS_x can still be used after this.
346
0
    SkFlagInfo* flag = gHead;
347
0
    gHead            = nullptr;
348
0
    while (flag != nullptr) {
349
0
        SkFlagInfo* next = flag->next();
350
0
        delete flag;
351
0
        flag = next;
352
0
    }
353
0
    if (helpPrinted) {
354
0
        exit(0);
355
0
    }
356
0
}
Unexecuted instantiation: CommandLineFlags::Parse(int, char const* const*)
Unexecuted instantiation: CommandLineFlags::Parse(int, char const* const*)
357
358
namespace {
359
360
0
template <typename Strings> bool ShouldSkipImpl(const Strings& strings, const char* name) {
361
0
    int    count      = strings.size();
362
0
    size_t testLen    = strlen(name);
363
0
    bool   anyExclude = count == 0;
364
0
    for (int i = 0; i < strings.size(); ++i) {
365
0
        const char* matchName = strings[i];
366
0
        size_t      matchLen  = strlen(matchName);
367
0
        bool        matchExclude, matchStart, matchEnd;
368
0
        if ((matchExclude = matchName[0] == '~')) {
369
0
            anyExclude = true;
370
0
            matchName++;
371
0
            matchLen--;
372
0
        }
373
0
        if ((matchStart = matchName[0] == '^')) {
374
0
            matchName++;
375
0
            matchLen--;
376
0
        }
377
0
        if ((matchEnd = matchName[matchLen - 1] == '$')) {
378
0
            matchLen--;
379
0
        }
380
0
        if (matchStart
381
0
                    ? (!matchEnd || matchLen == testLen) && strncmp(name, matchName, matchLen) == 0
382
0
                    : matchEnd
383
0
                              ? matchLen <= testLen &&
384
0
                                        strncmp(name + testLen - matchLen, matchName, matchLen) == 0
385
0
                              : strstr(name, matchName) != nullptr) {
386
0
            return matchExclude;
387
0
        }
388
0
    }
389
0
    return !anyExclude;
390
0
}
Unexecuted instantiation: CommandLineFlags.cpp:bool (anonymous namespace)::ShouldSkipImpl<SkTDArray<char const*> >(SkTDArray<char const*> const&, char const*)
Unexecuted instantiation: CommandLineFlags.cpp:bool (anonymous namespace)::ShouldSkipImpl<CommandLineFlags::StringArray>(CommandLineFlags::StringArray const&, char const*)
391
392
}  // namespace
393
394
0
bool CommandLineFlags::ShouldSkip(const SkTDArray<const char*>& strings, const char* name) {
395
0
    return ShouldSkipImpl(strings, name);
396
0
}
397
0
bool CommandLineFlags::ShouldSkip(const StringArray& strings, const char* name) {
398
0
    return ShouldSkipImpl(strings, name);
399
0
}