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