1 /* 2 * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "Platform.h" 27 28 #include "JavaVirtualMachine.h" 29 #include "LinuxPlatform.h" 30 #include "PlatformString.h" 31 #include "IniFile.h" 32 #include "Helpers.h" 33 #include "FilePath.h" 34 35 #include <stdlib.h> 36 #include <pwd.h> 37 #include <sys/file.h> 38 #include <sys/stat.h> 39 #include <errno.h> 40 #include <unistd.h> 41 #include <sys/types.h> 42 #include <limits.h> 43 #include <signal.h> 44 45 #define LINUX_JPACKAGE_TMP_DIR "/.java/jpackage/tmp" 46 47 TString GetEnv(const TString &name) { 48 TString result; 49 50 char *value = ::getenv((TCHAR*) name.c_str()); 51 52 if (value != NULL) { 53 result = value; 54 } 55 56 return result; 57 } 58 59 LinuxPlatform::LinuxPlatform(void) : Platform(), 60 PosixPlatform() { 61 FMainThread = pthread_self(); 62 } 63 64 LinuxPlatform::~LinuxPlatform(void) { 65 } 66 67 TString LinuxPlatform::GetPackageAppDirectory() { 68 return FilePath::IncludeTrailingSeparator( 69 GetPackageRootDirectory()) + _T("app"); 70 } 71 72 TString LinuxPlatform::GetAppName() { 73 TString result = GetModuleFileName(); 74 result = FilePath::ExtractFileName(result); 75 return result; 76 } 77 78 TString LinuxPlatform::GetPackageLauncherDirectory() { 79 return FilePath::IncludeTrailingSeparator( 80 GetPackageRootDirectory()) + _T("bin"); 81 } 82 83 TString LinuxPlatform::GetPackageRuntimeBinDirectory() { 84 return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) 85 + _T("runtime/bin"); 86 } 87 88 void LinuxPlatform::ShowMessage(TString title, TString description) { 89 printf("%s %s\n", PlatformString(title).toPlatformString(), 90 PlatformString(description).toPlatformString()); 91 fflush(stdout); 92 } 93 94 void LinuxPlatform::ShowMessage(TString description) { 95 TString appname = GetModuleFileName(); 96 appname = FilePath::ExtractFileName(appname); 97 ShowMessage(PlatformString(appname).toPlatformString(), 98 PlatformString(description).toPlatformString()); 99 } 100 101 TCHAR* LinuxPlatform::ConvertStringToFileSystemString(TCHAR* Source, 102 bool &release) { 103 // Not Implemented. 104 return NULL; 105 } 106 107 TCHAR* LinuxPlatform::ConvertFileSystemStringToString(TCHAR* Source, 108 bool &release) { 109 // Not Implemented. 110 return NULL; 111 } 112 113 TString LinuxPlatform::GetModuleFileName() { 114 ssize_t len = 0; 115 TString result; 116 DynamicBuffer<TCHAR> buffer(MAX_PATH); 117 if (buffer.GetData() == NULL) { 118 return result; 119 } 120 121 if ((len = readlink("/proc/self/exe", buffer.GetData(), 122 MAX_PATH - 1)) != -1) { 123 buffer[len] = '\0'; 124 result = buffer.GetData(); 125 } 126 127 return result; 128 } 129 130 TString LinuxPlatform::GetPackageRootDirectory() { 131 TString result; 132 TString filename = GetModuleFileName(); 133 TString binPath = FilePath::ExtractFilePath(filename); 134 135 size_t slash = binPath.find_last_of(TRAILING_PATHSEPARATOR); 136 if (slash != TString::npos) { 137 result = binPath.substr(0, slash); 138 } 139 140 return result; 141 } 142 143 TString LinuxPlatform::GetAppDataDirectory() { 144 TString result; 145 TString home = GetEnv(_T("HOME")); 146 147 if (home.empty() == false) { 148 result += FilePath::IncludeTrailingSeparator(home) + _T(".local"); 149 } 150 151 return result; 152 } 153 154 ISectionalPropertyContainer* LinuxPlatform::GetConfigFile(TString FileName) { 155 IniFile *result = new IniFile(); 156 if (result == NULL) { 157 return NULL; 158 } 159 160 result->LoadFromFile(FileName); 161 162 return result; 163 } 164 165 TString LinuxPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) { 166 TString result = FilePath::IncludeTrailingSeparator(RuntimePath) + 167 "lib/libjli.so"; 168 169 if (FilePath::FileExists(result) == false) { 170 result = FilePath::IncludeTrailingSeparator(RuntimePath) + 171 "lib/jli/libjli.so"; 172 if (FilePath::FileExists(result) == false) { 173 printf("Cannot find libjli.so!"); 174 } 175 } 176 177 return result; 178 } 179 180 bool LinuxPlatform::IsMainThread() { 181 bool result = (FMainThread == pthread_self()); 182 return result; 183 } 184 185 TString LinuxPlatform::getTmpDirString() { 186 return TString(LINUX_JPACKAGE_TMP_DIR); 187 } 188 189 TPlatformNumber LinuxPlatform::GetMemorySize() { 190 long pages = sysconf(_SC_PHYS_PAGES); 191 long page_size = sysconf(_SC_PAGE_SIZE); 192 TPlatformNumber result = pages * page_size; 193 result = result / 1048576; // Convert from bytes to megabytes. 194 return result; 195 } 196 197 void PosixProcess::Cleanup() { 198 if (FOutputHandle != 0) { 199 close(FOutputHandle); 200 FOutputHandle = 0; 201 } 202 203 if (FInputHandle != 0) { 204 close(FInputHandle); 205 FInputHandle = 0; 206 } 207 } 208 209 #define PIPE_READ 0 210 #define PIPE_WRITE 1 211 212 bool PosixProcess::Execute(const TString Application, 213 const std::vector<TString> Arguments, bool AWait) { 214 bool result = false; 215 216 if (FRunning == false) { 217 FRunning = true; 218 219 int handles[2]; 220 221 if (pipe(handles) == -1) { 222 return false; 223 } 224 225 struct sigaction sa; 226 sa.sa_handler = SIG_IGN; 227 sigemptyset(&sa.sa_mask); 228 sa.sa_flags = 0; 229 230 FChildPID = fork(); 231 232 // PID returned by vfork is 0 for the child process and the 233 // PID of the child process for the parent. 234 if (FChildPID == -1) { 235 // Error 236 TString message = PlatformString::Format( 237 _T("Error: Unable to create process %s"), 238 Application.data()); 239 throw Exception(message); 240 } else if (FChildPID == 0) { 241 Cleanup(); 242 TString command = Application; 243 244 for (std::vector<TString>::const_iterator iterator = 245 Arguments.begin(); iterator != Arguments.end(); 246 iterator++) { 247 command += TString(_T(" ")) + *iterator; 248 } 249 #ifdef DEBUG 250 printf("%s\n", command.data()); 251 #endif // DEBUG 252 253 dup2(handles[PIPE_READ], STDIN_FILENO); 254 dup2(handles[PIPE_WRITE], STDOUT_FILENO); 255 256 close(handles[PIPE_READ]); 257 close(handles[PIPE_WRITE]); 258 259 execl("/bin/sh", "sh", "-c", command.data(), (char *) 0); 260 261 _exit(127); 262 } else { 263 FOutputHandle = handles[PIPE_READ]; 264 FInputHandle = handles[PIPE_WRITE]; 265 266 if (AWait == true) { 267 ReadOutput(); 268 Wait(); 269 Cleanup(); 270 FRunning = false; 271 result = true; 272 } else { 273 result = true; 274 } 275 } 276 } 277 278 return result; 279 } 280 281 282 //---------------------------------------------------------------------------- 283 284 #ifndef __UNIX_JPACKAGE_PLATFORM__ 285 #define __UNIX_JPACKAGE_PLATFORM__ 286 287 /** Provide an abstraction for difference in the platform APIs, 288 e.g. string manipulation functions, etc. */ 289 #include <stdio.h> 290 #include <string.h> 291 #include <strings.h> 292 #include <sys/stat.h> 293 294 #define TCHAR char 295 296 #define _T(x) x 297 298 #define JPACKAGE_MULTIBYTE_SNPRINTF snprintf 299 300 #define JPACKAGE_SNPRINTF(buffer, sizeOfBuffer, count, format, ...) \ 301 snprintf((buffer), (count), (format), __VA_ARGS__) 302 303 #define JPACKAGE_PRINTF(format, ...) \ 304 printf((format), ##__VA_ARGS__) 305 306 #define JPACKAGE_FPRINTF(dest, format, ...) \ 307 fprintf((dest), (format), __VA_ARGS__) 308 309 #define JPACKAGE_SSCANF(buf, format, ...) \ 310 sscanf((buf), (format), __VA_ARGS__) 311 312 #define JPACKAGE_STRDUP(strSource) \ 313 strdup((strSource)) 314 315 //return "error code" (like on Windows) 316 317 static int JPACKAGE_STRNCPY(char *strDest, size_t numberOfElements, 318 const char *strSource, size_t count) { 319 char *s = strncpy(strDest, strSource, count); 320 // Duplicate behavior of the Windows' _tcsncpy_s() by adding a NULL 321 // terminator at the end of the string. 322 if (count < numberOfElements) { 323 s[count] = '\0'; 324 } else { 325 s[numberOfElements - 1] = '\0'; 326 } 327 return (s == strDest) ? 0 : 1; 328 } 329 330 #define JPACKAGE_STRICMP(x, y) \ 331 strcasecmp((x), (y)) 332 333 #define JPACKAGE_STRNICMP(x, y, cnt) \ 334 strncasecmp((x), (y), (cnt)) 335 336 #define JPACKAGE_STRNCMP(x, y, cnt) \ 337 strncmp((x), (y), (cnt)) 338 339 #define JPACKAGE_STRLEN(x) \ 340 strlen((x)) 341 342 #define JPACKAGE_STRSTR(x, y) \ 343 strstr((x), (y)) 344 345 #define JPACKAGE_STRCHR(x, y) \ 346 strchr((x), (y)) 347 348 #define JPACKAGE_STRRCHR(x, y) \ 349 strrchr((x), (y)) 350 351 #define JPACKAGE_STRPBRK(x, y) \ 352 strpbrk((x), (y)) 353 354 #define JPACKAGE_GETENV(x) \ 355 getenv((x)) 356 357 #define JPACKAGE_PUTENV(x) \ 358 putenv((x)) 359 360 #define JPACKAGE_STRCMP(x, y) \ 361 strcmp((x), (y)) 362 363 #define JPACKAGE_STRCPY(x, y) \ 364 strcpy((x), (y)) 365 366 #define JPACKAGE_STRCAT(x, y) \ 367 strcat((x), (y)) 368 369 #define JPACKAGE_ATOI(x) \ 370 atoi((x)) 371 372 #define JPACKAGE_FOPEN(x, y) \ 373 fopen((x), (y)) 374 375 #define JPACKAGE_FGETS(x, y, z) \ 376 fgets((x), (y), (z)) 377 378 #define JPACKAGE_REMOVE(x) \ 379 remove((x)) 380 381 #define JPACKAGE_SPAWNV(mode, cmd, args) \ 382 spawnv((mode), (cmd), (args)) 383 384 #define JPACKAGE_ISDIGIT(ch) isdigit(ch) 385 386 // for non-unicode, just return the input string for 387 // the following 2 conversions 388 #define JPACKAGE_NEW_MULTIBYTE(message) message 389 390 #define JPACKAGE_NEW_FROM_MULTIBYTE(message) message 391 392 // for non-unicode, no-op for the relase operation 393 // since there is no memory allocated for the 394 // string conversions 395 #define JPACKAGE_RELEASE_MULTIBYTE(tmpMBCS) 396 397 #define JPACKAGE_RELEASE_FROM_MULTIBYTE(tmpMBCS) 398 399 // The size will be used for converting from 1 byte to 1 byte encoding. 400 // Ensure have space for zero-terminator. 401 #define JPACKAGE_GET_SIZE_FOR_ENCODING(message, theLength) (theLength + 1) 402 403 #endif 404 #define xmlTagType 0 405 #define xmlPCDataType 1 406 407 typedef struct _xmlNode XMLNode; 408 typedef struct _xmlAttribute XMLAttribute; 409 410 struct _xmlNode { 411 int _type; // Type of node: tag, pcdata, cdate 412 TCHAR* _name; // Contents of node 413 XMLNode* _next; // Next node at same level 414 XMLNode* _sub; // First sub-node 415 XMLAttribute* _attributes; // List of attributes 416 }; 417 418 struct _xmlAttribute { 419 TCHAR* _name; // Name of attribute 420 TCHAR* _value; // Value of attribute 421 XMLAttribute* _next; // Next attribute for this tag 422 }; 423 424 // Public interface 425 static void RemoveNonAsciiUTF8FromBuffer(char *buf); 426 XMLNode* ParseXMLDocument(TCHAR* buf); 427 void FreeXMLDocument(XMLNode* root); 428 429 // Utility methods for parsing document 430 XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name); 431 TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name); 432 433 // Debugging 434 void PrintXMLDocument(XMLNode* node, int indt); 435 436 #include <sys/types.h> 437 #include <sys/stat.h> 438 #include <setjmp.h> 439 #include <stdlib.h> 440 #include <wctype.h> 441 442 #define JWS_assert(s, msg) \ 443 if (!(s)) { Abort(msg); } 444 445 446 // Internal declarations 447 static XMLNode* ParseXMLElement(void); 448 static XMLAttribute* ParseXMLAttribute(void); 449 static TCHAR* SkipWhiteSpace(TCHAR *p); 450 static TCHAR* SkipXMLName(TCHAR *p); 451 static TCHAR* SkipXMLComment(TCHAR *p); 452 static TCHAR* SkipXMLDocType(TCHAR *p); 453 static TCHAR* SkipXMLProlog(TCHAR *p); 454 static TCHAR* SkipPCData(TCHAR *p); 455 static int IsPCData(TCHAR *p); 456 static void ConvertBuiltInEntities(TCHAR* p); 457 static void SetToken(int type, TCHAR* start, TCHAR* end); 458 static void GetNextToken(void); 459 static XMLNode* CreateXMLNode(int type, TCHAR* name); 460 static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value); 461 static XMLNode* ParseXMLElement(void); 462 static XMLAttribute* ParseXMLAttribute(void); 463 static void FreeXMLAttribute(XMLAttribute* attr); 464 static void PrintXMLAttributes(XMLAttribute* attr); 465 static void indent(int indt); 466 467 static jmp_buf jmpbuf; 468 static XMLNode* root_node = NULL; 469 470 /** definition of error codes for setjmp/longjmp, 471 * that can be handled in ParseXMLDocument() 472 */ 473 #define JMP_NO_ERROR 0 474 #define JMP_OUT_OF_RANGE 1 475 476 #define NEXT_CHAR(p) { \ 477 if (*p != 0) { \ 478 p++; \ 479 } else { \ 480 longjmp(jmpbuf, JMP_OUT_OF_RANGE); \ 481 } \ 482 } 483 #define NEXT_CHAR_OR_BREAK(p) { \ 484 if (*p != 0) { \ 485 p++; \ 486 } else { \ 487 break; \ 488 } \ 489 } 490 #define NEXT_CHAR_OR_RETURN(p) { \ 491 if (*p != 0) { \ 492 p++; \ 493 } else { \ 494 return; \ 495 } \ 496 } 497 #define SKIP_CHARS(p,n) { \ 498 int i; \ 499 for (i = 0; i < (n); i++) { \ 500 if (*p != 0) { \ 501 p++; \ 502 } else { \ 503 longjmp(jmpbuf, JMP_OUT_OF_RANGE); \ 504 } \ 505 } \ 506 } 507 #define SKIP_CHARS_OR_BREAK(p,n) { \ 508 int i; \ 509 for (i = 0; i < (n); i++) { \ 510 if (*p != 0) { \ 511 p++; \ 512 } else { \ 513 break; \ 514 } \ 515 } \ 516 if (i < (n)) { \ 517 break; \ 518 } \ 519 } 520 521 /** Iterates through the null-terminated buffer (i.e., C string) and 522 * replaces all UTF-8 encoded character >255 with 255 523 * 524 * UTF-8 encoding: 525 * 526 * Range A: 0x0000 - 0x007F 527 * 0 | bits 0 - 7 528 * Range B : 0x0080 - 0x07FF : 529 * 110 | bits 6 - 10 530 * 10 | bits 0 - 5 531 * Range C : 0x0800 - 0xFFFF : 532 * 1110 | bits 12-15 533 * 10 | bits 6-11 534 * 10 | bits 0-5 535 */ 536 static void RemoveNonAsciiUTF8FromBuffer(char *buf) { 537 char* p; 538 char* q; 539 char c; 540 p = q = buf; 541 // We are not using NEXT_CHAR() to check if *q is NULL, as q is output 542 // location and offset for q is smaller than for p. 543 while (*p != '\0') { 544 c = *p; 545 if ((c & 0x80) == 0) { 546 /* Range A */ 547 *q++ = *p; 548 NEXT_CHAR(p); 549 } else if ((c & 0xE0) == 0xC0) { 550 /* Range B */ 551 *q++ = (char) 0xFF; 552 NEXT_CHAR(p); 553 NEXT_CHAR_OR_BREAK(p); 554 } else { 555 /* Range C */ 556 *q++ = (char) 0xFF; 557 NEXT_CHAR(p); 558 SKIP_CHARS_OR_BREAK(p, 2); 559 } 560 } 561 /* Null terminate string */ 562 *q = '\0'; 563 } 564 565 static TCHAR* SkipWhiteSpace(TCHAR *p) { 566 if (p != NULL) { 567 while (iswspace(*p)) 568 NEXT_CHAR_OR_BREAK(p); 569 } 570 return p; 571 } 572 573 static TCHAR* SkipXMLName(TCHAR *p) { 574 TCHAR c = *p; 575 /* Check if start of token */ 576 if (('a' <= c && c <= 'z') || 577 ('A' <= c && c <= 'Z') || 578 c == '_' || c == ':') { 579 580 while (('a' <= c && c <= 'z') || 581 ('A' <= c && c <= 'Z') || 582 ('0' <= c && c <= '9') || 583 c == '_' || c == ':' || c == '.' || c == '-') { 584 NEXT_CHAR(p); 585 c = *p; 586 if (c == '\0') break; 587 } 588 } 589 return p; 590 } 591 592 static TCHAR* SkipXMLComment(TCHAR *p) { 593 if (p != NULL) { 594 if (JPACKAGE_STRNCMP(p, _T("<!--"), 4) == 0) { 595 SKIP_CHARS(p, 4); 596 do { 597 if (JPACKAGE_STRNCMP(p, _T("-->"), 3) == 0) { 598 SKIP_CHARS(p, 3); 599 return p; 600 } 601 NEXT_CHAR(p); 602 } while (*p != '\0'); 603 } 604 } 605 return p; 606 } 607 608 static TCHAR* SkipXMLDocType(TCHAR *p) { 609 if (p != NULL) { 610 if (JPACKAGE_STRNCMP(p, _T("<!"), 2) == 0) { 611 SKIP_CHARS(p, 2); 612 while (*p != '\0') { 613 if (*p == '>') { 614 NEXT_CHAR(p); 615 return p; 616 } 617 NEXT_CHAR(p); 618 } 619 } 620 } 621 return p; 622 } 623 624 static TCHAR* SkipXMLProlog(TCHAR *p) { 625 if (p != NULL) { 626 if (JPACKAGE_STRNCMP(p, _T("<?"), 2) == 0) { 627 SKIP_CHARS(p, 2); 628 do { 629 if (JPACKAGE_STRNCMP(p, _T("?>"), 2) == 0) { 630 SKIP_CHARS(p, 2); 631 return p; 632 } 633 NEXT_CHAR(p); 634 } while (*p != '\0'); 635 } 636 } 637 return p; 638 } 639 640 /* Search for the built-in XML entities: 641 * & (&), < (<), > (>), ' ('), and "e(") 642 * and convert them to a real TCHARacter 643 */ 644 static void ConvertBuiltInEntities(TCHAR* p) { 645 TCHAR* q; 646 q = p; 647 // We are not using NEXT_CHAR() to check if *q is NULL, 648 // as q is output location and offset for q is smaller than for p. 649 while (*p) { 650 if (IsPCData(p)) { 651 /* dont convert &xxx values within PData */ 652 TCHAR *end; 653 end = SkipPCData(p); 654 while (p < end) { 655 *q++ = *p; 656 NEXT_CHAR(p); 657 } 658 } else { 659 if (JPACKAGE_STRNCMP(p, _T("&"), 5) == 0) { 660 *q++ = '&'; 661 SKIP_CHARS(p, 5); 662 } else if (JPACKAGE_STRNCMP(p, _T("<"), 4) == 0) { 663 *q = '<'; 664 SKIP_CHARS(p, 4); 665 } else if (JPACKAGE_STRNCMP(p, _T(">"), 4) == 0) { 666 *q = '>'; 667 SKIP_CHARS(p, 4); 668 } else if (JPACKAGE_STRNCMP(p, _T("'"), 6) == 0) { 669 *q = '\''; 670 SKIP_CHARS(p, 6); 671 } else if (JPACKAGE_STRNCMP(p, _T(""e;"), 7) == 0) { 672 *q = '\"'; 673 SKIP_CHARS(p, 7); 674 } else { 675 *q++ = *p; 676 NEXT_CHAR(p); 677 } 678 } 679 } 680 *q = '\0'; 681 } 682 683 /* ------------------------------------------------------------- */ 684 /* XML tokenizer */ 685 686 #define TOKEN_UNKNOWN 0 687 #define TOKEN_BEGIN_TAG 1 /* <tag */ 688 #define TOKEN_END_TAG 2 /* </tag */ 689 #define TOKEN_CLOSE_BRACKET 3 /* > */ 690 #define TOKEN_EMPTY_CLOSE_BRACKET 4 /* /> */ 691 #define TOKEN_PCDATA 5 /* pcdata */ 692 #define TOKEN_CDATA 6 /* cdata */ 693 #define TOKEN_EOF 7 694 695 static TCHAR* CurPos = NULL; 696 static TCHAR* CurTokenName = NULL; 697 static int CurTokenType; 698 static int MaxTokenSize = -1; 699 700 /* Copy token from buffer to Token variable */ 701 static void SetToken(int type, TCHAR* start, TCHAR* end) { 702 int len = end - start; 703 if (len > MaxTokenSize) { 704 if (CurTokenName != NULL) free(CurTokenName); 705 CurTokenName = (TCHAR *) malloc((len + 1) * sizeof (TCHAR)); 706 if (CurTokenName == NULL) { 707 return; 708 } 709 MaxTokenSize = len; 710 } 711 712 CurTokenType = type; 713 JPACKAGE_STRNCPY(CurTokenName, len + 1, start, len); 714 CurTokenName[len] = '\0'; 715 } 716 717 /* Skip XML comments, doctypes, and prolog tags */ 718 static TCHAR* SkipFilling(void) { 719 TCHAR *q = CurPos; 720 721 /* Skip white space and comment sections */ 722 do { 723 q = CurPos; 724 CurPos = SkipWhiteSpace(CurPos); 725 CurPos = SkipXMLComment(CurPos); /* Must be called befor DocTypes */ 726 CurPos = SkipXMLDocType(CurPos); /* <! ... > directives */ 727 CurPos = SkipXMLProlog(CurPos); /* <? ... ?> directives */ 728 } while (CurPos != q); 729 730 return CurPos; 731 } 732 733 /* Parses next token and initializes the global token variables above 734 The tokennizer automatically skips comments (<!-- comment -->) and 735 <! ... > directives. 736 */ 737 static void GetNextToken(void) { 738 TCHAR *p, *q; 739 740 /* Skip white space and comment sections */ 741 p = SkipFilling(); 742 743 if (p == NULL || *p == '\0') { 744 CurTokenType = TOKEN_EOF; 745 return; 746 } else if (p[0] == '<' && p[1] == '/') { 747 /* TOKEN_END_TAG */ 748 q = SkipXMLName(p + 2); 749 SetToken(TOKEN_END_TAG, p + 2, q); 750 p = q; 751 } else if (*p == '<') { 752 /* TOKEN_BEGIN_TAG */ 753 q = SkipXMLName(p + 1); 754 SetToken(TOKEN_BEGIN_TAG, p + 1, q); 755 p = q; 756 } else if (p[0] == '>') { 757 CurTokenType = TOKEN_CLOSE_BRACKET; 758 NEXT_CHAR(p); 759 } else if (p[0] == '/' && p[1] == '>') { 760 CurTokenType = TOKEN_EMPTY_CLOSE_BRACKET; 761 SKIP_CHARS(p, 2); 762 } else { 763 /* Search for end of data */ 764 q = p + 1; 765 while (*q && *q != '<') { 766 if (IsPCData(q)) { 767 q = SkipPCData(q); 768 } else { 769 NEXT_CHAR(q); 770 } 771 } 772 SetToken(TOKEN_PCDATA, p, q); 773 /* Convert all entities inside token */ 774 ConvertBuiltInEntities(CurTokenName); 775 p = q; 776 } 777 /* Advance pointer to beginning of next token */ 778 CurPos = p; 779 } 780 781 static XMLNode* CreateXMLNode(int type, TCHAR* name) { 782 XMLNode* node; 783 node = (XMLNode*) malloc(sizeof (XMLNode)); 784 if (node == NULL) { 785 return NULL; 786 } 787 node->_type = type; 788 node->_name = name; 789 node->_next = NULL; 790 node->_sub = NULL; 791 node->_attributes = NULL; 792 return node; 793 } 794 795 static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value) { 796 XMLAttribute* attr; 797 attr = (XMLAttribute*) malloc(sizeof (XMLAttribute)); 798 if (attr == NULL) { 799 return NULL; 800 } 801 attr->_name = name; 802 attr->_value = value; 803 attr->_next = NULL; 804 return attr; 805 } 806 807 XMLNode* ParseXMLDocument(TCHAR* buf) { 808 XMLNode* root; 809 int err_code = setjmp(jmpbuf); 810 switch (err_code) { 811 case JMP_NO_ERROR: 812 #ifndef _UNICODE 813 /* Remove UTF-8 encoding from buffer */ 814 RemoveNonAsciiUTF8FromBuffer(buf); 815 #endif 816 817 /* Get first Token */ 818 CurPos = buf; 819 GetNextToken(); 820 821 /* Parse document*/ 822 root = ParseXMLElement(); 823 break; 824 case JMP_OUT_OF_RANGE: 825 /* cleanup: */ 826 if (root_node != NULL) { 827 FreeXMLDocument(root_node); 828 root_node = NULL; 829 } 830 if (CurTokenName != NULL) free(CurTokenName); 831 fprintf(stderr, "Error during parsing jnlp file...\n"); 832 exit(-1); 833 break; 834 default: 835 root = NULL; 836 break; 837 } 838 839 return root; 840 } 841 842 static XMLNode* ParseXMLElement(void) { 843 XMLNode* node = NULL; 844 XMLNode* subnode = NULL; 845 XMLNode* nextnode = NULL; 846 XMLAttribute* attr = NULL; 847 848 if (CurTokenType == TOKEN_BEGIN_TAG) { 849 850 /* Create node for new element tag */ 851 node = CreateXMLNode(xmlTagType, JPACKAGE_STRDUP(CurTokenName)); 852 /* We need to save root node pointer to be able to cleanup 853 if an error happens during parsing */ 854 if (!root_node) { 855 root_node = node; 856 } 857 /* Parse attributes. This section eats a all input until 858 EOF, a > or a /> */ 859 attr = ParseXMLAttribute(); 860 while (attr != NULL) { 861 attr->_next = node->_attributes; 862 node->_attributes = attr; 863 attr = ParseXMLAttribute(); 864 } 865 866 /* This will eihter be a TOKEN_EOF, TOKEN_CLOSE_BRACKET, or a 867 * TOKEN_EMPTY_CLOSE_BRACKET */ 868 GetNextToken(); 869 870 if (CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET) { 871 GetNextToken(); 872 /* We are done with the sublevel - fall through to continue */ 873 /* parsing tags at the same level */ 874 } else if (CurTokenType == TOKEN_CLOSE_BRACKET) { 875 GetNextToken(); 876 877 /* Parse until end tag if found */ 878 node->_sub = ParseXMLElement(); 879 880 if (CurTokenType == TOKEN_END_TAG) { 881 /* Find closing bracket '>' for end tag */ 882 do { 883 GetNextToken(); 884 } while (CurTokenType != TOKEN_EOF && 885 CurTokenType != TOKEN_CLOSE_BRACKET); 886 GetNextToken(); 887 } 888 } 889 890 /* Continue parsing rest on same level */ 891 if (CurTokenType != TOKEN_EOF) { 892 /* Parse rest of stream at same level */ 893 node->_next = ParseXMLElement(); 894 } 895 return node; 896 897 } else if (CurTokenType == TOKEN_PCDATA) { 898 /* Create node for pcdata */ 899 node = CreateXMLNode(xmlPCDataType, JPACKAGE_STRDUP(CurTokenName)); 900 /* We need to save root node pointer to be able to cleanup 901 if an error happens during parsing */ 902 if (!root_node) { 903 root_node = node; 904 } 905 GetNextToken(); 906 return node; 907 } 908 909 /* Something went wrong. */ 910 return NULL; 911 } 912 913 /* Parses an XML attribute. */ 914 static XMLAttribute* ParseXMLAttribute(void) { 915 TCHAR* q = NULL; 916 TCHAR* name = NULL; 917 TCHAR* PrevPos = NULL; 918 919 do { 920 /* We need to check this condition to avoid endless loop 921 in case if an error happend during parsing. */ 922 if (PrevPos == CurPos) { 923 if (name != NULL) { 924 free(name); 925 name = NULL; 926 } 927 928 return NULL; 929 } 930 931 PrevPos = CurPos; 932 933 /* Skip whitespace etc. */ 934 SkipFilling(); 935 936 /* Check if we are done witht this attribute section */ 937 if (CurPos[0] == '\0' || 938 CurPos[0] == '>' || 939 (CurPos[0] == '/' && CurPos[1] == '>')) { 940 941 if (name != NULL) { 942 free(name); 943 name = NULL; 944 } 945 946 return NULL; 947 } 948 949 /* Find end of name */ 950 q = CurPos; 951 while (*q && !iswspace(*q) && *q != '=') NEXT_CHAR(q); 952 953 SetToken(TOKEN_UNKNOWN, CurPos, q); 954 if (name) { 955 free(name); 956 name = NULL; 957 } 958 name = JPACKAGE_STRDUP(CurTokenName); 959 960 /* Skip any whitespace */ 961 CurPos = q; 962 CurPos = SkipFilling(); 963 964 /* Next TCHARacter must be '=' for a valid attribute. 965 If it is not, this is really an error. 966 We ignore this, and just try to parse an attribute 967 out of the rest of the string. 968 */ 969 } while (*CurPos != '='); 970 971 NEXT_CHAR(CurPos); 972 CurPos = SkipWhiteSpace(CurPos); 973 /* Parse CDATA part of attribute */ 974 if ((*CurPos == '\"') || (*CurPos == '\'')) { 975 TCHAR quoteChar = *CurPos; 976 q = ++CurPos; 977 while (*q != '\0' && *q != quoteChar) NEXT_CHAR(q); 978 SetToken(TOKEN_CDATA, CurPos, q); 979 CurPos = q + 1; 980 } else { 981 q = CurPos; 982 while (*q != '\0' && !iswspace(*q)) NEXT_CHAR(q); 983 SetToken(TOKEN_CDATA, CurPos, q); 984 CurPos = q; 985 } 986 987 //Note: no need to free name and CurTokenName duplicate; they're assigned 988 // to an XMLAttribute structure in CreateXMLAttribute 989 990 return CreateXMLAttribute(name, JPACKAGE_STRDUP(CurTokenName)); 991 } 992 993 void FreeXMLDocument(XMLNode* root) { 994 if (root == NULL) return; 995 FreeXMLDocument(root->_sub); 996 FreeXMLDocument(root->_next); 997 FreeXMLAttribute(root->_attributes); 998 free(root->_name); 999 free(root); 1000 } 1001 1002 static void FreeXMLAttribute(XMLAttribute* attr) { 1003 if (attr == NULL) return; 1004 free(attr->_name); 1005 free(attr->_value); 1006 FreeXMLAttribute(attr->_next); 1007 free(attr); 1008 } 1009 1010 /* Find element at current level with a given name */ 1011 XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name) { 1012 if (root == NULL) return NULL; 1013 1014 if (root->_type == xmlTagType && JPACKAGE_STRCMP(root->_name, name) == 0) { 1015 return root; 1016 } 1017 1018 return FindXMLChild(root->_next, name); 1019 } 1020 1021 /* Search for an attribute with the given name and returns the contents. 1022 * Returns NULL if attribute is not found 1023 */ 1024 TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name) { 1025 if (attr == NULL) return NULL; 1026 if (JPACKAGE_STRCMP(attr->_name, name) == 0) return attr->_value; 1027 return FindXMLAttribute(attr->_next, name); 1028 } 1029 1030 void PrintXMLDocument(XMLNode* node, int indt) { 1031 if (node == NULL) return; 1032 1033 if (node->_type == xmlTagType) { 1034 JPACKAGE_PRINTF(_T("\n")); 1035 indent(indt); 1036 JPACKAGE_PRINTF(_T("<%s"), node->_name); 1037 PrintXMLAttributes(node->_attributes); 1038 if (node->_sub == NULL) { 1039 JPACKAGE_PRINTF(_T("/>\n")); 1040 } else { 1041 JPACKAGE_PRINTF(_T(">")); 1042 PrintXMLDocument(node->_sub, indt + 1); 1043 indent(indt); 1044 JPACKAGE_PRINTF(_T("</%s>"), node->_name); 1045 } 1046 } else { 1047 JPACKAGE_PRINTF(_T("%s"), node->_name); 1048 } 1049 PrintXMLDocument(node->_next, indt); 1050 } 1051 1052 static void PrintXMLAttributes(XMLAttribute* attr) { 1053 if (attr == NULL) return; 1054 1055 JPACKAGE_PRINTF(_T(" %s=\"%s\""), attr->_name, attr->_value); 1056 PrintXMLAttributes(attr->_next); 1057 } 1058 1059 static void indent(int indt) { 1060 int i; 1061 for (i = 0; i < indt; i++) { 1062 JPACKAGE_PRINTF(_T(" ")); 1063 } 1064 } 1065 1066 const TCHAR *CDStart = _T("<![CDATA["); 1067 const TCHAR *CDEnd = _T("]]>"); 1068 1069 static TCHAR* SkipPCData(TCHAR *p) { 1070 TCHAR *end = JPACKAGE_STRSTR(p, CDEnd); 1071 if (end != NULL) { 1072 return end + sizeof (CDEnd); 1073 } 1074 return (++p); 1075 } 1076 1077 static int IsPCData(TCHAR *p) { 1078 const int size = sizeof (CDStart); 1079 return (JPACKAGE_STRNCMP(CDStart, p, size) == 0); 1080 }