// ============================================================================
// This is a Servlet sample for the G-WAN Web Server (http://www.trustleap.com)
// ----------------------------------------------------------------------------
// loan.c: An AJAX Web application to help people calculate the cost of loans
//
// When clients use the loan.html form, we use the form fields to
// build new content which is then inserted into the original page
// (the javascript code in loan.html will tell you how AJAX works).
//
// GET and POST forms are processed with the SAME code (G-WAN does
// the URL/Entity parsing for C servlets).
//
// POST is often prefered to GET because the parameters are passed
// in the URL (so they are visible) while POST is using the request
// entity. This difference is *not* relevant in our case because we
// use AJAX to process the query (as a result, the URL is not seen
// by end-users in the Web browser 'address' field).
//
// Tip: When using XMLHttpRequest (AJAX), Web browsers split POST in two
// steps: they send headers and data separately.
// As GET takes only one step then you can save a connection by not
// using POST in your forms -enhancing your web server scalability.
// (this is an efficient way to boost AJAX graphic user interfaces)
//
// Using AJAX here saves you from using mod_rewrite to have "normal"
// search-friendly URLs while taking advantage of servlets.
//
// One of the most important things to keep in mind here is to make
// sure that clients will not lock-up your code with bad input data.
// The motto is 'filter'. You can also do it on the client side with
// javascript -but don't count on it. Filter again in your servlets
// because you just cannot trust what the network brings you (IPSec
// and SSL make absolutely no difference in this matter).
//
// Notes: xbuf_xcat() will insert ',' every 3 digits if you use uppercase
// formatters like: "%D", "%U", "%F" (instead of "%d", "%u", "%f").
// You can use the ' standard formater as well (I don't like it).
//
// To format s64/u64 values, use "%llu"/"%lld".
//
// Not using math.h (fabs, ceil, pow) or string.h (atod) gives you
// more control on what your code is doing and avoids system calls
// overhead. Of course you can use any library you want with G-WAN.
// This example only illustrates how fun *real* programming can be.
//
// I have been told that loan.php is shorter. That's true, but the
// PHP code does not even do half of what this C code does, is much
// less commented -and is much slower than loan.c.
//
// ============================================================================
#include "gwan.h" // G-WAN exported functions
// ----------------------------------------------------------------------------
// imported functions:
// get_reply(): get a pointer on the 'reply' dynamic buffer from the server
// set_reply(): send back the 'reply' dynamic buffer's pointer to the server
// xbuf_ncat(): like strncat(), but in the specified dynamic buffer
// xbuf_xcat(): formatted strcat() (a la printf) in a given dynamic buffer
// get_arg(): get the specified form field value
// escape_html(): translate HTML tags into harmless escaped sequences
// ----------------------------------------------------------------------------
// like atof() -but here you can *filter* input strings ("%16.2f" here)
// ----------------------------------------------------------------------------
long double atod(char *p)
{
u32 sign = 0;
while(*p == ' ') p++; // pass space characters
switch(*p)
{
case '-': sign=1;
case '+': p++;
}
long double d = 0.0, factor = 0.1;
u32 v, i = 16; // i:the maximum integral part we want to scan
while(i && (v=*p) && v >= '0' && v <= '9') // integer part
p++, d = 10. * d + (v - '0'), i--;
if(*p == '.' || *p == ',') // fractional part
{
p++; i = 2; // i:the maximum fractional precision we want to scan
while(i && (v=*p) && v >= '0' && v <= '9')
p++, d += (v - '0') * factor, factor *= 0.1, i--;
}
return sign?-d:d;
}
// ----------------------------------------------------------------------------
// return the smallest integer greater than 'x'
// ----------------------------------------------------------------------------
static inline u32 uceil(double x)
{
u32 ipart = (int)x;
if(x - ipart) return (x < 0.0)?ipart:ipart + 1;
else return (ipart);
}
// ----------------------------------------------------------------------------
// raise 'x' to the power of 'p'
// ----------------------------------------------------------------------------
static inline long double powd(long double x, u32 p)
{
switch(p)
{
case 0: return 1;
case 1: return x;
}
long double res = 1;
do
{ // for each bit of power 'p', multiply 'res' by the x^bit factor
if(p & 1) // [8421]
res *= x; // x^10 = x^8 * x^2 (power=10 is "1010")
x *= x, p >>= 1; // get next 'p' bit
}
while(p); // if(p==1024) 10 iterations, instead of: while(p--) x *= x;
return res;
}
// ----------------------------------------------------------------------------
// main() is receiving the query parameters ("csp?arg1&arg2&arg3...") in argv[]
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
// create a dynamic buffer and get a pointer on the server response buffer
xbuf_ctx reply; get_reply(argv, &reply);
// ---- no query parameters were provided, redirect client to "loan.html"
if(argc < 2)
{
static char redir[] = "HTTP/1.1 302 Found\r\n"
"Content-type:text/html\r\n"
"Location: loan.html\r\n\r\n"
""
"
Redirect"
"Click HERE for redirect.";
xbuf_ncat(&reply, redir, sizeof(redir) - 1);
// confirm the reply's dynamic buffer address and size to the server
// (they have changed when more memory is allocated during formatting)
set_reply(argv, &reply);
return (302); // return an HTTP code (302:'Found')
}
else // ---- if we have query parameters, we process a GET/POST form
{
char Months[][10] = {"January", "February", "March", "April", "May",
"June", "July", "August", "September", "October",
"November", "December"};
char szName[400] = {0};
double amount, down, rate, term, payment, interest, principle, cost;
int month = 0, year = 1, lastpayment = 1;
// the form field "names" we want to find values for
char *Name = "", *Amount = "0", *Down = "0", *Rate = "0", *Term = "0";
u64 start = cycles64(); // current CPU clock cycles' counter
// get the form field values (note the ending '=' name delimiter)
get_arg("name=", &Name, argc, argv);
get_arg("amount=", &Amount, argc, argv);
get_arg("rate=", &Rate, argc, argv);
get_arg("down=", &Down, argc, argv); // amount/percentage paid upfront
get_arg("term=", &Term, argc, argv);
// all litteral strings provided by a client must be escaped this way
// if you inject them into an HTML page
escape_html(szName, Name, sizeof(szName) - 1);
// filter input data to avoid all the useless/nasty cases
amount = atod(Amount);
if(amount < 1)
amount = 1;
down = atod(Down);
if(down > 0)
{
if(down < 100) // convert the percentage into an amount
down = amount * (down / 100.);
amount -= down;
}
rate = atod(Rate);
if(rate > 1)
{
if(rate > 19) // limit the damage...
rate = 20;
rate /= 100;
}
else
if(rate < 1)
rate = 1. / 100.;
term = atod(Term);
if(term < 0.1)
term = 1. / 12.;
// let's enter the daring zone, G-WAN v1.0.6 happily copes with it!
//else
//if(term>100) term = 100;
// calculate the monthly payment amount
payment = amount * (rate/12 / (1 - powd(1 / (1 + rate/12), term*12)));
cost = (term * 12 * payment) - amount;
// build the top of our HTML page
xbuf_xcat(&reply, ""
"Loan Calculator"
""
"
", year);
}
else
break;
}
}
// time the process and close the HTML page
xbuf_xcat(&reply,
"
This page was generated in %llU CPU clock cycles."
" (on a 3GHz CPU 1 ms = 3,000,000 cycles)"
" ", cycles64() - start);
}
// confirm the reply's dynamic buffer address and size to the server
// (they have changed when more memory is allocated during formatting)
set_reply(argv, &reply);
return (200); // return an HTTP code (200:'OK')
}
// ============================================================================
// End of Source Code
// ============================================================================