2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
12 * The Original Code is the Sablotron XSLT Processor.
14 * The Initial Developer of the Original Code is Ginger Alliance Ltd.
15 * Portions created by Ginger Alliance are Copyright (C) 2000-2002
16 * Ginger Alliance Ltd. All Rights Reserved.
18 * Contributor(s): Christian Lefebvre, Stefan Behnel
20 * Alternatively, the contents of this file may be used under the
21 * terms of the GNU General Public License Version 2 or later (the
22 * "GPL"), in which case the provisions of the GPL are applicable
23 * instead of those above. If you wish to allow use of your
24 * version of this file only under the terms of the GPL and not to
25 * allow others to use your version of this file under the MPL,
26 * indicate your decision by deleting the provisions above and
27 * replace them with the notice and other provisions required by
28 * the GPL. If you do not delete the provisions above, a recipient
29 * may use your version of this file under either the MPL or the
33 /*****************************************************************
37 *****************************************************************/
39 /*****************************************************************
41 *****************************************************************/
47 #define MAX_ARG_OR_PAR MAX_EQ
49 /*****************************************************************
51 *****************************************************************/
58 // include for time measurement
59 #include <time.h> // needed by <sys/timeb.h> for definition of time_t
61 #if defined(HAVE_CONFIG_H)
65 #ifdef SABLOT_DEBUGGER
69 #if defined(HAVE_SYS_TIMEB_H) || defined(WIN32)
70 #include <sys/timeb.h>
73 #ifdef HAVE_SYS_TYPES_H
74 #include <sys/types.h> // needed too in Windows
77 #ifdef HAVE_SYS_TIME_H
81 /*****************************************************************
82 vars to hold the results of switch parsing
83 *****************************************************************/
91 char * stringBase = NULL,
94 /*****************************************************************
96 *****************************************************************/
101 ID_SPS, ID_SPF, ID_SPS_ON_FILES, ID_BASE, ID_TIMES,
102 ID_LOG, ID_HELP, ID_DEBUG, ID_DEBUGGER, ID_DBG_HELP, ID_FLAGS,
103 ID_VERSION, ID_MEASURE, ID_BATCH_XML, ID_BATCH_XSL,
110 char *longName, shortName;
111 char type; // Integer, String, Ordinal
115 struct SwitchData switches[] =
117 {ID_SPS, "use-SPS", 'S', 'O', &numberMode},
118 {ID_SPF, "use-SPF", 'F', 'O', &numberMode},
119 {ID_SPS_ON_FILES, "use-SPS-on-files", 0, 'O', &numberMode},
120 {ID_BASE, "base", 'b', 'S', &stringBase},
121 {ID_TIMES, "times", 't', 'N', &numberTimes},
122 {ID_LOG, "log-file", 'L', 'S', &stringLog},
123 {ID_HELP, "help", '?', 'O', &numberMode},
124 {ID_DEBUG, "debug", 0, 'O', &numberMode},
125 {ID_DEBUGGER, "debugger", 0, 'O', &numberMode},
126 {ID_DBG_HELP, "debug-options", 0, 'O', &numberMode},
127 {ID_FLAGS, "flags", 'f', 'N', &commandFlags},
128 {ID_VERSION, "version", 'v', 'O', &numberMode},
129 {ID_MEASURE, "measure", 'm', 'O', &numberMeasure},
130 {ID_BATCH_XML, "batch-xml", 'x', 'O', &batchMode},
131 {ID_BATCH_XSL, "batch-xsl", 's', 'O', &batchMode},
132 {ID_CHAIN_XSL, "chain-xsl", 'c', 'O', &batchMode},
133 {ID_NONE, NULL, 0, 0, NULL }
136 /*****************************************************************
138 *****************************************************************/
141 "\nUsage for single XSLT:\n"\
142 "\tsabcmd [options] <stylesheet> [<input> [<output>]] [assignments]\n"\
143 "\nUsage in batch processing:\n"\
144 " chain multiple stylesheets, starting with a single input file:\n"\
145 " \tsabcmd [options] --chain-xsl <input> <output> [<stylesheet>]+ [assignments]\n"\
146 " run multiple stylesheets on a single input file:\n"\
147 " \tsabcmd [options] --batch-xml <input> [<stylesheet> <output>]+ [assignments]\n"\
148 " run a single stylesheet on multiple input files:\n"\
149 " \tsabcmd [options] --batch-xsl <stylesheet> [<input> <output>]+ [assignments]\n"\
151 "\t--chain-xsl, -c single input file, multiple chained stylesheets\n"\
152 "\t--batch-xml, -x single input file, multiple stylesheets\n"\
153 "\t--batch-xsl, -s multiple input files, single stylesheet\n"\
154 "\t--base=NAME, -b set the hard base URI to NAME\n"\
155 "\t--debug-options display information on debugging options\n"\
156 "\t--help, -? display this help message\n"\
157 "\t--log-file=NAME, -L set the log file, turn logging on\n"\
158 "\t--measure, -m measure the processing time\n"\
159 "\t--version, -v display version information\n"\
160 "Defaults:\n\t<input> = stdin, <output> = stdout\n"\
161 "\tlogging off, no hard base URI\n"\
163 "\t- assignments define named buffers (name=value)\n"\
164 "\t and top-level params ($name=value).\n"\
165 "\t- to specify value in an option, use -b=NAME or -b NAME\n"\
166 "\t (correspondingly for the equivalent long options)";
168 char askhelp[] = "Type sabcmd --help to display a help message.\n";
169 char msgConflict[] = "conflict with preceding switches: ";
170 char version_txt[] = "\nsabcmd "SAB_VERSION" ("SAB_DATE")\n"\
171 "copyright (C) 2000 - 2002 Ginger Alliance (www.gingerall.com)\n";
172 char dbg_usage[] = "\nDebugging options:\n"\
173 "\t--debug\t\t\tdisplay results of the command line parse\n"\
174 "\t--debugger\t\trun the xslt debugger\n"\
175 "\t--times=COUNT, -t\trun sabcmd the specified number of times\n"\
176 "\t--flags=FLAGS, -f\tset processor flags for the processing\n"\
177 "\t--use-SPF, -F\t\tuse SablotProcessFiles().\n"\
178 "\t--use-SPS, -S\t\tuse SablotProcessStrings(). Give 2 args\n"\
179 "\t\t\t\t(stylesheet, input). Precede each by @.\n" \
180 "\t--use-SPS-on-files\tuse SablotProcessStrings() on the contents\n"\
181 "\t\t\t\tof the given files.";
182 /* removed the GPL notice:
183 char startup_licence[]="sabcmd and Sablotron come with ABSOLUTELY NO WARRANTY. "\
184 "They are free software,\n"\
185 "and you are welcome to redistribute them under certain conditions.\n"\
186 "For details on warranty and the redistribution terms, see the README file.\n";
188 char startup_licence[] = "The Sablotron XSLT Processor comes with NO WARRANTY.\n"\
189 "It is subject to the Mozilla Public License Version 1.1.\n"\
190 "Alternatively, you may use Sablotron under the GNU General Public License.\n";
192 /*****************************************************************
194 *****************************************************************/
196 void saberr(char *msg1, char *msg2)
198 fprintf(stderr, "Error: %s", msg1);
199 if (msg2) fputs(msg2, stderr);
200 fprintf(stderr, "\n%s", askhelp);
204 void saberrn(char *msg, int num)
207 sprintf(buf,"%d",num);
211 int chrpos(char *text, char c)
213 char *p = strchr(text, c);
214 return p ? (int)(p - text) : -1;
217 void freefirst(char **array)
219 for (; *array; array += 2)
223 void copyAssignment(char **array, int index, char *text, int split)
226 if (index >= MAX_ARG_OR_PAR)
227 saberr("too many assignments", NULL);
228 array[2 * index + 1] = text + split + 1;
229 p = array[2 * index] = (char*) malloc(split + 1);
230 memcpy(p, text, split);
234 /*****************************************************************
236 *****************************************************************/
238 // globals to hold the names and assignments
240 char* arrayBare[MAX_BARE];
241 char* arrayEq[MAX_EQ];
242 int indexBare = 0, indexEq = 0;
244 /*****************************************************************
246 *****************************************************************/
248 int max_strncmp(char *s1, char *s2, int len1)
250 int len2 = strlen(s2);
251 return strncmp(s1, s2, len1 >= len2 ? len1 : len2);
254 int findSwitch(char *text)
256 int islong = (text[1] == '-');
257 char *stripped = text + (islong ? 2 : 1);
258 int eqpos = chrpos(stripped, '=');
260 while (switches[index].id != ID_NONE)
262 if ((eqpos >= 0 && islong && !max_strncmp(stripped, switches[index].longName, eqpos)) ||
263 (eqpos < 0 && islong && !strcmp(stripped, switches[index].longName)) ||
264 (stripped[0] == switches[index].shortName && ! islong && (eqpos == 1 || !stripped[1])))
271 /*****************************************************************
273 ord is the 1-based index of the switch!
274 *****************************************************************/
276 #define cassign(WHERE, WHAT, TEXT) \
277 { if (WHERE) saberr(msgConflict, (TEXT)); else WHERE = (WHAT); }
280 int applySwitch(int ord, char *text, char *following)
283 char type = switches[--ord].type; // make ord 0-based
286 int eqpos = chrpos(text, '=');
288 value = text + eqpos + 1;
291 if ((type == 'S' || type == 'N') && following && *following != '-')
297 switch (switches[ord].type)
301 if (!value) saberr("switch needs a string value: ", text);
302 cassign(*(char**)(switches[ord].destination), value, text);
309 number = strtol(value, &stopper, 0);
310 if (!value || *stopper)
311 saberr("switch needs a number value: ", text);
312 cassign(*(int*)(switches[ord].destination), (int) number, text);
317 saberr("value not recognized in switch: ", text);
318 cassign(*(int*)(switches[ord].destination), switches[ord].id, text);
321 saberr("error processing the switches", NULL);
326 /*****************************************************************
328 *****************************************************************/
330 void readSwitches(int argc, char** argv)
333 for (i = 1; i < argc; i++)
335 if (argv[i][0] == '-')
337 if (!(ord = findSwitch(argv[i])))
338 saberr("invalid switch ", argv[i]);
340 i += applySwitch(ord, argv[i],
341 (i < argc-1) ? argv[i+1] : NULL);
346 if (((chrpos(argv[i], '=')) != -1) && (argv[i][0] != BARE_SIGN))
348 if (indexEq >= MAX_EQ)
349 saberrn("too many assignments, allowing ", MAX_EQ);
351 arrayEq[indexEq++] = argv[i];
355 if (indexEq >= MAX_BARE)
356 saberrn("too many names, allowing ", MAX_BARE);
358 arrayBare[indexBare++] = argv[i] + (argv[i][0] == BARE_SIGN);
364 /*****************************************************************
366 *****************************************************************/
368 void xformToPairs(char** array, int count,
369 char** _params, int* _paramsC,
370 char** _args, int* _argsC)
373 *_paramsC = *_argsC = 0;
374 for (i = 0 ; i < count; i++)
376 pos = chrpos(array[i], '=');
377 if (array[i][0] == '$')
378 copyAssignment(_params, (*_paramsC)++, array[i] + 1, pos-1);
380 copyAssignment(_args, (*_argsC)++, array[i], pos);
382 _args[2 * *_argsC] = NULL;
383 _params[2 * *_paramsC] = NULL;
386 /*****************************************************************
388 for debugging sabcmd itself
389 *****************************************************************/
391 #define safe(PTR) (PTR? PTR : "[null]")
393 void dumparray(char* caption, char** array, int count, int asPairs)
396 printf("%s (%d)\n",caption, count);
402 printf(" = %s\n",safe(p[1]));
413 void debug(int cParams, char **params, int cArgs, char **args)
415 puts("\nCommand line parse results:");
416 dumparray("Names", arrayBare, indexBare, 0);
417 dumparray("Parameters", params, cParams, 1);
418 dumparray("Named buffers", args, cArgs, 1);
419 printf("Settings \n\tTimes\t\t%d \n\tMode\t\t%d\n",
420 numberTimes, numberMode);
421 printf("\tBase\t\t%s \n\tLog\t\t%s\n", safe(stringBase), safe(stringLog));
424 double getExactTime()
428 struct _timeb theTime;
430 ret = theTime.time + theTime.millitm/1000.0;
431 #elif defined (HAVE_FTIME)
432 struct timeb theTime;
434 ret = theTime.time + theTime.millitm/1000.0;
435 #elif defined (HAVE_GETTIMEOFDAY)
437 gettimeofday(&theTime, NULL);
438 ret = theTime.tv_sec + theTime.tv_usec/1000000.0;
440 #error "Can't find function ftime() or similar"
446 /*****************************************************************
448 *****************************************************************/
450 void full_version_txt()
453 puts(startup_licence);
457 char* readToBuffer(char *filename)
462 FILE *f = fopen(filename, "rt");
464 saberr("cannot open file ", filename);
466 buf = (char*) malloc(length + 1);
467 count = fread(buf, 1, length + 1, f);
470 saberr("file too large (64k limit)", NULL);
476 int runSingleXSLT(const char **arrayParams, const char **arrayArgs,
480 char *resultURI = NULL;
484 if (ecode = SablotCreateSituation(&situa))
488 SablotSetOptions(situa, commandFlags);
490 if (ecode = SablotCreateProcessorForSituation(situa, &theProcessor))
494 SablotSetLog(theProcessor, stringLog, 0);
497 SablotSetBase(theProcessor, stringBase);
500 resultURI = (char*)"file://stdout";
502 resultURI = arrayBare[2];
504 ecode = SablotRunProcessor(theProcessor,
506 indexBare <= 1 ? (char*)"file://stdin" : arrayBare[1],
508 arrayParams, arrayArgs);
511 SablotGetResultArg(theProcessor, resultURI, resultArg);
514 SablotDestroyProcessor(theProcessor);
517 SablotDestroySituation(situa);
523 int runBatchXSLT(const char **arrayParams, const char **arrayArgs,
526 void *theProcessor = NULL, *sabSit = NULL, *preparsed = NULL;
527 char *resultURI = (char*)"file://stdout";
530 int argcount = 2, firstarg = 1;
532 if ((batchMode != ID_BATCH_XML) &&
533 (batchMode != ID_BATCH_XSL) &&
534 (batchMode != ID_CHAIN_XSL))
535 return runSingleXSLT(arrayParams, arrayArgs, resultArg);
537 ecode = SablotCreateSituation(&sabSit);
542 SablotSetOptions(sabSit, commandFlags);
544 ecode = SablotCreateProcessorForSituation(sabSit, &theProcessor);
549 SablotSetLog(theProcessor, stringLog, 0);
552 SablotSetBase(theProcessor, stringBase);
557 ecode = SablotParseStylesheet(sabSit, arrayBare[0], &preparsed);
560 ecode = SablotParse(sabSit, arrayBare[0], &preparsed);
563 input = arrayBare[0];
570 for (int t = firstarg; (t <= indexBare-argcount) && (ecode == 0); t += argcount)
574 const char **params = arrayParams;
575 while ((params[0] != NULL) && (ecode == 0))
577 ecode = SablotAddParam(sabSit, theProcessor, params[0], params[1]);
584 const char **args = arrayArgs;
585 while ((args[0] != NULL) && (ecode == 0))
587 ecode = SablotAddArgBuffer(sabSit, theProcessor, args[0], args[1]);
598 const char *document = arrayBare[t];
599 const char *output = arrayBare[t+1];
600 SablotAddArgTree(sabSit, theProcessor, "stylesheet", preparsed);
601 ecode = SablotRunProcessorGen(sabSit, theProcessor, "arg:stylesheet", document, output);
605 const char *stylesheet = arrayBare[t];
606 const char *output = arrayBare[t+1];
607 SablotAddArgTree(sabSit, theProcessor, "source", preparsed);
608 ecode = SablotRunProcessorGen(sabSit, theProcessor, stylesheet, "arg:source", output);
612 const char *stylesheet = arrayBare[t];
614 if (t < indexBare-argcount)
615 output = (char*)"arg:output";
617 output = arrayBare[1];
619 ecode = SablotRunProcessorGen(sabSit, theProcessor, stylesheet, input, output);
623 if (input == arrayBare[0])
624 input = "arg:buffer";
626 SablotFree(*resultArg);
628 SablotGetResultArg(theProcessor, output, resultArg);
629 SablotAddArgBuffer(sabSit, theProcessor, "buffer", *resultArg);
636 if ((ecode == 0) && (batchMode != ID_CHAIN_XSL))
637 SablotGetResultArg(theProcessor, resultURI, resultArg);
640 SablotDestroyDocument(sabSit, preparsed);
643 SablotDestroyProcessor(theProcessor);
646 SablotDestroySituation(sabSit);
653 #ifdef SABLOT_DEBUGGER
658 printf("Debugegr is not supported in this build\n");
662 int main(int argc, char *argv[])
664 int indexParams, indexArgs, ecode, i;
665 char *arrayParams[2 * MAX_ARG_OR_PAR + 2],
666 *arrayArgs[2 * MAX_ARG_OR_PAR + 2];
667 char *resultArg = NULL;
670 // the following set the globals arrayBare and arrayEq
671 // we get the number of words/assignments in indexBare/indexEq
673 readSwitches(argc, argv);
679 puts(usage); return 0;
683 puts(dbg_usage); return 0;
687 full_version_txt(); return 0;
691 runDebugger(); return 0;
695 if (!indexBare && numberMode != ID_DEBUG)
698 saberr("stylesheet not given", NULL);
701 xformToPairs(arrayEq, indexEq,
702 arrayParams, &indexParams,
703 arrayArgs, &indexArgs);
705 if (numberMode == ID_DEBUG)
707 debug(indexParams, arrayParams, indexArgs, arrayArgs);
714 timeZero = getExactTime();
716 for (i = 0; i < numberTimes; i++)
722 if ((batchMode == ID_BATCH_XML) || (batchMode == ID_BATCH_XSL) || (batchMode == ID_CHAIN_XSL))
723 ecode = runBatchXSLT((const char**)arrayParams,
724 (const char**)arrayArgs, &resultArg);
726 ecode = runSingleXSLT((const char**)arrayParams,
727 (const char**)arrayArgs, &resultArg);
732 saberr("SablotProcessStrings() needs exactly two arguments", NULL);
733 ecode = SablotProcessStrings(arrayBare[0], arrayBare[1], &resultArg);
735 case ID_SPS_ON_FILES:
737 char *sheetBuf, *inputBuf;
739 saberr("need exactly two arguments", NULL);
740 sheetBuf = readToBuffer(arrayBare[0]);
741 inputBuf = readToBuffer(arrayBare[1]);
742 ecode = SablotProcessStrings(sheetBuf, inputBuf, &resultArg);
747 ecode = SablotProcessFiles(arrayBare[0],
748 indexBare <= 1 ? (char*)"file://stdin" : arrayBare[1],
749 indexBare <= 2 ? (char*)"file://stdout" : arrayBare[2]);
758 fprintf(stderr, "output buffer sent to stdout\n");
760 SablotFree(resultArg);
766 fprintf(stderr, "Total time: %4.3f seconds\n", getExactTime() - timeZero);
768 freefirst(arrayArgs);
769 freefirst(arrayParams);