NORM-mirror/src/unix/unixPostProcess.cpp

415 lines
12 KiB
C++
Raw Blame History

/*********************************************************************
*
* AUTHORIZATION TO USE AND DISTRIBUTE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that:
*
* (1) source code distributions retain this paragraph in its entirety,
*
* (2) distributions including binary code include this paragraph in
* its entirety in the documentation or other materials provided
* with the distribution, and
*
* (3) all advertising materials mentioning features or use of this
* software display the following acknowledgment:
*
* "This product includes software written and developed
* by Brian Adamson and Joe Macker of the Naval Research
* Laboratory (NRL)."
*
* The name of NRL, the name(s) of NRL employee(s), or any entity
* of the United States Government may not be used to endorse or
* promote products derived from this software, nor does the
* inclusion of the NRL written and developed software directly or
* indirectly suggest NRL or United States Government endorsement
* of this product.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
********************************************************************/
// This file contains code for various UNIX MDP post processor options
/*******************************************************************
* Netscape[tm] post processor routines
*/
// Some portions of this code
// Copyright <20> 1996 Netscape Communications Corporation,
// all rights reserved.
#include "normPostProcess.h"
#include "protoDebug.h"
#include <errno.h>
#include <string.h> // for strerror()
#include <signal.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h> // for exit()
#include <stdio.h>
#ifndef sighandler_t
#ifndef sig_t
typedef void (*sighandler_t)(int);
#else
typedef sig_t sighandler_t;
#endif // if/else !sig_t
#endif // !sighandler_t
#ifdef NETSCAPE_SUPPORT
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmu/WinUtil.h> /* for XmuClientWindow() */
/* vroot.h is a header file which lets a client get along with `virtual root'
window managers like swm, tvtwm, olvwm, etc. If you don't have this header
file, you can find it at "http://home.netscape.com/newsref/std/vroot.h".
If you don't care about supporting virtual root window managers, you can
comment this line out.
*/
#include "vroot.h"
#define MOZILLA_VERSION_PROP "_MOZILLA_VERSION"
static Atom XA_MOZILLA_VERSION = 0;
bool CheckForNetscape(const char* cmd);
bool NetscapeIsRunning(Window *result);
bool NetscapeCheckWindow(Window window);
#endif // NETSCAPE_SUPPORT
class UnixPostProcessor : public NormPostProcessor
{
public:
~UnixPostProcessor();
bool IsActive() {return (0 != process_id);}
bool ProcessFile(const char* path);
void Kill();
void OnSIGCHLD();
private:
friend class NormPostProcessor;
UnixPostProcessor();
int process_id;
#ifdef NETSCAPE_SUPPORT
Window window_id;
#endif // NETSCAPE_SUPPORT
}; // end class UnixPostProcessor
UnixPostProcessor::UnixPostProcessor()
: process_id(0)
#ifdef NETSCAPE_SUPPORT
,window_id(0)
#endif // NETSCAPE_SUPPORT
{
}
NormPostProcessor* NormPostProcessor::Create()
{
return static_cast<NormPostProcessor*>(new UnixPostProcessor);
} // end NormPostProcessor::Create()
UnixPostProcessor::~UnixPostProcessor()
{
if (IsActive()) Kill();
}
bool UnixPostProcessor::ProcessFile(const char* path)
{
const char** argv = (const char**)process_argv;
int argc = process_argc;
#ifdef NETSCAPE_SUPPORT
// Special support for finding a Netscape window and
// use the openURL remote command to display received file
#define USE_ACTIVE_WINDOW 0xffffffff
const char* myArgs[32];
char wid_text[32], url_text[PATH_MAX+64];
if (CheckForNetscape(argv[0]))
{
int i = 0;
myArgs[i++] = process_argv[0];
Window w = 0;
// (TBD) We could put in a hack to look for the old
// window if we made "w" static when "window_id" = 0xffffffff
// This would help us capture the proper window for file display
// It's still a hack but it might work a little better without
// too much trouble
if (NetscapeIsRunning(&w))
{
// Use -remote command to display file in current
// or new window
if (window_id)
{
if (NetscapeCheckWindow(window_id))
{
// Use the same window as last time
myArgs[i++] = "-id";
sprintf(wid_text, "0x%lx", window_id);
myArgs[i++] = wid_text;
myArgs[i++] = "-noraise";
myArgs[i++] = "-remote";
sprintf(url_text, "openURL(file://%s)", path);
myArgs[i++] = url_text;
}
else if (USE_ACTIVE_WINDOW == window_id)
{
// Capture the open window we found
myArgs[i++] = "-id";
sprintf(wid_text, "0x%lx", w);
myArgs[i++] = wid_text;
window_id = w;
myArgs[i++] = "-noraise";
myArgs[i++] = "-remote";
sprintf(url_text, "openURL(file://%s)", path);
myArgs[i++] = url_text;
}
else // user must have closed old window so open another
{
window_id = USE_ACTIVE_WINDOW;
//myArgs[i++] = "-raise";
myArgs[i++] = "-remote";
sprintf(url_text, "openURL(file://%s,new-window)", path);
myArgs[i++] = url_text;
}
}
else
{
// We're starting fresh, open a new window
window_id = USE_ACTIVE_WINDOW;
//myArgs[i++] = "-raise";
myArgs[i++] = "-remote";
sprintf(url_text, "openURL(file://%s,new-window)", path);
myArgs[i++] = url_text;
}
argv = myArgs;
}
else
{
if (IsActive()) Kill();
window_id = USE_ACTIVE_WINDOW;
myArgs[i++] = path;
} // end if/else (NetscapeIsRunning())
myArgs[i] = NULL;
argc = i - 1;
argv = myArgs;
}
else
#endif // NETSCAPE_SUPPORT
{
if (IsActive()) Kill();
argv[argc] = path;
}
// 1) temporarily disable signal handling
sighandler_t sigtermHandler = signal(SIGTERM, SIG_DFL);
sighandler_t sigintHandler = signal(SIGINT, SIG_DFL);
sighandler_t sigchldHandler = signal(SIGCHLD, SIG_DFL);
switch((process_id = fork()))
{
case -1: // error
DMSG(0, "UnixPostProcessor::ProcessFile fork() error: %s\n", strerror(errno));
process_id = 0;
process_argv[process_argc] = NULL;
return false;
case 0: // child
if (execvp((char*)argv[0], (char**)argv) < 0)
{
DMSG(0, "UnixPostProcessor::ProcessFile execvp() error: %s\n", strerror(errno));
exit(-1);
}
break;
default: // parent
process_argv[process_argc] = NULL;
// Restore signal handlers for parent
signal(SIGTERM, sigtermHandler);
signal(SIGINT, sigintHandler);
// The use of "waitpid()" here is a work-around
// for an IRIX SIGCHLD issue
int status;
while (waitpid(-1, &status, WNOHANG) > 0);
signal(SIGCHLD, sigchldHandler);
break;
}
return true;
} // end UnixPostProcessor::ProcessFile()
void UnixPostProcessor::Kill()
{
if (!IsActive()) return;
int count = 0;
while((kill(process_id, SIGTERM) != 0) && count < 10)
{
if (errno == ESRCH) break;
count++;
DMSG(0, "UnixPostProcessor::Kill kill() error: %s\n", strerror(errno));
}
count = 0;
int status;
while((waitpid(process_id, &status, 0) != process_id) && count < 10)
{
if (errno == ECHILD) break;
count++;
DMSG(0, "UnixPostProcessor::Kill waitpid() error: %s\n", strerror(errno));
}
process_id = 0;
} // end UnixPostProcessor::Kill()
void UnixPostProcessor::OnSIGCHLD()
{
// See if post processor exited itself
int status;
if (wait(&status) == process_id) process_id = 0;
} // end UnixPostProcessor::HandleSIGCHLD()
#ifdef NETSCAPE_SUPPORT
bool CheckForNetscape(const char* cmd)
{
// See if it's Netscape post processing
// for which we provide special support
char txt[8];
const char *ptr = strrchr(cmd, PROTO_PATH_DELIMITER);
if (ptr)
ptr++;
else
ptr = cmd;
strncpy(txt, ptr, 8);
for (int i=0; i<8; i++) txt[i] = toupper(txt[i]);
if (!strncmp(txt, "NETSCAPE", 8))
return true;
else
return false;
} // end CheckForNetscape()
bool NetscapeIsRunning(Window *result)
{
int i;
Window root2, parent, *kids;
unsigned int nkids;
*result = 0;
Display *dpy = XOpenDisplay(NULL);
if (!dpy)
{
fprintf(stderr, "mdp: XOpenDisplay() error\n");
return false;
}
if (!XA_MOZILLA_VERSION)
XA_MOZILLA_VERSION = XInternAtom(dpy, MOZILLA_VERSION_PROP, False);
Window root = RootWindowOfScreen(DefaultScreenOfDisplay(dpy));
if (!XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
{
fprintf(stderr, "mdp: XQueryTree failed on display %s\n", DisplayString (dpy));
XCloseDisplay(dpy);
return false;
}
// Note: root != root2 is possible with virtual root WMs.
if (!(kids && nkids))
{
fprintf(stderr, "mdp: root window has no children on display %s\n",
DisplayString (dpy));
XCloseDisplay(dpy);
return false;
}
for (i = nkids-1; i >= 0; i--)
{
Atom type;
int format;
unsigned long nitems, bytesafter;
unsigned char *version = 0;
Window w = XmuClientWindow (dpy, kids[i]);
int status = XGetWindowProperty (dpy, w, XA_MOZILLA_VERSION,
0, (65536 / sizeof (long)),
False, XA_STRING,
&type, &format, &nitems, &bytesafter,
&version);
if (! version) continue;
XFree (version);
if (status == Success && type != None)
{
*result = w;
break;
}
}
XCloseDisplay(dpy);
return (*result ? true : false);
} // end NetscapeIsRunning()
bool NetscapeCheckWindow(Window window)
{
int i;
Window root2, parent, *kids;
unsigned int nkids;
Display *dpy = XOpenDisplay(NULL);
if (!dpy)
{
fprintf(stderr, "mdp: XOpenDisplay() error\n");
return false;
}
if (!XA_MOZILLA_VERSION)
XA_MOZILLA_VERSION = XInternAtom(dpy, MOZILLA_VERSION_PROP, False);
Window root = RootWindowOfScreen(DefaultScreenOfDisplay (dpy));
if (!XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
{
fprintf(stderr, "mdp: XQueryTree failed on display %s\n", DisplayString (dpy));
XCloseDisplay(dpy);
return false;
}
// Note: root != root2 is possible with virtual root WMs.
if (!(kids && nkids)) return false;
for (i = nkids-1; i >= 0; i--)
{
Atom type;
int format;
unsigned long nitems, bytesafter;
unsigned char *version = 0;
Window w = XmuClientWindow (dpy, kids[i]);
int status = XGetWindowProperty (dpy, w, XA_MOZILLA_VERSION,
0, (65536 / sizeof (long)),
False, XA_STRING,
&type, &format, &nitems, &bytesafter,
&version);
if (!version) continue;
XFree (version);
if (status == Success && type != None)
{
if (w == window)
{
XCloseDisplay(dpy);
return true;
}
else
{
continue;
}
}
}
XCloseDisplay(dpy);
return false;
} // end NetscapeCheckWindow()
#endif // NETSCAPE_SUPPORT