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