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