Back to index

lightning-sunbird  0.9+nobinonly
xprintutil.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the X11 print system utilities library.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "xprintutil.h"
00039  
00040 #include <stdlib.h>
00041 #include <string.h>
00042 #include <ctype.h>
00043 #include <stdio.h>
00044 #include <limits.h>
00045 #include <errno.h>
00046 #include <locale.h>
00047 
00048 #ifdef XPU_USE_NSPR
00049 #include "plstr.h"
00050 #undef strtok_r
00051 #define strtok_r(s1, s2, x) PL_strtok_r((s1), (s2), (x))
00052 #endif /* XPU_USE_NSPR */
00053 
00054 /* List of tokens which can be used to separate entries in the 
00055  * $XPSERVERLIST env var */
00056 static const char XPServerListSeparators[] = " \t\v\n\r\f";
00057 
00058 /* conformace only; X11 API does (currrently) not make use of |const|. 
00059  * If Xlib API gets fixed these macros can be turned into empty 
00060  * placeholders... (|#define MAKE_STRING_WRITABLE(x)|) :-)
00061  */
00062 #define MAKE_STRING_WRITABLE(str) (((str)?((str) = strdup(str)):0))
00063 #define FREE_WRITABLE_STRING(str) free((void *)(str))
00064 #define STRING_AS_WRITABLE(str) ((char *)(str))
00065 
00066 /* Local prototypes */
00067 static const char *XpuGetDefaultXpPrintername(void);
00068 static const char *XpuGetXpServerList( void );
00069 static const char *XpuEnumerateXpAttributeValue( const char *value, void **vcptr );
00070 static const char *XpuGetCurrentAttributeGroup( void **vcptr );
00071 static void XpuDisposeEnumerateXpAttributeValue( void **vc );
00072 static Bool XpuEnumerateMediumSourceSizes( Display *pdpy, XPContext pcontext,
00073                                            const char **tray_name,
00074                                            const char **medium_name, int *mbool, 
00075                                            float *ma1, float *ma2, float *ma3, float *ma4,
00076                                            void **vcptr );
00077 static void XpuDisposeEnumerateMediumSourceSizes( void **vc );
00078 
00079 /*
00080 ** XprintUtil functions start with Xpu
00081 **
00082 */
00083 
00084 int XpuCheckExtension( Display *pdpy )
00085 {
00086   char *display = XDisplayString(pdpy);
00087   short major = 0,
00088         minor = 0;
00089 
00090   if( XpQueryVersion(pdpy, &major, &minor) != 0 )
00091   {
00092     XPU_DEBUG_ONLY(printf("XpuCheckExtension: XpQueryVersion '%s' %d %d\n", XPU_NULLXSTR(display), (int)major, (int)minor));
00093     return(1);
00094   }
00095   else
00096   {
00097     XPU_DEBUG_ONLY(printf("XpuCheckExtension: XpQueryVersion '%s' returned 0(=Xprint not supported)\n", XPU_NULLXSTR(display)));
00098   }
00099   
00100   return(0);
00101 }
00102 
00103 /* Get the default printer name from the XPRINTER env var.
00104  * If XPRINTER env var is not present looks for PDPRINTER, LPDEST, and
00105  * PRINTER (in that order)
00106  * See CDE's DtPrintSetupBox(3) manual page, too...
00107  */
00108 static
00109 const char *XpuGetDefaultXpPrintername(void)
00110 {
00111   const char *s;
00112   /* BUG/TODO: XpPrinter resource needs to be sourced, too... */
00113   s = getenv("XPRINTER");
00114   if( !s )
00115   {
00116     s = getenv("PDPRINTER");
00117     if( !s )
00118     {
00119       s = getenv("LPDEST");
00120       if( !s )
00121       {
00122         s = getenv("PRINTER");
00123       }
00124     }
00125   }  
00126   return s;
00127 }
00128 
00129 static
00130 const char *XpuGetXpServerList( void )
00131 {
00132   const char *s;
00133   /* BUG/TODO: XpServerList resource needs to be sourced first, then append 
00134    * contents of XPSERVERLIST, then remove duplicates...
00135    */
00136   s = getenv("XPSERVERLIST"); 
00137   if( s == NULL )
00138     s = "";
00139     
00140   return(s);
00141 }
00142 
00143 
00144 Bool XpuXprintServersAvailable( void )
00145 {
00146   const char *s;
00147   int         c = 0;
00148   /* BUGs/ToDo:
00149    * - XpServerList resource needs to be sourced, too...
00150    *   (see comment for |XpuGetXpServerList|, too)
00151    * - There should be some validation whether the server entries
00152    *   are
00153    *     a) valid (easy :)
00154    *   and
00155    *     b) available (hard to implement when XOpenDisplay() should be avoided)
00156    */
00157   s = getenv("XPSERVERLIST");
00158   /* Check if serverlist is non-empty */
00159   if (s)
00160   {
00161     while( *s++ )
00162     {
00163       if( !isspace(*s) )
00164         c++;
00165     }
00166   }
00167   /* a valid server name must at least contain the ':'-seperator
00168    * and a number (e.g. ":1") */
00169   return( c >= 2 );
00170 }
00171 
00172 
00173 static 
00174 int XpuGetPrinter2( char *printer, char *display, Display **pdpyptr, XPContext *pcontextptr )
00175 {
00176   Display   *pdpy;
00177   XPContext  pcontext;
00178   
00179   XPU_DEBUG_ONLY(printf("XpuGetPrinter2: probing display '%s' for '%s'\n", XPU_NULLXSTR(display), XPU_NULLXSTR(printer)));
00180   
00181   if( (pdpy = XOpenDisplay(display)) != NULL )
00182   {
00183     if( XpuCheckExtension(pdpy) )
00184     {
00185       XPPrinterList list;
00186       int           list_count;
00187       
00188       /* get list of available printers... */
00189       list = XpGetPrinterList(pdpy, printer, &list_count);        
00190       if( list != NULL ) XpFreePrinterList(list);
00191       
00192       /* ...and check if printer exists... */
00193       if( (list != NULL) && (list_count > 0) )
00194       {
00195         if( (pcontext = XpCreateContext(pdpy, printer)) != None )
00196         {
00197           *pdpyptr     = pdpy;
00198           *pcontextptr = pcontext;
00199           return(1);
00200         }
00201         
00202         XPU_DEBUG_ONLY(printf("XpuGetPrinter2: could not create print context for '%s'\n", XPU_NULLXSTR(printer)));
00203       }
00204     }
00205     else
00206     {
00207       XPU_DEBUG_ONLY(printf("display '%s' does not support the Xprint extension\n", XPU_NULLXSTR(display)));
00208     }  
00209 
00210     XCloseDisplay(pdpy);
00211     return(0);
00212   }
00213   else
00214   {
00215     XPU_DEBUG_ONLY(printf("could not open display '%s'\n", XPU_NULLXSTR(display)));
00216     return(0);
00217   }
00218 }
00219 
00220 
00221 /* acceps "printer" or "printer@display" */
00222 int XpuGetPrinter( const char *arg_printername, Display **pdpyptr, XPContext *pcontextptr )
00223 {
00224   Display       *pdpy;
00225   XPContext      pcontext;
00226   char          *printername;
00227   char          *s;
00228   char          *tok_lasts;
00229   
00230   *pdpyptr     = NULL;
00231   *pcontextptr = None;
00232   
00233   XPU_DEBUG_ONLY(printf("XpuGetPrinter: looking for '%s'\n", XPU_NULLXSTR(arg_printername)));
00234   
00235   /* strtok_r will modify string - duplicate it first... */
00236   printername = strdup(arg_printername);
00237   if( printername == NULL )
00238     return(0);
00239   
00240   if( (s = strtok_r(printername, "@", &tok_lasts)) != NULL )
00241   {
00242     char *name = s;
00243     char *display = strtok_r(NULL, "@", &tok_lasts);
00244     
00245     /* if we have a display - open it and grab printer */
00246     if( display != NULL )
00247     {
00248       if( XpuGetPrinter2(name, display, pdpyptr, pcontextptr) )
00249       {
00250         free(printername);
00251         return(1);
00252       }
00253     }
00254     /* if we did not get a display, travel througth all displays */
00255     else
00256     {
00257       char *sl = strdup(XpuGetXpServerList());
00258       
00259       if( sl != NULL )
00260       {
00261         for( display = strtok_r(sl, XPServerListSeparators, &tok_lasts) ; 
00262              display != NULL ; 
00263              display = strtok_r(NULL, XPServerListSeparators, &tok_lasts) )
00264         {       
00265           if( XpuGetPrinter2(name, display, pdpyptr, pcontextptr) )
00266           {
00267             free(sl);
00268             free(printername);
00269             return(1);
00270           } 
00271         }
00272         
00273         free(sl);
00274       }
00275     }
00276   }
00277   
00278   free(printername);
00279   XPU_DEBUG_ONLY(printf("XpuGetPrinter: failure\n"));
00280   
00281   return(0);
00282 }
00283 
00284 
00285 void XpuClosePrinterDisplay(Display *pdpy, XPContext pcontext)
00286 {
00287   if( pdpy )
00288   {
00289     if( pcontext != None )
00290       XpDestroyContext(pdpy, pcontext);
00291       
00292     XCloseDisplay(pdpy);
00293   }
00294 }
00295 
00296 void XpuSetOneAttribute( Display *pdpy, XPContext pcontext, 
00297                          XPAttributes type, const char *attribute_name, const char *value, XPAttrReplacement replacement_rule )
00298 {
00299   /* Alloc buffer for sprintf() stuff below */
00300   char *buffer = (char *)malloc(strlen(attribute_name)+strlen(value)+4);
00301   
00302   if( buffer != NULL )
00303   {
00304     sprintf(buffer, "%s: %s", attribute_name, value);      
00305     XpSetAttributes(pdpy, pcontext, type, buffer, replacement_rule);
00306     free(buffer);
00307   }  
00308 }
00309 
00310 void XpuSetOneLongAttribute( Display *pdpy, XPContext pcontext, 
00311                              XPAttributes type, const char *attribute_name, long value, XPAttrReplacement replacement_rule )
00312 {
00313   /* Alloc buffer for sprintf() stuff below */
00314   char *buffer = (char *)malloc(strlen(attribute_name)+32+4);
00315   
00316   if( buffer != NULL )
00317   {
00318     sprintf(buffer, "%s: %ld", attribute_name, value);      
00319     XpSetAttributes(pdpy, pcontext, type, buffer, replacement_rule);
00320     free(buffer);
00321   }  
00322 }
00323 
00324 /* Check if attribute value is supported or not
00325  * Use this function _only_ if XpuGetSupported{Job,Doc,Page}Attributes()
00326  * does not help you...
00327  */
00328 int XpuCheckSupported( Display *pdpy, XPContext pcontext, XPAttributes type, const char *attribute_name, const char *query )
00329 {
00330   char *value;
00331   void *tok_lasts;
00332   
00333   MAKE_STRING_WRITABLE(attribute_name);
00334   if( attribute_name == NULL )
00335     return(0);
00336     
00337   value = XpGetOneAttribute(pdpy, pcontext, type, STRING_AS_WRITABLE(attribute_name));   
00338   
00339   XPU_DEBUG_ONLY(printf("XpuCheckSupported: XpGetOneAttribute(%s) returned '%s'\n", XPU_NULLXSTR(attribute_name), XPU_NULLXSTR(value)));
00340 
00341   FREE_WRITABLE_STRING(attribute_name);
00342   
00343   if( value != NULL )
00344   {
00345     const char *s;
00346     
00347     for( s = XpuEnumerateXpAttributeValue(value, &tok_lasts) ; s != NULL ; s = XpuEnumerateXpAttributeValue(NULL, &tok_lasts) )
00348     {
00349       XPU_DEBUG_ONLY(printf("XpuCheckSupported: probing '%s'=='%s'\n", XPU_NULLXSTR(s), XPU_NULLXSTR(query)));
00350       if( !strcmp(s, query) )
00351       {
00352         XFree(value);
00353         XpuDisposeEnumerateXpAttributeValue(&tok_lasts);
00354         return(1);
00355       }  
00356     }
00357     
00358     XpuDisposeEnumerateXpAttributeValue(&tok_lasts);
00359     XFree(value);
00360   }  
00361   
00362   return(0);
00363 }
00364 
00365 
00366 int XpuSetJobTitle( Display *pdpy, XPContext pcontext, const char *title )
00367 {
00368   if( XpuGetSupportedJobAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_JOB_NAME )
00369   {
00370     char *encoded_title;
00371     
00372     encoded_title = XpuResourceEncode(title);
00373     if (!encoded_title)
00374       return(0);
00375     XpuSetOneAttribute(pdpy, pcontext, XPJobAttr, "*job-name", encoded_title, XPAttrMerge);
00376     XpuResourceFreeString(encoded_title);
00377     return(1);
00378   }
00379   else
00380   {
00381     XPU_DEBUG_ONLY(printf("XpuSetJobTitle: XPUATTRIBUTESUPPORTED_JOB_NAME not supported ('%s')\n", XPU_NULLXSTR(title)));
00382     return(0); 
00383   }  
00384 }
00385     
00386 int XpuGetOneLongAttribute( Display *pdpy, XPContext pcontext, XPAttributes type, const char *attribute_name, long *result )
00387 {
00388   char *s;
00389   
00390   MAKE_STRING_WRITABLE(attribute_name);
00391   if( attribute_name == NULL )
00392     return(0);
00393   s = XpGetOneAttribute(pdpy, pcontext, type, STRING_AS_WRITABLE(attribute_name));
00394   
00395   if(s && *s) 
00396   {
00397     long tmp;
00398     
00399     XPU_DEBUG_ONLY(printf("XpuGetOneLongAttribute: '%s' got '%s'\n", XPU_NULLXSTR(attribute_name), XPU_NULLXSTR(s)));
00400     
00401     tmp = strtol(s, (char **)NULL, 10);
00402     
00403     if( !(((tmp == 0L) || (tmp == LONG_MIN) || (tmp == LONG_MAX)) && 
00404           ((errno == ERANGE) || (errno == EINVAL))) )
00405     {
00406       *result = tmp;
00407       XFree(s);
00408       XPU_DEBUG_ONLY(printf("XpuGetOneLongAttribute: result %ld\n", *result));
00409       FREE_WRITABLE_STRING(attribute_name);
00410       return(1);
00411     }            
00412   }
00413   
00414   if( s != NULL ) 
00415     XFree(s);
00416   
00417   FREE_WRITABLE_STRING(attribute_name);
00418   
00419   return(0);
00420 }
00421 
00422 
00423 #ifdef DEBUG
00424 /* debug only */
00425 void dumpXpAttributes( Display *pdpy, XPContext pcontext )
00426 {
00427   char *s;
00428   printf("------------------------------------------------\n");
00429   printf("--> Job\n%s\n",         s=XpuGetJobAttributes(pdpy, pcontext));     XFree(s);
00430   printf("--> Doc\n%s\n",         s=XpuGetDocAttributes(pdpy, pcontext));     XFree(s);
00431   printf("--> Page\n%s\n",        s=XpuGetPageAttributes(pdpy, pcontext));    XFree(s);
00432   printf("--> Printer\n%s\n",     s=XpuGetPrinterAttributes(pdpy, pcontext)); XFree(s);
00433   printf("--> Server\n%s\n",      s=XpuGetServerAttributes(pdpy, pcontext));  XFree(s);
00434   printf("image resolution %d\n", (int)XpGetImageResolution(pdpy, pcontext));
00435   printf("------------------------------------------------\n");
00436 }
00437 #endif /* DEBUG */    
00438 
00439 
00440 typedef struct XpuIsNotifyEventContext_
00441 {
00442   int event_base;
00443   int detail;
00444 } XpuIsNotifyEventContext;
00445 
00446 static
00447 Bool IsXpNotifyEvent( Display *pdpy, XEvent *ev, XPointer arg )
00448 {
00449   Bool match;
00450   XpuIsNotifyEventContext *context = (XpuIsNotifyEventContext *)arg;
00451   XPPrintEvent *pev = (XPPrintEvent *)ev;
00452   
00453   match = pev->type == (context->event_base+XPPrintNotify) && 
00454           pev->detail == context->detail;
00455 
00456   XPU_DEBUG_ONLY(printf("XpuWaitForPrintNotify: %d=IsXpNotifyEvent(%d,%d)\n",
00457                  (int)match,
00458                  (int)pev->type,
00459                  (int)pev->detail));
00460   return match;
00461 }
00462 
00463 void XpuWaitForPrintNotify( Display *pdpy, int xp_event_base, int detail )
00464 {
00465   XEvent                  dummy;
00466   XpuIsNotifyEventContext matchcontext;
00467 
00468   matchcontext.event_base = xp_event_base;
00469   matchcontext.detail     = detail;
00470   XIfEvent(pdpy, &dummy, IsXpNotifyEvent, (XPointer)&matchcontext);
00471 }      
00472 
00473 static
00474 const char *skip_matching_brackets(const char *start)
00475 {
00476   const char *s     = start;
00477   int         level = 0;
00478   
00479   if( !start )
00480       return(NULL);
00481   
00482   do
00483   {
00484     switch(*s++)
00485     {
00486       case '\0': return(NULL);
00487       case '{': level++; break;
00488       case '}': level--; break;
00489     }
00490   } while(level > 0);
00491 
00492   return(s);
00493 }
00494 
00495 
00496 static
00497 const char *search_next_space(const char *start)
00498 {
00499   const char *s     = start;
00500   int         level = 0;
00501   
00502   if( !start )
00503     return(NULL);
00504   
00505   for(;;)
00506   {   
00507     if( isspace(*s) )
00508       return(s);
00509 
00510     if( *s=='\0' )
00511       return(NULL);      
00512 
00513     s++;
00514   }
00515 }
00516 
00517 /* PRIVATE context data for XpuEnumerateXpAttributeValue() */
00518 typedef struct _XpuAttributeValueEnumeration
00519 {
00520   char   *value;
00521   size_t  original_value_len; /* original length of value */
00522   char   *group;
00523   char   *start;
00524   char   *s;
00525 } XpuAttributeValueEnumeration;
00526 
00527 
00528 /* Hacked parser for Xp values and enumerations */
00529 static
00530 const char *XpuEnumerateXpAttributeValue( const char *value, void **vcptr )
00531 {
00532   XpuAttributeValueEnumeration **cptr = (XpuAttributeValueEnumeration **)vcptr;
00533   XpuAttributeValueEnumeration  *context;
00534   const char                    *tmp;
00535   
00536   if( !cptr )
00537     return(NULL);
00538     
00539   if( value )
00540   {
00541     XpuAttributeValueEnumeration *e;
00542     const char *s = value;
00543     Bool        isGroup = FALSE;
00544   
00545     e = (XpuAttributeValueEnumeration *)malloc(sizeof(XpuAttributeValueEnumeration));
00546     if( !e )
00547       return NULL;
00548   
00549     /* Skip leading '{'. */
00550     while(*s=='{' && isGroup==FALSE)
00551     {
00552       s++;
00553       isGroup = TRUE;
00554     }  
00555     /* Skip leading blanks. */
00556     while(isspace(*s))
00557       s++;
00558     
00559     e->group = NULL;
00560     
00561     /* Read group name. */
00562     if( isGroup )
00563     { 
00564       tmp = s;  
00565       while(!isspace(*s))
00566         s++;
00567       if(strncmp(tmp, "''", s-tmp) != 0)
00568       {
00569         e->group = strdup(tmp);
00570         e->group[s-tmp] = '\0';
00571       }
00572     }
00573   
00574     e->original_value_len = strlen(s);
00575     e->value = (char *)malloc(e->original_value_len+4); /* We may look up to three bytes beyond the string */
00576     strcpy(e->value, s);
00577     memset(e->value+e->original_value_len+1, 0, 3); /* quad termination */
00578     e->start = e->s = e->value;
00579     
00580     *cptr = e;
00581   }
00582   
00583   context = *cptr;
00584   
00585   if( !context || !context->s )
00586     return(NULL);
00587    
00588   /* Skip leading blanks, '\'' or '}' */
00589   while(isspace(*(context->s)) || *(context->s)=='\'' /*|| *(context->s)=='}'*/ )
00590     context->s++;
00591 
00592   if( *(context->s) == '\0' )
00593     return(NULL);
00594 
00595   context->start = context->s;
00596   if( *(context->start) == '{' )
00597     context->s = (char *)skip_matching_brackets(context->start);
00598   else
00599     context->s = (char *)search_next_space(context->start);
00600     
00601   /* end of string reached ? */
00602   if( context->s )
00603   {   
00604     *(context->s) = '\0';
00605     context->s++;
00606   }
00607   
00608   /* Check if we reached a new attribute group */
00609   tmp = context->start;
00610   while(isspace(*tmp))
00611     tmp++;   
00612   if( *tmp=='}' )
00613   {
00614     void *prev_cptr = *vcptr;
00615     
00616     tmp+=2; /* We have 3*'\0' at the end of the string - this is legal! */
00617     if( *tmp!='\0' )
00618     {
00619       const char *ret;
00620    
00621       /* Start the parser again */
00622       *vcptr = NULL;
00623       ret = XpuEnumerateXpAttributeValue(tmp, vcptr);
00624     
00625       /* Free old context */
00626       XpuDisposeEnumerateXpAttributeValue(&prev_cptr);
00627     
00628       return(ret);
00629     }
00630     else
00631     {
00632       return(NULL);
00633     }
00634   }
00635   
00636   return(context->start);   
00637 }
00638 
00639 /* Get enumeration group for last string returned by |XpuEnumerateXpAttributeValue|... */
00640 static
00641 const char *XpuGetCurrentAttributeGroup( void **vcptr )
00642 {
00643   XpuAttributeValueEnumeration **cptr = (XpuAttributeValueEnumeration **)vcptr;
00644   if( !cptr )
00645     return(NULL);
00646   if( !*cptr )
00647     return(NULL);
00648     
00649   return((*cptr)->group);
00650 }
00651 
00652 
00653 static
00654 void XpuDisposeEnumerateXpAttributeValue( void **vc )
00655 { 
00656   if( vc )
00657   {
00658     XpuAttributeValueEnumeration *context = *((XpuAttributeValueEnumeration **)vc);
00659     free(context->value);
00660     if(context->group)
00661       free(context->group);
00662     free(context);   
00663   }
00664 }
00665 
00666 /* parse a paper size string 
00667  * (example: '{na-letter False {6.3500 209.5500 6.3500 273.0500}}') */
00668 static
00669 Bool XpuParseMediumSourceSize( const char *value, 
00670                                const char **medium_name, int *mbool, 
00671                                float *ma1, float *ma2, float *ma3, float *ma4 )
00672 {
00673   const char *s;
00674   char       *d, 
00675              *name;
00676   char       *boolbuf;
00677   size_t      value_len;
00678   int         num_input_items;
00679   const char *cur_locale;
00680   
00681   if( value && value[0]!='{' && value[0]!='\0' )
00682     return(False);
00683     
00684   value_len = strlen(value);
00685   
00686   /* alloc buffer for |medium_name| and |boolbuf| in one step
00687    * (both must be large enougth to hold at least |strlen(value)+1| bytes) */  
00688   name = (char *)malloc(value_len*2 + 4);
00689   boolbuf = name + value_len+2; /* |boolbuf| starts directly after |name| */
00690   
00691   /* remove '{' && '}' */
00692   s = value;
00693   d = name;
00694   do
00695   {
00696     *d = tolower(*s);
00697     
00698     if( *s!='{' && *s!='}' )
00699       d++;
00700     
00701     s++;  
00702   }
00703   while(*s);
00704   *d = '\0';
00705     
00706   /* separate medium name from string */
00707   d = (char *)search_next_space(name);
00708   if( !d )
00709   {
00710     free(name);
00711     return(False);
00712   }  
00713   *d = '\0';
00714   *medium_name = name;
00715   
00716   /* ... continue to parse the remaining string... */
00717   d++;
00718   
00719 
00720   /* Force C/POSIX radix for scanf()-parsing (see bug 131831 ("Printing
00721    * does not work in de_AT@euro locale")), do the parsing and restore
00722    * the original locale.
00723    * XXX: This may affect all threads and not only the calling one...
00724    */
00725   {
00726 #define CUR_LOCALE_SIZE 256
00727     char cur_locale[CUR_LOCALE_SIZE+1];
00728     strncpy(cur_locale, setlocale(LC_NUMERIC, NULL), CUR_LOCALE_SIZE);
00729     cur_locale[CUR_LOCALE_SIZE]='\0';
00730     setlocale(LC_NUMERIC, "C"); 
00731     num_input_items = sscanf(d, "%s %f %f %f %f", boolbuf, ma1, ma2, ma3, ma4);
00732     setlocale(LC_NUMERIC, cur_locale);
00733 #undef CUR_LOCALE_SIZE
00734   }
00735 
00736   if( num_input_items != 5 )
00737   {
00738     free(name);
00739     return(False);
00740   }
00741 
00742   if( !strcmp(boolbuf, "true") )
00743     *mbool = True;
00744   else if( !strcmp(boolbuf, "false") )
00745     *mbool = False;
00746   else
00747   {
00748     free(name);
00749     return(False);    
00750   }
00751   return(True);
00752 }
00753 
00754 
00755 /* parse a paper size string 
00756  * (example: '{na-letter False {6.3500 209.5500 6.3500 273.0500}}') */
00757 static
00758 Bool XpuEnumerateMediumSourceSizes( Display *pdpy, XPContext pcontext,
00759                                     const char **tray_name,
00760                                     const char **medium_name, int *mbool, 
00761                                     float *ma1, float *ma2, float *ma3, float *ma4,
00762                                     void **vcptr )
00763 {
00764   const char *medium_spec;
00765   const char *value = NULL;
00766   
00767   if( pdpy && pcontext )
00768   {
00769     value = XpGetOneAttribute(pdpy, pcontext, XPPrinterAttr, "medium-source-sizes-supported");
00770     if( !value )
00771       return(False);
00772   }
00773 
00774   while(1)
00775   {  
00776     medium_spec = XpuEnumerateXpAttributeValue(value, vcptr);
00777     
00778     if( value )
00779     {
00780       XFree((void *)value);
00781       value = NULL;
00782     }
00783 
00784     /* enumeration done? */
00785     if( !medium_spec )
00786       return(False);
00787 
00788     if (XpuParseMediumSourceSize(medium_spec, 
00789                                  medium_name, mbool, 
00790                                  ma1, ma2, ma3, ma4))
00791     {
00792       *tray_name = XpuGetCurrentAttributeGroup(vcptr);
00793       return(True);
00794     }
00795     else
00796     {
00797       /* Should never ever happen! */
00798       fprintf(stderr, "XpuEnumerateMediumSourceSize: error parsing '%s'\n", medium_spec);
00799     }
00800   }
00801   /* not reached */   
00802 }
00803 
00804 static
00805 void XpuDisposeEnumerateMediumSourceSizes( void **vc )
00806 {
00807   XpuDisposeEnumerateXpAttributeValue(vc);
00808 }  
00809 
00810 
00811 /* future: Migrate this functionality into |XpGetPrinterList| - just do
00812  * not pass a |Display *| to |XpGetPrinterList|
00813  */
00814 XPPrinterList XpuGetPrinterList( const char *printer, int *res_list_count )
00815 {
00816   XPPrinterRec *rec = NULL;
00817   int           rec_count = 1; /* Allocate one more |XPPrinterRec| structure
00818                                 * as terminator */
00819   char         *sl;
00820   const char   *default_printer_name = XpuGetDefaultXpPrintername();
00821   int           default_printer_rec_index = -1;
00822 
00823   if( !res_list_count )
00824     return(NULL); 
00825   
00826   sl = strdup(XpuGetXpServerList());
00827   MAKE_STRING_WRITABLE(printer);
00828     
00829   if( sl != NULL )
00830   {
00831     char *display;
00832     char *tok_lasts;
00833     
00834     for( display = strtok_r(sl, XPServerListSeparators, &tok_lasts) ; 
00835          display != NULL ; 
00836          display = strtok_r(NULL, XPServerListSeparators, &tok_lasts) )
00837     {
00838       Display *pdpy;
00839       
00840       if( (pdpy = XOpenDisplay(display)) != NULL )
00841       {
00842         XPPrinterList list;
00843         int           list_count;
00844         size_t        display_len = strlen(display);
00845 
00846         /* get list of available printers... */
00847         list = XpGetPrinterList(pdpy, STRING_AS_WRITABLE(printer), &list_count);        
00848       
00849         if( list && list_count )
00850         {
00851           int i;
00852           
00853           for( i = 0 ; i < list_count ; i++ )
00854           {
00855             char *s;
00856             
00857             /* Workaround for http://bugzilla.mozilla.org/show_bug.cgi?id=193499 
00858              * ("Xprint print/print preview crashes Mozilla") where the Solaris
00859              * Xprt may create invalid entries (e.g. |XpGetPrinterList| will
00860              * return |list[i].name==NULL| due to empty lines in the printer list.
00861              */
00862             if( !list[i].name )
00863               continue;
00864             
00865             rec_count++;
00866             rec = (XPPrinterRec *)realloc(rec, sizeof(XPPrinterRec)*rec_count);
00867             if( !rec ) /* failure */
00868               break;
00869               
00870             s = (char *)malloc(strlen(list[i].name)+display_len+4);
00871             sprintf(s, "%s@%s", list[i].name, display);
00872             rec[rec_count-2].name = s;
00873             rec[rec_count-2].desc = (list[i].desc)?(strdup(list[i].desc)):(NULL);
00874             
00875             /* Test for default printer (if the user set one).*/
00876             if( default_printer_name )
00877             {
00878               /* Default_printer_name may either contain the FQPN(=full
00879                * qualified printer name ("foo@myhost:5") or just the name
00880                * ("foo")) */
00881               if( (!strcmp(list[i].name, default_printer_name)) ||
00882                   (!strcmp(s,            default_printer_name)) )
00883               {
00884                 /* Remember index of default printer that we can swap it to 
00885                  * the head of the array below... */
00886                 default_printer_rec_index = rec_count-2;
00887               }
00888             }  
00889           }
00890           
00891           XpFreePrinterList(list);
00892         }
00893                  
00894         XCloseDisplay(pdpy);
00895       }
00896     }
00897     
00898     free(sl);
00899   }
00900   
00901   if( rec )
00902   {
00903     /* users: DO NOT COUNT ON THIS DETAIL 
00904      * (this is only to make current impl. of XpuFreePrinterList() easier)
00905      * I may remove this implementation detail in a later revision of
00906      * the library!
00907      */
00908     rec[rec_count-1].name = NULL;
00909     rec[rec_count-1].desc = NULL;
00910     rec_count--;
00911   }
00912   else
00913   {
00914     rec_count = 0;
00915   }
00916   
00917   /* The default printer is always the first one in the printer list... */
00918   if( (default_printer_rec_index != -1) && rec )
00919   {
00920     XPPrinterRec tmp;
00921     tmp = rec[0];
00922     rec[0] = rec[default_printer_rec_index];
00923     rec[default_printer_rec_index] = tmp;
00924   }
00925     
00926   *res_list_count = rec_count;
00927   FREE_WRITABLE_STRING(printer);
00928   return(rec);
00929 }      
00930 
00931 
00932 void XpuFreePrinterList( XPPrinterList list )
00933 {
00934   if( list )
00935   {
00936     XPPrinterRec *curr = list;
00937   
00938     /* See the warning abouve about using this implementation detail for
00939      * checking for the list's end... */
00940     while( curr->name != NULL )
00941     {
00942       free(curr->name);
00943       if(curr->desc)
00944         free(curr->desc);
00945       curr++;
00946     }
00947   
00948     free(list);
00949   }
00950 }
00951 
00952 /* Set number of copies to print from this document */
00953 int XpuSetDocumentCopies( Display *pdpy, XPContext pcontext, long num_copies )
00954 {
00955   if( XpuGetSupportedDocAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_COPY_COUNT)
00956   {
00957     XpuSetOneLongAttribute(pdpy, pcontext, XPDocAttr, "*copy-count", num_copies, XPAttrMerge);
00958     return(1);
00959   }
00960   else
00961   {
00962     XPU_DEBUG_ONLY(printf("XpuSetContentOrientation: XPUATTRIBUTESUPPORTED_COPY_COUNT not supported\n"));
00963        
00964     /* Failure... */
00965     return(0);
00966   }  
00967 }
00968 
00969 XpuMediumSourceSizeList XpuGetMediumSourceSizeList( Display *pdpy, XPContext pcontext, int *numEntriesPtr )
00970 {
00971   XpuMediumSourceSizeList list = NULL;
00972   int                     rec_count = 1; /* allocate one more |XpuMediumSourceSizeRec| structure
00973                                           * as terminator */
00974   Bool                    status;
00975   float                   ma1,
00976                           ma2,
00977                           ma3,
00978                           ma4;
00979   char                   *value;
00980   void                   *tok_lasts;
00981   const char             *tray_name,
00982                          *medium_name;
00983   int                     mbool;
00984   const char             *default_tray,
00985                          *default_medium;
00986   int                     default_medium_rec_index = -1;
00987   
00988   default_tray   = XpGetOneAttribute(pdpy, pcontext, XPDocAttr, "default-input-tray");
00989   if(!default_tray)
00990   {
00991     fprintf(stderr, "XpuGetMediumSourceSizeList: Internal error, no 'default-input-tray' found.\n");
00992     return(NULL);
00993   }
00994   default_medium = XpGetOneAttribute(pdpy, pcontext, XPDocAttr, "default-medium");
00995   if(!default_medium)
00996   {
00997     fprintf(stderr, "XpuGetMediumSourceSizeList: Internal error, no 'default-medium' found.\n");
00998     XFree((void *)default_tray);
00999     return(NULL);
01000   }
01001   
01002   for( status = XpuEnumerateMediumSourceSizes(pdpy, pcontext, &tray_name, &medium_name, &mbool,
01003                                               &ma1, &ma2, &ma3, &ma4, &tok_lasts) ;
01004        status != False ;
01005        status = XpuEnumerateMediumSourceSizes(NULL, None,     &tray_name, &medium_name, &mbool, 
01006                                               &ma1, &ma2, &ma3, &ma4, &tok_lasts) )
01007   {
01008     rec_count++;
01009     list = (XpuMediumSourceSizeRec *)realloc(list, sizeof(XpuMediumSourceSizeRec)*rec_count);
01010     if( !list )
01011       return(NULL);
01012     
01013     list[rec_count-2].tray_name   = (tray_name)?(strdup(tray_name)):(NULL);
01014     list[rec_count-2].medium_name = strdup(medium_name);
01015     list[rec_count-2].mbool       = mbool;
01016     list[rec_count-2].ma1         = ma1;
01017     list[rec_count-2].ma2         = ma2;
01018     list[rec_count-2].ma3         = ma3;
01019     list[rec_count-2].ma4         = ma4;
01020     
01021     /* Default medium ? */
01022     if( (!strcmp(medium_name, default_medium)) && 
01023         ((tray_name && (*default_tray))?(!strcmp(tray_name, default_tray)):(True)) )
01024     {
01025       default_medium_rec_index = rec_count-2;
01026     }
01027   }  
01028 
01029   XpuDisposeEnumerateMediumSourceSizes(&tok_lasts);
01030 
01031   if( list )
01032   {
01033     /* users: DO NOT COUNT ON THIS DETAIL 
01034      * (this is only to make current impl. of XpuFreeMediumSourceSizeList() easier)
01035      * I may remove this implementation detail in a later revision of
01036      * the library! */
01037     list[rec_count-1].tray_name  = NULL;
01038     list[rec_count-1].medium_name = NULL;
01039     rec_count--;
01040   }
01041   else
01042   {
01043     rec_count = 0;
01044   }
01045 
01046   /* Make the default medium always the first item in the list... */
01047   if( (default_medium_rec_index != -1) && list )
01048   {
01049     XpuMediumSourceSizeRec tmp;
01050     tmp = list[0];
01051     list[0] = list[default_medium_rec_index];
01052     list[default_medium_rec_index] = tmp;
01053   }
01054 
01055   *numEntriesPtr = rec_count; 
01056   return(list);
01057 }
01058 
01059 void XpuFreeMediumSourceSizeList( XpuMediumSourceSizeList list )
01060 {
01061   if( list )
01062   {
01063     XpuMediumSourceSizeRec *curr = list;
01064   
01065     /* See the warning abouve about using this implementation detail for
01066      * checking for the list's end... */
01067     while( curr->medium_name != NULL )
01068     {
01069       if( curr->tray_name)
01070         free((void *)curr->tray_name);
01071       free((void *)curr->medium_name);
01072       curr++;
01073     }
01074   
01075     free(list);
01076   }
01077 }
01078 
01079 static
01080 int XpuSetMediumSourceSize( Display *pdpy, XPContext pcontext, XPAttributes type, XpuMediumSourceSizeRec *medium_spec )
01081 {
01082   /* Set the "default-medium" and "*default-input-tray" 
01083    * (if |XpuEnumerateMediumSourceSizes| returned one) XPDocAttr's
01084    * attribute and return */
01085   if (medium_spec->tray_name)
01086   {
01087     XpuSetOneAttribute(pdpy, pcontext, type, "*default-input-tray", medium_spec->tray_name, XPAttrMerge);
01088   }
01089   XpuSetOneAttribute(pdpy, pcontext, type, "*default-medium", medium_spec->medium_name, XPAttrMerge);
01090   
01091   return( 1 );
01092 }
01093 
01094 /* Set document medium size */
01095 int XpuSetDocMediumSourceSize( Display *pdpy, XPContext pcontext, XpuMediumSourceSizeRec *medium_spec )
01096 {
01097   XpuSupportedFlags doc_supported_flags;
01098   
01099   doc_supported_flags = XpuGetSupportedDocAttributes(pdpy, pcontext);
01100 
01101   if( (doc_supported_flags & XPUATTRIBUTESUPPORTED_DEFAULT_MEDIUM) == 0 )
01102     return( 0 );
01103     
01104   if (medium_spec->tray_name)
01105   {
01106     if( (doc_supported_flags & XPUATTRIBUTESUPPORTED_DEFAULT_INPUT_TRAY) == 0 )
01107       return( 0 );  
01108   }
01109 
01110   return XpuSetMediumSourceSize(pdpy, pcontext, XPDocAttr, medium_spec);
01111 }
01112 
01113 /* Set page medium size */
01114 int XpuSetPageMediumSourceSize( Display *pdpy, XPContext pcontext, XpuMediumSourceSizeRec *medium_spec )
01115 {
01116   XpuSupportedFlags page_supported_flags;
01117   
01118   page_supported_flags = XpuGetSupportedPageAttributes(pdpy, pcontext);
01119 
01120   if( (page_supported_flags & XPUATTRIBUTESUPPORTED_DEFAULT_MEDIUM) == 0 )
01121     return( 0 );
01122     
01123   if (medium_spec->tray_name)
01124   {
01125     if( (page_supported_flags & XPUATTRIBUTESUPPORTED_DEFAULT_INPUT_TRAY) == 0 )
01126       return( 0 );  
01127   }
01128 
01129   return XpuSetMediumSourceSize(pdpy, pcontext, XPPageAttr, medium_spec);
01130 }
01131 
01132 #ifndef ABS
01133 #define ABS(x) ((x)<0?-(x):(x))
01134 #endif /* ABS */
01135 #define MORE_OR_LESS_EQUAL(a, b, tolerance) (ABS((a) - (b)) <= (tolerance))
01136 
01137 XpuMediumSourceSizeRec *
01138 XpuFindMediumSourceSizeBySize( XpuMediumSourceSizeList mlist, int mlist_count, 
01139                                float page_width_mm, float page_height_mm, float tolerance )
01140 {
01141   int i;
01142   for( i = 0 ; i < mlist_count ; i++ )
01143   {
01144     XpuMediumSourceSizeRec *curr = &mlist[i];
01145     float total_width  = curr->ma1 + curr->ma2,
01146           total_height = curr->ma3 + curr->ma4;
01147 
01148     /* Match width/height*/
01149     if( ((page_width_mm !=-1.f)?(MORE_OR_LESS_EQUAL(total_width,  page_width_mm,  tolerance)):(True)) &&
01150         ((page_height_mm!=-1.f)?(MORE_OR_LESS_EQUAL(total_height, page_height_mm, tolerance)):(True)) )
01151     {
01152       return(curr);
01153     }
01154   }
01155 
01156   return(NULL);
01157 }
01158 
01159 XpuMediumSourceSizeRec *
01160 XpuFindMediumSourceSizeByBounds( XpuMediumSourceSizeList mlist, int mlist_count, 
01161                                  float m1, float m2, float m3, float m4, float tolerance )
01162 {
01163   int i;
01164   for( i = 0 ; i < mlist_count ; i++ )
01165   {
01166     XpuMediumSourceSizeRec *curr = &mlist[i];
01167 
01168     /* Match bounds */
01169     if( ((m1!=-1.f)?(MORE_OR_LESS_EQUAL(curr->ma1, m1, tolerance)):(True)) &&
01170         ((m2!=-1.f)?(MORE_OR_LESS_EQUAL(curr->ma2, m2, tolerance)):(True)) &&
01171         ((m3!=-1.f)?(MORE_OR_LESS_EQUAL(curr->ma3, m3, tolerance)):(True)) &&
01172         ((m4!=-1.f)?(MORE_OR_LESS_EQUAL(curr->ma4, m4, tolerance)):(True)) )
01173     {
01174       return(curr);
01175     }
01176   }
01177 
01178   return(NULL);
01179 }
01180 
01181 XpuMediumSourceSizeRec *
01182 XpuFindMediumSourceSizeByName( XpuMediumSourceSizeList mlist, int mlist_count, 
01183                                const char *tray_name, const char *medium_name )
01184 {
01185   int i;
01186   for( i = 0 ; i < mlist_count ; i++ )
01187   {
01188     XpuMediumSourceSizeRec *curr = &mlist[i];
01189 
01190     /* Match by tray name and/or medium name */
01191     if( ((tray_name && curr->tray_name)?(!strcasecmp(curr->tray_name, tray_name)):(tray_name==NULL)) &&
01192         ((medium_name)?(!strcasecmp(curr->medium_name, medium_name)):(True)) )
01193     {
01194       return(curr);
01195     }
01196   }
01197 
01198   return(NULL);
01199 }
01200 
01201 XpuResolutionList XpuGetResolutionList( Display *pdpy, XPContext pcontext, int *numEntriesPtr )
01202 {
01203   XpuResolutionList list = NULL;
01204   int               rec_count = 1; /* Allocate one more |XpuResolutionRec| structure
01205                                     * as terminator */
01206   char             *value;
01207   char             *tok_lasts;
01208   const char       *s;
01209   long              default_resolution = -1;
01210   int               default_resolution_rec_index = -1;
01211   char              namebuf[64];
01212 
01213   /* Get default document resolution */
01214   if( XpuGetOneLongAttribute(pdpy, pcontext, XPDocAttr, "default-printer-resolution", &default_resolution) != 1 )
01215   {
01216     default_resolution = -1;
01217   }
01218   
01219   value = XpGetOneAttribute(pdpy, pcontext, XPPrinterAttr, "printer-resolutions-supported");
01220   if (!value)
01221   {
01222     fprintf(stderr, "XpuGetResolutionList: Internal error, no 'printer-resolutions-supported' XPPrinterAttr found.\n");
01223     return(NULL);
01224   }
01225   
01226   for( s = strtok_r(value, " ", &tok_lasts) ;
01227        s != NULL ;
01228        s = strtok_r(NULL, " ", &tok_lasts) )
01229   {
01230     long tmp;
01231     
01232     tmp = strtol(s, (char **)NULL, 10);
01233     
01234     if( ((tmp == 0L) || (tmp == LONG_MIN) || (tmp == LONG_MAX)) && 
01235         ((errno == ERANGE) || (errno == EINVAL)) )
01236     {
01237       fprintf(stderr, "XpuGetResolutionList: Internal parser errror for '%s'.\n", s);
01238       continue;
01239     }    
01240   
01241     rec_count++;
01242     list = (XpuResolutionRec *)realloc(list, sizeof(XpuResolutionRec)*rec_count);
01243     if( !list )
01244       return(NULL);
01245     
01246     sprintf(namebuf, "%lddpi", tmp);
01247     list[rec_count-2].name   = strdup(namebuf);
01248     list[rec_count-2].x_dpi  = tmp;
01249     list[rec_count-2].y_dpi  = tmp;
01250 
01251     if( default_resolution != -1 )
01252     {
01253       /* Is this the default resolution ? */
01254       if( (list[rec_count-2].x_dpi == default_resolution) &&
01255           (list[rec_count-2].y_dpi == default_resolution) )
01256       {
01257         default_resolution_rec_index = rec_count-2;
01258       }
01259     }  
01260   }  
01261 
01262   XFree(value);
01263 
01264   if( list )
01265   {
01266     /* users: DO NOT COUNT ON THIS DETAIL 
01267      * (this is only to make current impl. of XpuGetResolutionList() easier)
01268      * We may remove this implementation detail in a later revision of
01269      * the library! */
01270     list[rec_count-1].name   = NULL;
01271     list[rec_count-1].x_dpi  = -1;
01272     list[rec_count-1].y_dpi  = -1;
01273     rec_count--;
01274   }
01275   else
01276   {
01277     rec_count = 0;
01278   }
01279 
01280   /* Make the default resolution always the first item in the list... */
01281   if( (default_resolution_rec_index != -1) && list )
01282   {
01283     XpuResolutionRec tmp;
01284     tmp = list[0];
01285     list[0] = list[default_resolution_rec_index];
01286     list[default_resolution_rec_index] = tmp;
01287   }
01288 
01289   *numEntriesPtr = rec_count; 
01290   return(list);
01291 }
01292 
01293 void XpuFreeResolutionList( XpuResolutionList list )
01294 {
01295   if( list )
01296   { 
01297     XpuResolutionRec *curr = list;
01298   
01299     /* See the warning abouve about using this implementation detail for
01300      * checking for the list's end... */
01301     while( curr->name != NULL )
01302     {
01303       free((void *)curr->name);
01304       curr++;
01305     }  
01306 
01307     free(list);
01308   }
01309 }
01310 
01311 /* Find resolution in resolution list.
01312  */
01313 XpuResolutionRec *XpuFindResolutionByName( XpuResolutionList list, int list_count, const char *name)
01314 {
01315   int i;
01316   
01317   for( i = 0 ; i < list_count ; i++ )
01318   {
01319     XpuResolutionRec *curr = &list[i];
01320     if (!strcasecmp(curr->name, name))
01321       return curr;
01322   }
01323 
01324   return NULL;
01325 }
01326 
01327 /* Get default page (if defined) or document resolution
01328  * this function may fail in the following conditions:
01329  * - No default resolution set yet
01330  * - X DPI != Y DPI (not yet implemented in Xprt)
01331  */
01332 Bool XpuGetResolution( Display *pdpy, XPContext pcontext, long *x_dpi_ptr, long *y_dpi_ptr )
01333 {
01334   long dpi;
01335 
01336   /* Try to get the current page's resolution (pages may differ in resolution if the DDX supports this) */
01337   if( XpuGetOneLongAttribute(pdpy, pcontext, XPPageAttr, "default-printer-resolution", &dpi) == 1 )
01338   {
01339     *x_dpi_ptr = dpi;
01340     *y_dpi_ptr = dpi;
01341     return True;
01342   }
01343 
01344   /* Get document resolution */
01345   if( XpuGetOneLongAttribute(pdpy, pcontext, XPDocAttr, "default-printer-resolution", &dpi) == 1 )
01346   {
01347     *x_dpi_ptr = dpi;
01348     *y_dpi_ptr = dpi;
01349     return True;
01350   }
01351 
01352   return False;
01353 }
01354 
01355 static
01356 int XpuSetResolution( Display *pdpy, XPContext pcontext, XPAttributes type, XpuResolutionRec *rec )
01357 {
01358   if( rec->x_dpi != rec->y_dpi )
01359   {
01360     fprintf(stderr, "XpuSetResolution: internal error: x_dpi != y_dpi not supported yet.\n");
01361     return 0;
01362   }
01363 
01364   XpuSetOneLongAttribute(pdpy, pcontext, type, "*default-printer-resolution", rec->x_dpi, XPAttrMerge); 
01365   return( 1 );
01366 }
01367 
01368 /* Set document resolution 
01369  * Retun error if printer does not support setting a resolution
01370  */
01371 int XpuSetDocResolution( Display *pdpy, XPContext pcontext, XpuResolutionRec *rec )
01372 {
01373   if( (XpuGetSupportedDocAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_DEFAULT_PRINTER_RESOLUTION) == 0 )
01374     return( 0 );
01375     
01376   return XpuSetResolution(pdpy, pcontext, XPDocAttr, rec);
01377 }
01378 
01379 /* Set page medium size 
01380  * Retun error if printer does not support setting a resolution or if per-page
01381  * resolution changes are not allowed.
01382  */
01383 int XpuSetPageResolution( Display *pdpy, XPContext pcontext, XpuResolutionRec *rec )
01384 {
01385   if( (XpuGetSupportedPageAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_DEFAULT_PRINTER_RESOLUTION) == 0 )
01386     return( 0 );
01387     
01388   return XpuSetResolution(pdpy, pcontext, XPPageAttr, rec);
01389 }
01390 
01391 XpuOrientationList XpuGetOrientationList( Display *pdpy, XPContext pcontext, int *numEntriesPtr )
01392 {
01393   XpuOrientationList list = NULL;
01394   int                rec_count = 1; /* Allocate one more |XpuOrientationRec|
01395                                      * structure as terminator */
01396   char              *value;
01397   char              *tok_lasts;
01398   const char        *s;
01399   const char        *default_orientation = NULL;
01400   int                default_orientation_rec_index = -1;
01401 
01402   /* Get default document orientation */
01403   default_orientation = XpGetOneAttribute(pdpy, pcontext, XPDocAttr, "content-orientation"); 
01404   if( !default_orientation )
01405   {
01406     fprintf(stderr, "XpuGetOrientationList: Internal error, no 'content-orientation' XPDocAttr found.\n");
01407     return(NULL);
01408   }
01409   
01410   value = XpGetOneAttribute(pdpy, pcontext, XPPrinterAttr, "content-orientations-supported");
01411   if (!value)
01412   {
01413     fprintf(stderr, "XpuGetOrientationList: Internal error, no 'content-orientations-supported' XPPrinterAttr found.\n");
01414     return(NULL);
01415   }
01416   
01417   for( s = strtok_r(value, " ", &tok_lasts) ;
01418        s != NULL ;
01419        s = strtok_r(NULL, " ", &tok_lasts) )
01420   { 
01421     rec_count++;
01422     list = (XpuOrientationRec *)realloc(list, sizeof(XpuOrientationRec)*rec_count);
01423     if( !list )
01424       return(NULL);
01425     
01426     list[rec_count-2].orientation = strdup(s);
01427 
01428     /* Default resolution ? */
01429     if( !strcmp(list[rec_count-2].orientation, default_orientation) )
01430     {
01431       default_orientation_rec_index = rec_count-2;
01432     }
01433   }  
01434 
01435   XFree(value);
01436   XFree((void *)default_orientation);
01437 
01438   if( list )
01439   {
01440     /* users: DO NOT COUNT ON THIS DETAIL 
01441      * (this is only to make current impl. of XpuFreeOrientationList() easier)
01442      * I may remove this implementation detail in a later revision of
01443      * the library! */
01444     list[rec_count-1].orientation = NULL;
01445     rec_count--;
01446   }
01447   else
01448   {
01449     rec_count = 0;
01450   }
01451 
01452   /* Make the default orientation always the first item in the list... */
01453   if( (default_orientation_rec_index != -1) && list )
01454   {
01455     XpuOrientationRec tmp;
01456     tmp = list[0];
01457     list[0] = list[default_orientation_rec_index];
01458     list[default_orientation_rec_index] = tmp;
01459   }
01460 
01461   *numEntriesPtr = rec_count; 
01462   return(list);
01463 }
01464 
01465 void XpuFreeOrientationList( XpuOrientationList list )
01466 {
01467   if( list )
01468   {
01469     XpuOrientationRec *curr = list;
01470   
01471     /* See the warning abouve about using this implementation detail for
01472      * checking for the list's end... */
01473     while( curr->orientation != NULL )
01474     {
01475       free((void *)curr->orientation);
01476       curr++;
01477     }   
01478     free(list);
01479   }
01480 }
01481 
01482 XpuOrientationRec *
01483 XpuFindOrientationByName( XpuOrientationList list, int list_count, const char *orientation )
01484 {
01485   int i;
01486   
01487   for( i = 0 ; i < list_count ; i++ )
01488   {
01489     XpuOrientationRec *curr = &list[i];
01490     if (!strcasecmp(curr->orientation, orientation))
01491       return curr;
01492   }
01493 
01494   return(NULL);
01495 }
01496 
01497 static
01498 int XpuSetOrientation( Display *pdpy, XPContext pcontext, XPAttributes type, XpuOrientationRec *rec )
01499 {
01500   XpuSetOneAttribute(pdpy, pcontext, type, "*content-orientation", rec->orientation, XPAttrMerge);
01501   return(1);
01502 }
01503 
01504 /* Set document orientation 
01505  * Retun error if printer does not support setting an orientation
01506  */
01507 int XpuSetDocOrientation( Display *pdpy, XPContext pcontext, XpuOrientationRec *rec )
01508 {
01509   if( (XpuGetSupportedDocAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_CONTENT_ORIENTATION) == 0 )
01510     return( 0 );
01511     
01512   return XpuSetOrientation(pdpy, pcontext, XPDocAttr, rec);
01513 }
01514 
01515 /* Set page orientation
01516  * Retun error if printer does not support setting an orientation or if
01517  * per-page orientations changes are not allowed
01518  */
01519 int XpuSetPageOrientation( Display *pdpy, XPContext pcontext, XpuOrientationRec *rec )
01520 {
01521   if( (XpuGetSupportedPageAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_CONTENT_ORIENTATION) == 0 )
01522     return( 0 );
01523     
01524   return XpuSetOrientation(pdpy, pcontext, XPPageAttr, rec);
01525 }
01526 
01527 XpuPlexList XpuGetPlexList( Display *pdpy, XPContext pcontext, int *numEntriesPtr )
01528 {
01529   XpuPlexList  list = NULL;
01530   int          rec_count = 1; /* Allocate one more |XpuPlexList| structure
01531                                * as terminator */
01532   char        *value;
01533   char        *tok_lasts;
01534   const char  *s;
01535   const char  *default_plex = NULL;
01536   int          default_plex_rec_index = -1;
01537 
01538   /* Get default document plex */
01539   default_plex = XpGetOneAttribute(pdpy, pcontext, XPDocAttr, "plex"); 
01540   if( !default_plex )
01541   {
01542     fprintf(stderr, "XpuGetPlexList: Internal error, no 'plex' XPDocAttr found.\n");
01543     return(NULL);
01544   }
01545    
01546   value = XpGetOneAttribute(pdpy, pcontext, XPPrinterAttr, "plexes-supported");
01547   if (!value)
01548   {
01549     fprintf(stderr, "XpuGetPlexList: Internal error, no 'plexes-supported' XPPrinterAttr found.\n");
01550     return(NULL);
01551   }
01552   
01553   for( s = strtok_r(value, " ", &tok_lasts) ;
01554        s != NULL ;
01555        s = strtok_r(NULL, " ", &tok_lasts) )
01556   { 
01557     rec_count++;
01558     list = (XpuPlexRec *)realloc(list, sizeof(XpuPlexRec)*rec_count);
01559     if( !list )
01560       return(NULL);
01561     
01562     list[rec_count-2].plex = strdup(s);
01563 
01564     /* Default plex ? */
01565     if( !strcmp(list[rec_count-2].plex, default_plex) )
01566     {
01567       default_plex_rec_index = rec_count-2;
01568     }
01569   }  
01570 
01571   XFree(value);
01572   XFree((void *)default_plex);
01573 
01574   if( list )
01575   {
01576     /* users: DO NOT COUNT ON THIS DETAIL 
01577      * (this is only to make current impl. of XpuFreePlexList() easier)
01578      * I may remove this implementation detail in a later revision of
01579      * the library! */
01580     list[rec_count-1].plex = NULL;
01581     rec_count--;
01582   }
01583   else
01584   {
01585     rec_count = 0;
01586   }
01587 
01588   /* Make the default plex always the first item in the list... */
01589   if( (default_plex_rec_index != -1) && list )
01590   {
01591     XpuPlexRec tmp;
01592     tmp = list[0];
01593     list[0] = list[default_plex_rec_index];
01594     list[default_plex_rec_index] = tmp;
01595   }
01596 
01597   *numEntriesPtr = rec_count; 
01598   return(list);
01599 }
01600 
01601 void XpuFreePlexList( XpuPlexList list )
01602 {
01603   if( list )
01604   {
01605     XpuPlexRec *curr = list;
01606   
01607     /* See the warning abouve about using this implementation detail for
01608      * checking for the list's end... */
01609     while( curr->plex != NULL )
01610     {
01611       free((void *)curr->plex);
01612       curr++;
01613     }   
01614     free(list);
01615   }
01616 }
01617 
01618 XpuPlexRec *
01619 XpuFindPlexByName( XpuPlexList list, int list_count, const char *plex )
01620 {
01621   int i;
01622   
01623   for( i = 0 ; i < list_count ; i++ )
01624   {
01625     XpuPlexRec *curr = &list[i];
01626     if (!strcasecmp(curr->plex, plex))
01627       return curr;
01628   }
01629 
01630   return(NULL);
01631 }
01632 
01633 static
01634 int XpuSetContentPlex( Display *pdpy, XPContext pcontext, XPAttributes type, XpuPlexRec *rec )
01635 {
01636   XpuSetOneAttribute(pdpy, pcontext, type, "*plex", rec->plex, XPAttrMerge);
01637   return(1);
01638 }
01639 
01640 /* Set document plex 
01641  * Retun error if printer does not support setting an plex
01642  */
01643 int XpuSetDocPlex( Display *pdpy, XPContext pcontext, XpuPlexRec *rec )
01644 {
01645   if( (XpuGetSupportedDocAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_PLEX) == 0 )
01646     return( 0 );
01647     
01648   return XpuSetContentPlex(pdpy, pcontext, XPDocAttr, rec);
01649 }
01650 
01651 /* Set page plex
01652  * Retun error if printer does not support setting an plex or if
01653  * per-page plex changes are not allowed
01654  */
01655 int XpuSetPagePlex( Display *pdpy, XPContext pcontext, XpuPlexRec *rec )
01656 {
01657   if( (XpuGetSupportedPageAttributes(pdpy, pcontext) & XPUATTRIBUTESUPPORTED_PLEX) == 0 )
01658     return( 0 );
01659     
01660   return XpuSetContentPlex(pdpy, pcontext, XPPageAttr, rec);
01661 }
01662 
01663 
01664 XpuColorspaceList XpuGetColorspaceList( Display *pdpy, XPContext pcontext, int *numEntriesPtr )
01665 {
01666   XpuColorspaceList list = NULL;
01667   int               rec_count = 1; /* Allocate one more |XpuColorspaceRec| structure
01668                                     * as terminator */
01669   char              namebuf[256];  /* Temporary name buffer for colorspace names */
01670   int               i;             /* Loop counter */
01671   int               nvi;           /* Number of visuals */
01672   Screen           *pscreen;       /* Print screen */
01673   XVisualInfo       viproto;       /* fill in for getting info */
01674   XVisualInfo      *vip;           /* retured info */
01675 
01676   pscreen = XpGetScreenOfContext(pdpy, pcontext);
01677 
01678   nvi = 0;
01679   viproto.screen = XScreenNumberOfScreen(pscreen);
01680   vip = XGetVisualInfo(pdpy, VisualScreenMask, &viproto, &nvi);
01681   if (!vip)
01682   {
01683     fprintf(stderr, "XpuGetColorspaceList: Internal error: vip == NULL\n");
01684     return NULL;
01685   }
01686   
01687   for( i = 0 ; i < nvi ; i++ )
01688   {
01689     XVisualInfo *vcurr = vip+i;
01690     char         cbuff[64];
01691     const char  *class = NULL;
01692 
01693 #ifdef USE_MOZILLA_TYPES
01694     /* Workaround for the current limitation of the gfx/src/xlibrgb code
01695      * which cannot handle depths > 24bit yet */
01696     if( vcurr->depth > 24 )
01697       continue;
01698 #endif /* USE_MOZILLA_TYPES */
01699  
01700     rec_count++;
01701     list = (XpuColorspaceRec *)realloc(list, sizeof(XpuColorspaceRec)*rec_count);
01702     if( !list )
01703       return NULL;
01704 
01705     /* ToDO: This needs to be updated for the COLORSPACE X11 extension
01706      * once it is ready and approved by the XOrg arch board. */
01707     switch (vcurr->class) {
01708       case StaticGray:   class = "StaticGray";  break;
01709       case GrayScale:    class = "GrayScale";   break;
01710       case StaticColor:  class = "StaticColor"; break;
01711       case PseudoColor:  class = "PseudoColor"; break;
01712       case TrueColor:    class = "TrueColor";   break;
01713       case DirectColor:  class = "DirectColor"; break;
01714       default: /* Needed for forward compatibility to the COLORSPACE extension */
01715         sprintf (cbuff, "unknown_class_%x", vcurr->class);
01716         class = cbuff;
01717         break;
01718     }
01719 
01720     if (vcurr->bits_per_rgb == 8)
01721     {
01722       sprintf(namebuf, "%s/%dbit", class, vcurr->depth);
01723     }
01724     else
01725     {
01726       sprintf(namebuf, "%s/%dbit/%dbpg", class, vcurr->depth, vcurr->bits_per_rgb);
01727     }
01728     list[rec_count-2].name       = strdup(namebuf);
01729     list[rec_count-2].visualinfo = *vcurr;
01730   }  
01731  
01732   XFree((char *)vip);
01733 
01734   if( list )
01735   {
01736     /* users: DO NOT COUNT ON THIS DETAIL 
01737      * (this is only to make current impl. of XpuGetResolutionList() easier)
01738      * We may remove this implementation detail in a later revision of
01739      * the library! */
01740     list[rec_count-1].name = NULL;
01741     rec_count--;
01742   }
01743   else
01744   {
01745     rec_count = 0;
01746   }
01747 
01748   *numEntriesPtr = rec_count; 
01749   return(list);
01750 }
01751 
01752 void XpuFreeColorspaceList( XpuColorspaceList list )
01753 {
01754   if( list )
01755   { 
01756     XpuColorspaceRec *curr = list;
01757   
01758     /* See the warning abouve about using this implementation detail for
01759      * checking for the list's end... */
01760     while( curr->name != NULL )
01761     {
01762       free((void *)curr->name);
01763       curr++;
01764     }  
01765 
01766     free(list);
01767   }
01768 }
01769 
01770 XpuColorspaceRec *
01771 XpuFindColorspaceByName( XpuColorspaceList list, int list_count, const char *name )
01772 {
01773   int i;
01774   
01775   for( i = 0 ; i < list_count ; i++ )
01776   {
01777     XpuColorspaceRec *curr = &list[i];
01778     if (!strcmp(curr->name, name))
01779       return curr;
01780   }
01781 
01782   return(NULL);
01783 }
01784 
01785 Bool XpuGetEnableFontDownload( Display *pdpy, XPContext pcontext )
01786 {
01787   Bool  enableFontDownload;
01788   char *value;
01789   
01790   value = XpGetOneAttribute(pdpy, pcontext, XPPrinterAttr, "xp-listfonts-modes-supported"); 
01791   if( !value )
01792   {
01793     fprintf(stderr, "XpuGetEnableFontDownload: xp-listfonts-modes-supported printer attribute not found.\n");
01794     return False;
01795   }
01796   
01797   enableFontDownload = (strstr(value, "xp-list-glyph-fonts") != NULL);
01798   XFree(value);
01799   return enableFontDownload;
01800 }
01801 
01802 int XpuSetEnableFontDownload( Display *pdpy, XPContext pcontext, Bool enableFontDownload )
01803 {
01804   char *value,
01805        *newvalue;
01806   
01807   value = XpGetOneAttribute(pdpy, pcontext, XPPrinterAttr, "xp-listfonts-modes-supported"); 
01808   if( !value )
01809   {
01810     fprintf(stderr, "XpuSetEnableFontDownload: xp-listfonts-modes-supported printer attribute not found.\n");
01811     return 0; /* failure */
01812   }
01813   
01814   /* Set "xp-list-glyph-fonts" */
01815   if( enableFontDownload )
01816   {
01817     /* Return success if "xp-list-glyph-fonts" is already set */
01818     if( strstr(value, "xp-list-glyph-fonts") != NULL )
01819     {
01820       XFree(value);
01821       return 1; /* success */
01822     }
01823 
01824     newvalue = malloc(strlen(value) + 33);
01825     if( !newvalue )
01826     {
01827       XFree(value);
01828       return 0; /* failure */
01829     }
01830 
01831     sprintf(newvalue, "%s xp-list-glyph-fonts", value);
01832     XpuSetOneAttribute(pdpy, pcontext, XPDocAttr, "*xp-listfonts-modes", newvalue, XPAttrMerge);
01833 
01834     free(newvalue);
01835     XFree(value);
01836     return 1; /* success */
01837   }
01838   else
01839   {
01840     char *s, /* copy string "source" */
01841          *d; /* copy string "destination" */
01842     
01843     /* Return success if "xp-list-glyph-fonts" not set */
01844     d = strstr(value, "xp-list-glyph-fonts");
01845     if( d == NULL )
01846     {
01847       XFree(value);
01848       return 1; /* success */
01849     }
01850 
01851     /* strip "xp-list-glyph-fonts" from |value| */
01852     s = d+19/*strlen("xp-list-glyph-fonts")*/;
01853     while( (*d++ = *s++) != '\0' )
01854       ;
01855 
01856     XpuSetOneAttribute(pdpy, pcontext, XPDocAttr, "*xp-listfonts-modes", value, XPAttrMerge);
01857 
01858     XFree(value);
01859     return 1; /* success */
01860   } 
01861 }
01862 
01863 /* Return flags to indicate which attributes are supported and which not... */
01864 static
01865 XpuSupportedFlags XpuGetSupportedAttributes( Display *pdpy, XPContext pcontext, XPAttributes type, const char *attribute_name )
01866 {
01867   char              *value;
01868   void              *tok_lasts;
01869   XpuSupportedFlags  flags = 0;
01870   
01871   MAKE_STRING_WRITABLE(attribute_name);
01872   if( attribute_name == NULL )
01873     return(0);
01874     
01875   value = XpGetOneAttribute(pdpy, pcontext, type, STRING_AS_WRITABLE(attribute_name));   
01876   
01877   FREE_WRITABLE_STRING(attribute_name);
01878   
01879   if( value != NULL )
01880   {
01881     const char *s;
01882     
01883     for( s = XpuEnumerateXpAttributeValue(value, &tok_lasts) ; s != NULL ; s = XpuEnumerateXpAttributeValue(NULL, &tok_lasts) )
01884     {
01885            if( !strcmp(s, "job-name") )                   flags |= XPUATTRIBUTESUPPORTED_JOB_NAME;
01886       else if( !strcmp(s, "job-owner") )                  flags |= XPUATTRIBUTESUPPORTED_JOB_OWNER;
01887       else if( !strcmp(s, "notification-profile") )       flags |= XPUATTRIBUTESUPPORTED_NOTIFICATION_PROFILE;
01888       else if( !strcmp(s, "copy-count") )                 flags |= XPUATTRIBUTESUPPORTED_COPY_COUNT;
01889       else if( !strcmp(s, "document-format") )            flags |= XPUATTRIBUTESUPPORTED_DOCUMENT_FORMAT;
01890       else if( !strcmp(s, "content-orientation") )        flags |= XPUATTRIBUTESUPPORTED_CONTENT_ORIENTATION;
01891       else if( !strcmp(s, "default-printer-resolution") ) flags |= XPUATTRIBUTESUPPORTED_DEFAULT_PRINTER_RESOLUTION;
01892       else if( !strcmp(s, "default-input-tray") )         flags |= XPUATTRIBUTESUPPORTED_DEFAULT_INPUT_TRAY;
01893       else if( !strcmp(s, "default-medium") )             flags |= XPUATTRIBUTESUPPORTED_DEFAULT_MEDIUM;
01894       else if( !strcmp(s, "plex") )                       flags |= XPUATTRIBUTESUPPORTED_PLEX;
01895       else if( !strcmp(s, "xp-listfonts-modes") )         flags |= XPUATTRIBUTESUPPORTED_LISTFONTS_MODES;
01896     }
01897     
01898     XpuDisposeEnumerateXpAttributeValue(&tok_lasts);
01899     XFree(value);
01900   }  
01901   
01902   return(flags);
01903 }
01904 
01905 XpuSupportedFlags XpuGetSupportedJobAttributes(Display *pdpy, XPContext pcontext)
01906 {
01907   return XpuGetSupportedAttributes(pdpy, pcontext, XPPrinterAttr, "job-attributes-supported");
01908 }
01909 
01910 XpuSupportedFlags XpuGetSupportedDocAttributes(Display *pdpy, XPContext pcontext)
01911 {
01912   return XpuGetSupportedAttributes(pdpy, pcontext, XPPrinterAttr, "document-attributes-supported");
01913 }
01914 
01915 XpuSupportedFlags XpuGetSupportedPageAttributes(Display *pdpy, XPContext pcontext)
01916 {
01917   return XpuGetSupportedAttributes(pdpy, pcontext, XPPrinterAttr, "xp-page-attributes-supported");
01918 }
01919 
01920 /* Encode  string  for  usage  in a Xrm  resource  database  as
01921  * defined  in  X(7):  [...]  To  allow  a  Value  to  begin
01922  * with  whitespace,  the  two-character  sequence   ``\space''
01923  * (backslash  followed by space) is recognized and replaced by
01924  * a space character, and the two-character  sequence  ``\tab''
01925  * (backslash  followed  by  horizontal  tab) is recognized and
01926  * replaced by a horizontal tab character.  To allow a Value to
01927  * contain   embedded  newline  characters,  the  two-character
01928  * sequence ``\n'' is recognized  and  replaced  by  a  newline
01929  * character.   To  allow  a Value to be broken across multiple
01930  * lines in a text file,  the  two-character  sequence  ``\new-
01931  * line''  (backslash  followed  by  newline) is recognized and
01932  * removed from the value.  To allow a Value to  contain  arbi-
01933  * trary character codes, the four-character sequence ``\nnn'',
01934  * where  each  n  is  a  digit  character  in  the  range   of
01935  * ``0''-``7'',  is  recognized and replaced with a single byte
01936  * that contains the octal value  specified  by  the  sequence.
01937  * Finally, the two-character sequence ``\\'' is recognized and
01938  * replaced with a single backslash.
01939  */
01940 char *XpuResourceEncode( const char *s )
01941 {
01942   size_t  slen;
01943   char   *res;
01944   char   *d;
01945   int     i,
01946           c;
01947 
01948   slen = strlen(s);
01949   res  = malloc(slen*4+1);
01950   if (!res)
01951     return NULL;
01952   
01953   d = res;
01954   i = slen;
01955   while (i--) {
01956     c = *s++;
01957     if (c == '\n') {
01958       if (i) {
01959         *d++ = '\\';
01960         *d++ = 'n';
01961         *d++ = '\\';
01962         *d++ = '\n';
01963       }
01964       else {
01965         *d++ = '\\';
01966         *d++ = 'n';
01967       }
01968     } else if (c == '\\') {
01969         *d++ = '\\';
01970         *d++ = '\\';
01971     }
01972     else if ((c < ' ' && c != '\t') ||
01973             ((unsigned char)c >= 0x7F && (unsigned char)c < 0xA0)) {
01974         sprintf(d, "\\%03o", (unsigned char)c);
01975         d += 4;
01976     }
01977     else {
01978         *d++ = c;
01979     }
01980   }
01981 
01982   *d = '\0';
01983   
01984   return res;
01985 }
01986 
01987 #ifdef XXXJULIEN_NOTNOW
01988 char *XpuResourceDecode( const char *str )
01989 {
01990 }
01991 #endif /* XXXJULIEN_NOTNOW */
01992 
01993 void XpuResourceFreeString( char *s )
01994 {
01995   free(s);
01996 }
01997 
01998 const char *XpuXmbToCompoundText(Display *dpy, const char *xmbtext)
01999 {
02000   XTextProperty   xtp;
02001   int             xcr;
02002   char           *xtl[2];
02003   char           *ct;
02004 
02005   if (strlen(xmbtext) == 0)
02006     return strdup(xmbtext);
02007   
02008   memset(&xtp, 0, sizeof(xtp));
02009   xtl[0] = (char *)xmbtext;
02010   xtl[1] = NULL;
02011   
02012   xcr = XmbTextListToTextProperty(dpy, xtl, 1, XCompoundTextStyle, &xtp);
02013   
02014   if (xcr == XNoMemory || xcr == XLocaleNotSupported)
02015   {
02016     fprintf(stderr, "XpuXmbToCompoundText: XmbTextListToTextProperty failure.\n");
02017     return strdup(xmbtext);
02018   }
02019 
02020   /* Did conversion succeed (some unconvertible characters
02021    * are not a problem) ? */
02022   if ( !((xcr == Success) || (xcr > 0)) ||
02023        (xtp.value == NULL))
02024   {
02025     fprintf(stderr, "XpuXmbToCompoundText: XmbTextListToTextProperty failure 2.\n");
02026     return strdup(xmbtext);
02027   }
02028   
02029   ct = malloc(xtp.nitems+1);
02030   if (!ct)
02031   {
02032     XFree(xtp.value);
02033     return NULL;
02034   }
02035   memcpy(ct, xtp.value, xtp.nitems);
02036   ct[xtp.nitems] = '\0';  
02037 
02038   XFree(xtp.value);
02039   
02040   return ct;
02041 }
02042 
02043 void XpuFreeCompundTextString( const char *s )
02044 {
02045   free((void *)s);
02046 }
02047 
02048 const char *XpuCompoundTextToXmb(Display *dpy, const char *ct)
02049 {
02050   XTextProperty   xtp;
02051   int             xcr;
02052   char          **xtl = NULL;
02053   int             xtl_count = 0;
02054   char           *xmb;
02055   int             xmb_len = 0;
02056   int             i;
02057 
02058   if (strlen(ct) == 0)
02059     return strdup(ct);
02060     
02061   xtp.value    = (unsigned char *)ct;
02062   xtp.nitems   = strlen(ct); 
02063   xtp.encoding = XInternAtom(dpy, "COMPOUND_TEXT", False);
02064   xtp.format   = 8;
02065   
02066   xcr = XmbTextPropertyToTextList(dpy, &xtp, &xtl, &xtl_count);
02067   
02068   if (xcr == XNoMemory || xcr == XLocaleNotSupported)
02069   {
02070     fprintf(stderr, "XpuCompoundTextToXmb: XmbTextPropertyToTextList failure 1.\n");
02071     return strdup(ct);
02072   }
02073 
02074   /* Did conversion succeed (some unconvertible characters
02075    * are not a problem) ? */
02076   if ( !((xcr == Success) || (xcr > 0)) ||
02077        (xtl == NULL))
02078   {
02079     fprintf(stderr, "XpuCompoundTextToXmb: XmbTextPropertyToTextList failure 2.\n");
02080     return strdup(ct);
02081   }
02082    
02083   for (i = 0; i < xtl_count; i++)
02084   {
02085     xmb_len += strlen(xtl[i]);
02086   }
02087   xmb = malloc (xmb_len + 1);
02088   if (!xmb)
02089   {
02090     XFreeStringList(xtl);
02091     return NULL;
02092   }
02093   xmb[0] = '\0'; /* Catch zero-length case */
02094   for (i = 0; i < xtl_count; i++)
02095   {
02096     strcat(xmb, xtl[i]);
02097   }
02098   
02099   XFreeStringList(xtl); 
02100   
02101   return xmb;
02102 }
02103 
02104 void XpuFreeXmbString( const char *s )
02105 {
02106   free((void *)s);
02107 }
02108 
02109 /* EOF. */