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