1 /*
   2  * Copyright (c) 2014, 2016, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 
  33 
  34 #include "Platform.h"
  35 
  36 #ifdef LINUX
  37 
  38 #include "JavaVirtualMachine.h"
  39 #include "LinuxPlatform.h"
  40 #include "PlatformString.h"
  41 
  42 
  43 #include <stdlib.h>
  44 #include <pwd.h>
  45 
  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(), GenericPlatform(), PosixPlatform() {
  60     FMainThread = pthread_self();
  61 }
  62 
  63 LinuxPlatform::~LinuxPlatform(void) {
  64 }
  65 
  66 void LinuxPlatform::ShowMessage(TString title, TString description) {
  67     printf("%s %s\n", PlatformString(title).toPlatformString(), PlatformString(description).toPlatformString());
  68     fflush(stdout);
  69 }
  70 
  71 void LinuxPlatform::ShowMessage(TString description) {
  72     TString appname = GetModuleFileName();
  73     appname = FilePath::ExtractFileName(appname);
  74     ShowMessage(PlatformString(appname).toPlatformString(), PlatformString(description).toPlatformString());
  75 }
  76 
  77 TCHAR* LinuxPlatform::ConvertStringToFileSystemString(TCHAR* Source, bool &release) {
  78     // Not Implemented.
  79     return NULL;
  80 }
  81 
  82 TCHAR* LinuxPlatform::ConvertFileSystemStringToString(TCHAR* Source, bool &release) {
  83     // Not Implemented.
  84     return NULL;
  85 }
  86 
  87 TString LinuxPlatform::GetModuleFileName() {
  88     TString result;
  89     DynamicBuffer<TCHAR> buffer(MAX_PATH);
  90 
  91     if (readlink("/proc/self/exe", buffer.GetData(), MAX_PATH - 1) != -1) {
  92         result = buffer.GetData();
  93     }
  94 
  95     return result;
  96 }
  97 
  98 void LinuxPlatform::SetCurrentDirectory(TString Value) {
  99     chdir(PlatformString(Value).toPlatformString());
 100 }
 101 
 102 TString LinuxPlatform::GetPackageRootDirectory() {
 103     TString filename = GetModuleFileName();
 104     return FilePath::ExtractFilePath(filename);
 105 }
 106 
 107 TString LinuxPlatform::GetAppDataDirectory() {
 108     TString result;
 109     TString home = GetEnv(_T("HOME"));
 110 
 111     if (home.empty() == false) {
 112         result += FilePath::IncludeTrailingSeparater(home) + _T(".local");
 113     }
 114 
 115     return result;
 116 }
 117 
 118 ISectionalPropertyContainer* LinuxPlatform::GetConfigFile(TString FileName) {
 119     IniFile *result = new IniFile();
 120 
 121     if (result->LoadFromFile(FileName) == false) {
 122         // New property file format was not found, attempt to load old property file format.
 123         Helpers::LoadOldConfigFile(FileName, result);
 124     }
 125 
 126     return result;
 127 }
 128 
 129 TString LinuxPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) {
 130 #ifdef USE_JLI_LAUNCH
 131     TString result = FilePath::IncludeTrailingSeparater(RuntimePath) +
 132         "lib/"JAVAARCH"/jli/libjli.so";
 133 
 134     if (FilePath::FileExists(result) == false) {
 135         result = FilePath::IncludeTrailingSeparater(RuntimePath) +
 136             "lib/"JAVAARCH"/jli/libjli.so";
 137     }
 138 #else
 139     TString result = FilePath::IncludeTrailingSeparater(RuntimePath) +
 140         "jre/lib/"JAVAARCH"/client/libjvm.so";
 141 
 142     if (FilePath::FileExists(result) == false) {
 143         result = FilePath::IncludeTrailingSeparater(RuntimePath) +
 144             "jre/lib/"JAVAARCH"/server/libjvm.so";
 145     }
 146 
 147     if (FilePath::FileExists(result) == false) {
 148         result = FilePath::IncludeTrailingSeparater(RuntimePath) +
 149             "lib/"JAVAARCH"/server/libjvm.so";
 150     }
 151 
 152     if (FilePath::FileExists(result) == false) {
 153         result = FilePath::IncludeTrailingSeparater(RuntimePath) +
 154             "lib/"JAVAARCH"/server/libjvm.so";
 155     }
 156 #endif
 157 
 158     return result;
 159 }
 160 
 161 TString LinuxPlatform::GetSystemJRE() {
 162     if (GetAppCDSState() != cdsDisabled) {
 163         //TODO throw exception
 164         return _T("");
 165     }
 166 
 167     TString result;
 168     TString jreHome = GetEnv("JRE_HOME");
 169 
 170     if (jreHome.empty() == false) {
 171         result = FilePath::IncludeTrailingSeparater(jreHome);
 172 
 173         if (FilePath::FileExists(result + _T("lib/rt.jar")) == false) {
 174             result = FilePath::IncludeTrailingSeparater(jreHome) + _T("jre");
 175 
 176             if (FilePath::FileExists(result + _T("/lib/rt.jar")) == false) {
 177                 //check redhat location
 178                 if (FilePath::FileExists(_T("/usr/java/latest/jre/lib/rt.jar")) == true) {
 179                     result = _T("/usr/java/latest/jre");
 180                 }
 181                 else if (FilePath::FileExists(_T("/usr/lib/jvm/default-java/jre/lib/rt.jar")) == true) {
 182                     result = _T("/usr/lib/jvm/default-java/jre");
 183                 }
 184                 else {
 185                     result = _T("");
 186                 }
 187             }
 188         }
 189     }
 190 
 191     return result;
 192 }
 193 
 194 TString LinuxPlatform::GetSystemJVMLibraryFileName() {
 195     TString result;
 196     TString jreHome = GetSystemJRE();
 197 
 198     if (jreHome.empty() == false && FilePath::DirectoryExists(jreHome) == true) {
 199         result = FilePath::IncludeTrailingSeparater(jreHome) +
 200             _T("/lib/"JAVAARCH"/client/libjvm.so");
 201 
 202         if (FilePath::FileExists(result) == false) {
 203             result = FilePath::IncludeTrailingSeparater(jreHome) +
 204                 _T("/lib/"JAVAARCH"/server/libjvm.so");
 205         }
 206     }
 207 
 208     return result;
 209 }
 210 
 211 bool LinuxPlatform::IsMainThread() {
 212     bool result = (FMainThread == pthread_self());
 213     return result;
 214 }
 215 
 216 TPlatformNumber LinuxPlatform::GetMemorySize() {
 217     long pages = sysconf(_SC_PHYS_PAGES);
 218     long page_size = sysconf(_SC_PAGE_SIZE);
 219     TPlatformNumber result = pages * page_size;
 220     result = result / 1048576; // Convert from bytes to megabytes.
 221     return result;
 222 }
 223 
 224 #ifdef DEBUG
 225 bool LinuxPlatform::IsNativeDebuggerPresent() {
 226     // gdb opens file descriptors stdin=3, stdout=4, stderr=5 whereas
 227     // a typical prog uses only stdin=0, stdout=1, stderr=2.
 228     bool result = false;
 229     FILE *fd = fopen("/tmp", "r");
 230 
 231     if (fileno(fd) > 5) {
 232         result = true;
 233     }
 234 
 235     fclose(fd);
 236     return result;
 237 }
 238 
 239 int LinuxPlatform::GetProcessID() {
 240     int pid = getpid();
 241     return pid;
 242 }
 243 #endif //DEBUG
 244 
 245 //--------------------------------------------------------------------------------------------------
 246 
 247 #ifndef __UNIX_DEPLOY_PLATFORM__
 248 #define __UNIX_DEPLOY_PLATFORM__
 249 
 250 /** Provide an abstraction for difference in the platform APIs,
 251      e.g. string manipulation functions, etc. */
 252 #include <stdio.h>
 253 #include <string.h>
 254 #include <strings.h>
 255 #include <sys/stat.h>
 256 
 257 #define TCHAR char
 258 
 259 #define _T(x) x
 260 
 261 #define DEPLOY_MULTIBYTE_SNPRINTF snprintf
 262 
 263 #define DEPLOY_SNPRINTF(buffer, sizeOfBuffer, count, format, ...) \
 264     snprintf((buffer), (count), (format), __VA_ARGS__)
 265 
 266 #define DEPLOY_PRINTF(format, ...) \
 267     printf((format), ##__VA_ARGS__)
 268 
 269 #define DEPLOY_FPRINTF(dest, format, ...) \
 270     fprintf((dest), (format), __VA_ARGS__)
 271 
 272 #define DEPLOY_SSCANF(buf, format, ...) \
 273     sscanf((buf), (format), __VA_ARGS__)
 274 
 275 #define DEPLOY_STRDUP(strSource) \
 276     strdup((strSource))
 277 
 278 //return "error code" (like on Windows)
 279 static int DEPLOY_STRNCPY(char *strDest, size_t numberOfElements, const char *strSource, size_t count) {
 280     char *s = strncpy(strDest, strSource, count);
 281     // Duplicate behavior of the Windows' _tcsncpy_s() by adding a NULL
 282     // terminator at the end of the string.
 283     if (count < numberOfElements) {
 284         s[count] = '\0';
 285     } else {
 286         s[numberOfElements - 1] = '\0';
 287     }
 288     return (s == strDest) ? 0 : 1;
 289 }
 290 
 291 static int DEPLOY_STRNCAT(char *strDest, size_t numberOfElements, const char *strSource, size_t count) {
 292     // strncat always return null terminated string
 293     char *s = strncat(strDest, strSource, count);
 294     return (s == strDest) ? 0 : 1;
 295 }
 296 
 297 #define DEPLOY_STRICMP(x, y) \
 298     strcasecmp((x), (y))
 299 
 300 #define DEPLOY_STRNICMP(x, y, cnt) \
 301     strncasecmp((x), (y), (cnt))
 302 
 303 #define DEPLOY_STRNCMP(x, y, cnt) \
 304     strncmp((x), (y), (cnt))
 305 
 306 #define DEPLOY_STRLEN(x) \
 307     strlen((x))
 308 
 309 #define DEPLOY_STRSTR(x, y) \
 310     strstr((x), (y))
 311 
 312 #define DEPLOY_STRCHR(x, y) \
 313     strchr((x), (y))
 314 
 315 #define DEPLOY_STRRCHR(x, y) \
 316     strrchr((x), (y))
 317 
 318 #define DEPLOY_STRPBRK(x, y) \
 319     strpbrk((x), (y))
 320 
 321 #define DEPLOY_GETENV(x) \
 322     getenv((x))
 323 
 324 #define DEPLOY_PUTENV(x) \
 325     putenv((x))
 326 
 327 #define DEPLOY_STRCMP(x, y) \
 328     strcmp((x), (y))
 329 
 330 #define DEPLOY_STRCPY(x, y) \
 331     strcpy((x), (y))
 332 
 333 #define DEPLOY_STRCAT(x, y) \
 334     strcat((x), (y))
 335 
 336 #define DEPLOY_ATOI(x) \
 337     atoi((x))
 338 
 339 static int getFileSize(TCHAR* filename) {
 340     struct stat statBuf;
 341     if (stat(filename, &statBuf) == 0) {
 342         return statBuf.st_size;
 343     }
 344     return -1;
 345 }
 346 
 347 #define DEPLOY_FILE_SIZE(filename) getFileSize(filename)
 348 
 349 #define DEPLOY_FOPEN(x, y) \
 350     fopen((x), (y))
 351 
 352 #define DEPLOY_FGETS(x, y, z) \
 353     fgets((x), (y), (z))
 354 
 355 #define DEPLOY_REMOVE(x) \
 356     remove((x))
 357 
 358 #define DEPLOY_SPAWNV(mode, cmd, args) \
 359     spawnv((mode), (cmd), (args))
 360 
 361 #define DEPLOY_ISDIGIT(ch) isdigit(ch)
 362 
 363 // for non-unicode, just return the input string for
 364 // the following 2 conversions
 365 #define DEPLOY_NEW_MULTIBYTE(message) message
 366 
 367 #define DEPLOY_NEW_FROM_MULTIBYTE(message) message
 368 
 369 // for non-unicode, no-op for the relase operation
 370 // since there is no memory allocated for the
 371 // string conversions
 372 #define DEPLOY_RELEASE_MULTIBYTE(tmpMBCS)
 373 
 374 #define DEPLOY_RELEASE_FROM_MULTIBYTE(tmpMBCS)
 375 
 376 // The size will be used for converting from 1 byte to 1 byte encoding.
 377 // Ensure have space for zero-terminator.
 378 #define DEPLOY_GET_SIZE_FOR_ENCODING(message, theLength) (theLength + 1)
 379 
 380 #endif
 381 #define xmlTagType    0
 382 #define xmlPCDataType 1
 383 
 384 typedef struct _xmlNode XMLNode;
 385 typedef struct _xmlAttribute XMLAttribute;
 386 
 387 struct _xmlNode {
 388     int           _type;        // Type of node: tag, pcdata, cdate
 389     TCHAR*         _name;       // Contents of node
 390     XMLNode*      _next;        // Next node at same level
 391     XMLNode*      _sub;         // First sub-node
 392     XMLAttribute* _attributes;  // List of attributes
 393 };
 394 
 395 struct _xmlAttribute {
 396     TCHAR* _name;               // Name of attribute
 397     TCHAR* _value;              // Value of attribute
 398     XMLAttribute* _next;        // Next attribute for this tag
 399 };
 400 
 401 // Public interface
 402 static void     RemoveNonAsciiUTF8FromBuffer(char *buf);
 403 XMLNode* ParseXMLDocument    (TCHAR* buf);
 404 void     FreeXMLDocument     (XMLNode* root);
 405 
 406 // Utility methods for parsing document
 407 XMLNode* FindXMLChild        (XMLNode* root,      const TCHAR* name);
 408 TCHAR*    FindXMLAttribute    (XMLAttribute* attr, const TCHAR* name);
 409 
 410 // Debugging
 411 void PrintXMLDocument(XMLNode* node, int indt);
 412 
 413 
 414 #include <sys/types.h>
 415 #include <sys/stat.h>
 416 #include <setjmp.h>
 417 #include <stdlib.h>
 418 #include <wctype.h>
 419 
 420 
 421 #define JWS_assert(s, msg)      \
 422     if (!(s)) { Abort(msg); }
 423 
 424 
 425 // Internal declarations
 426 static XMLNode*      ParseXMLElement(void);
 427 static XMLAttribute* ParseXMLAttribute(void);
 428 static TCHAR*         SkipWhiteSpace(TCHAR *p);
 429 static TCHAR*         SkipXMLName(TCHAR *p);
 430 static TCHAR*         SkipXMLComment(TCHAR *p);
 431 static TCHAR*         SkipXMLDocType(TCHAR *p);
 432 static TCHAR*         SkipXMLProlog(TCHAR *p);
 433 static TCHAR*         SkipPCData(TCHAR *p);
 434 static int           IsPCData(TCHAR *p);
 435 static void          ConvertBuiltInEntities(TCHAR* p);
 436 static void          SetToken(int type, TCHAR* start, TCHAR* end);
 437 static void          GetNextToken(void);
 438 static XMLNode*      CreateXMLNode(int type, TCHAR* name);
 439 static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value);
 440 static XMLNode*      ParseXMLElement(void);
 441 static XMLAttribute* ParseXMLAttribute(void);
 442 static void          FreeXMLAttribute(XMLAttribute* attr);
 443 static void          PrintXMLAttributes(XMLAttribute* attr);
 444 static void          indent(int indt);
 445 
 446 static jmp_buf       jmpbuf;
 447 static XMLNode*      root_node = NULL;
 448 
 449 /** definition of error codes for setjmp/longjmp,
 450  *  that can be handled in ParseXMLDocument()
 451  */
 452 #define JMP_NO_ERROR     0
 453 #define JMP_OUT_OF_RANGE 1
 454 
 455 #define NEXT_CHAR(p) {if (*p != 0) { p++;} else {longjmp(jmpbuf, JMP_OUT_OF_RANGE);}}
 456 #define NEXT_CHAR_OR_BREAK(p) {if (*p != 0) { p++;} else {break;}}
 457 #define NEXT_CHAR_OR_RETURN(p) {if (*p != 0) { p++;} else {return;}}
 458 #define SKIP_CHARS(p,n) {int i; for (i = 0; i < (n); i++) \
 459                                             {if (*p != 0) { p++;} else \
 460                                                 {longjmp(jmpbuf, JMP_OUT_OF_RANGE);}}}
 461 #define SKIP_CHARS_OR_BREAK(p,n) {int i; for (i = 0; i < (n); i++) \
 462                                             {if (*p != 0) { p++;} else {break;}} \
 463                                             {if (i < (n)) {break;}}}
 464 
 465 /** Iterates through the null-terminated buffer (i.e., C string) and replaces all
 466  *  UTF-8 encoded character >255 with 255
 467  *
 468  *  UTF-8 encoding:
 469  *
 470  *   Range A:  0x0000 - 0x007F
 471  *                               0 | bits 0 - 7
 472  *   Range B : 0x0080 - 0x07FF  :
 473  *                               110 | bits 6 - 10
 474  *                               10  | bits 0 - 5
 475  *   Range C : 0x0800 - 0xFFFF  :
 476  *                               1110 | bits 12-15
 477  *                               10   | bits  6-11
 478  *                               10   | bits  0-5
 479  */
 480 static void RemoveNonAsciiUTF8FromBuffer(char *buf) {
 481     char* p;
 482     char* q;
 483     char c;
 484     p = q = buf;
 485     /* We are not using NEXT_CHAR() to check if *q is NULL, as q is output location
 486        and offset for q is smaller than for p. */
 487     while(*p != '\0') {
 488         c = *p;
 489         if ( (c & 0x80) == 0) {
 490             /* Range A */
 491             *q++ = *p;
 492             NEXT_CHAR(p);
 493         } else if ((c & 0xE0) == 0xC0){
 494             /* Range B */
 495             *q++ = (char)0xFF;
 496             NEXT_CHAR(p);
 497             NEXT_CHAR_OR_BREAK(p);
 498         } else {
 499             /* Range C */
 500             *q++ = (char)0xFF;
 501             NEXT_CHAR(p);
 502             SKIP_CHARS_OR_BREAK(p, 2);
 503         }
 504     }
 505     /* Null terminate string */
 506     *q = '\0';
 507 }
 508 
 509 /* --------------------------------------------------------------------- */
 510 
 511 static TCHAR* SkipWhiteSpace(TCHAR *p) {
 512     if (p != NULL) {
 513         while(iswspace(*p))
 514             NEXT_CHAR_OR_BREAK(p);
 515     }
 516     return p;
 517 }
 518 
 519 static TCHAR* SkipXMLName(TCHAR *p) {
 520     TCHAR c = *p;
 521     /* Check if start of token */
 522     if ( ('a' <= c && c <= 'z') ||
 523          ('A' <= c && c <= 'Z') ||
 524          c == '_' || c == ':') {
 525 
 526         while( ('a' <= c && c <= 'z') ||
 527                ('A' <= c && c <= 'Z') ||
 528                ('0' <= c && c <= '9') ||
 529                c == '_' || c == ':' || c == '.' || c == '-' ) {
 530             NEXT_CHAR(p);
 531             c = *p;
 532             if (c == '\0') break;
 533         }
 534     }
 535     return p;
 536 }
 537 
 538 static TCHAR* SkipXMLComment(TCHAR *p) {
 539     if (p != NULL) {
 540         if (DEPLOY_STRNCMP(p, _T("<!--"), 4) == 0) {
 541             SKIP_CHARS(p, 4);
 542             do {
 543                 if (DEPLOY_STRNCMP(p, _T("-->"), 3) == 0) {
 544                     SKIP_CHARS(p, 3);
 545                     return p;
 546                 }
 547                 NEXT_CHAR(p);
 548             } while(*p != '\0');
 549         }
 550     }
 551     return p;
 552 }
 553 
 554 static TCHAR* SkipXMLDocType(TCHAR *p) {
 555     if (p != NULL) {
 556         if (DEPLOY_STRNCMP(p, _T("<!"), 2) == 0) {
 557             SKIP_CHARS(p, 2);
 558             while (*p != '\0') {
 559                 if (*p == '>') {
 560                     NEXT_CHAR(p);
 561                     return p;
 562                 }
 563                 NEXT_CHAR(p);
 564             }
 565         }
 566     }
 567     return p;
 568 }
 569 
 570 static TCHAR* SkipXMLProlog(TCHAR *p) {
 571     if (p != NULL) {
 572         if (DEPLOY_STRNCMP(p, _T("<?"), 2) == 0) {
 573             SKIP_CHARS(p, 2);
 574             do {
 575                 if (DEPLOY_STRNCMP(p, _T("?>"), 2) == 0) {
 576                     SKIP_CHARS(p, 2);
 577                     return p;
 578                 }
 579                 NEXT_CHAR(p);
 580             } while(*p != '\0');
 581         }
 582     }
 583     return p;
 584 }
 585 
 586 /* Search for the built-in XML entities:
 587  * &amp; (&), &lt; (<), &gt; (>), &apos; ('), and &quote(")
 588  * and convert them to a real TCHARacter
 589  */
 590 static void ConvertBuiltInEntities(TCHAR* p) {
 591     TCHAR* q;
 592     q = p;
 593     /* We are not using NEXT_CHAR() to check if *q is NULL, as q is output location
 594        and offset for q is smaller than for p. */
 595     while(*p) {
 596       if (IsPCData(p)) {
 597         /* dont convert &xxx values within PData */
 598         TCHAR *end;
 599         end = SkipPCData(p);
 600         while(p < end) {
 601             *q++ = *p;
 602             NEXT_CHAR(p);
 603         }
 604       } else {
 605         if (DEPLOY_STRNCMP(p, _T("&amp;"), 5) == 0) {
 606             *q++ = '&';
 607             SKIP_CHARS(p, 5);
 608         } else if (DEPLOY_STRNCMP(p, _T("&lt;"), 4)  == 0) {
 609             *q = '<';
 610             SKIP_CHARS(p, 4);
 611         } else if (DEPLOY_STRNCMP(p, _T("&gt;"), 4)  == 0) {
 612             *q = '>';
 613             SKIP_CHARS(p, 4);
 614         } else if (DEPLOY_STRNCMP(p, _T("&apos;"), 6)  == 0) {
 615             *q = '\'';
 616             SKIP_CHARS(p, 6);
 617         } else if (DEPLOY_STRNCMP(p, _T("&quote;"), 7)  == 0) {
 618             *q = '\"';
 619             SKIP_CHARS(p, 7);
 620         } else {
 621             *q++ = *p;
 622             NEXT_CHAR(p);
 623         }
 624       }
 625     }
 626     *q = '\0';
 627 }
 628 
 629 /* ------------------------------------------------------------- */
 630 /* XML tokenizer */
 631 
 632 #define TOKEN_UNKNOWN             0
 633 #define TOKEN_BEGIN_TAG           1  /* <tag */
 634 #define TOKEN_END_TAG             2  /* </tag */
 635 #define TOKEN_CLOSE_BRACKET       3  /* >  */
 636 #define TOKEN_EMPTY_CLOSE_BRACKET 4  /* /> */
 637 #define TOKEN_PCDATA              5  /* pcdata */
 638 #define TOKEN_CDATA               6  /* cdata */
 639 #define TOKEN_EOF                 7
 640 
 641 static TCHAR* CurPos       = NULL;
 642 static TCHAR* CurTokenName        = NULL;
 643 static int   CurTokenType;
 644 static int   MaxTokenSize = -1;
 645 
 646 /* Copy token from buffer to Token variable */
 647 static void SetToken(int type, TCHAR* start, TCHAR* end) {
 648     int len = end - start;
 649     if (len > MaxTokenSize) {
 650         if (CurTokenName != NULL) free(CurTokenName);
 651         CurTokenName = (TCHAR *)malloc((len + 1) * sizeof(TCHAR));
 652         if (CurTokenName == NULL ) {
 653             return;
 654         }
 655         MaxTokenSize = len;
 656     }
 657 
 658     CurTokenType = type;
 659     DEPLOY_STRNCPY(CurTokenName, len + 1, start, len);
 660     CurTokenName[len] = '\0';
 661 }
 662 
 663 /* Skip XML comments, doctypes, and prolog tags */
 664 static TCHAR* SkipFilling(void) {
 665     TCHAR *q = CurPos;
 666 
 667     /* Skip white space and comment sections */
 668     do {
 669         q = CurPos;
 670         CurPos = SkipWhiteSpace(CurPos);
 671         CurPos = SkipXMLComment(CurPos); /* Must be called befor DocTypes */
 672         CurPos = SkipXMLDocType(CurPos); /* <! ... > directives */
 673         CurPos = SkipXMLProlog(CurPos);   /* <? ... ?> directives */
 674     } while(CurPos != q);
 675 
 676     return CurPos;
 677 }
 678 
 679 /* Parses next token and initializes the global token variables above
 680    The tokennizer automatically skips comments (<!-- comment -->) and
 681    <! ... > directives.
 682 */
 683 static void GetNextToken(void) {
 684     TCHAR *p, *q;
 685 
 686     /* Skip white space and comment sections */
 687     p = SkipFilling();
 688 
 689     if (p == NULL || *p == '\0') {
 690         CurTokenType = TOKEN_EOF;
 691         return;
 692     } else if (p[0] == '<' && p[1] == '/') {
 693         /* TOKEN_END_TAG */
 694         q = SkipXMLName(p + 2);
 695         SetToken(TOKEN_END_TAG, p + 2, q);
 696         p = q;
 697     } else  if (*p == '<') {
 698         /* TOKEN_BEGIN_TAG */
 699         q = SkipXMLName(p + 1);
 700         SetToken(TOKEN_BEGIN_TAG, p + 1, q);
 701         p = q;
 702     } else if (p[0] == '>') {
 703         CurTokenType = TOKEN_CLOSE_BRACKET;
 704         NEXT_CHAR(p);
 705     } else if (p[0] == '/' && p[1] == '>') {
 706         CurTokenType = TOKEN_EMPTY_CLOSE_BRACKET;
 707         SKIP_CHARS(p, 2);
 708     } else {
 709         /* Search for end of data */
 710         q = p + 1;
 711         while(*q && *q != '<') {
 712             if (IsPCData(q)) {
 713                 q = SkipPCData(q);
 714             } else {
 715                 NEXT_CHAR(q);
 716             }
 717         }
 718         SetToken(TOKEN_PCDATA, p, q);
 719         /* Convert all entities inside token */
 720         ConvertBuiltInEntities(CurTokenName);
 721         p = q;
 722     }
 723     /* Advance pointer to beginning of next token */
 724     CurPos = p;
 725 }
 726 
 727 static XMLNode* CreateXMLNode(int type, TCHAR* name) {
 728     XMLNode* node;
 729     node  = (XMLNode*)malloc(sizeof(XMLNode));
 730     if (node == NULL) {
 731         return NULL;
 732     }
 733     node->_type = type;
 734     node->_name = name;
 735     node->_next = NULL;
 736     node->_sub  = NULL;
 737     node->_attributes = NULL;
 738     return node;
 739 }
 740 
 741 static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value) {
 742     XMLAttribute* attr;
 743     attr = (XMLAttribute*)malloc(sizeof(XMLAttribute));
 744     if (attr == NULL) {
 745         return NULL;
 746     }
 747     attr->_name = name;
 748     attr->_value = value;
 749     attr->_next =  NULL;
 750     return attr;
 751 }
 752 
 753 XMLNode* ParseXMLDocument(TCHAR* buf) {
 754     XMLNode* root;
 755     int err_code = setjmp(jmpbuf);
 756     switch (err_code)
 757     {
 758     case JMP_NO_ERROR:
 759 #ifndef _UNICODE
 760         /* Remove UTF-8 encoding from buffer */
 761         RemoveNonAsciiUTF8FromBuffer(buf);
 762 #endif
 763 
 764         /* Get first Token */
 765         CurPos = buf;
 766         GetNextToken();
 767 
 768         /* Parse document*/
 769         root =  ParseXMLElement();
 770     break;
 771     case JMP_OUT_OF_RANGE:
 772         /* cleanup: */
 773         if (root_node != NULL) {
 774             FreeXMLDocument(root_node);
 775             root_node = NULL;
 776         }
 777         if (CurTokenName != NULL) free(CurTokenName);
 778         fprintf(stderr,"Error during parsing jnlp file...\n");
 779         exit(-1);
 780     break;
 781     default:
 782         root = NULL;
 783     break;
 784     }
 785 
 786     return root;
 787 }
 788 
 789 static XMLNode* ParseXMLElement(void) {
 790     XMLNode*  node     = NULL;
 791     XMLNode*  subnode  = NULL;
 792     XMLNode*  nextnode = NULL;
 793     XMLAttribute* attr = NULL;
 794 
 795     if (CurTokenType == TOKEN_BEGIN_TAG) {
 796 
 797         /* Create node for new element tag */
 798         node = CreateXMLNode(xmlTagType, DEPLOY_STRDUP(CurTokenName));
 799         /* We need to save root node pointer to be able to cleanup
 800            if an error happens during parsing */
 801         if(!root_node) {
 802             root_node = node;
 803         }
 804         /* Parse attributes. This section eats a all input until
 805            EOF, a > or a /> */
 806         attr = ParseXMLAttribute();
 807         while(attr != NULL) {
 808           attr->_next = node->_attributes;
 809           node->_attributes = attr;
 810           attr = ParseXMLAttribute();
 811         }
 812 
 813         /* This will eihter be a TOKEN_EOF, TOKEN_CLOSE_BRACKET, or a
 814          * TOKEN_EMPTY_CLOSE_BRACKET */
 815         GetNextToken();
 816 
 817         /* Skip until '>', '/>' or EOF. This should really be an error, */
 818         /* but we are loose */
 819 //        if(CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET ||
 820 //               CurTokenType == TOKEN_CLOSE_BRACKET ||
 821 //               CurTokenType  == TOKEN_EOF) {
 822 //            println("XML Parsing error: wrong kind of token found");
 823 //            return NULL;
 824 //        }
 825 
 826         if (CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET) {
 827             GetNextToken();
 828             /* We are done with the sublevel - fall through to continue */
 829             /* parsing tags at the same level */
 830         } else if (CurTokenType == TOKEN_CLOSE_BRACKET) {
 831             GetNextToken();
 832 
 833             /* Parse until end tag if found */
 834             node->_sub  = ParseXMLElement();
 835 
 836             if (CurTokenType == TOKEN_END_TAG) {
 837                 /* Find closing bracket '>' for end tag */
 838                 do {
 839                    GetNextToken();
 840                 } while(CurTokenType != TOKEN_EOF && CurTokenType != TOKEN_CLOSE_BRACKET);
 841                 GetNextToken();
 842             }
 843         }
 844 
 845         /* Continue parsing rest on same level */
 846         if (CurTokenType != TOKEN_EOF) {
 847                 /* Parse rest of stream at same level */
 848                 node->_next = ParseXMLElement();
 849         }
 850         return node;
 851 
 852     } else if (CurTokenType == TOKEN_PCDATA) {
 853         /* Create node for pcdata */
 854         node = CreateXMLNode(xmlPCDataType, DEPLOY_STRDUP(CurTokenName));
 855         /* We need to save root node pointer to be able to cleanup
 856            if an error happens during parsing */
 857         if(!root_node) {
 858             root_node = node;
 859         }
 860         GetNextToken();
 861         return node;
 862     }
 863 
 864     /* Something went wrong. */
 865     return NULL;
 866 }
 867 
 868 /* Parses an XML attribute. */
 869 static XMLAttribute* ParseXMLAttribute(void) {
 870     TCHAR* q = NULL;
 871     TCHAR* name = NULL;
 872     TCHAR* PrevPos = NULL;
 873 
 874     do
 875     {
 876         /* We need to check this condition to avoid endless loop
 877            in case if an error happend during parsing. */
 878         if (PrevPos == CurPos) {
 879             if (name != NULL) {
 880                 free(name);
 881                 name = NULL;
 882             }
 883 
 884             return NULL;
 885         }
 886 
 887         PrevPos = CurPos;
 888 
 889         /* Skip whitespace etc. */
 890         SkipFilling();
 891 
 892         /* Check if we are done witht this attribute section */
 893         if (CurPos[0] == '\0' ||
 894             CurPos[0] == '>' ||
 895             CurPos[0] == '/' && CurPos[1] == '>') {
 896 
 897             if (name != NULL) {
 898                 free(name);
 899                 name = NULL;
 900             }
 901 
 902             return NULL;
 903         }
 904 
 905         /* Find end of name */
 906         q = CurPos;
 907         while(*q && !iswspace(*q) && *q !='=') NEXT_CHAR(q);
 908 
 909         SetToken(TOKEN_UNKNOWN, CurPos, q);
 910         if (name) {
 911             free(name);
 912             name = NULL;
 913         }
 914         name = DEPLOY_STRDUP(CurTokenName);
 915 
 916         /* Skip any whitespace */
 917         CurPos = q;
 918         CurPos = SkipFilling();
 919 
 920         /* Next TCHARacter must be '=' for a valid attribute.
 921            If it is not, this is really an error.
 922            We ignore this, and just try to parse an attribute
 923            out of the rest of the string.
 924         */
 925     } while(*CurPos != '=');
 926 
 927     NEXT_CHAR(CurPos);
 928     CurPos = SkipWhiteSpace(CurPos);
 929     /* Parse CDATA part of attribute */
 930     if ((*CurPos == '\"') || (*CurPos == '\'')) {
 931         TCHAR quoteChar = *CurPos;
 932         q = ++CurPos;
 933         while(*q != '\0' && *q != quoteChar) NEXT_CHAR(q);
 934         SetToken(TOKEN_CDATA, CurPos, q);
 935         CurPos = q + 1;
 936     } else {
 937         q = CurPos;
 938         while(*q != '\0' && !iswspace(*q)) NEXT_CHAR(q);
 939         SetToken(TOKEN_CDATA, CurPos, q);
 940         CurPos = q;
 941     }
 942 
 943     //Note: no need to free name and CurTokenName duplicate; they're assigned
 944     // to an XMLAttribute structure in CreateXMLAttribute
 945 
 946     return CreateXMLAttribute(name, DEPLOY_STRDUP(CurTokenName));
 947 }
 948 
 949 void FreeXMLDocument(XMLNode* root) {
 950   if (root == NULL) return;
 951   FreeXMLDocument(root->_sub);
 952   FreeXMLDocument(root->_next);
 953   FreeXMLAttribute(root->_attributes);
 954   free(root->_name);
 955   free(root);
 956 }
 957 
 958 static void FreeXMLAttribute(XMLAttribute* attr) {
 959   if (attr == NULL) return;
 960   free(attr->_name);
 961   free(attr->_value);
 962   FreeXMLAttribute(attr->_next);
 963   free(attr);
 964 }
 965 
 966 /* Find element at current level with a given name */
 967 XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name) {
 968   if (root == NULL) return NULL;
 969 
 970   if (root->_type == xmlTagType && DEPLOY_STRCMP(root->_name, name) == 0) {
 971     return root;
 972   }
 973 
 974   return FindXMLChild(root->_next, name);
 975 }
 976 
 977 /* Search for an attribute with the given name and returns the contents. Returns NULL if
 978  * attribute is not found
 979  */
 980 TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name) {
 981   if (attr == NULL) return NULL;
 982   if (DEPLOY_STRCMP(attr->_name, name) == 0) return attr->_value;
 983   return FindXMLAttribute(attr->_next, name);
 984 }
 985 
 986 
 987 void PrintXMLDocument(XMLNode* node, int indt) {
 988   if (node == NULL) return;
 989 
 990   if (node->_type == xmlTagType) {
 991      DEPLOY_PRINTF(_T("\n"));
 992      indent(indt);
 993      DEPLOY_PRINTF(_T("<%s"), node->_name);
 994      PrintXMLAttributes(node->_attributes);
 995      if (node->_sub == NULL) {
 996        DEPLOY_PRINTF(_T("/>\n"));
 997      } else {
 998        DEPLOY_PRINTF(_T(">"));
 999        PrintXMLDocument(node->_sub, indt + 1);
1000        indent(indt);
1001        DEPLOY_PRINTF(_T("</%s>"), node->_name);
1002      }
1003   } else {
1004     DEPLOY_PRINTF(_T("%s"), node->_name);
1005   }
1006   PrintXMLDocument(node->_next, indt);
1007 }
1008 
1009 static void PrintXMLAttributes(XMLAttribute* attr) {
1010   if (attr == NULL) return;
1011 
1012   DEPLOY_PRINTF(_T(" %s=\"%s\""), attr->_name, attr->_value);
1013   PrintXMLAttributes(attr->_next);
1014 }
1015 
1016 static void indent(int indt) {
1017     int i;
1018     for(i = 0; i < indt; i++) {
1019         DEPLOY_PRINTF(_T("  "));
1020     }
1021 }
1022 
1023 const TCHAR *CDStart = _T("<![CDATA[");
1024 const TCHAR *CDEnd = _T("]]>");
1025 
1026 
1027 static TCHAR* SkipPCData(TCHAR *p) {
1028     TCHAR *end = DEPLOY_STRSTR(p, CDEnd);
1029     if (end != NULL) {
1030         return end+sizeof(CDEnd);
1031     }
1032     return (++p);
1033 }
1034 
1035 static int IsPCData(TCHAR *p) {
1036     return (DEPLOY_STRNCMP(CDStart, p, sizeof(CDStart)) == 0);
1037 }
1038 
1039 //--------------------------------------------------------------------------------------------------
1040 
1041 LinuxJavaUserPreferences::LinuxJavaUserPreferences(void) : JavaUserPreferences() {
1042 }
1043 
1044 LinuxJavaUserPreferences::~LinuxJavaUserPreferences(void) {
1045 }
1046 
1047 TString LinuxJavaUserPreferences::GetUserPrefFileName(TString Appid) {
1048     TString result;
1049     struct passwd *pw = getpwuid(getuid());
1050     TString homedir = pw->pw_dir;
1051     TString userOverrideFileName = FilePath::IncludeTrailingSeparater(homedir) +
1052         FilePath::IncludeTrailingSeparater(_T(".java/.userPrefs")) +
1053         FilePath::IncludeTrailingSeparater(Appid) +
1054         _T("JVMUserOptions/prefs.xml");
1055 
1056     if (FilePath::FileExists(userOverrideFileName) == true) {
1057         result = userOverrideFileName;
1058     }
1059 
1060     return result;
1061 }
1062 
1063 OrderedMap<TString, TString> ReadNode(XMLNode* node) {
1064     OrderedMap<TString, TString> result;
1065     XMLNode* keyNode = FindXMLChild(node->_sub, _T("entry"));
1066 
1067     while (keyNode != NULL) {
1068         TString key = FindXMLAttribute(keyNode->_attributes, _T("key"));
1069         TString value = FindXMLAttribute(keyNode->_attributes, _T("value"));
1070         keyNode = keyNode->_next;
1071 
1072         if (key.empty() == false) {
1073             result.Append(key, value);
1074         }
1075     }
1076 
1077     return result;
1078 }
1079 
1080 OrderedMap<TString, TString> GetJvmUserArgs(TString filename) {
1081     OrderedMap<TString, TString> result;
1082 
1083     if (FilePath::FileExists(filename) == true) {
1084         //scan file for the key
1085         FILE* fp = fopen(PlatformString(filename).toPlatformString(), "r");
1086 
1087         if (fp != NULL) {
1088             fseek(fp, 0, SEEK_END);
1089             long fsize = ftell(fp);
1090             rewind(fp);
1091             DynamicBuffer<char> buffer(fsize + 1);
1092             fread(buffer.GetData(), fsize, 1, fp);
1093             fclose(fp);
1094             buffer[fsize] = 0;
1095 
1096             XMLNode* node = NULL;
1097             XMLNode* doc = ParseXMLDocument(buffer.GetData());
1098 
1099             if (doc != NULL) {
1100                 node = FindXMLChild(doc, _T("map"));
1101 
1102                 if (node != NULL) {
1103                     result = ReadNode(node);
1104                 }
1105             }
1106         }
1107     }
1108 
1109     return result;
1110 }
1111 
1112 bool LinuxJavaUserPreferences::Load(TString Appid) {
1113     bool result = false;
1114     TString filename = GetUserPrefFileName(Appid);
1115 
1116     if (FilePath::FileExists(filename) == true) {
1117         FMap = GetJvmUserArgs(filename);
1118         result = true;
1119     }
1120 
1121     return result;
1122 }
1123 
1124 //--------------------------------------------------------------------------------------------------
1125 
1126 #endif // LINUX