1 mike 1.1 /*
2 **==============================================================================
3 **
4 ** Open Management Infrastructure (OMI)
5 **
6 ** Copyright (c) Microsoft Corporation
7 **
8 ** Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 ** use this file except in compliance with the License. You may obtain a copy
10 ** of the License at
11 **
12 ** http://www.apache.org/licenses/LICENSE-2.0
13 **
14 ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 ** MERCHANTABLITY OR NON-INFRINGEMENT.
18 **
19 ** See the Apache 2 License for the specific language governing permissions
20 ** and limitations under the License.
21 **
22 mike 1.1 **==============================================================================
23 */
24
25 #ifndef MI_CHAR_TYPE
26 # define MI_CHAR_TYPE 1
27 #endif
28
29 #include <common.h>
30 #include <gen/gen.h>
31 #include <base/paths.h>
|
32 krisbash 1.4 #include <pal/format.h>
|
33 mike 1.1 #include <base/env.h>
34 #include <base/conf.h>
|
35 krisbash 1.4 #include <base/omigetopt.h>
36 #include <windows/config.h>
|
37 mike 1.1
38 static const char HELP[] = "\
39 Usage: %s [OPTIONS] PATH CLASSNAME[=ALIAS] ...\n\
40 \n\
41 OVERVIEW:\n\
42 This program generates provider source code from MOF class definitions.\n\
43 PATH is a file that contains the MOF definitions (or includes them).\n\
44 It must include any dependent MOF defintions as well (such as the CIM\n\
45 schema). The PATH is followed by a list of CLASSNAME arguments, which are\n\
46 the names of the MOF classes to be generated. Each CLASSNAME argument may\n\
47 be followed by an optional ALIAS argument (separated by an equal sign).\n\
48 The ALIAS provides an alternative name for the class to be used within\n\
49 the C source code. For example, 'CIM_ComputerSystem=CompSys' says to\n\
50 use 'CompSys' in C sources instead of 'CIM_ComputerSystem'.\n\
51 \n\
52 OPTIONS:\n\
53 -I PATH Search this directory for included MOF files.\n\
54 -D Generate 'Description' qualifiers.\n\
55 -V Generate 'Values' and 'ValueMap' qualifiers.\n\
56 -M Generate 'MappingStrings' qualifiers.\n\
|
57 krisbash 1.4 -O Generate 'ModelCorrespondence' qualifiers.\n\
|
58 mike 1.1 -S Generate standard CIM qualifier declarations.\n\
59 -B Generate boolean qualifiers.\n\
60 -Q Suppress qualifier declarations generation.\n\
61 -q Quiet mode - do not print anything on stdout.\n\
62 -h, --help Print this help message.\n\
63 -v, --version Print the program version.\n\
64 -s SCHEMA Specify C name of the schema [schemaDecl].\n\
65 -n Suppress provider generation (only schema.c).\n\
66 --cpp Generate cpp skeleton for the provider.\n\
67 -d PATH Place output files in this directory.\n\
68 -l Generate strings.rc file.\n\
69 -f Set filter support flag into MI_Main().\n\
70 -A Generate single association function.\n\
71 -e CLASS Generate extra class with this name.\n\
72 -y NAME Use NAME as entry point (default MI_Main).\n\
|
73 krisbash 1.4 --no-warnings Print no warnings.\n\
|
74 mike 1.1 -C, --schemafile PATH Alternative path of main CIM schema file.\n\
75 -m PROVIDERNAME Generate provider makefile.\n\
76 --nogi CLASSNAME Set MI_ProviderFT.GetInstance to NULL for\n\
77 the given class. This forces the provider\n\
78 manager to use the EnumerateInstances method\n\
79 to satisfy all GetInstance requests on this\n\
80 class.\n\
81 \n\
82 EXAMPLES:\n\
83 The following example generates a 'MSFT_ComputerSystem' class, which is\n\
84 defined in schema.mof.\n\
85 \n\
86 $ omigen schema.mof MSFT_ComputerSystem=CompSys\n\
87 Created CompSys.h\n\
88 Created CompSys.c\n\
89 Created schema.c\n\
90 Created module.c\n\
91 \n\
92 CompSys.h defines the 'CompSys' structure. schema.c defines run-time type\n\
93 information for the CompSys structure. CompSys.c contains the provider\n\
94 stubs and module.c contains the MI_Main() provider entry point.\n\
95 mike 1.1 \n\
96 FILES:\n\
97 .omigenrc\n\
98 This file contains an option per line as it might appear on the \n\
99 command line (-OPTION [ARGUMENT]). These options are appended to the\n\
100 command line options, which take precedence. The program first\n\
101 attempts to open this file in the current directory followed by\n\
102 the home directory (given by the 'HOME' environment variable).\n\
103 The following is an example of the contents of this file.\n\
104 \n\
105 -I /opt/microsoft/sca/mof/cim222\n\
106 -I /opt/microsoft/sca/mof/extras\n\
107 \n\
108 ";
109
110 using namespace std;
111
112 static const char* arg0;
113
114 //==============================================================================
115 //
116 mike 1.1 // err()
117 //
118 // Writes a formatted error message to standard error (preceded by argv[0])
119 // and then exists.
120 //
121 //==============================================================================
122
123 PRINTF_FORMAT(1, 2)
124 static void FUNCTION_NEVER_RETURNS err(const char* fmt, ...)
125 {
126 fprintf(stderr, "%s: ", arg0);
127
128 va_list ap;
129 va_start(ap, fmt);
130 vfprintf(stderr, fmt, ap);
131 va_end(ap);
132
133 fputc('\n', stderr);
134 exit(1);
135 }
136
137 mike 1.1 static void _GetCommandLineDestDirOption(
138 int* argc_,
139 const char* argv[])
140 {
141 int argc = *argc_;
142 int i;
143 const char* destdir = NULL;
144
145 for (i = 1; i < argc; )
146 {
147 if (strcmp(argv[i], "--destdir") == 0)
148 {
149 if (i + 1 == argc)
150 err("missing argument for --destdir option");
151
152 destdir = argv[i+1];
153 memmove((char*)&argv[i], (char*)&argv[i+2],
154 sizeof(char*) * (argc-i-1));
155 argc -= 2;
156 }
157 else if (strncmp(argv[i], "--destdir=", 10) == 0)
158 mike 1.1 {
159 destdir = argv[i] + 10;
160 memmove((char*)&argv[i], (char*)&argv[i+1],
161 sizeof(char*) * (argc-i));
162
163 argc -= 1;
164 }
165 else
166 i++;
167 }
168
169 if (destdir)
170 {
171 if (SetPath(ID_DESTDIR, destdir) != 0)
172 err("failed to set destdir");
173 }
174
175 *argc_ = argc;
176 }
177
178 //==============================================================================
179 mike 1.1 //
180 // _GetCommandLineOptions()
181 //
182 // This function processes command line options. Each option begins with
183 // the '-' character followed by a single alphabetic character. An option
184 // may be followed by an optional argument. GeneratorOptions and their
185 // optional arguments are removed from the argc-argv array upon return.
186 // Extracted options are stored in the options parameter (see the
187 // GeneratorOptions structure defined above).
188 //
189 //==============================================================================
190
191 static string schemafile;
192
193 static void _GetCommandLineOptions(
194 int& argc,
195 const char**& argv,
196 GeneratorOptions& options)
197 {
198 GetOptState state = GETOPTSTATE_INITIALIZER;
199
200 mike 1.1 const char* opts[] =
201 {
202 "-I:",
203 "-h",
204 "--help",
205 "-v",
206 "--version",
207 "-D",
208 "-V",
209 "-M",
|
210 krisbash 1.4 "-O",
|
211 mike 1.1 "-S",
212 "-B",
213 "-Q",
214 "-f",
215 "-q",
|
216 krisbash 1.4 "--no-warnings",
|
217 mike 1.1 "-a",
218 "-n",
219 "--cpp",
220 "-s:",
221 "-d:",
222 "-e:",
223 "-y:",
224 "-l",
225 "-reg:",
226 "-A",
227 "-C:",
228 "--schemafile:",
229 "-m:",
230 "--nogi:",
231 NULL,
232 };
233
234 /* For each argument */
235 for (;;)
236 {
237 int r = GetOpt(&argc, (const char**)argv, opts, &state);
238 mike 1.1
239 if (r == 1)
240 break;
241
242 if (r == -1)
243 err("%s", state.err);
244
245 /* Check for -I option */
246 if (strcmp(state.opt, "-I") == 0)
247 {
248 options.paths.push_back(state.arg);
249 }
250 else if (strcmp(state.opt, "-h") == 0 ||
251 strcmp(state.opt, "--help") == 0)
252 {
253 printf(HELP, arg0);
254 exit(1);
255 }
256 else if (strcmp(state.opt, "-v") == 0 ||
257 strcmp(state.opt, "--version") == 0)
258 {
|
259 krisbash 1.4 #if defined(CONFIG_OS_WINDOWS)
260 printf(ZT("%s: %S\n"), arg0,
261 CONFIG_PRODUCT L"-" CONFIG_VERSION L" - " CONFIG_DATE);
262 #else
|
263 mike 1.1 printf("%s: %s\n", arg0,
264 CONFIG_PRODUCT "-" CONFIG_VERSION " - " CONFIG_DATE);
|
265 krisbash 1.4 #endif
|
266 mike 1.1 exit(0);
267 }
268 else if (strcmp(state.opt, "-D") == 0)
269 {
270 options.descriptions = true;
271 }
272 else if (strcmp(state.opt, "-V") == 0)
273 {
274 options.values = true;
275 }
276 else if (strcmp(state.opt, "-M") == 0)
277 {
278 options.mappingStrings = true;
279 }
|
280 krisbash 1.4 else if (strcmp(state.opt, "-O") == 0)
281 {
282 options.modelCorrespondence = true;
283 }
|
284 mike 1.1 else if (strcmp(state.opt, "-S") == 0)
285 {
286 options.standardQualifiers = true;
287 }
288 else if (strcmp(state.opt, "-B") == 0)
289 {
290 options.booleanQualifiers = true;
291 }
292 else if (strcmp(state.opt, "-Q") == 0)
293 {
294 options.ignoreAllQualifiers = true;
295 }
296 else if (strcmp(state.opt, "-f") == 0)
297 {
298 options.filterSupport = true;
299 }
300 else if (strcmp(state.opt, "-q") == 0)
301 {
302 options.quiet = true;
303 }
|
304 krisbash 1.4 else if (strcmp(state.opt, "--no-warnings") == 0)
|
305 mike 1.1 {
|
306 krisbash 1.4 options.no_warnings = true;
|
307 mike 1.1 }
308 else if (strcmp(state.opt, "-a") == 0)
309 {
310 options.all = true;
311 }
312 else if (strcmp(state.opt, "-n") == 0)
313 {
314 options.noProviders = true;
315 }
316 else if (strcmp(state.opt, "--cpp") == 0)
317 {
318 options.cpp = true;
319 }
320 else if (strcmp(state.opt, "-s") == 0)
321 {
322 options.schema = state.arg;
323 }
324 else if (strcmp(state.opt, "-d") == 0)
325 {
326 options.dir = state.arg;
327 }
328 mike 1.1 else if (strcmp(state.opt, "-e") == 0)
329 {
330 options.extraClasses.push_back(state.arg);
331 }
332 else if (strcmp(state.opt, "-y") == 0)
333 {
334 options.entryPoint = state.arg;
335 }
336 else if (strcmp(state.opt, "-l") == 0)
337 {
338 options.localize = true;
339 }
340 else if (strcmp(state.opt, "-reg") == 0)
341 {
342 options.providerRegistryPath = state.arg;
343 }
344 else if (strcmp(state.opt, "-A") == 0)
345 {
346 options.association = true;
347 }
348 else if (strcmp(state.opt, "-C") == 0 ||
349 mike 1.1 strcmp(state.opt, "--schemafile") == 0)
350 {
351 schemafile = state.arg;
352
353 if (schemafile.size() && access(schemafile.c_str(), R_OK) != 0)
354 {
355 err("file given by %s option does not exist: %s",
356 state.opt, schemafile.c_str());
357 }
358 }
359 else if (strcmp(state.opt, "-m") == 0)
360 {
361 options.providerName = state.arg;
362 }
363 else if (strcmp(state.opt, "--nogi") == 0)
364 {
365 options.noGetInstance.push_back(state.arg);
366 }
|
367 krisbash 1.4 else if (strncmp(state.opt, "--", 2) == 0 && IsNickname(state.opt+2))
368 {
369 if (SetPathFromNickname(state.opt+2, state.arg) != 0)
370 err(ZT("SetPathFromNickname() failed"));
371 }
|
372 mike 1.1 }
373 }
374
375 //==============================================================================
376 //
377 // FindConfigFile()
378 //
379 // Find the configuration file by checking in following order:
380 //
381 // ./.omigenrc
382 // home/.omigenrc
383 // sysconfdir/.omigenrc
384 //
385 //==============================================================================
386
|
387 krisbash 1.4 static int FindConfigFile(_Pre_writable_size_(PAL_MAX_PATH_SIZE) char path[PAL_MAX_PATH_SIZE])
|
388 mike 1.1 {
389 /* Look in current directory */
390 {
|
391 krisbash 1.4 Strlcpy(path, "./.omigenrc", PAL_MAX_PATH_SIZE);
|
392 mike 1.1
393 if (access(path, R_OK) == 0)
394 return 0;
395 }
396
397 /* Look in HOME directory */
398 char* home = Dupenv("HOME");
399 if (home)
400 {
|
401 krisbash 1.4 Strlcpy(path, home, PAL_MAX_PATH_SIZE);
402 Strlcat(path, "/.omigenrc", PAL_MAX_PATH_SIZE);
|
403 mike 1.1
404 if (access(path, R_OK) == 0)
405 {
|
406 krisbash 1.4 PAL_Free(home);
|
407 mike 1.1 return 0;
408 }
|
409 krisbash 1.4 PAL_Free(home);
|
410 mike 1.1 }
411
412 /* Look in system config directory */
413 {
|
414 krisbash 1.4 Strlcpy(path, OMI_GetPath(ID_DESTDIR), PAL_MAX_PATH_SIZE);
415 Strlcat(path, "/", PAL_MAX_PATH_SIZE);
416 Strlcat(path, OMI_GetPath(ID_SYSCONFDIR), PAL_MAX_PATH_SIZE);
417 Strlcat(path, "/omigen.conf", PAL_MAX_PATH_SIZE);
|
418 mike 1.1
419 if (access(path, R_OK) == 0)
420 return 0;
421 }
422
423 /* Not found */
424 return -1;
425 }
426
427 //==============================================================================
428 //
429 // _GetConfigFileOptions()
430 //
431 // Read options from the omigen configuration file.
432 //
433 //==============================================================================
434
435 static void _GetConfigFileOptions(GeneratorOptions& opts)
436 {
|
437 krisbash 1.4 char path[PAL_MAX_PATH_SIZE];
|
438 mike 1.1 Conf* conf;
439
440 MI_UNUSED(opts);
441
442 /* Form the configuration file path */
443 if (FindConfigFile(path) != 0)
444 err("failed to find configuration file");
445
446 /* Open the configuration file */
447 conf = Conf_Open(path);
448 if (!conf)
449 err("failed to open configuration file: %s", path);
450
451 /* For each key=value pair in configuration file */
452 for (;;)
453 {
454 const char* key;
455 const char* value;
456 int r = Conf_Read(conf, &key, &value);
457
458 if (r == -1)
459 mike 1.1 err("%s: %s\n", path, Conf_Error(conf));
460
461 if (r == 1)
462 break;
463
464 if (strcmp(key, "schemafile") == 0)
465 {
466 schemafile = value;
467
468 if (schemafile.size() && access(schemafile.c_str(), R_OK) != 0)
469 {
470 err("%s(%u): file given by '%s' key does not exist: %s",
471 path, Conf_Line(conf), key, schemafile.c_str());
472 }
473 }
474 else
475 err("%s(%u): unknown key: %s", path, Conf_Line(conf), key);
476 }
477
478 /* Close configuration file */
479 Conf_Close(conf);
480 mike 1.1 }
481
482 //==============================================================================
483 //
484 // _EncodeStr()
485 //
486 // Encodes special characters in a string; replaces all 'lookfor wtih replacewith
487 //
488 //==============================================================================
489
490 static void _EncodeStr(
491 std::string& str,
492 const std::string lookfor,
493 const std::string& replacewith)
494 {
495 std::string::size_type start = 0;
496 std::string::size_type pos;
497
498 for(;;)
499 {
500 pos = str.find(lookfor, start);
501 mike 1.1
502 if (pos == std::string::npos)
503 break;
504
505 str.replace(pos, lookfor.size(), replacewith);
506 start = pos + replacewith.size();
507 }
508 }
509
|
510 krisbash 1.4 int MI_MAIN_CALL main(int argc, const char** argv)
|
511 mike 1.1 {
512 arg0 = argv[0];
513 GeneratorOptions options;
514
515
516 // command line
517 {
518 for (int i = 1; i < argc; i++)
519 {
520 std::string arg = argv[i];
521
522 // escape special characters
523 _EncodeStr(arg, "\\", "\\\\");
524 _EncodeStr(arg, "\"", "\\\"");
525 _EncodeStr(arg, " ", "\\ ");
526
527 if (!options.cmdLine.empty())
528 options.cmdLine += " ";
529
530 options.cmdLine += arg;
531 }
532 mike 1.1 }
533
534 // Get --destdir command-line option.
535 _GetCommandLineDestDirOption(&argc, argv);
536
537 // Set path of default CIM schema file.
|
538 krisbash 1.4 schemafile = OMI_GetPath(ID_SCHEMAFILE);
|
539 mike 1.1
540 // Get configuraiton file options.
541 _GetConfigFileOptions(options);
542
543 // Get command-line options.
544 _GetCommandLineOptions(argc, argv, options);
545
546 // Check arguments. There must one or mores class name arguments unless
547 // the -a options was given.
548 if (argc < 2 || (!options.all && argc < 3))
549 {
550 fprintf(stderr, "Usage: %s PATH CLASSNAME[=ALIAS] ...\n", arg0);
551 fprintf(stderr, "Try '%s -h' for help\n\n", arg0);
552 exit(1);
553 }
554
555 // ProgramName argument:
556 string programName = argv[0];
557
558 // MofFiles argument (only one file):
559 vector<string> mofFiles;
560 mike 1.1 {
561 if (schemafile.size())
562 mofFiles.push_back(schemafile);
563
564 mofFiles.push_back(argv[1]);
565 }
566
567 // ClassNames argument:
568 vector<string> classNames;
569 {
570 for (int i = 2; i < argc; i++)
571 classNames.push_back(argv[i]);
572 }
573
574 // Invoke generator:
575 return GeneratorMain(programName, mofFiles, classNames, options);
576 }
|