summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEgbert Eich <eich@suse.de>2004-04-23 18:43:47 +0000
committerEgbert Eich <eich@suse.de>2004-04-23 18:43:47 +0000
commit715f625d68e0403b7024b7dcc61bf6bcf73cc0ac (patch)
tree1f205ab05fe4237d422192701f0e71ad591c23a8
parente9392f87e287d40ec19427c2ab3f5a408f27cac8 (diff)
-rw-r--r--include/X11/XprintUtil/xprintutil.h219
-rw-r--r--src/xprintutil.c1678
-rw-r--r--src/xprintutil_printtofile.c498
3 files changed, 2395 insertions, 0 deletions
diff --git a/include/X11/XprintUtil/xprintutil.h b/include/X11/XprintUtil/xprintutil.h
new file mode 100644
index 0000000..60dad3b
--- /dev/null
+++ b/include/X11/XprintUtil/xprintutil.h
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+#ifndef XPRINTUTIL_H
+#define XPRINTUTIL_H 1
+/******************************************************************************
+ ******************************************************************************
+ **
+ ** (c) Copyright 2001-2004 Roland Mainz <roland.mainz@nrubsig.org>
+ **
+ ** Permission is hereby granted, free of charge, to any person obtaining a copy
+ ** of this software and associated documentation files (the "Software"), to deal
+ ** in the Software without restriction, including without limitation the rights
+ ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ ** copies of the Software, and to permit persons to whom the Software is
+ ** furnished to do so, subject to the following conditions:
+ **
+ ** The above copyright notice and this permission notice shall be included in
+ ** all copies or substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ ** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ ** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ **
+ ** Except as contained in this notice, the names of the copyright holders shall
+ ** not be used in advertising or otherwise to promote the sale, use or other
+ ** dealings in this Software without prior written authorization from said
+ ** copyright holders.
+ **
+ ******************************************************************************
+ *****************************************************************************/
+
+
+/* Force ANSI C prototypes from X11 headers */
+#ifndef FUNCPROTO
+#define FUNCPROTO 15
+#endif /* !FUNCPROTO */
+
+#include <X11/Xlibint.h>
+#include <X11/extensions/Print.h>
+#include <X11/Intrinsic.h>
+
+/* I don't know how to make this "better" yet... ;-( */
+#ifdef USE_MOZILLA_TYPES
+#include <prtypes.h>
+#include <prmem.h>
+#include <prthread.h>
+#define XPU_USE_NSPR 1
+/*
+ * Disabled for now - Threaded codepath does not work properly always.
+ * See bug 134570 ("Print-to-file not working with threaded XprintUtil")
+ * #define XPU_USE_THREADS 1
+ */
+#endif /* USE_MOZILLA_TYPES */
+
+#ifdef DEBUG
+/* trace function calls */
+#define XPU_TRACE(EX) (puts(#EX),EX)
+/* trace function calls in child */
+#define XPU_TRACE_CHILD(EX) (puts("child: " #EX),EX)
+/* execute function EX only in debug mode */
+#define XPU_DEBUG_ONLY(EX) (EX)
+#else
+#define XPU_TRACE(EX) (EX)
+#define XPU_TRACE_CHILD(EX) (EX)
+#define XPU_DEBUG_ONLY(EX)
+#endif /* DEBUG */
+
+/* debug/logging: replace NULLptrs with "<NULL>" string */
+#define XPU_NULLXSTR(s) (((s)!=NULL)?(s):("<NULL>"))
+
+/*
+ * Struct for XpuGetMediumSourceSizeList(), XpuFreeMediumSourceSizeList(),
+ * XpuSetDocMediumSourceSize(), XpuSetPageMediumSourceSize(),
+ * XpuFindMediumSourceSizeBy*()
+ */
+typedef struct {
+ const char *tray_name;
+ const char *medium_name;
+ int mbool;
+ float ma1;
+ float ma2;
+ float ma3;
+ float ma4;
+} XpuMediumSourceSizeRec, *XpuMediumSourceSizeList;
+
+/*
+ * Struct for XpuGetResolutionList(), XpuFreeResolutionList(),
+ * XpuGetResolution(), XpuSetPageResolution(), XpuSetDocResolution(),
+ * XpuFindResolution()
+ */
+typedef struct {
+ long dpi;
+ /* ToDo: Support for Xdpi != Ydpi */
+} XpuResolutionRec, *XpuResolutionList;
+
+/*
+ * Struct for XpuGetOrientationList(), XpuFreeOrientationList(),
+ * XpuFindOrientationBy*(), XpuSetPageResolution(),
+ * XpuSetDocOrientation()
+ */
+typedef struct {
+ const char *orientation;
+} XpuOrientationRec, *XpuOrientationList;
+
+/*
+ * Struct for XpuGetPlexList(), XpuFreePlexList(), XpuFindPlexBy*(),
+ * XpuSetDocPlex(), XpuSetPagePlex()
+ */
+typedef struct {
+ const char *plex;
+} XpuPlexRec, *XpuPlexList;
+
+
+/* XPUATTRIBUTESUPPORTED_*:
+ * Flags which indicate whether it is allowed to set/change a specific attribute
+ */
+typedef long XpuSupportedFlags;
+/* Job attributes */
+#define XPUATTRIBUTESUPPORTED_JOB_NAME (1L<<0)
+#define XPUATTRIBUTESUPPORTED_JOB_OWNER (1L<<1)
+#define XPUATTRIBUTESUPPORTED_NOTIFICATION_PROFILE (1L<<2)
+/* Document/Page attributes */
+#define XPUATTRIBUTESUPPORTED_COPY_COUNT (1L<<3)
+#define XPUATTRIBUTESUPPORTED_DOCUMENT_FORMAT (1L<<4)
+#define XPUATTRIBUTESUPPORTED_CONTENT_ORIENTATION (1L<<5)
+#define XPUATTRIBUTESUPPORTED_DEFAULT_PRINTER_RESOLUTION (1L<<6)
+#define XPUATTRIBUTESUPPORTED_DEFAULT_INPUT_TRAY (1L<<7)
+#define XPUATTRIBUTESUPPORTED_DEFAULT_MEDIUM (1L<<8)
+#define XPUATTRIBUTESUPPORTED_PLEX (1L<<9)
+
+/* prototypes */
+_XFUNCPROTOBEGIN
+
+int XpuCheckExtension( Display *pdpy );
+
+/* Create/destroy connection to printer */
+int XpuGetPrinter( const char *printername, Display **pdpyptr, XPContext *pcontextptr );
+void XpuClosePrinterDisplay(Display *pdpy, XPContext pcontext);
+
+/* Misc. functions */
+void XpuSetOneAttribute( Display *pdpy, XPContext pcontext,
+ XPAttributes type, const char *attribute_name, const char *value, XPAttrReplacement replacement_rule );
+void XpuSetOneLongAttribute( Display *pdpy, XPContext pcontext,
+ XPAttributes type, const char *attribute_name, long value, XPAttrReplacement replacement_rule );
+int XpuCheckSupported( Display *pdpy, XPContext pcontext, XPAttributes type, const char *attribute_name, const char *query );
+int XpuSetJobTitle( Display *pdpy, XPContext pcontext, const char *title );
+int XpuGetOneLongAttribute( Display *pdpy, XPContext pcontext, XPAttributes type, const char *attribute_name, long *result );
+#ifdef DEBUG
+void dumpXpAttributes( Display *pdpy, XPContext pcontext );
+#endif /* DEBUG */
+void XpuWaitForPrintNotify( Display *pdpy, int xp_event_base, int detail );
+
+/* Get list of printers */
+XPPrinterList XpuGetPrinterList( const char *printer, int *res_list_count );
+void XpuFreePrinterList( XPPrinterList list );
+
+/* Set number of document copies */
+int XpuSetDocumentCopies( Display *pdpy, XPContext pcontext, long num_copies );
+
+/* Get/Set/Query supported mediums (paper sizes) */
+XpuMediumSourceSizeList XpuGetMediumSourceSizeList( Display *pdpy, XPContext pcontext, int *numEntriesPtr );
+void XpuFreeMediumSourceSizeList( XpuMediumSourceSizeList list );
+int XpuSetDocMediumSourceSize( Display *pdpy, XPContext pcontext, XpuMediumSourceSizeRec *medium_spec );
+int XpuSetPageMediumSourceSize( Display *pdpy, XPContext pcontext, XpuMediumSourceSizeRec *medium_spec );
+XpuMediumSourceSizeRec *
+XpuFindMediumSourceSizeBySize( XpuMediumSourceSizeList mlist, int mlist_count,
+ float page_width_mm, float page_height_mm, float tolerance );
+XpuMediumSourceSizeRec *
+XpuFindMediumSourceSizeByBounds( XpuMediumSourceSizeList mlist, int mlist_count,
+ float m1, float m2, float m3, float m4, float tolerance );
+XpuMediumSourceSizeRec *
+XpuFindMediumSourceSizeByName( XpuMediumSourceSizeList mlist, int mlist_count,
+ const char *tray_name, const char *medium_name );
+
+/* Get/Set resolution */
+XpuResolutionList XpuGetResolutionList( Display *pdpy, XPContext pcontext, int *numEntriesPtr );
+void XpuFreeResolutionList( XpuResolutionList list );
+Bool XpuGetResolution( Display *pdpy, XPContext pcontext, long *dpi );
+Bool XpuSetPageResolution( Display *pdpy, XPContext pcontext, XpuResolutionRec * );
+Bool XpuSetDocResolution( Display *pdpy, XPContext pcontext, XpuResolutionRec * );
+XpuResolutionRec *XpuFindResolution( XpuResolutionList list, int list_count, long min_dpi, long max_dpi );
+
+/* Get/Set orientation */
+XpuOrientationList XpuGetOrientationList( Display *pdpy, XPContext pcontext, int *numEntriesPtr );
+void XpuFreeOrientationList( XpuOrientationList list );
+XpuOrientationRec *
+XpuFindOrientationByName( XpuOrientationList list, int list_count, const char *orientation );
+int XpuSetDocOrientation( Display *pdpy, XPContext pcontext, XpuOrientationRec *rec );
+int XpuSetPageOrientation( Display *pdpy, XPContext pcontext, XpuOrientationRec *rec );
+
+/* Get/set plex modes */
+XpuPlexList XpuGetPlexList( Display *pdpy, XPContext pcontext, int *numEntriesPtr );
+void XpuFreePlexList( XpuPlexList list );
+XpuPlexRec *XpuFindPlexByName( XpuPlexList list, int list_count, const char *plex );
+int XpuSetDocPlex( Display *pdpy, XPContext pcontext, XpuPlexRec *rec );
+int XpuSetPagePlex( Display *pdpy, XPContext pcontext, XpuPlexRec *rec );
+
+/* Start job to printer (spooler) or file */
+void XpuStartJobToSpooler(Display *pdpy);
+void *XpuStartJobToFile( Display *pdpy, XPContext pcontext, const char *filename );
+XPGetDocStatus XpuWaitForPrintFileChild( void *handle );
+
+/* Get flags which indicate whether it is allowed to set/change a specific attribute */
+XpuSupportedFlags XpuGetSupportedJobAttributes(Display *pdpy, XPContext pcontext);
+XpuSupportedFlags XpuGetSupportedDocAttributes(Display *pdpy, XPContext pcontext);
+XpuSupportedFlags XpuGetSupportedPageAttributes(Display *pdpy, XPContext pcontext);
+
+_XFUNCPROTOEND
+
+#define XpuGetJobAttributes( pdpy, pcontext ) XpGetAttributes( (pdpy), (pcontext), XPJobAttr )
+#define XpuGetDocAttributes( pdpy, pcontext ) XpGetAttributes( (pdpy), (pcontext), XPDocAttr )
+#define XpuGetPageAttributes( pdpy, pcontext ) XpGetAttributes( (pdpy), (pcontext), XPPageAttr )
+#define XpuGetPrinterAttributes( pdpy, pcontext ) XpGetAttributes( (pdpy), (pcontext), XPPrinterAttr )
+#define XpuGetServerAttributes( pdpy, pcontext ) XpGetAttributes( (pdpy), (pcontext), XPServerAttr )
+
+#endif /* !XPRINTUTIL_H */
+/* EOF. */
diff --git a/src/xprintutil.c b/src/xprintutil.c
new file mode 100644
index 0000000..43bff69
--- /dev/null
+++ b/src/xprintutil.c
@@ -0,0 +1,1678 @@
+/******************************************************************************
+ ******************************************************************************
+ **
+ ** (c) Copyright 2001-2004 Roland Mainz <roland.mainz@nrubsig.org>
+ **
+ ** Permission is hereby granted, free of charge, to any person obtaining a copy
+ ** of this software and associated documentation files (the "Software"), to deal
+ ** in the Software without restriction, including without limitation the rights
+ ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ ** copies of the Software, and to permit persons to whom the Software is
+ ** furnished to do so, subject to the following conditions:
+ **
+ ** The above copyright notice and this permission notice shall be included in
+ ** all copies or substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ ** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ ** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ **
+ ** Except as contained in this notice, the names of the copyright holders shall
+ ** not be used in advertising or otherwise to promote the sale, use or other
+ ** dealings in this Software without prior written authorization from said
+ ** copyright holders.
+ **
+ ******************************************************************************
+ *****************************************************************************/
+
+#include "xprintutil.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <locale.h>
+
+#ifdef XPU_USE_NSPR
+#include "plstr.h"
+#undef strtok_r
+#define strtok_r(s1, s2, x) PL_strtok_r((s1), (s2), (x))
+#endif /* USE_MOZILLA_TYPES */
+
+/* List of tokens which can be used to seperate entries in the
+ * $XPSERVERLIST env var */
+static const char XPServerListSeparators[] = " \t\v\n\r\f";
+
+/* conformace only; X11 API does (currrently) not make use of |const|.
+ * If Xlib API gets fixed these macros can be turned into empty
+ * placeholders... (|#define MAKE_STRING_WRITABLE(x)|) :-)
+ */
+#define MAKE_STRING_WRITABLE(str) (((str)?((str) = strdup(str)):0))
+#define FREE_WRITABLE_STRING(str) free((void *)(str))
+#define STRING_AS_WRITABLE(str) ((char *)(str))
+
+/* Local prototypes */
+static const char *XpuGetDefaultXpPrintername(void);
+static const char *XpuGetXpServerList( void );
+static const char *XpuEnumerateXpAttributeValue( const char *value, void **vcptr );
+static const char *XpuGetCurrentAttributeGroup( void **vcptr );
+static void XpuDisposeEnumerateXpAttributeValue( void **vc );
+static Bool XpuEnumerateMediumSourceSizes( Display *pdpy, XPContext pcontext,
+ const char **tray_name,
+ const char **medium_name, int *mbool,
+ float *ma1, float *ma2, float *ma3, float *ma4,
+ void **vcptr );
+static void XpuDisposeEnumerateMediumSourceSizes( void **vc );
+
+/*
+** XprintUtil functions start with Xpu
+**
+*/
+
+int XpuCheckExtension( Display *pdpy )
+{
+ char *display = XDisplayString(pdpy);
+ short major = 0,
+ minor = 0;
+
+ if( XpQueryVersion(pdpy, &major, &minor) != 0 )
+ {
+ XPU_DEBUG_ONLY(printf("XpuCheckExtension: XpQueryVersion '%s' %d %d\n", XPU_NULLXSTR(display), (int)major, (int)minor));
+ return(1);
+ }
+ else
+ {
+ XPU_DEBUG_ONLY(printf("XpuCheckExtension: XpQueryVersion '%s' returned 0(=Xprint not supported)\n", XPU_NULLXSTR(display)));
+ }
+
+ return(0);
+}
+
+/* Get the default printer name from the XPRINTER env var.
+ * If XPRINTER env var is not present looks for PDPRINTER, LPDEST, and
+ * PRINTER (in that order)
+ * See CDE's DtPrintSetupBox(3) manual page, too...
+ */
+static
+const char *XpuGetDefaultXpPrintername(void)
+{
+ const char *s;
+ /* BUG/TODO: XpPrinter resource needs to be sourced, too... */
+ s = getenv("XPRINTER");
+ if( !s )
+ {
+ s = getenv("PDPRINTER");
+ if( !s )
+ {
+ s = getenv("LPDEST");
+ if( !s )
+ {
+ s = getenv("PRINTER");
+ }
+ }
+ }
+ return s;
+}
+
+static
+const char *XpuGetXpServerList( void )
+{
+ const char *s;
+ /* BUG/TODO: XpServerList resource needs to be sourced first, then append
+ * contents of XPSERVERLIST, then remove duplicates...
+ */
+ s = getenv("XPSERVERLIST");
+ if( s == NULL )
+ s = "";
+
+ return(s);
+}
+
+
+static
+int XpuGetPrinter2( char *printer, char *display, Display **pdpyptr, XPContext *pcontextptr )
+{
+ Display *pdpy;
+ XPContext pcontext;
+
+ XPU_DEBUG_ONLY(printf("XpuGetPrinter2: probing display '%s' for '%s'\n", XPU_NULLXSTR(display), XPU_NULLXSTR(printer)));
+
+ if( (pdpy = XOpenDisplay(display)) != NULL )
+ {
+ if( XpuCheckExtension(pdpy) )
+ {
+ XPPrinterList list;
+ int list_count;
+
+ /* get list of available printers... */
+ list = XpGetPrinterList(pdpy, printer, &list_count);
+ if( list != NULL ) XpFreePrinterList(list);
+
+ /* ...and check if printer exists... */
+ if( (list != NULL) && (list_count > 0) )
+ {
+ if( (pcontext = XpCreateContext(pdpy, printer)) != None )
+ {
+ *pdpyptr = pdpy;
+ *pcontextptr = pcontext;
+ return(1);
+ }
+
+ XPU_DEBUG_ONLY(printf("XpuGetPrinter2: could not create print context for '%s'\n", XPU_NULLXSTR(printer)));
+ }
+ }
+ else
+ {
+ XPU_DEBUG_ONLY(printf("display '%s' does not support the Xprint extension\n", XPU_NULLXSTR(display)));
+ }
+
+ XCloseDisplay(pdpy);
+ return(0);
+ }
+ else
+ {
+ XPU_DEBUG_ONLY(printf("could not open display '%s'\n", XPU_NULLXSTR(display)));
+ return(0);
+ }
+}
+
+
+/* acceps "printer" or "printer@display" */
+int XpuGetPrinter( const char *arg_printername, Display **pdpyptr, XPContext *pcontextptr )
+{
+ Display *pdpy;
+ XPContext pcontext;
+ char *printername;
+ char *s;
+ char *tok_lasts;
+
+ *pdpyptr = NULL;
+ *pcontextptr = None;
+
+ XPU_DEBUG_ONLY(printf("XpuGetPrinter: looking for '%s'\n", XPU_NULLXSTR(arg_printername)));
+
+ /* strtok_r will modify string - duplicate it first... */
+ printername = strdup(arg_printername);
+ if( printername == NULL )
+ return(0);
+
+ if( (s = strtok_r(printername, "@", &tok_lasts)) != NULL )
+ {
+ char *name = s;
+ char *display = strtok_r(NULL, "@", &tok_lasts);
+
+ /* if we have a display - open it and grab printer */
+ if( display != NULL )
+ {
+ if( XpuGetPrinter2(name, display, pdpyptr, pcontextptr) )
+ {
+ free(printername);
+ return(1);
+ }
+ }
+ /* if we did not get a display, travel througth all displays */
+ else
+ {
+ char *sl = strdup(XpuGetXpServerList());
+
+ if( sl != NULL )
+ {
+ for( display = strtok_r(sl, XPServerListSeparators, &tok_lasts) ;
+ display != NULL ;
+ display = strtok_r(NULL, XPServerListSeparators, &tok_lasts) )
+ {
+ if( XpuGetPrinter2(name, display, pdpyptr, pcontextptr) )
+ {
+ free(sl);
+ free(printername);
+ return(1);
+ }
+ }
+
+ free(sl);
+ }
+ }
+ }
+
+ free(printername);
+ XPU_DEBUG_ONLY(printf("XpuGetPrinter: failure\n"));
+
+ return(0);
+}
+
+
+void XpuClosePrinterDisplay(Display *pdpy, XPContext pcontext)
+{
+ if( pdpy )
+ {
+ if( pcontext != None )
+ XpDestroyContext(pdpy, pcontext);
+
+ XCloseDisplay(pdpy);
+ }
+}
+
+void XpuSetOneAttribute( Display *pdpy, XPContext pcontext,
+ XPAttributes type, const char *attribute_name, const char *value, XPAttrReplacement replacement_rule )
+{
+ /* Alloc buffer for sprintf() stuff below */
+ char *buffer = (char *)malloc(strlen(attribute_name)+strlen(value)+4);
+
+ if( buffer != NULL )
+ {
+ sprintf(buffer, "%s: %s", attribute_name, value);
+ XpSetAttributes(pdpy, pcontext, type, buffer, replacement_rule);
+ free(buffer);
+ }
+}
+
+void XpuSetOneLongAttribute( Display *pdpy, XPContext pcontext,
+ XPAttributes type, const char *attribute_name, long value, XPAttrReplacement replacement_rule )
+{
+ /* Alloc buffer for sprintf() stuff below */
+ char *buffer = (char *)malloc(strlen(attribute_name)+32+4);
+
+ if( buffer != NULL )
+ {
+ sprintf(buffer, "%s: %ld", attribute_name, value);
+ XpSetAttributes(pdpy, pcontext, type, buffer, replacement_rule);
+ free(buffer);
+ }
+}
+
+/* Check if attribute value is supported or not
+ * Use this function _only_ if XpuGetSupported{Job,Doc,Page}Attributes()
+ * does not help you...
+ */
+int XpuCheckSupported( Display *pdpy, XPContext pcontext, XPAttributes type, const char *attribute_name, const char *query )
+{
+ char *value;
+ void *tok_lasts;
+
+ MAKE_STRING_WRITABLE(attribute_name);
+ if( attribute_name == NULL )
+ return(0);
+
+ value = XpGetOneAttribute(pdpy, pcontext, type, STRING_AS_WRITABLE(attribute_name));
+
+ XPU_DEBUG_ONLY(printf("XpuCheckSupported: XpGetOneAttribute(%s) returned '%s'\n", XPU_NULLXSTR(attribute_name), XPU_NULLXSTR(value)));
+
+ FREE_WRITABLE_STRING(attribute_name);
+
+ if( value != NULL )
+ {
+ const char *s;
+
+ for( s = XpuEnumerateXpAttributeValue(value, &tok_lasts) ; s != NULL ; s = XpuEnumerateXpAttributeValue(NULL, &tok_lasts) )
+ {
+ XPU_DEBUG_ONLY(printf("XpuCheckSupported: probing '%s'=='%s'\n", XPU_NULLXSTR(s), XPU_NULLXSTR(query)));
+ if( !strcmp(s, query) )
+ {
+ XFree(value);
+ XpuDisposeEnumerateXpAttributeValue(&tok_lasts);
+ return(1);
+ }
+ }
+
+ XpuDisposeEnumerateXpAttributeValue(&tok_lasts);
+ XFree(value);
+ }
+
+ return(0);
+}
+
+
+int XpuSetJobTitle( Display *pdpy, XPContext pcontext, const char *title )
+{
+ if( XpuGetSupportedJobAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_JOB_NAME )
+ {
+ XpuSetOneAttribute(pdpy, pcontext, XPJobAttr, "*job-name", title, XPAttrMerge);
+ return(1);
+ }
+ else
+ {
+ XPU_DEBUG_ONLY(printf("XpuSetJobTitle: XPUATTRIBUTESUPPORTED_JOB_NAME not supported ('%s')\n", XPU_NULLXSTR(title)));
+ return(0);
+ }
+}
+
+int XpuGetOneLongAttribute( Display *pdpy, XPContext pcontext, XPAttributes type, const char *attribute_name, long *result )
+{
+ char *s;
+
+ MAKE_STRING_WRITABLE(attribute_name);
+ if( attribute_name == NULL )
+ return(0);
+ s = XpGetOneAttribute(pdpy, pcontext, type, STRING_AS_WRITABLE(attribute_name));
+
+ if(s && *s)
+ {
+ long tmp;
+
+ XPU_DEBUG_ONLY(printf("XpuGetOneLongAttribute: '%s' got '%s'\n", XPU_NULLXSTR(attribute_name), XPU_NULLXSTR(s)));
+
+ tmp = strtol(s, (char **)NULL, 10);
+
+ if( !(((tmp == 0L) || (tmp == LONG_MIN) || (tmp == LONG_MAX)) &&
+ ((errno == ERANGE) || (errno == EINVAL))) )
+ {
+ *result = tmp;
+ XFree(s);
+ XPU_DEBUG_ONLY(printf("XpuGetOneLongAttribute: result %ld\n", *result));
+ FREE_WRITABLE_STRING(attribute_name);
+ return(1);
+ }
+ }
+
+ if( s != NULL )
+ XFree(s);
+
+ FREE_WRITABLE_STRING(attribute_name);
+
+ return(0);
+}
+
+
+#ifdef DEBUG
+/* debug only */
+void dumpXpAttributes( Display *pdpy, XPContext pcontext )
+{
+ char *s;
+ printf("------------------------------------------------\n");
+ printf("--> Job\n%s\n", s=XpuGetJobAttributes(pdpy, pcontext)); XFree(s);
+ printf("--> Doc\n%s\n", s=XpuGetDocAttributes(pdpy, pcontext)); XFree(s);
+ printf("--> Page\n%s\n", s=XpuGetPageAttributes(pdpy, pcontext)); XFree(s);
+ printf("--> Printer\n%s\n", s=XpuGetPrinterAttributes(pdpy, pcontext)); XFree(s);
+ printf("--> Server\n%s\n", s=XpuGetServerAttributes(pdpy, pcontext)); XFree(s);
+ printf("image resolution %d\n", (int)XpGetImageResolution(pdpy, pcontext));
+ printf("------------------------------------------------\n");
+}
+#endif /* DEBUG */
+
+
+typedef struct XpuIsNotifyEventContext_
+{
+ int event_base;
+ int detail;
+} XpuIsNotifyEventContext;
+
+static
+Bool IsXpNotifyEvent( Display *pdpy, XEvent *ev, XPointer arg )
+{
+ Bool match;
+ XpuIsNotifyEventContext *context = (XpuIsNotifyEventContext *)arg;
+ XPPrintEvent *pev = (XPPrintEvent *)ev;
+
+ match = pev->type == (context->event_base+XPPrintNotify) &&
+ pev->detail == context->detail;
+
+ XPU_DEBUG_ONLY(printf("XpuWaitForPrintNotify: %d=IsXpNotifyEvent(%d,%d)\n",
+ (int)match,
+ (int)pev->type,
+ (int)pev->detail));
+ return match;
+}
+
+void XpuWaitForPrintNotify( Display *pdpy, int xp_event_base, int detail )
+{
+ XEvent dummy;
+ XpuIsNotifyEventContext matchcontext;
+
+ matchcontext.event_base = xp_event_base;
+ matchcontext.detail = detail;
+ XIfEvent(pdpy, &dummy, IsXpNotifyEvent, (XPointer)&matchcontext);
+}
+
+static
+const char *skip_matching_brackets(const char *start)
+{
+ const char *s = start;
+ int level = 0;
+
+ if( !start )
+ return(NULL);
+
+ do
+ {
+ switch(*s++)
+ {
+ case '\0': return(NULL);
+ case '{': level++; break;
+ case '}': level--; break;
+ }
+ } while(level > 0);
+
+ return(s);
+}
+
+
+static
+const char *search_next_space(const char *start)
+{
+ const char *s = start;
+ int level = 0;
+
+ if( !start )
+ return(NULL);
+
+ for(;;)
+ {
+ if( isspace(*s) )
+ return(s);
+
+ if( *s=='\0' )
+ return(NULL);
+
+ s++;
+ }
+}
+
+/* PRIVATE context data for XpuEnumerateXpAttributeValue() */
+typedef struct _XpuAttributeValueEnumeration
+{
+ char *value;
+ size_t original_value_len; /* original length of value */
+ char *group;
+ char *start;
+ char *s;
+} XpuAttributeValueEnumeration;
+
+
+/* Hacked parser for Xp values and enumerations */
+static
+const char *XpuEnumerateXpAttributeValue( const char *value, void **vcptr )
+{
+ XpuAttributeValueEnumeration **cptr = (XpuAttributeValueEnumeration **)vcptr;
+ XpuAttributeValueEnumeration *context;
+ const char *tmp;
+
+ if( !cptr )
+ return(NULL);
+
+ if( value )
+ {
+ XpuAttributeValueEnumeration *e;
+ const char *s = value;
+ Bool isGroup = FALSE;
+
+ e = (XpuAttributeValueEnumeration *)malloc(sizeof(XpuAttributeValueEnumeration));
+ if( !e )
+ return NULL;
+
+ /* Skip leading '{'. */
+ while(*s=='{' && isGroup==FALSE)
+ {
+ s++;
+ isGroup = TRUE;
+ }
+ /* Skip leading blanks. */
+ while(isspace(*s))
+ s++;
+
+ e->group = NULL;
+
+ /* Read group name. */
+ if( isGroup )
+ {
+ tmp = s;
+ while(!isspace(*s))
+ s++;
+ if(strncmp(tmp, "''", s-tmp) != 0)
+ {
+ e->group = strdup(tmp);
+ e->group[s-tmp] = '\0';
+ }
+ }
+
+ e->original_value_len = strlen(s);
+ e->value = (char *)malloc(e->original_value_len+4); /* We may look up to three bytes beyond the string */
+ strcpy(e->value, s);
+ memset(e->value+e->original_value_len+1, 0, 3); /* quad termination */
+ e->start = e->s = e->value;
+
+ *cptr = e;
+ }
+
+ context = *cptr;
+
+ if( !context || !context->s )
+ return(NULL);
+
+ /* Skip leading blanks, '\'' or '}' */
+ while(isspace(*(context->s)) || *(context->s)=='\'' /*|| *(context->s)=='}'*/ )
+ context->s++;
+
+ if( *(context->s) == '\0' )
+ return(NULL);
+
+ context->start = context->s;
+ if( *(context->start) == '{' )
+ context->s = (char *)skip_matching_brackets(context->start);
+ else
+ context->s = (char *)search_next_space(context->start);
+
+ /* end of string reached ? */
+ if( context->s )
+ {
+ *(context->s) = '\0';
+ context->s++;
+ }
+
+ /* Check if we reached a new attribute group */
+ tmp = context->start;
+ while(isspace(*tmp))
+ tmp++;
+ if( *tmp=='}' )
+ {
+ void *prev_cptr = *vcptr;
+
+ tmp+=2; /* We have 3*'\0' at the end of the string - this is legal! */
+ if( *tmp!='\0' )
+ {
+ const char *ret;
+
+ /* Start the parser again */
+ *vcptr = NULL;
+ ret = XpuEnumerateXpAttributeValue(tmp, vcptr);
+
+ /* Free old context */
+ XpuDisposeEnumerateXpAttributeValue(&prev_cptr);
+
+ return(ret);
+ }
+ else
+ {
+ return(NULL);
+ }
+ }
+
+ return(context->start);
+}
+
+/* Get enumeration group for last string returned by |XpuEnumerateXpAttributeValue|... */
+static
+const char *XpuGetCurrentAttributeGroup( void **vcptr )
+{
+ XpuAttributeValueEnumeration **cptr = (XpuAttributeValueEnumeration **)vcptr;
+ if( !cptr )
+ return(NULL);
+ if( !*cptr )
+ return(NULL);
+
+ return((*cptr)->group);
+}
+
+
+static
+void XpuDisposeEnumerateXpAttributeValue( void **vc )
+{
+ if( vc )
+ {
+ XpuAttributeValueEnumeration *context = *((XpuAttributeValueEnumeration **)vc);
+ free(context->value);
+ if(context->group)
+ free(context->group);
+ free(context);
+ }
+}
+
+/* parse a paper size string
+ * (example: '{na-letter False {6.3500 209.5500 6.3500 273.0500}}') */
+static
+Bool XpuParseMediumSourceSize( const char *value,
+ const char **medium_name, int *mbool,
+ float *ma1, float *ma2, float *ma3, float *ma4 )
+{
+ const char *s;
+ char *d,
+ *name;
+ char *boolbuf;
+ size_t value_len;
+ int num_input_items;
+ const char *cur_locale;
+
+ if( value && value[0]!='{' && value[0]!='\0' )
+ return(False);
+
+ value_len = strlen(value);
+
+ /* alloc buffer for |medium_name| and |boolbuf| in one step
+ * (both must be large enougth to hold at least |strlen(value)+1| bytes) */
+ name = (char *)malloc(value_len*2 + 4);
+ boolbuf = name + value_len+2; /* |boolbuf| starts directly after |name| */
+
+ /* remove '{' && '}' */
+ s = value;
+ d = name;
+ do
+ {
+ *d = tolower(*s);
+
+ if( *s!='{' && *s!='}' )
+ d++;
+
+ s++;
+ }
+ while(*s);
+ *d = '\0';
+
+ /* seperate medium name from string */
+ d = (char *)search_next_space(name);
+ if( !d )
+ {
+ free(name);
+ return(False);
+ }
+ *d = '\0';
+ *medium_name = name;
+
+ /* ... continue to parse the remaining string... */
+ d++;
+
+
+ /* Force C/POSIX radix for scanf()-parsing (see bug 131831 ("Printing
+ * does not work in de_AT@euro locale")), do the parsing and restore
+ * the original locale.
+ * XXX: This may affect all threads and not only the calling one...
+ */
+ {
+#define CUR_LOCALE_SIZE 256
+ char cur_locale[CUR_LOCALE_SIZE+1];
+ strncpy(cur_locale, setlocale(LC_NUMERIC, NULL), CUR_LOCALE_SIZE);
+ cur_locale[CUR_LOCALE_SIZE]='\0';
+ setlocale(LC_NUMERIC, "C");
+ num_input_items = sscanf(d, "%s %f %f %f %f", boolbuf, ma1, ma2, ma3, ma4);
+ setlocale(LC_NUMERIC, cur_locale);
+#undef CUR_LOCALE_SIZE
+ }
+
+ if( num_input_items != 5 )
+ {
+ free(name);
+ return(False);
+ }
+
+ if( !strcmp(boolbuf, "true") )
+ *mbool = True;
+ else if( !strcmp(boolbuf, "false") )
+ *mbool = False;
+ else
+ {
+ free(name);
+ return(False);
+ }
+ return(True);
+}
+
+
+/* parse a paper size string
+ * (example: '{na-letter False {6.3500 209.5500 6.3500 273.0500}}') */
+static
+Bool XpuEnumerateMediumSourceSizes( Display *pdpy, XPContext pcontext,
+ const char **tray_name,
+ const char **medium_name, int *mbool,
+ float *ma1, float *ma2, float *ma3, float *ma4,
+ void **vcptr )
+{
+ const char *medium_spec;
+ const char *value = NULL;
+
+ if( pdpy && pcontext )
+ {
+ value = XpGetOneAttribute(pdpy, pcontext, XPPrinterAttr, "medium-source-sizes-supported");
+ if( !value )
+ return(False);
+ }
+
+ while(1)
+ {
+ medium_spec = XpuEnumerateXpAttributeValue(value, vcptr);
+
+ if( value )
+ {
+ XFree((void *)value);
+ value = NULL;
+ }
+
+ /* enumeration done? */
+ if( !medium_spec )
+ return(False);
+
+ if (XpuParseMediumSourceSize(medium_spec,
+ medium_name, mbool,
+ ma1, ma2, ma3, ma4))
+ {
+ *tray_name = XpuGetCurrentAttributeGroup(vcptr);
+ return(True);
+ }
+ else
+ {
+ /* Should never ever happen! */
+ fprintf(stderr, "XpuEnumerateMediumSourceSize: error parsing '%s'\n", medium_spec);
+ }
+ }
+ /* not reached */
+}
+
+static
+void XpuDisposeEnumerateMediumSourceSizes( void **vc )
+{
+ XpuDisposeEnumerateXpAttributeValue(vc);
+}
+
+
+/* future: Migrate this functionality into |XpGetPrinterList| - just do
+ * not pass a |Display *| to |XpGetPrinterList|
+ */
+XPPrinterList XpuGetPrinterList( const char *printer, int *res_list_count )
+{
+ XPPrinterRec *rec = NULL;
+ int rec_count = 1; /* Allocate one more |XPPrinterRec| structure
+ * as terminator */
+ char *sl;
+ const char *default_printer_name = XpuGetDefaultXpPrintername();
+ int default_printer_rec_index = -1;
+
+ if( !res_list_count )
+ return(NULL);
+
+ sl = strdup(XpuGetXpServerList());
+ MAKE_STRING_WRITABLE(printer);
+
+ if( sl != NULL )
+ {
+ char *display;
+ char *tok_lasts;
+
+ for( display = strtok_r(sl, XPServerListSeparators, &tok_lasts) ;
+ display != NULL ;
+ display = strtok_r(NULL, XPServerListSeparators, &tok_lasts) )
+ {
+ Display *pdpy;
+
+ if( (pdpy = XOpenDisplay(display)) != NULL )
+ {
+ XPPrinterList list;
+ int list_count;
+ size_t display_len = strlen(display);
+
+ /* get list of available printers... */
+ list = XpGetPrinterList(pdpy, STRING_AS_WRITABLE(printer), &list_count);
+
+ if( list && list_count )
+ {
+ int i;
+
+ for( i = 0 ; i < list_count ; i++ )
+ {
+ char *s;
+
+ /* Workaround for http://bugzilla.mozilla.org/show_bug.cgi?id=193499
+ * ("Xprint print/print preview crashes Mozilla") where the Solaris
+ * Xprt may create invalid entries (e.g. |XpGetPrinterList| will
+ * return |list[i].name==NULL| due to empty lines in the printer list.
+ */
+ if( !list[i].name )
+ continue;
+
+ rec_count++;
+ rec = (XPPrinterRec *)realloc(rec, sizeof(XPPrinterRec)*rec_count);
+ if( !rec ) /* failure */
+ break;
+
+ s = (char *)malloc(strlen(list[i].name)+display_len+4);
+ sprintf(s, "%s@%s", list[i].name, display);
+ rec[rec_count-2].name = s;
+ rec[rec_count-2].desc = (list[i].desc)?(strdup(list[i].desc)):(NULL);
+
+ /* Test for default printer (if the user set one).*/
+ if( default_printer_name )
+ {
+ /* Default_printer_name may either contain the FQPN(=full
+ * qualified printer name ("foo@myhost:5") or just the name
+ * ("foo")) */
+ if( (!strcmp(list[i].name, default_printer_name)) ||
+ (!strcmp(s, default_printer_name)) )
+ {
+ /* Remember index of default printer that we can swap it to
+ * the head of the array below... */
+ default_printer_rec_index = rec_count-2;
+ }
+ }
+ }
+
+ XpFreePrinterList(list);
+ }
+
+ XCloseDisplay(pdpy);
+ }
+ }
+
+ free(sl);
+ }
+
+ if( rec )
+ {
+ /* users: DO NOT COUNT ON THIS DETAIL
+ * (this is only to make current impl. of XpuFreePrinterList() easier)
+ * I may remove this implementation detail in a later revision of
+ * the library!
+ */
+ rec[rec_count-1].name = NULL;
+ rec[rec_count-1].desc = NULL;
+ rec_count--;
+ }
+ else
+ {
+ rec_count = 0;
+ }
+
+ /* The default printer is always the first one in the printer list... */
+ if( (default_printer_rec_index != -1) && rec )
+ {
+ XPPrinterRec tmp;
+ tmp = rec[0];
+ rec[0] = rec[default_printer_rec_index];
+ rec[default_printer_rec_index] = tmp;
+ }
+
+ *res_list_count = rec_count;
+ FREE_WRITABLE_STRING(printer);
+ return(rec);
+}
+
+
+void XpuFreePrinterList( XPPrinterList list )
+{
+ if( list )
+ {
+ XPPrinterRec *curr = list;
+
+ /* See the warning abouve about using this implementation detail for
+ * checking for the list's end... */
+ while( curr->name != NULL )
+ {
+ free(curr->name);
+ if(curr->desc)
+ free(curr->desc);
+ curr++;
+ }
+
+ free(list);
+ }
+}
+
+/* Set number of copies to print from this document */
+int XpuSetDocumentCopies( Display *pdpy, XPContext pcontext, long num_copies )
+{
+ if( XpuGetSupportedDocAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_COPY_COUNT)
+ {
+ XpuSetOneLongAttribute(pdpy, pcontext, XPDocAttr, "*copy-count", num_copies, XPAttrMerge);
+ return(1);
+ }
+ else
+ {
+ XPU_DEBUG_ONLY(printf("XpuSetContentOrientation: XPUATTRIBUTESUPPORTED_COPY_COUNT not supported\n"));
+
+ /* Failure... */
+ return(0);
+ }
+}
+
+XpuMediumSourceSizeList XpuGetMediumSourceSizeList( Display *pdpy, XPContext pcontext, int *numEntriesPtr )
+{
+ XpuMediumSourceSizeList list = NULL;
+ int rec_count = 1; /* allocate one more |XpuMediumSourceSizeRec| structure
+ * as terminator */
+ Bool status;
+ float ma1,
+ ma2,
+ ma3,
+ ma4;
+ char *value;
+ void *tok_lasts;
+ const char *tray_name,
+ *medium_name;
+ int mbool;
+ const char *default_tray,
+ *default_medium;
+ int default_medium_rec_index = -1;
+
+ default_tray = XpGetOneAttribute(pdpy, pcontext, XPDocAttr, "default-input-tray");
+ if(!default_tray)
+ {
+ fprintf(stderr, "XpuGetMediumSourceSizeList: Internal error, no 'default-input-tray' found.\n");
+ return(NULL);
+ }
+ default_medium = XpGetOneAttribute(pdpy, pcontext, XPDocAttr, "default-medium");
+ if(!default_medium)
+ {
+ fprintf(stderr, "XpuGetMediumSourceSizeList: Internal error, no 'default-medium' found.\n");
+ XFree((void *)default_tray);
+ return(NULL);
+ }
+
+ for( status = XpuEnumerateMediumSourceSizes(pdpy, pcontext, &tray_name, &medium_name, &mbool,
+ &ma1, &ma2, &ma3, &ma4, &tok_lasts) ;
+ status != False ;
+ status = XpuEnumerateMediumSourceSizes(NULL, None, &tray_name, &medium_name, &mbool,
+ &ma1, &ma2, &ma3, &ma4, &tok_lasts) )
+ {
+ rec_count++;
+ list = (XpuMediumSourceSizeRec *)realloc(list, sizeof(XpuMediumSourceSizeRec)*rec_count);
+ if( !list )
+ return(NULL);
+
+ list[rec_count-2].tray_name = (tray_name)?(strdup(tray_name)):(NULL);
+ list[rec_count-2].medium_name = strdup(medium_name);
+ list[rec_count-2].mbool = mbool;
+ list[rec_count-2].ma1 = ma1;
+ list[rec_count-2].ma2 = ma2;
+ list[rec_count-2].ma3 = ma3;
+ list[rec_count-2].ma4 = ma4;
+
+ /* Default medium ? */
+ if( (!strcmp(medium_name, default_medium)) &&
+ ((tray_name && (*default_tray))?(!strcmp(tray_name, default_tray)):(True)) )
+ {
+ default_medium_rec_index = rec_count-2;
+ }
+ }
+
+ XpuDisposeEnumerateMediumSourceSizes(&tok_lasts);
+
+ if( list )
+ {
+ /* users: DO NOT COUNT ON THIS DETAIL
+ * (this is only to make current impl. of XpuFreeMediumSourceSizeList() easier)
+ * I may remove this implementation detail in a later revision of
+ * the library! */
+ list[rec_count-1].tray_name = NULL;
+ list[rec_count-1].medium_name = NULL;
+ rec_count--;
+ }
+ else
+ {
+ rec_count = 0;
+ }
+
+ /* Make the default medium always the first item in the list... */
+ if( (default_medium_rec_index != -1) && list )
+ {
+ XpuMediumSourceSizeRec tmp;
+ tmp = list[0];
+ list[0] = list[default_medium_rec_index];
+ list[default_medium_rec_index] = tmp;
+ }
+
+ *numEntriesPtr = rec_count;
+ return(list);
+}
+
+void XpuFreeMediumSourceSizeList( XpuMediumSourceSizeList list )
+{
+ if( list )
+ {
+ XpuMediumSourceSizeRec *curr = list;
+
+ /* See the warning abouve about using this implementation detail for
+ * checking for the list's end... */
+ while( curr->medium_name != NULL )
+ {
+ if( curr->tray_name)
+ free((void *)curr->tray_name);
+ free((void *)curr->medium_name);
+ curr++;
+ }
+
+ free(list);
+ }
+}
+
+static
+int XpuSetMediumSourceSize( Display *pdpy, XPContext pcontext, XPAttributes type, XpuMediumSourceSizeRec *medium_spec )
+{
+ /* Set the "default-medium" and "*default-input-tray"
+ * (if |XpuEnumerateMediumSourceSizes| returned one) XPDocAttr's
+ * attribute and return */
+ if (medium_spec->tray_name)
+ {
+ XpuSetOneAttribute(pdpy, pcontext, type, "*default-input-tray", medium_spec->tray_name, XPAttrMerge);
+ }
+ XpuSetOneAttribute(pdpy, pcontext, type, "*default-medium", medium_spec->medium_name, XPAttrMerge);
+
+ return( 1 );
+}
+
+/* Set document medium size */
+int XpuSetDocMediumSourceSize( Display *pdpy, XPContext pcontext, XpuMediumSourceSizeRec *medium_spec )
+{
+ XpuSupportedFlags doc_supported_flags;
+
+ doc_supported_flags = XpuGetSupportedDocAttributes(pdpy, pcontext);
+
+ if( (doc_supported_flags & XPUATTRIBUTESUPPORTED_DEFAULT_MEDIUM) == 0 )
+ return( 0 );
+
+ if (medium_spec->tray_name)
+ {
+ if( (doc_supported_flags & XPUATTRIBUTESUPPORTED_DEFAULT_INPUT_TRAY) == 0 )
+ return( 0 );
+ }
+
+ return XpuSetMediumSourceSize(pdpy, pcontext, XPDocAttr, medium_spec);
+}
+
+/* Set page medium size */
+int XpuSetPageMediumSourceSize( Display *pdpy, XPContext pcontext, XpuMediumSourceSizeRec *medium_spec )
+{
+ XpuSupportedFlags page_supported_flags;
+
+ page_supported_flags = XpuGetSupportedPageAttributes(pdpy, pcontext);
+
+ if( (page_supported_flags & XPUATTRIBUTESUPPORTED_DEFAULT_MEDIUM) == 0 )
+ return( 0 );
+
+ if (medium_spec->tray_name)
+ {
+ if( (page_supported_flags & XPUATTRIBUTESUPPORTED_DEFAULT_INPUT_TRAY) == 0 )
+ return( 0 );
+ }
+
+ return XpuSetMediumSourceSize(pdpy, pcontext, XPPageAttr, medium_spec);
+}
+
+#ifndef ABS
+#define ABS(x) ((x)<0?-(x):(x))
+#endif /* ABS */
+#define MORE_OR_LESS_EQUAL(a, b, tolerance) (ABS((a) - (b)) <= (tolerance))
+
+XpuMediumSourceSizeRec *
+XpuFindMediumSourceSizeBySize( XpuMediumSourceSizeList mlist, int mlist_count,
+ float page_width_mm, float page_height_mm, float tolerance )
+{
+ int i;
+ for( i = 0 ; i < mlist_count ; i++ )
+ {
+ XpuMediumSourceSizeRec *curr = &mlist[i];
+ float total_width = curr->ma1 + curr->ma2,
+ total_height = curr->ma3 + curr->ma4;
+
+ /* Match width/height*/
+ if( ((page_width_mm !=-1.f)?(MORE_OR_LESS_EQUAL(total_width, page_width_mm, tolerance)):(True)) &&
+ ((page_height_mm!=-1.f)?(MORE_OR_LESS_EQUAL(total_height, page_height_mm, tolerance)):(True)) )
+ {
+ return(curr);
+ }
+ }
+
+ return(NULL);
+}
+
+XpuMediumSourceSizeRec *
+XpuFindMediumSourceSizeByBounds( XpuMediumSourceSizeList mlist, int mlist_count,
+ float m1, float m2, float m3, float m4, float tolerance )
+{
+ int i;
+ for( i = 0 ; i < mlist_count ; i++ )
+ {
+ XpuMediumSourceSizeRec *curr = &mlist[i];
+
+ /* Match bounds */
+ if( ((m1!=-1.f)?(MORE_OR_LESS_EQUAL(curr->ma1, m1, tolerance)):(True)) &&
+ ((m2!=-1.f)?(MORE_OR_LESS_EQUAL(curr->ma2, m2, tolerance)):(True)) &&
+ ((m3!=-1.f)?(MORE_OR_LESS_EQUAL(curr->ma3, m3, tolerance)):(True)) &&
+ ((m4!=-1.f)?(MORE_OR_LESS_EQUAL(curr->ma4, m4, tolerance)):(True)) )
+ {
+ return(curr);
+ }
+ }
+
+ return(NULL);
+}
+
+XpuMediumSourceSizeRec *
+XpuFindMediumSourceSizeByName( XpuMediumSourceSizeList mlist, int mlist_count,
+ const char *tray_name, const char *medium_name )
+{
+ int i;
+ for( i = 0 ; i < mlist_count ; i++ )
+ {
+ XpuMediumSourceSizeRec *curr = &mlist[i];
+
+ /* Match by tray name and/or medium name */
+ if( ((tray_name && curr->tray_name)?(!strcasecmp(curr->tray_name, tray_name)):(tray_name==NULL)) &&
+ ((medium_name)?(!strcasecmp(curr->medium_name, medium_name)):(True)) )
+ {
+ return(curr);
+ }
+ }
+
+ return(NULL);
+}
+
+XpuResolutionList XpuGetResolutionList( Display *pdpy, XPContext pcontext, int *numEntriesPtr )
+{
+ XpuResolutionList list = NULL;
+ int rec_count = 1; /* Allocate one more |XpuResolutionRec| structure
+ * as terminator */
+ char *value;
+ char *tok_lasts;
+ const char *s;
+ long default_resolution = -1;
+ int default_resolution_rec_index = -1;
+
+ /* Get default document resolution */
+ if( XpuGetOneLongAttribute(pdpy, pcontext, XPDocAttr, "default-printer-resolution", &default_resolution) != 1 )
+ {
+ default_resolution = -1;
+ }
+
+ value = XpGetOneAttribute(pdpy, pcontext, XPPrinterAttr, "printer-resolutions-supported");
+ if (!value)
+ {
+ fprintf(stderr, "XpuGetResolutionList: Internal error, no 'printer-resolutions-supported' XPPrinterAttr found.\n");
+ return(NULL);
+ }
+
+ for( s = strtok_r(value, " ", &tok_lasts) ;
+ s != NULL ;
+ s = strtok_r(NULL, " ", &tok_lasts) )
+ {
+ long tmp;
+
+ tmp = strtol(s, (char **)NULL, 10);
+
+ if( ((tmp == 0L) || (tmp == LONG_MIN) || (tmp == LONG_MAX)) &&
+ ((errno == ERANGE) || (errno == EINVAL)) )
+ {
+ fprintf(stderr, "XpuGetResolutionList: Internal parser errror for '%s'.\n", s);
+ continue;
+ }
+
+ rec_count++;
+ list = (XpuResolutionRec *)realloc(list, sizeof(XpuResolutionRec)*rec_count);
+ if( !list )
+ return(NULL);
+
+ list[rec_count-2].dpi = tmp;
+
+ if( default_resolution != -1 )
+ {
+ /* Is this the default resolution ? */
+ if( list[rec_count-2].dpi == default_resolution )
+ {
+ default_resolution_rec_index = rec_count-2;
+ }
+ }
+ }
+
+ XFree(value);
+
+ if( list )
+ {
+ /* users: DO NOT COUNT ON THIS DETAIL
+ * (this is only to make current impl. of XpuFreeMediumSourceSizeList() easier)
+ * I may remove this implementation detail in a later revision of
+ * the library! */
+ list[rec_count-1].dpi = -1;
+ rec_count--;
+ }
+ else
+ {
+ rec_count = 0;
+ }
+
+ /* Make the default resolution always the first item in the list... */
+ if( (default_resolution_rec_index != -1) && list )
+ {
+ XpuResolutionRec tmp;
+ tmp = list[0];
+ list[0] = list[default_resolution_rec_index];
+ list[default_resolution_rec_index] = tmp;
+ }
+
+ *numEntriesPtr = rec_count;
+ return(list);
+}
+
+void XpuFreeResolutionList( XpuResolutionList list )
+{
+ if( list )
+ {
+ free(list);
+ }
+}
+
+/* Find resolution in resolution list.
+ * Lower resolutions are preferred over larger resolution if |dpi_a| <= |dpi_b|,
+ * otherwise larger resolutions are preferred over small resolutions
+ */
+XpuResolutionRec *XpuFindResolution( XpuResolutionList list, int list_count, long dpi_a, long dpi_b )
+{
+ XpuResolutionRec *match = NULL;
+ int i;
+
+ if( dpi_a <= dpi_b )
+ {
+ /* Search list, lower resolutions are better... */
+ for( i = 0 ; i < list_count ; i++ )
+ {
+ XpuResolutionRec *curr = &list[i];
+
+ if( curr->dpi >= dpi_a && curr->dpi <= dpi_b )
+ {
+ if( !match || (curr->dpi < match->dpi) )
+ {
+ match = curr;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Search list, higher resolutions are better... */
+ for( i = 0 ; i < list_count ; i++ )
+ {
+ XpuResolutionRec *curr = &list[i];
+
+ if( curr->dpi >= dpi_b && curr->dpi <= dpi_a )
+ {
+ if( !match || (curr->dpi > match->dpi) )
+ {
+ match = curr;
+ }
+ }
+ }
+ }
+
+ return(match);
+}
+
+/* Get default page (if defined) or document resolution
+ * this function may fail in the following conditions:
+ * - No default resolution set yet
+ * - X DPI != Y DPI (not yet implemented in Xprt)
+ */
+Bool XpuGetResolution( Display *pdpy, XPContext pcontext, long *dpi_ptr )
+{
+ /* Try to get the current page's resolution (pages may differ in resolution if the DDX supports this) */
+ if( XpuGetOneLongAttribute(pdpy, pcontext, XPPageAttr, "default-printer-resolution", dpi_ptr) == 1 )
+ {
+ return True;
+ }
+
+ /* Get document resolution */
+ if( XpuGetOneLongAttribute(pdpy, pcontext, XPDocAttr, "default-printer-resolution", dpi_ptr) == 1 )
+ {
+ return True;
+ }
+
+ return False;
+}
+
+static
+int XpuSetResolution( Display *pdpy, XPContext pcontext, XPAttributes type, XpuResolutionRec *rec )
+{
+ XpuSetOneLongAttribute(pdpy, pcontext, type, "*default-printer-resolution", rec->dpi, XPAttrMerge);
+ return( 1 );
+}
+
+/* Set document resolution
+ * Retun error if printer does not support setting a resolution
+ */
+int XpuSetDocResolution( Display *pdpy, XPContext pcontext, XpuResolutionRec *rec )
+{
+ if( (XpuGetSupportedDocAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_DEFAULT_PRINTER_RESOLUTION) == 0 )
+ return( 0 );
+
+ return XpuSetResolution(pdpy, pcontext, XPDocAttr, rec);
+}
+
+/* Set page medium size
+ * Retun error if printer does not support setting a resolution or if per-page
+ * resolution changes are not allowed.
+ */
+int XpuSetPageResolution( Display *pdpy, XPContext pcontext, XpuResolutionRec *rec )
+{
+ if( (XpuGetSupportedPageAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_DEFAULT_PRINTER_RESOLUTION) == 0 )
+ return( 0 );
+
+ return XpuSetResolution(pdpy, pcontext, XPPageAttr, rec);
+}
+
+XpuOrientationList XpuGetOrientationList( Display *pdpy, XPContext pcontext, int *numEntriesPtr )
+{
+ XpuOrientationList list = NULL;
+ int rec_count = 1; /* Allocate one more |XpuOrientationRec|
+ * structure as terminator */
+ char *value;
+ char *tok_lasts;
+ const char *s;
+ const char *default_orientation = NULL;
+ int default_orientation_rec_index = -1;
+
+ /* Get default document orientation */
+ default_orientation = XpGetOneAttribute(pdpy, pcontext, XPDocAttr, "content-orientation");
+ if( !default_orientation )
+ {
+ fprintf(stderr, "XpuGetOrientationList: Internal error, no 'content-orientation' XPDocAttr found.\n");
+ return(NULL);
+ }
+
+ value = XpGetOneAttribute(pdpy, pcontext, XPPrinterAttr, "content-orientations-supported");
+ if (!value)
+ {
+ fprintf(stderr, "XpuGetOrientationList: Internal error, no 'content-orientations-supported' XPPrinterAttr found.\n");
+ return(NULL);
+ }
+
+ for( s = strtok_r(value, " ", &tok_lasts) ;
+ s != NULL ;
+ s = strtok_r(NULL, " ", &tok_lasts) )
+ {
+ rec_count++;
+ list = (XpuOrientationRec *)realloc(list, sizeof(XpuOrientationRec)*rec_count);
+ if( !list )
+ return(NULL);
+
+ list[rec_count-2].orientation = strdup(s);
+
+ /* Default resolution ? */
+ if( !strcmp(list[rec_count-2].orientation, default_orientation) )
+ {
+ default_orientation_rec_index = rec_count-2;
+ }
+ }
+
+ XFree(value);
+ XFree((void *)default_orientation);
+
+ if( list )
+ {
+ /* users: DO NOT COUNT ON THIS DETAIL
+ * (this is only to make current impl. of XpuFreeOrientationList() easier)
+ * I may remove this implementation detail in a later revision of
+ * the library! */
+ list[rec_count-1].orientation = NULL;
+ rec_count--;
+ }
+ else
+ {
+ rec_count = 0;
+ }
+
+ /* Make the default orientation always the first item in the list... */
+ if( (default_orientation_rec_index != -1) && list )
+ {
+ XpuOrientationRec tmp;
+ tmp = list[0];
+ list[0] = list[default_orientation_rec_index];
+ list[default_orientation_rec_index] = tmp;
+ }
+
+ *numEntriesPtr = rec_count;
+ return(list);
+}
+
+void XpuFreeOrientationList( XpuOrientationList list )
+{
+ if( list )
+ {
+ XpuOrientationRec *curr = list;
+
+ /* See the warning abouve about using this implementation detail for
+ * checking for the list's end... */
+ while( curr->orientation != NULL )
+ {
+ free((void *)curr->orientation);
+ curr++;
+ }
+ free(list);
+ }
+}
+
+XpuOrientationRec *
+XpuFindOrientationByName( XpuOrientationList list, int list_count, const char *orientation )
+{
+ int i;
+
+ for( i = 0 ; i < list_count ; i++ )
+ {
+ XpuOrientationRec *curr = &list[i];
+ if (!strcasecmp(curr->orientation, orientation))
+ return curr;
+ }
+
+ return(NULL);
+}
+
+static
+int XpuSetOrientation( Display *pdpy, XPContext pcontext, XPAttributes type, XpuOrientationRec *rec )
+{
+ XpuSetOneAttribute(pdpy, pcontext, type, "*content-orientation", rec->orientation, XPAttrMerge);
+ return(1);
+}
+
+/* Set document orientation
+ * Retun error if printer does not support setting an orientation
+ */
+int XpuSetDocOrientation( Display *pdpy, XPContext pcontext, XpuOrientationRec *rec )
+{
+ if( (XpuGetSupportedDocAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_CONTENT_ORIENTATION) == 0 )
+ return( 0 );
+
+ return XpuSetOrientation(pdpy, pcontext, XPDocAttr, rec);
+}
+
+/* Set page orientation
+ * Retun error if printer does not support setting an orientation or if
+ * per-page orientations changes are not allowed
+ */
+int XpuSetPageOrientation( Display *pdpy, XPContext pcontext, XpuOrientationRec *rec )
+{
+ if( (XpuGetSupportedPageAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_CONTENT_ORIENTATION) == 0 )
+ return( 0 );
+
+ return XpuSetOrientation(pdpy, pcontext, XPPageAttr, rec);
+}
+
+XpuPlexList XpuGetPlexList( Display *pdpy, XPContext pcontext, int *numEntriesPtr )
+{
+ XpuPlexList list = NULL;
+ int rec_count = 1; /* Allocate one more |XpuPlexList| structure
+ * as terminator */
+ char *value;
+ char *tok_lasts;
+ const char *s;
+ const char *default_plex = NULL;
+ int default_plex_rec_index = -1;
+
+ /* Get default document plex */
+ default_plex = XpGetOneAttribute(pdpy, pcontext, XPDocAttr, "plex");
+ if( !default_plex )
+ {
+ fprintf(stderr, "XpuGetPlexList: Internal error, no 'plex' XPDocAttr found.\n");
+ return(NULL);
+ }
+
+ value = XpGetOneAttribute(pdpy, pcontext, XPPrinterAttr, "plexes-supported");
+ if (!value)
+ {
+ fprintf(stderr, "XpuGetPlexList: Internal error, no 'plexes-supported' XPPrinterAttr found.\n");
+ return(NULL);
+ }
+
+ for( s = strtok_r(value, " ", &tok_lasts) ;
+ s != NULL ;
+ s = strtok_r(NULL, " ", &tok_lasts) )
+ {
+ rec_count++;
+ list = (XpuPlexRec *)realloc(list, sizeof(XpuPlexRec)*rec_count);
+ if( !list )
+ return(NULL);
+
+ list[rec_count-2].plex = strdup(s);
+
+ /* Default plex ? */
+ if( !strcmp(list[rec_count-2].plex, default_plex) )
+ {
+ default_plex_rec_index = rec_count-2;
+ }
+ }
+
+ XFree(value);
+ XFree((void *)default_plex);
+
+ if( list )
+ {
+ /* users: DO NOT COUNT ON THIS DETAIL
+ * (this is only to make current impl. of XpuFreePlexList() easier)
+ * I may remove this implementation detail in a later revision of
+ * the library! */
+ list[rec_count-1].plex = NULL;
+ rec_count--;
+ }
+ else
+ {
+ rec_count = 0;
+ }
+
+ /* Make the default plex always the first item in the list... */
+ if( (default_plex_rec_index != -1) && list )
+ {
+ XpuPlexRec tmp;
+ tmp = list[0];
+ list[0] = list[default_plex_rec_index];
+ list[default_plex_rec_index] = tmp;
+ }
+
+ *numEntriesPtr = rec_count;
+ return(list);
+}
+
+void XpuFreePlexList( XpuPlexList list )
+{
+ if( list )
+ {
+ XpuPlexRec *curr = list;
+
+ /* See the warning abouve about using this implementation detail for
+ * checking for the list's end... */
+ while( curr->plex != NULL )
+ {
+ free((void *)curr->plex);
+ curr++;
+ }
+ free(list);
+ }
+}
+
+XpuPlexRec *
+XpuFindPlexByName( XpuPlexList list, int list_count, const char *plex )
+{
+ int i;
+
+ for( i = 0 ; i < list_count ; i++ )
+ {
+ XpuPlexRec *curr = &list[i];
+ if (!strcasecmp(curr->plex, plex))
+ return curr;
+ }
+
+ return(NULL);
+}
+
+static
+int XpuSetContentPlex( Display *pdpy, XPContext pcontext, XPAttributes type, XpuPlexRec *rec )
+{
+ XpuSetOneAttribute(pdpy, pcontext, type, "*plex", rec->plex, XPAttrMerge);
+ return(1);
+}
+
+/* Set document plex
+ * Retun error if printer does not support setting an plex
+ */
+int XpuSetDocPlex( Display *pdpy, XPContext pcontext, XpuPlexRec *rec )
+{
+ if( (XpuGetSupportedDocAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_PLEX) == 0 )
+ return( 0 );
+
+ return XpuSetContentPlex(pdpy, pcontext, XPDocAttr, rec);
+}
+
+/* Set page plex
+ * Retun error if printer does not support setting an plex or if
+ * per-page plex changes are not allowed
+ */
+int XpuSetPagePlex( Display *pdpy, XPContext pcontext, XpuPlexRec *rec )
+{
+ if( (XpuGetSupportedPageAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_PLEX) == 0 )
+ return( 0 );
+
+ return XpuSetContentPlex(pdpy, pcontext, XPPageAttr, rec);
+}
+
+
+/* Return flags to indicate which attributes are supported and which not... */
+static
+XpuSupportedFlags XpuGetSupportedAttributes( Display *pdpy, XPContext pcontext, XPAttributes type, const char *attribute_name )
+{
+ char *value;
+ void *tok_lasts;
+ XpuSupportedFlags flags = 0;
+
+ MAKE_STRING_WRITABLE(attribute_name);
+ if( attribute_name == NULL )
+ return(0);
+
+ value = XpGetOneAttribute(pdpy, pcontext, type, STRING_AS_WRITABLE(attribute_name));
+
+ FREE_WRITABLE_STRING(attribute_name);
+
+ if( value != NULL )
+ {
+ const char *s;
+
+ for( s = XpuEnumerateXpAttributeValue(value, &tok_lasts) ; s != NULL ; s = XpuEnumerateXpAttributeValue(NULL, &tok_lasts) )
+ {
+ if( !strcmp(s, "job-name") ) flags |= XPUATTRIBUTESUPPORTED_JOB_NAME;
+ else if( !strcmp(s, "job-owner") ) flags |= XPUATTRIBUTESUPPORTED_JOB_OWNER;
+ else if( !strcmp(s, "notification-profile") ) flags |= XPUATTRIBUTESUPPORTED_NOTIFICATION_PROFILE;
+ else if( !strcmp(s, "copy-count") ) flags |= XPUATTRIBUTESUPPORTED_COPY_COUNT;
+ else if( !strcmp(s, "document-format") ) flags |= XPUATTRIBUTESUPPORTED_DOCUMENT_FORMAT;
+ else if( !strcmp(s, "content-orientation") ) flags |= XPUATTRIBUTESUPPORTED_CONTENT_ORIENTATION;
+ else if( !strcmp(s, "default-printer-resolution") ) flags |= XPUATTRIBUTESUPPORTED_DEFAULT_PRINTER_RESOLUTION;
+ else if( !strcmp(s, "default-input-tray") ) flags |= XPUATTRIBUTESUPPORTED_DEFAULT_INPUT_TRAY;
+ else if( !strcmp(s, "default-medium") ) flags |= XPUATTRIBUTESUPPORTED_DEFAULT_MEDIUM;
+ else if( !strcmp(s, "plex") ) flags |= XPUATTRIBUTESUPPORTED_PLEX;
+ }
+
+ XpuDisposeEnumerateXpAttributeValue(&tok_lasts);
+ XFree(value);
+ }
+
+ return(flags);
+}
+
+XpuSupportedFlags XpuGetSupportedJobAttributes(Display *pdpy, XPContext pcontext)
+{
+ return XpuGetSupportedAttributes(pdpy, pcontext, XPPrinterAttr, "job-attributes-supported");
+}
+
+XpuSupportedFlags XpuGetSupportedDocAttributes(Display *pdpy, XPContext pcontext)
+{
+ return XpuGetSupportedAttributes(pdpy, pcontext, XPPrinterAttr, "document-attributes-supported");
+}
+
+XpuSupportedFlags XpuGetSupportedPageAttributes(Display *pdpy, XPContext pcontext)
+{
+ return XpuGetSupportedAttributes(pdpy, pcontext, XPPrinterAttr, "xp-page-attributes-supported");
+}
+
+/* EOF. */
diff --git a/src/xprintutil_printtofile.c b/src/xprintutil_printtofile.c
new file mode 100644
index 0000000..331e7ad
--- /dev/null
+++ b/src/xprintutil_printtofile.c
@@ -0,0 +1,498 @@
+/******************************************************************************
+ ******************************************************************************
+ **
+ ** (c) Copyright 2001-2004 Roland Mainz <roland.mainz@nrubsig.org>
+ **
+ ** Permission is hereby granted, free of charge, to any person obtaining a copy
+ ** of this software and associated documentation files (the "Software"), to deal
+ ** in the Software without restriction, including without limitation the rights
+ ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ ** copies of the Software, and to permit persons to whom the Software is
+ ** furnished to do so, subject to the following conditions:
+ **
+ ** The above copyright notice and this permission notice shall be included in
+ ** all copies or substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ ** IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ ** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ **
+ ** Except as contained in this notice, the names of the copyright holders shall
+ ** not be used in advertising or otherwise to promote the sale, use or other
+ ** dealings in this Software without prior written authorization from said
+ ** copyright holders.
+ **
+ ******************************************************************************
+ *****************************************************************************/
+
+#include "xprintutil.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#ifdef XPU_USE_THREADS
+#include <time.h>
+#ifdef XPU_USE_NSPR
+#include <prthread.h>
+#else
+#include <pthread.h>
+#endif /* XPU_USE_NSPR */
+#endif /* XPU_USE_THREADS */
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/* local prototypes */
+#ifdef DEBUG
+static void PrintXPGetDocStatus( XPGetDocStatus status );
+#endif
+static Bool XNextEventTimeout( Display *display, XEvent *event_return, struct timeval *timeout );
+static void *XpuPrintToFile( Display *pdpy, XPContext pcontext, const char *filename );
+static void MyPrintToFileProc( Display *pdpy, XPContext pcontext, unsigned char *data, unsigned int data_len, XPointer client_data );
+static void MyFinishProc( Display *pdpy, XPContext pcontext, XPGetDocStatus status, XPointer client_data );
+#ifdef XPU_USE_NSPR
+static void PrintToFile_Consumer( void *handle );
+#else
+static void *PrintToFile_Consumer( void *handle );
+#endif
+
+void XpuStartJobToSpooler(Display *pdpy)
+{
+ XpStartJob(pdpy, XPSpool);
+}
+
+void *XpuStartJobToFile( Display *pdpy, XPContext pcontext, const char *filename )
+{
+ void *handle;
+
+ XpStartJob(pdpy, XPGetData);
+ handle = XpuPrintToFile(pdpy, pcontext, filename);
+
+ if (!handle)
+ {
+ /* Cancel the print job and discard all events... */
+ XpCancelJob(pdpy, True);
+ }
+
+ return(handle);
+}
+
+#ifdef DEBUG
+/* DEBUG: Print XPGetDocStatus */
+static
+void PrintXPGetDocStatus( XPGetDocStatus status )
+{
+ switch(status)
+ {
+ case XPGetDocFinished: puts("PrintXPGetDocStatus: XPGetDocFinished"); break;
+ case XPGetDocSecondConsumer: puts("PrintXPGetDocStatus: XPGetDocSecondConsumer"); break;
+ case XPGetDocError: puts("PrintXPGetDocStatus: XPGetDocError"); break;
+ default: puts("PrintXPGetDocStatus: <unknown value"); break;
+ }
+}
+#endif /* DEBUG */
+
+
+/* XNextEvent() with timeout */
+static
+Bool XNextEventTimeout( Display *display, XEvent *event_return, struct timeval *timeout )
+{
+ int res;
+ fd_set readfds;
+ int display_fd = XConnectionNumber(display);
+
+ /* small shortcut... */
+ if( timeout == NULL )
+ {
+ XNextEvent(display, event_return);
+ return(True);
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(display_fd, &readfds);
+
+ /* Note/bug: In the case of internal X events (like used to trigger callbacks
+ * registered by XpGetDocumentData()&co.) select() will return with "new info"
+ * - but XNextEvent() below processes these _internal_ events silently - and
+ * will block if there are no other non-internal events.
+ * The workaround here is to check with XEventsQueued() if there are non-internal
+ * events queued - if not select() will be called again - unfortunately we use
+ * the old timeout here instead of the "remaining" time... (this only would hurt
+ * if the timeout would be really long - but for current use with values below
+ * 1/2 secs it does not hurt... =:-)
+ */
+ while( XEventsQueued(display, QueuedAfterFlush) == 0 )
+ {
+ res = select(display_fd+1, &readfds, NULL, NULL, timeout);
+
+ switch(res)
+ {
+ case -1: /* select() error - should not happen */
+ perror("XNextEventTimeout: select() failure");
+ return(False);
+ case 0: /* timeout */
+ return(False);
+ }
+ }
+
+ XNextEvent(display, event_return);
+ return(True);
+}
+
+
+#ifdef XPU_USE_THREADS
+/**
+ ** XpuPrintToFile() - threaded version
+ ** Create consumer thread which creates it's own display connection to print server
+ ** (a 2nd display connection/thread is required to avoid deadlocks within Xlib),
+ ** registers (Xlib-internal) consumer callback (via XpGetDocumentData(3Xp)) and
+ ** processes/eats all incoming events via MyPrintToFileProc(). A final call to
+ ** MyPrintToFileProc() cleans-up all stuff and sets the "done" flag.
+ ** Note that these callbacks are called directly by Xlib while waiting for events in
+ ** XNextEvent() because XpGetDocumentData() registeres them as "internal" callbacks,
+ ** e.g. XNextEvent() does _not_ return before/after processing these events !!
+ **
+ ** Usage:
+ ** XpStartJob(pdpy, XPGetData);
+ ** handle = XpuPrintToFile(pdpy, pcontext, "myfile");
+ ** // render something
+ ** XpEndJob(); // or XpCancelJob()
+ ** status = XpuWaitForPrintFileChild(handle);
+ **
+ */
+
+typedef struct
+{
+#ifdef XPU_USE_NSPR
+ PRThread *prthread;
+#else
+ pthread_t tid;
+#endif
+ char *displayname;
+ Display *pdpy;
+ Display *parent_pdpy;
+ XPContext pcontext;
+ const char *file_name;
+ FILE *file;
+ XPGetDocStatus status;
+ Bool done;
+} MyPrintFileData;
+
+
+static
+void *XpuPrintToFile( Display *pdpy, XPContext pcontext, const char *filename )
+{
+ MyPrintFileData *mpfd; /* warning: shared between threads !! */
+
+ if( (mpfd = malloc(sizeof(MyPrintFileData))) == NULL )
+ return(NULL);
+
+ mpfd->parent_pdpy = pdpy;
+ mpfd->displayname = XDisplayString(pdpy);
+ mpfd->pdpy = NULL;
+ mpfd->pcontext = pcontext;
+ mpfd->file_name = filename;
+ mpfd->file = NULL;
+ mpfd->status = XPGetDocError;
+
+ /* make sure we can open the file for writing */
+ if( (mpfd->file = fopen(mpfd->file_name, "w")) == NULL )
+ {
+ /* fopen() error */
+ free(mpfd);
+ return(NULL);
+ }
+
+ /* its important to flush before we start the consumer thread,
+ * to make sure that the XpStartJob gets through first in the parent
+ */
+ XFlush(pdpy);
+#ifdef XPU_USE_NSPR
+ if( (mpfd->prthread = PR_CreateThread(PR_SYSTEM_THREAD, PrintToFile_Consumer, mpfd, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0)) == NULL )
+#else
+ if( pthread_create(&(mpfd->tid), NULL, PrintToFile_Consumer, mpfd) != 0 )
+#endif
+ {
+ /* pthread_create() error */
+ fclose(mpfd->file);
+ free(mpfd);
+ return(NULL);
+ }
+
+ /* we're still in the parent */
+ XPU_DEBUG_ONLY(printf("### parent started consumer thread.\n" ));
+ return(mpfd);
+}
+
+
+XPGetDocStatus XpuWaitForPrintFileChild( void *handle )
+{
+ MyPrintFileData *mpfd = (MyPrintFileData *)handle;
+ void *res;
+ XPGetDocStatus status;
+
+ /* Flush data a last time to make sure we send all data to Xprt and that
+ * the Xlib internal hooks have called a last time */
+ XFlush(mpfd->parent_pdpy);
+
+#ifdef XPU_USE_NSPR
+ if( PR_JoinThread(mpfd->prthread) != PR_SUCCESS )
+ perror("XpuWaitForPrintFileChild: PR_JoinThread() failure"); /* fixme(later): use NSPR error handling calls... */
+#else
+ if( XPU_TRACE(pthread_join(mpfd->tid, &res)) != 0 )
+ perror("XpuWaitForPrintFileChild: pthread_join() failure");
+#endif
+
+ status = mpfd->status;
+ free(handle);
+
+ XPU_DEBUG_ONLY(PrintXPGetDocStatus(status));
+ return(status);
+}
+
+#else /* XPU_USE_THREADS */
+/**
+ ** XpuPrintToFile() - fork() version
+ ** Create consumer thread which creates it's own display connection to print server
+ ** (a 2nd display connection/process is required to avoid deadlocks within Xlib),
+ ** registers (Xlib-internal) consumer callback (via XpGetDocumentData(3Xp)) and
+ ** processes/eats all incoming events via MyPrintToFileProc(). A final call to
+ ** MyPrintToFileProc() cleans-up all stuff and sets the "done" flag.
+ ** Note that these callbacks are called directly by Xlib while waiting for events in
+ ** XNextEvent() because XpGetDocumentData() registeres them as "internal" callbacks,
+ ** e.g. XNextEvent() does _not_ return before/after processing these events !!
+ **
+ ** Usage:
+ ** XpStartJob(pdpy, XPGetData);
+ ** handle = XpuPrintToFile(pdpy, pcontext, "myfile");
+ ** // render something
+ ** XpEndJob(); // or XpCancelJob()
+ ** status = XpuWaitForPrintFileChild(handle);
+ **
+ */
+typedef struct
+{
+ pid_t pid;
+ int pipe[2]; /* child-->parent communication pipe */
+ const char *displayname;
+ Display *pdpy;
+ Display *parent_pdpy;
+ XPContext pcontext;
+ const char *file_name;
+ FILE *file;
+ XPGetDocStatus status;
+ Bool done;
+} MyPrintFileData;
+
+
+static
+void *XpuPrintToFile( Display *pdpy, XPContext pcontext, const char *filename )
+{
+ MyPrintFileData *mpfd;
+
+ if( (mpfd = (MyPrintFileData *)malloc(sizeof(MyPrintFileData))) == NULL )
+ return(NULL);
+
+ /* create pipe */
+ if( pipe(mpfd->pipe) == -1 )
+ {
+ /* this should never happen, but... */
+ perror("XpuPrintToFile: cannot create pipe");
+ free(mpfd);
+ return(NULL);
+ }
+
+ mpfd->parent_pdpy = pdpy;
+ mpfd->displayname = XDisplayString(pdpy);
+ mpfd->pcontext = pcontext;
+ mpfd->file_name = filename;
+ mpfd->file = NULL;
+ mpfd->status = XPGetDocError;
+
+ /* make sure we can open the file for writing */
+ if( (mpfd->file = fopen(mpfd->file_name, "w")) == NULL )
+ {
+ /* fopen() error */
+ close(mpfd->pipe[1]);
+ close(mpfd->pipe[0]);
+ free(mpfd);
+ return(NULL);
+ }
+
+ /* its important to flush before we fork, to make sure that the
+ * XpStartJob gets through first in the parent
+ */
+ XFlush(pdpy);
+
+ mpfd->pid = fork();
+
+ if( mpfd->pid == 0 )
+ {
+ /* we're now in the fork()'ed child */
+ PrintToFile_Consumer(mpfd);
+ }
+ else if( mpfd->pid < 0 )
+ {
+ /* fork() error */
+ close(mpfd->pipe[1]);
+ close(mpfd->pipe[0]);
+ fclose(mpfd->file);
+ free(mpfd);
+ return(NULL);
+ }
+
+ /* we're still in the parent */
+ XPU_DEBUG_ONLY(printf("### parent fork()'ed consumer child.\n"));
+
+ /* child will write into file - we don't need it anymore here... :-) */
+ fclose(mpfd->file);
+ close(mpfd->pipe[1]);
+ return(mpfd);
+}
+
+
+XPGetDocStatus XpuWaitForPrintFileChild( void *handle )
+{
+ MyPrintFileData *mpfd = (MyPrintFileData *)handle;
+ int res;
+ XPGetDocStatus status = XPGetDocError; /* used when read() from pipe fails */
+
+ /* Flush data a last time to make sure we send all data to Xprt and that
+ * the Xlib internal hooks have called a last time */
+ XFlush(mpfd->parent_pdpy);
+
+ if( XPU_TRACE(waitpid(mpfd->pid, &res, 0)) == -1 )
+ perror("XpuWaitForPrintFileChild: waitpid failure");
+
+ /* read the status from the child */
+ if( read(mpfd->pipe[0], &status, sizeof(XPGetDocStatus)) != sizeof(XPGetDocStatus) )
+ {
+ perror("XpuWaitForPrintFileChild: can't read XPGetDocStatus");
+ }
+ close(mpfd->pipe[0]);
+
+ free(handle);
+
+ XPU_DEBUG_ONLY(PrintXPGetDocStatus(status));
+ return(status);
+}
+#endif /* XPU_USE_THREADS */
+
+
+static
+void MyPrintToFileProc( Display *pdpy,
+ XPContext pcontext,
+ unsigned char *data,
+ unsigned int data_len,
+ XPointer client_data )
+{
+ MyPrintFileData *mpfd = (MyPrintFileData *)client_data;
+
+ /* write to the file */
+ XPU_TRACE_CHILD((void)fwrite(data, data_len, 1, mpfd->file)); /* what about error handling ? */
+}
+
+
+static
+void MyFinishProc( Display *pdpy,
+ XPContext pcontext,
+ XPGetDocStatus status,
+ XPointer client_data )
+{
+ MyPrintFileData *mpfd = (MyPrintFileData *)client_data;
+
+ /* remove the file if unsuccessful */
+ if( status != XPGetDocFinished )
+ {
+ XPU_DEBUG_ONLY(printf("MyFinishProc: error %d\n", (int)status));
+ XPU_TRACE_CHILD(remove(mpfd->file_name));
+ }
+
+ XPU_TRACE_CHILD((void)fclose(mpfd->file)); /* what about error handling ? */
+
+ mpfd->status = status;
+ mpfd->done = True;
+}
+
+
+static
+#ifdef XPU_USE_NSPR
+void PrintToFile_Consumer( void *handle )
+#else
+void *PrintToFile_Consumer( void *handle )
+#endif
+{
+ MyPrintFileData *mpfd = (MyPrintFileData *)handle;
+ XEvent dummy;
+ struct timeval timeout;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 100000; /* 1/10 s */
+
+ XPU_DEBUG_ONLY(printf("### child running, getting data from '%s'.\n", mpfd->displayname));
+
+ /* we cannot reuse fork()'ed display handles - our child needs his own one */
+ if( (mpfd->pdpy = XPU_TRACE_CHILD(XOpenDisplay(mpfd->displayname))) == NULL )
+ {
+ perror("child cannot open display");
+#ifdef XPU_USE_NSPR
+ return;
+#else
+ return(NULL);
+#endif
+ }
+
+ mpfd->done = False;
+
+ /* register "consumer" callbacks */
+ if( XPU_TRACE_CHILD(XpGetDocumentData(mpfd->pdpy, mpfd->pcontext,
+ MyPrintToFileProc, MyFinishProc,
+ (XPointer)mpfd)) == 0 )
+ {
+ XPU_DEBUG_ONLY(printf("XpGetDocumentData cannot register callbacks\n"));
+#ifdef XPU_USE_NSPR
+ return;
+#else
+ return(NULL);
+#endif
+ }
+
+ /* loop forever - libXp has registered hidden event callbacks for the consumer
+ * callbacks - the finishCB will call set the "done" boolean after all...
+ */
+ while( mpfd->done != True )
+ {
+ XNextEventTimeout(mpfd->pdpy, &dummy, &timeout);
+ }
+
+ XCloseDisplay(mpfd->pdpy);
+
+#ifdef XPU_USE_THREADS
+#ifdef XPU_USE_NSPR
+ return;
+#else
+ return(NULL);
+#endif
+#else
+ /* write the status to the parent */
+ if( XPU_TRACE_CHILD(write(mpfd->pipe[1], &mpfd->status, sizeof(XPGetDocStatus))) != sizeof(XPGetDocStatus) )
+ {
+ perror("PrintToFile_Consumer: can't write XPGetDocStatus");
+ }
+
+ /* we don't do any free's or close's, as we are just
+ * going to exit, in fact, get out without calling any C++
+ * destructors, etc., as we don't want anything funny to happen
+ * to the parent
+ */
+ XPU_TRACE_CHILD(_exit(EXIT_SUCCESS));
+#endif /* XPU_USE_THREADS */
+}
+
+/* EOF. */