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