src/jdk.incubator.jpackage/linux/native/libapplauncher/LinuxPlatform.cpp
branchJDK-8200758-branch
changeset 58994 b09ba68c6a19
parent 58302 718bd56695b3
equal deleted inserted replaced
58993:b5e1baa9d2c3 58994:b09ba68c6a19
       
     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("lib/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 }