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