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