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