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