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  * &amp; (&), &lt; (<), &gt; (>), &apos; ('), and &quote(")
 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("&amp;"), 5) == 0) {
 654                 *q++ = '&';
 655                 SKIP_CHARS(p, 5);
 656             } else if (JPACKAGE_STRNCMP(p, _T("&lt;"), 4) == 0) {
 657                 *q = '<';
 658                 SKIP_CHARS(p, 4);
 659             } else if (JPACKAGE_STRNCMP(p, _T("&gt;"), 4) == 0) {
 660                 *q = '>';
 661                 SKIP_CHARS(p, 4);
 662             } else if (JPACKAGE_STRNCMP(p, _T("&apos;"), 6) == 0) {
 663                 *q = '\'';
 664                 SKIP_CHARS(p, 6);
 665             } else if (JPACKAGE_STRNCMP(p, _T("&quote;"), 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 }