1/*
2 *  Copyright 2006 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15
16#if defined(WEBRTC_WIN)
17#include "webrtc/base/win32.h"
18#include <shellapi.h>
19#endif
20
21#include "webrtc/base/flags.h"
22
23namespace rtc {
24// -----------------------------------------------------------------------------
25// Implementation of Flag
26
27Flag::Flag(const char* file, const char* name, const char* comment,
28           Type type, void* variable, FlagValue default__)
29    : file_(file),
30      name_(name),
31      comment_(comment),
32      type_(type),
33      variable_(reinterpret_cast<FlagValue*>(variable)),
34      default_(default__) {
35  FlagList::Register(this);
36}
37
38
39void Flag::SetToDefault() {
40  // Note that we cannot simply do '*variable_ = default_;' since
41  // flag variables are not really of type FlagValue and thus may
42  // be smaller! The FlagValue union is simply 'overlayed' on top
43  // of a flag variable for convenient access. Since union members
44  // are guarantee to be aligned at the beginning, this works.
45  switch (type_) {
46    case Flag::BOOL:
47      variable_->b = default_.b;
48      return;
49    case Flag::INT:
50      variable_->i = default_.i;
51      return;
52    case Flag::FLOAT:
53      variable_->f = default_.f;
54      return;
55    case Flag::STRING:
56      variable_->s = default_.s;
57      return;
58  }
59  FATAL() << "unreachable code";
60}
61
62
63static const char* Type2String(Flag::Type type) {
64  switch (type) {
65    case Flag::BOOL: return "bool";
66    case Flag::INT: return "int";
67    case Flag::FLOAT: return "float";
68    case Flag::STRING: return "string";
69  }
70  FATAL() << "unreachable code";
71}
72
73
74static void PrintFlagValue(Flag::Type type, FlagValue* p) {
75  switch (type) {
76    case Flag::BOOL:
77      printf("%s", (p->b ? "true" : "false"));
78      return;
79    case Flag::INT:
80      printf("%d", p->i);
81      return;
82    case Flag::FLOAT:
83      printf("%f", p->f);
84      return;
85    case Flag::STRING:
86      printf("%s", p->s);
87      return;
88  }
89  FATAL() << "unreachable code";
90}
91
92
93void Flag::Print(bool print_current_value) {
94  printf("  --%s (%s)  type: %s  default: ", name_, comment_,
95          Type2String(type_));
96  PrintFlagValue(type_, &default_);
97  if (print_current_value) {
98    printf("  current value: ");
99    PrintFlagValue(type_, variable_);
100  }
101  printf("\n");
102}
103
104
105// -----------------------------------------------------------------------------
106// Implementation of FlagList
107
108Flag* FlagList::list_ = NULL;
109
110
111FlagList::FlagList() {
112  list_ = NULL;
113}
114
115void FlagList::Print(const char* file, bool print_current_value) {
116  // Since flag registration is likely by file (= C++ file),
117  // we don't need to sort by file and still get grouped output.
118  const char* current = NULL;
119  for (Flag* f = list_; f != NULL; f = f->next()) {
120    if (file == NULL || file == f->file()) {
121      if (current != f->file()) {
122        printf("Flags from %s:\n", f->file());
123        current = f->file();
124      }
125      f->Print(print_current_value);
126    }
127  }
128}
129
130
131Flag* FlagList::Lookup(const char* name) {
132  Flag* f = list_;
133  while (f != NULL && strcmp(name, f->name()) != 0)
134    f = f->next();
135  return f;
136}
137
138
139void FlagList::SplitArgument(const char* arg,
140                             char* buffer, int buffer_size,
141                             const char** name, const char** value,
142                             bool* is_bool) {
143  *name = NULL;
144  *value = NULL;
145  *is_bool = false;
146
147  if (*arg == '-') {
148    // find the begin of the flag name
149    arg++;  // remove 1st '-'
150    if (*arg == '-')
151      arg++;  // remove 2nd '-'
152    if (arg[0] == 'n' && arg[1] == 'o') {
153      arg += 2;  // remove "no"
154      *is_bool = true;
155    }
156    *name = arg;
157
158    // find the end of the flag name
159    while (*arg != '\0' && *arg != '=')
160      arg++;
161
162    // get the value if any
163    if (*arg == '=') {
164      // make a copy so we can NUL-terminate flag name
165      int n = static_cast<int>(arg - *name);
166      CHECK_LT(n, buffer_size);
167      memcpy(buffer, *name, n * sizeof(char));
168      buffer[n] = '\0';
169      *name = buffer;
170      // get the value
171      *value = arg + 1;
172    }
173  }
174}
175
176
177int FlagList::SetFlagsFromCommandLine(int* argc, const char** argv,
178                                      bool remove_flags) {
179  // parse arguments
180  for (int i = 1; i < *argc; /* see below */) {
181    int j = i;  // j > 0
182    const char* arg = argv[i++];
183
184    // split arg into flag components
185    char buffer[1024];
186    const char* name;
187    const char* value;
188    bool is_bool;
189    SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);
190
191    if (name != NULL) {
192      // lookup the flag
193      Flag* flag = Lookup(name);
194      if (flag == NULL) {
195        fprintf(stderr, "Error: unrecognized flag %s\n", arg);
196        return j;
197      }
198
199      // if we still need a flag value, use the next argument if available
200      if (flag->type() != Flag::BOOL && value == NULL) {
201        if (i < *argc) {
202          value = argv[i++];
203        } else {
204          fprintf(stderr, "Error: missing value for flag %s of type %s\n",
205            arg, Type2String(flag->type()));
206          return j;
207        }
208      }
209
210      // set the flag
211      char empty[] = { '\0' };
212      char* endp = empty;
213      switch (flag->type()) {
214        case Flag::BOOL:
215          *flag->bool_variable() = !is_bool;
216          break;
217        case Flag::INT:
218          *flag->int_variable() = strtol(value, &endp, 10);
219          break;
220        case Flag::FLOAT:
221          *flag->float_variable() = strtod(value, &endp);
222          break;
223        case Flag::STRING:
224          *flag->string_variable() = value;
225          break;
226      }
227
228      // handle errors
229      if ((flag->type() == Flag::BOOL && value != NULL) ||
230          (flag->type() != Flag::BOOL && is_bool) ||
231          *endp != '\0') {
232        fprintf(stderr, "Error: illegal value for flag %s of type %s\n",
233          arg, Type2String(flag->type()));
234        return j;
235      }
236
237      // remove the flag & value from the command
238      if (remove_flags)
239        while (j < i)
240          argv[j++] = NULL;
241    }
242  }
243
244  // shrink the argument list
245  if (remove_flags) {
246    int j = 1;
247    for (int i = 1; i < *argc; i++) {
248      if (argv[i] != NULL)
249        argv[j++] = argv[i];
250    }
251    *argc = j;
252  }
253
254  // parsed all flags successfully
255  return 0;
256}
257
258void FlagList::Register(Flag* flag) {
259  assert(flag != NULL && strlen(flag->name()) > 0);
260  CHECK(!Lookup(flag->name())) << "flag " << flag->name() << " declared twice";
261  flag->next_ = list_;
262  list_ = flag;
263}
264
265#if defined(WEBRTC_WIN)
266WindowsCommandLineArguments::WindowsCommandLineArguments() {
267  // start by getting the command line.
268  LPTSTR command_line = ::GetCommandLine();
269   // now, convert it to a list of wide char strings.
270  LPWSTR *wide_argv = ::CommandLineToArgvW(command_line, &argc_);
271  // now allocate an array big enough to hold that many string pointers.
272  argv_ = new char*[argc_];
273
274  // iterate over the returned wide strings;
275  for(int i = 0; i < argc_; ++i) {
276    std::string s = rtc::ToUtf8(wide_argv[i], wcslen(wide_argv[i]));
277    char *buffer = new char[s.length() + 1];
278    rtc::strcpyn(buffer, s.length() + 1, s.c_str());
279
280    // make sure the argv array has the right string at this point.
281    argv_[i] = buffer;
282  }
283  LocalFree(wide_argv);
284}
285
286WindowsCommandLineArguments::~WindowsCommandLineArguments() {
287  // need to free each string in the array, and then the array.
288  for(int i = 0; i < argc_; i++) {
289    delete[] argv_[i];
290  }
291
292  delete[] argv_;
293}
294#endif  // WEBRTC_WIN
295
296}  // namespace rtc
297