Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/CmdLineAndEnvUtils.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
6
7
#ifndef mozilla_CmdLineAndEnvUtils_h
8
#define mozilla_CmdLineAndEnvUtils_h
9
10
// NB: This code may be used outside of xul and thus must not depend on XPCOM
11
12
#if defined(MOZILLA_INTERNAL_API)
13
#include "prenv.h"
14
#include "prprf.h"
15
#include <string.h>
16
#elif defined(XP_WIN)
17
#include <stdlib.h>
18
#endif
19
20
#if defined(XP_WIN)
21
#include "mozilla/Move.h"
22
#include "mozilla/UniquePtr.h"
23
#include "mozilla/Vector.h"
24
25
#include <wchar.h>
26
#include <windows.h>
27
#endif // defined(XP_WIN)
28
29
#include "mozilla/MemoryChecking.h"
30
#include "mozilla/TypedEnumBits.h"
31
32
#include <ctype.h>
33
#include <stdint.h>
34
35
// Undo X11/X.h's definition of None
36
#undef None
37
38
namespace mozilla {
39
40
enum ArgResult {
41
  ARG_NONE  = 0,
42
  ARG_FOUND = 1,
43
  ARG_BAD   = 2 // you wanted a param, but there isn't one
44
};
45
46
template <typename CharT>
47
inline void
48
RemoveArg(int& argc, CharT **argv)
49
0
{
50
0
  do {
51
0
    *argv = *(argv + 1);
52
0
    ++argv;
53
0
  } while (*argv);
54
0
55
0
  --argc;
56
0
}
57
58
namespace internal {
59
60
template <typename FuncT, typename CharT>
61
static inline bool
62
strimatch(FuncT aToLowerFn, const CharT* lowerstr, const CharT* mixedstr)
63
126
{
64
129
  while(*lowerstr) {
65
129
    if (!*mixedstr) return false; // mixedstr is shorter
66
129
    if (static_cast<CharT>(aToLowerFn(*mixedstr)) != *lowerstr) return false; // no match
67
3
68
3
    ++lowerstr;
69
3
    ++mixedstr;
70
3
  }
71
126
72
126
  if (*mixedstr) return false; // lowerstr is shorter
73
0
74
0
  return true;
75
0
}
nsAppRunner.cpp:bool mozilla::internal::strimatch<int (*)(int), char>(int (*)(int), char const*, char const*)
Line
Count
Source
63
126
{
64
129
  while(*lowerstr) {
65
129
    if (!*mixedstr) return false; // mixedstr is shorter
66
129
    if (static_cast<CharT>(aToLowerFn(*mixedstr)) != *lowerstr) return false; // no match
67
3
68
3
    ++lowerstr;
69
3
    ++mixedstr;
70
3
  }
71
126
72
126
  if (*mixedstr) return false; // lowerstr is shorter
73
0
74
0
  return true;
75
0
}
Unexecuted instantiation: nsAppRunner.cpp:bool mozilla::internal::strimatch<unsigned int (*)(unsigned int), wchar_t>(unsigned int (*)(unsigned int), wchar_t const*, wchar_t const*)
76
77
} // namespace internal
78
79
inline bool
80
strimatch(const char* lowerstr, const char* mixedstr)
81
126
{
82
126
  return internal::strimatch(&tolower, lowerstr, mixedstr);
83
126
}
84
85
inline bool
86
strimatch(const wchar_t* lowerstr, const wchar_t* mixedstr)
87
0
{
88
0
  return internal::strimatch(&towlower, lowerstr, mixedstr);
89
0
}
90
91
enum class FlagLiteral
92
{
93
  osint,
94
  safemode
95
};
96
97
template <typename CharT, FlagLiteral Literal>
98
inline const CharT* GetLiteral();
99
100
#define DECLARE_FLAG_LITERAL(enum_name, literal)                 \
101
  template <> inline                                             \
102
  const char* GetLiteral<char, FlagLiteral::enum_name>()         \
103
3
  {                                                              \
104
3
    return literal;                                              \
105
3
  }                                                              \
Unexecuted instantiation: char const* mozilla::GetLiteral<char, (mozilla::FlagLiteral)0>()
char const* mozilla::GetLiteral<char, (mozilla::FlagLiteral)1>()
Line
Count
Source
103
3
  {                                                              \
104
3
    return literal;                                              \
105
3
  }                                                              \
106
                                                                 \
107
  template <> inline                                             \
108
  const wchar_t* GetLiteral<wchar_t, FlagLiteral::enum_name>()   \
109
0
  {                                                              \
110
0
    return L##literal;                                           \
111
0
  }
Unexecuted instantiation: wchar_t const* mozilla::GetLiteral<wchar_t, (mozilla::FlagLiteral)0>()
Unexecuted instantiation: wchar_t const* mozilla::GetLiteral<wchar_t, (mozilla::FlagLiteral)1>()
112
113
DECLARE_FLAG_LITERAL(osint, "osint")
114
DECLARE_FLAG_LITERAL(safemode, "safe-mode")
115
116
enum class CheckArgFlag : uint32_t
117
{
118
  None = 0,
119
  CheckOSInt = (1 << 0), // Retrun ARG_BAD if osint arg is also present.
120
  RemoveArg = (1 << 1)   // Remove the argument from the argv array.
121
};
122
123
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CheckArgFlag)
124
125
/**
126
 * Check for a commandline flag. If the flag takes a parameter, the
127
 * parameter is returned in aParam. Flags may be in the form -arg or
128
 * --arg (or /arg on win32).
129
 *
130
 * @param aArgc The argc value.
131
 * @param aArgv The original argv.
132
 * @param aArg the parameter to check. Must be lowercase.
133
 * @param aParam if non-null, the -arg <data> will be stored in this pointer.
134
 *        This is *not* allocated, but rather a pointer to the argv data.
135
 * @param aFlags Flags @see CheckArgFlag
136
 */
137
template <typename CharT>
138
inline ArgResult
139
CheckArg(int& aArgc, CharT** aArgv, const CharT* aArg, const CharT **aParam,
140
         CheckArgFlag aFlags)
141
42
{
142
42
  MOZ_ASSERT(aArgv && aArg);
143
42
144
42
  CharT **curarg = aArgv + 1; // skip argv[0]
145
42
  ArgResult ar = ARG_NONE;
146
42
147
210
  while (*curarg) {
148
168
    CharT *arg = curarg[0];
149
168
150
168
    if (arg[0] == '-'
151
#if defined(XP_WIN)
152
        || *arg == '/'
153
#endif
154
126
        ) {
155
126
      ++arg;
156
126
157
126
      if (*arg == '-') {
158
0
        ++arg;
159
0
      }
160
126
161
126
      if (strimatch(aArg, arg)) {
162
0
        if (aFlags & CheckArgFlag::RemoveArg) {
163
0
          RemoveArg(aArgc, curarg);
164
0
        } else {
165
0
          ++curarg;
166
0
        }
167
0
168
0
        if (!aParam) {
169
0
          ar = ARG_FOUND;
170
0
          break;
171
0
        }
172
0
173
0
        if (*curarg) {
174
0
          if (**curarg == '-'
175
#if defined(XP_WIN)
176
              || **curarg == '/'
177
#endif
178
0
              ) {
179
0
            return ARG_BAD;
180
0
          }
181
0
182
0
          *aParam = *curarg;
183
0
184
0
          if (aFlags & CheckArgFlag::RemoveArg) {
185
0
            RemoveArg(aArgc, curarg);
186
0
          }
187
0
188
0
          ar = ARG_FOUND;
189
0
          break;
190
0
        }
191
0
192
0
        return ARG_BAD;
193
0
      }
194
126
    }
195
168
196
168
    ++curarg;
197
168
  }
198
42
199
42
  if ((aFlags & CheckArgFlag::CheckOSInt) && ar == ARG_FOUND) {
200
0
    ArgResult arOSInt = CheckArg(aArgc, aArgv,
201
0
                                 GetLiteral<CharT, FlagLiteral::osint>(),
202
0
                                 static_cast<const CharT**>(nullptr),
203
0
                                 CheckArgFlag::None);
204
0
    if (arOSInt == ARG_FOUND) {
205
0
      ar = ARG_BAD;
206
0
#if defined(MOZILLA_INTERNAL_API)
207
0
      PR_fprintf(PR_STDERR, "Error: argument --osint is invalid\n");
208
0
#endif // defined(MOZILLA_INTERNAL_API)
209
0
    }
210
0
  }
211
42
212
42
  return ar;
213
42
}
214
215
#if defined(XP_WIN)
216
217
namespace internal {
218
219
/**
220
 * Get the length that the string will take and takes into account the
221
 * additional length if the string needs to be quoted and if characters need to
222
 * be escaped.
223
 */
224
inline int
225
ArgStrLen(const wchar_t *s)
226
{
227
  int backslashes = 0;
228
  int i = wcslen(s);
229
  bool hasDoubleQuote = wcschr(s, L'"') != nullptr;
230
  // Only add doublequotes if the string contains a space or a tab
231
  bool addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
232
233
  if (addDoubleQuotes) {
234
    i += 2; // initial and final duoblequote
235
  }
236
237
  if (hasDoubleQuote) {
238
    while (*s) {
239
      if (*s == '\\') {
240
        ++backslashes;
241
      } else {
242
        if (*s == '"') {
243
          // Escape the doublequote and all backslashes preceding the doublequote
244
          i += backslashes + 1;
245
        }
246
247
        backslashes = 0;
248
      }
249
250
      ++s;
251
    }
252
  }
253
254
  return i;
255
}
256
257
/**
258
 * Copy string "s" to string "d", quoting the argument as appropriate and
259
 * escaping doublequotes along with any backslashes that immediately precede
260
 * doublequotes.
261
 * The CRT parses this to retrieve the original argc/argv that we meant,
262
 * see STDARGV.C in the MSVC CRT sources.
263
 *
264
 * @return the end of the string
265
 */
266
inline wchar_t*
267
ArgToString(wchar_t *d, const wchar_t *s)
268
{
269
  int backslashes = 0;
270
  bool hasDoubleQuote = wcschr(s, L'"') != nullptr;
271
  // Only add doublequotes if the string contains a space or a tab
272
  bool addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
273
274
  if (addDoubleQuotes) {
275
    *d = '"'; // initial doublequote
276
    ++d;
277
  }
278
279
  if (hasDoubleQuote) {
280
    int i;
281
    while (*s) {
282
      if (*s == '\\') {
283
        ++backslashes;
284
      } else {
285
        if (*s == '"') {
286
          // Escape the doublequote and all backslashes preceding the doublequote
287
          for (i = 0; i <= backslashes; ++i) {
288
            *d = '\\';
289
            ++d;
290
          }
291
        }
292
293
        backslashes = 0;
294
      }
295
296
      *d = *s;
297
      ++d; ++s;
298
    }
299
  } else {
300
    wcscpy(d, s);
301
    d += wcslen(s);
302
  }
303
304
  if (addDoubleQuotes) {
305
    *d = '"'; // final doublequote
306
    ++d;
307
  }
308
309
  return d;
310
}
311
312
} // namespace internal
313
314
/**
315
 * Creates a command line from a list of arguments.
316
 */
317
inline UniquePtr<wchar_t[]>
318
MakeCommandLine(int argc, wchar_t **argv)
319
{
320
  int i;
321
  int len = 0;
322
323
  // The + 1 of the last argument handles the allocation for null termination
324
  for (i = 0; i < argc; ++i) {
325
    len += internal::ArgStrLen(argv[i]) + 1;
326
  }
327
328
  // Protect against callers that pass 0 arguments
329
  if (len == 0) {
330
    len = 1;
331
  }
332
333
  auto s = MakeUnique<wchar_t[]>(len);
334
  if (!s) {
335
    return s;
336
  }
337
338
  wchar_t *c = s.get();
339
  for (i = 0; i < argc; ++i) {
340
    c = internal::ArgToString(c, argv[i]);
341
    if (i + 1 != argc) {
342
      *c = ' ';
343
      ++c;
344
    }
345
  }
346
347
  *c = '\0';
348
349
  return s;
350
}
351
352
inline bool
353
SetArgv0ToFullBinaryPath(wchar_t* aArgv[])
354
{
355
  if (!aArgv) {
356
    return false;
357
  }
358
359
  DWORD bufLen = MAX_PATH;
360
  mozilla::UniquePtr<wchar_t[]> buf;
361
  DWORD retLen;
362
363
  while (true) {
364
    buf = mozilla::MakeUnique<wchar_t[]>(bufLen);
365
    retLen = ::GetModuleFileNameW(nullptr, buf.get(), bufLen);
366
    if (!retLen) {
367
      return false;
368
    }
369
370
    if (retLen == bufLen && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
371
      bufLen *= 2;
372
      continue;
373
    }
374
375
    break;
376
  }
377
378
  // Upon success, retLen *excludes* the null character
379
  ++retLen;
380
381
  // Since we're likely to have a bunch of unused space in buf, let's reallocate
382
  // a string to the actual size of the file name.
383
  auto newArgv_0 = mozilla::MakeUnique<wchar_t[]>(retLen);
384
  if (wcscpy_s(newArgv_0.get(), retLen, buf.get())) {
385
    return false;
386
  }
387
388
  // We intentionally leak newArgv_0 into argv[0]
389
  aArgv[0] = newArgv_0.release();
390
  MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(aArgv[0]);
391
  return true;
392
}
393
394
#endif // defined(XP_WIN)
395
396
// Save literal putenv string to environment variable.
397
inline void
398
SaveToEnv(const char *aEnvString)
399
6
{
400
6
#if defined(MOZILLA_INTERNAL_API)
401
6
  char *expr = strdup(aEnvString);
402
6
  if (expr) {
403
6
    PR_SetEnv(expr);
404
6
  }
405
6
406
6
  // We intentionally leak |expr| here since it is required by PR_SetEnv.
407
6
  MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(expr);
408
#elif defined(XP_WIN)
409
  // This is the same as the NSPR implementation
410
  // (Note that we don't need to do a strdup for this case; the CRT makes a copy)
411
  _putenv(aEnvString);
412
#else
413
#error "Not implemented for this configuration"
414
#endif
415
}
416
417
inline bool
418
EnvHasValue(const char* aVarName)
419
6
{
420
6
#if defined(MOZILLA_INTERNAL_API)
421
6
  const char* val = PR_GetEnv(aVarName);
422
6
  return val && *val;
423
#elif defined(XP_WIN)
424
  // This is the same as the NSPR implementation
425
  const char* val = getenv(aVarName);
426
  return val && *val;
427
#else
428
#error "Not implemented for this configuration"
429
#endif
430
}
431
432
} // namespace mozilla
433
434
#endif // mozilla_CmdLineAndEnvUtils_h