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