15 while ((p = str.find(
"+", p)) != std::string::npos) {
16 str.replace(p, 1, 1,
' ');
21 while ((p = str.find(
"&", p)) != std::string::npos) {
22 if ( (c = str.find(
";", p)) != std::string::npos ) {
24 if (str[p+1] ==
'#') {
26 ch = (char) strtol(str.substr(p+3, c-p-3).c_str(), NULL, 16);
28 ch = (char) strtol(str.substr(p+2, c-p-2).c_str(), NULL, 10);
31 str.replace(p, c-p+1, 1, ch);
34 str.replace(p, c-p+1, 1, (
char)
html::HTML2Char(str.substr(p+1, c-p-1).c_str()));
41 while ((p = str.find(
"%", p)) != std::string::npos) {
42 ch = (char) strtol(str.substr(p+1, 2).c_str(), NULL, 16);
45 str.replace(p, 3, 1, ch);
54 std::string result = str;
56 char* tmp = (
char*)malloc(20);
58 for (uint32 n=0; n<result.size(); n++) {
59 switch(c = result[n]) {
61 result.replace(n, 1,
"<br>\n");
65 result.replace(n, 1,
"");
69 result.replace(n, 1,
" ");
73 if ( (c < 32) || (c > 122) ||
74 ( (c > 90) && (c < 97) ) ||
75 ( (c > 57) && (c < 65) ) ||
76 ( (c > 33) && (c < 48) ) ) {
77 snprintf(tmp, 20,
"&#x%x;", (
int) c & 0xFF);
78 result.replace(n, 1, tmp);
79 n+=(uint32)strlen(tmp)-1;
91 if (strcmp(str,
"amp") == 0)
93 else if (strcmp(str,
"lt") == 0)
95 else if (strcmp(str,
"gt") == 0)
97 else if (strcmp(str,
"quot") == 0)
99 else if (strcmp(str,
"apos") == 0)
101 else if (strcmp(str,
"lsquo") == 0)
102 return (
unsigned char)0x91;
103 else if (strcmp(str,
"rsquo") == 0)
104 return (
unsigned char)0x92;
105 else if (strcmp(str,
"sbquo") == 0)
106 return (
unsigned char)0x82;
107 else if (strcmp(str,
"ldquo") == 0)
108 return (
unsigned char)0x93;
109 else if (strcmp(str,
"rdquo") == 0)
110 return (
unsigned char)0x94;
111 else if (strcmp(str,
"bdquo") == 0)
112 return (
unsigned char)0x84;
113 else if (strcmp(str,
"dagger") == 0)
114 return (
unsigned char)0x86;
115 else if (strcmp(str,
"Dagger") == 0)
116 return (
unsigned char)0x87;
117 else if (strcmp(str,
"permil") == 0)
118 return (
unsigned char)0x89;
119 else if (strcmp(str,
"lsaquo") == 0)
120 return (
unsigned char)0x8B;
121 else if (strcmp(str,
"rsaquo") == 0)
122 return (
unsigned char)0x9B;
123 else if (strcmp(str,
"trade") == 0)
124 return (
unsigned char)0x99;
137bool GetMimeType(
char* dest,
const char* filename, uint32 maxsize) {
139 return GetMimeType(dest, filename, maxsize, isBinary);
142bool GetMimeType(
char* dest,
const char* filename, uint32 maxsize,
bool &isBinary) {
149 else if (!filename) {
168 return (
utils::strcpyavail(dest,
"application/vnd.android.package-archive", maxsize,
false) != 0);
237 return (
utils::strcpyavail(dest,
"application/vnd.ms-powerpoint", maxsize,
false) != 0);
240 return (
utils::strcpyavail(dest,
"application/vnd.ms-fontobject", maxsize,
false) != 0);
246 return (
utils::strcpyavail(dest,
"application/x-font-truetype", maxsize,
false) != 0);
248 return (
utils::strcpyavail(dest,
"application/x-font-opentype", maxsize,
false) != 0);
260 size_t colon = url.find(
':');
261 if (colon == std::string::npos)
264 return url.substr(0, colon);
272 size_t colon = url.find(
':');
273 if (colon == std::string::npos)
276 return url.substr(colon+1);
280 size_t start = url.find(
"//");
281 if (start == std::string::npos)
284 size_t offset = url.find(
"@");
285 if (offset == std::string::npos)
288 return url.substr(start, offset-start);
292 size_t offset = url.find(
"@");
293 if (offset == std::string::npos) {
294 offset = url.find(
"//");
295 if (offset == std::string::npos)
303 size_t end = url.find(
':', offset);
304 if (end != std::string::npos)
305 return url.substr(offset, end-offset);
307 end = url.find(
'/', offset);
308 if (end != std::string::npos)
309 return url.substr(offset, end-offset);
311 return url.substr(offset);
315 size_t offset = url.find(
"@");
316 if (offset == std::string::npos) {
317 offset = url.find(
"//");
318 if (offset == std::string::npos)
326 size_t colon = url.find(
':', offset);
327 if (colon == std::string::npos)
334 if (url.find(
"http:") == 0)
336 else if (url.find(
"https:") == 0)
338 else if (url.find(
"ftp:") == 0)
341 size_t offset = url.find(
"//");
342 if (offset == std::string::npos)
345 return url.substr(0, offset);
349 size_t offset = url.find(
"//");
350 if (offset == std::string::npos)
355 size_t end = url.find(
'/', offset);
356 if (end != std::string::npos)
357 return url.substr(end);
373 if (
HTML2Char(
"notanentity") !=
' ') {
unittest::fail(
"HTML test: HTML2Char unknown not space\n");
return false; }
380 if (
EncodeHTML(
"Hello123") !=
"Hello123") {
unittest::fail(
"HTML test: EncodeHTML altered plain text\n");
return false; }
389 if (
EncodeHTML(
"\r") !=
"") {
unittest::fail(
"HTML test: EncodeHTML carriage return not stripped\n");
return false; }
390 if (
EncodeHTML(
"\t") !=
" ") {
unittest::fail(
"HTML test: EncodeHTML tab mismatch\n");
return false; }
399 if (
DecodeHTML(
"<tag>") !=
"<tag>") {
unittest::fail(
"HTML test: DecodeHTML lt/gt mismatch\n");
return false; }
405 if (
DecodeHTML(
"hello+world") !=
"hello world") {
unittest::fail(
"HTML test: DecodeHTML plus mismatch\n");
return false; }
416 bool isBinary =
true;
417 if (!
GetMimeType(mime,
"page.html",
sizeof(mime), isBinary) || strcmp(mime,
"text/html") != 0) {
418 unittest::fail(
"HTML test: GetMimeType html mismatch\n");
return false;
420 if (isBinary) {
unittest::fail(
"HTML test: GetMimeType html flagged binary\n");
return false; }
421 if (!
GetMimeType(mime,
"image.png",
sizeof(mime), isBinary) || strcmp(mime,
"image/png") != 0) {
422 unittest::fail(
"HTML test: GetMimeType png mismatch\n");
return false;
424 if (!isBinary) {
unittest::fail(
"HTML test: GetMimeType png not flagged binary\n");
return false; }
425 if (!
GetMimeType(mime,
"data.json",
sizeof(mime), isBinary) || strcmp(mime,
"application/json") != 0) {
426 unittest::fail(
"HTML test: GetMimeType json mismatch\n");
return false;
429 if (!
GetMimeType(mime,
"file.unknownext",
sizeof(mime), isBinary) || strcmp(mime,
"application/octet-stream") != 0) {
430 unittest::fail(
"HTML test: GetMimeType unknown fallback mismatch\n");
return false;
433 if (!
GetMimeType(mime,
"doc.txt",
sizeof(mime)) || strcmp(mime,
"text/plain") != 0) {
434 unittest::fail(
"HTML test: GetMimeType 3-arg txt mismatch\n");
return false;
445 std::string url =
"http://user:pass@example.com:8080/path/to/page";
455 std::string simple =
"http://host.example.org/";
468 std::string sample =
"Tom & Jerry <said> \"hi\"\nwith\ttabs";
471 for (
int i = 0; i < N; i++)
474 if (enc.empty()) {
unittest::fail(
"HTML test: EncodeHTML produced empty result\n");
return false; }
478 for (
int i = 0; i < N; i++)
481 if (dec.empty()) {
unittest::fail(
"HTML test: DecodeHTML produced empty result\n");
return false; }
484 unittest::metric(
"encode_throughput", (
double)N / encUs * 1e6,
"ops/s",
true);
486 unittest::metric(
"decode_throughput", (
double)N / decUs * 1e6,
"ops/s",
true);
497 "HTML entity encode/decode, MIME type lookup, URL parsing",
"util");
HTML/URL helper utilities: entity encoding/decoding, MIME type lookup and URL component parsing.
CMSDK time: µs-resolution 64-bit timestamps and the Time Mapping Constant (TMC).
Small, dependency-free unit test harness used by all CMSDK object tests.
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
uint32 Ascii2Uint32(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 32-bit decimal integer from a substring.
const char * stristr(const char *str, const char *substr, uint32 len=0)
Case-insensitive strstr.
bool TextEndsWith(const char *str, const char *end, bool caseSensitive=true)
Test whether str ends with end.
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
bool HTML_UnitTest()
Self-contained unit test of the HTML/URL helpers.
std::string GetURIFromURL(std::string url)
Extract the URI (path plus query) from a URL.
std::string GetProtocolFromURL(std::string url)
Extract the protocol/scheme from a URL.
bool GetMimeType(char *dest, const char *filename, uint32 maxsize)
Look up the MIME type for a filename based on its extension.
bool IsMimeBinary(const char *type)
Determine whether a MIME type denotes binary content.
std::string GetUsernameFromURL(std::string url)
Extract the username from a URL of the form scheme://user:pass@host/...
unsigned char HTML2Char(const char *str)
Convert a single HTML entity (e.g.
std::string EncodeHTML(std::string str)
Encode a plain string for safe embedding in HTML (e.g.
std::string GetPasswordFromURL(std::string url)
Extract the password from a URL of the form scheme://user:pass@host/...
std::string GetHostFromURL(std::string url)
Extract the host name (or IP literal) from a URL.
uint16 GetPortFromURL(std::string url)
Extract the port number from a URL.
std::string DecodeHTML(std::string str)
Decode HTML entities in a string (e.g.
std::string GetAuthenticationFromURL(std::string url)
Extract the full "user:pass" authentication section from a URL.
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
void Register_HTML_Tests()