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