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