webApiGen generates a
cgiMain function to be called by the cgic library. The idea is to provide a collection of functions for an
AJA JSON) environment, although it can be used to emit straight html. The program takes a specification of an api and generates
cgiMain() along with some helper functions.
You are not intended to put any webApiGen generated programs on the public internet. There is code to stop the program doing very much if invoked from anywhere but localhost; remove it at your peril. In addition, it's probably a lousy idea to run webApiGen generated programs whilst connected to the snake pit the internet has become.
This program doesn't do it for you. You need a reasonable understanding of:
you need a rudimentary understanding of the stl. These people know a bit.
Unzip webApiGen.zip, put webApiGen.exe and the gnu dlls somewhere on your path.
You will need the cgic library, otherwise the generated code will never get called. You will need to have WagAboutPage.h and WagErrorPage.h on your include path when compiling generated code: these files are supplied in the runTimeSource directory. A batch file, example\compile.bat, is supplied for building the example: as it stands this uses gcc and g++.
webApiGen -h? -v? filename?
version. Writes version and exits.
help. Writes usage text and exits.
filename. Input filename. If omitted, stdin is read.
webApiGen is used to specify and generate code for a cgi program. The only methods we support are those used by cgic, GET and POST.
To specify inputs we need a path name and query names; code is generated to deal with GETs or POSTs to path, with (name, value) pairs from the query part of the URI sent to the cgi script.
For instance if example.wag looks like:
/hello get (str)
/* visit, for example, localhost/wagExample.exe/hello?str=world */
if ($1.size() == 1)
"<body>hello %s</body></html>", $1.c_str());
webApiGen example.wag gives us a
cgiMain, in file exampleDBMain.cpp, like so:
std::string host = cgiRemoteHost;
std::string addr = cgiRemoteAddr ;
funcSigs.push_back("/hello (str) [get] return html");
Stop remote invocation of this program
if (!(host == "localhost" || addr == "127.0.0.1"))
"<body>attempt to connect from %s %s denied</body>"
std::string path = cgiPathInfo;
if (path == "/webapigen") // webapigen has dibs on webapigen
WagAboutPage about("1.4", "exampleDB", "2012-11-23T15:54:04Z");
else if (path == "/hello")
/* hello (str) [get] return html */;
std::map<std::string, std::vector<std::string> > userArrays;
std::map<std::string, std::string> userVars;
/* visit, for example, localhost/wagExample.exe/hello?str=world */
if (userVars["str"].size() == 1)
"<body>hello %s</body></html>", userVars["str"].c_str());
err.writeHtml(cgiOut, path, funcSigs);
Note that two variables are supplied:
std::map<std::string, std::vector<std::string> > userArrays and
std::map<std::string, std::string> userVars. The maps are keyed on the control names; if the parameter was declared to be an array (using
), it's values will be in
userArrays["control-name"] as a
std::vector<std::string> otherwise it will be in
userVars["control-name"] as a
Note also that the user code delimiters
endcode must be the only words appearing on the line.
Compile this as follows (you may need to adjust for compiler etc):
rem file ..\example\compile.bat @echo off set MIN_GW_BIN=c:\bin\MinGWNew\bin set OLDPATH=%PATH% set PATH=%MIN_GW_BIN%;%OLDPATH% set GPPPATH="%MIN_GW_BIN%\g++.exe" set GCCPATH="%MIN_GW_BIN%\gcc.exe" set WAGPATH=..\gcc\webApiGen echo ************************************ echo ****** MAKING WAGEXAMPLE.EXE ******* echo ************************************ %WAGPATH% example.wag %GCCPATH% -c cgic.c %GPPPATH% -o wagExample.exe -I../runTimeSource exampleDBMain.cpp cgic.o del cgic.o set MIN_GW_BIN= set GGPPPATH= set GCCPATH= set PATH=%OLDPATH% set OLDPATH=
and you have a cgi program to put on a server.
If you put wagExample.exe on a server and visit localhost/path/to/wagExample.exe/webapigen, you get a full fat web page with some server data on it; localhost/path/to/wagExample.exe/hello?str=world gets you a "hello world" page.
You can put a return statement at the end of the function: possible values for this are
void and, the default,
return json gets you
cgiHeaderContentType("application/json"); and so on;
return void generates
cgiHeaderStatus(204, "No Content");. The programmer needs to ensure the output is well formed...
The grammar is:
NAME IDENTIFIER SEMI_COLONreturnType:;
paramList COMMA IDENTIFIER
paramList COMMA IDENTIFIER ARRAY_DECL
This looks like
version "1.3" api-name SomeRandomName;
This (i) stops us compiling outdated source files and (ii) says our output file is going to be called
SomeRandomNameMain.cpp. An optional code block, like
code ... endcode
can be used to #include files, declare variables etc etc near the head of the output file.
Our cgi program, let's call it
SomeRandomName.exe, will be at
http://localhost/SomeRandomName.exe. The various functions we define are invoked by visting a URI (or making an XMLHTTPRequest) like
http://localhost/SomeRandomName.exe/functionName?parameter=value. The webapigen definition of this would look like:
/functionName get (parameter) code // some c++ goes here endcode return plain
We can specify a return type (the default is
/functionName we need to define the function. This looks like
(paramList) followed by a
paramList is a comma separated list of identifiers. The identifiers may have a
 after them (an
array_decl) to show that we're expecting a lot of values for this parameter name.
There is a special parameter identifier
$pageState. The idea here is that the client page is throwing everything at us and we pick the bones out of it in the code block. This may be redundant.
Finally, we do some real work. The cgi script has been invoked (perhaps by XMLHTTPRequest or plain old html) by a client which has supplied (name, value) pairs as parameters. One of two things happens:
the parameter list is empty.
you write code to emit whatever type you've said you're going to emit.
in this case a variable called
userParams (of type
std::map<std::string, std::vector<std::string> >) has been declared and initialized for you.
userParams["parameterName"] yields the value(s), in a std::vector
Alternatively you can use positional parameters to reference values: a parameter list like
(i, j, k) could be referenced with
$3 in the source file's codeblock; webapigen would substitute
$1 and so on. A positional parameter has type
std::map<std::string, std::vector<std::string> >. The example demonstrates this function.
You need to ensure that your output is in accord with the the return type.
the code block is wrapped like so:
code // c++ goes here endcode
the words code and endcode must be the only letters on the line.
A potential problem: you may get confused (I did!) if the client provides the parameter values under a different parameter name to the one you're expecting. For example, if the client page has
<input value="value" name="param" type="checkbox">
on a form and your cgi spec has a path like
/function get (parameter)
a reference to
userParams["parameter"] won't get you anything, it'll be in
to compile the webapigen output file.
to compile cgic.c (as C code, not C++)
to have WagAboutPage.h and WagErrorPage.h on your include path
to compile any files
#included, plus any supporting bits and bobs, eg for database access.
For what it's worth I tend to do it a la Java, a constructor for a helper class that gets the ducks in a row and then call a function on the class like
writeJson() as appropriate.
gcc\compile.bat. you will need g++, sed, yacc and flex on your path.
The supplied binary may have dll dependencies that mean you have to recompile the source on your machine.
Input that ends
"endcode<EOF>" fails to parse,
"endcode\n<EOF>" will work.
userVars decl written when no userVars - fixed 1.1
cgi-url etc are redundant - fixed 1.1
/ get func() should be valid
apiUrl is redundant and has been removed
consequently apigenParameters is also redundant
unecessary userVars decl no longer written
/webapigen now gets you a summary of the api as well as server variables.
/webapigen tweak: WagAboutPage now uses api spec id in constructor
$1, $2 etc in code blocks for path definitions are expanded to userVars["..."] in generated code. This expression has type std::string.
the banner now has a timestamp and a function list.
return statement introduced, call to
cgiHeaderContentType is now generated.
$1, $2 etc in code blocks for path definitions are expanded to userParams["..."] in generated code. This expression has type
pp webApiGen.txt | pandoc --toc -N -c webApiGen.css -s -5 -o webApiGen.html