utils.c revision 05b261ec
1/*
2
3Copyright 1987, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27
28Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
29Copyright 1994 Quarterdeck Office Systems.
30
31                        All Rights Reserved
32
33Permission to use, copy, modify, and distribute this software and its
34documentation for any purpose and without fee is hereby granted,
35provided that the above copyright notice appear in all copies and that
36both that copyright notice and this permission notice appear in
37supporting documentation, and that the names of Digital and
38Quarterdeck not be used in advertising or publicity pertaining to
39distribution of the software without specific, written prior
40permission.
41
42DIGITAL AND QUARTERDECK DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
43SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
44FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT
45OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
46OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
47OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
48OR PERFORMANCE OF THIS SOFTWARE.
49
50*/
51
52#ifdef HAVE_DIX_CONFIG_H
53#include <dix-config.h>
54#endif
55
56#ifdef __CYGWIN__
57#include <stdlib.h>
58#include <signal.h>
59#endif
60
61#if defined(WIN32) && !defined(__CYGWIN__)
62#include <X11/Xwinsock.h>
63#endif
64#include <X11/Xos.h>
65#include <stdio.h>
66#include <time.h>
67#if !defined(WIN32) || !defined(__MINGW32__)
68#include <sys/time.h>
69#include <sys/resource.h>
70#endif
71#include "misc.h"
72#include <X11/X.h>
73#define XSERV_t
74#define TRANS_SERVER
75#define TRANS_REOPEN
76#include <X11/Xtrans/Xtrans.h>
77#include "input.h"
78#include "dixfont.h"
79#include "osdep.h"
80#include "extension.h"
81#ifdef X_POSIX_C_SOURCE
82#define _POSIX_C_SOURCE X_POSIX_C_SOURCE
83#include <signal.h>
84#undef _POSIX_C_SOURCE
85#else
86#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
87#include <signal.h>
88#else
89#define _POSIX_SOURCE
90#include <signal.h>
91#undef _POSIX_SOURCE
92#endif
93#endif
94#ifndef WIN32
95#include <sys/wait.h>
96#endif
97#if !defined(SYSV) && !defined(WIN32) && !defined(Lynx) && !defined(QNX4)
98#include <sys/resource.h>
99#endif
100#include <sys/stat.h>
101#include <ctype.h>    /* for isspace */
102#include <stdarg.h>
103
104#if defined(DGUX)
105#include <sys/resource.h>
106#include <netdb.h>
107#endif
108
109#include <stdlib.h>	/* for malloc() */
110
111#if defined(TCPCONN) || defined(STREAMSCONN)
112# ifndef WIN32
113#  include <netdb.h>
114# endif
115#endif
116
117#include "opaque.h"
118
119#ifdef SMART_SCHEDULE
120#include "dixstruct.h"
121#endif
122
123#ifdef XKB
124#include <xkbsrv.h>
125#endif
126#ifdef XCSECURITY
127#include "securitysrv.h"
128#endif
129
130#ifdef RENDER
131#include "picture.h"
132#endif
133
134#ifdef XPRINT
135#include "DiPrint.h"
136#endif
137
138_X_EXPORT Bool noTestExtensions;
139#ifdef BIGREQS
140_X_EXPORT Bool noBigReqExtension = FALSE;
141#endif
142#ifdef COMPOSITE
143_X_EXPORT Bool noCompositeExtension = FALSE;
144#endif
145
146#ifdef DAMAGE
147_X_EXPORT Bool noDamageExtension = FALSE;
148#endif
149#ifdef DBE
150_X_EXPORT Bool noDbeExtension = FALSE;
151#endif
152#ifdef DPMSExtension
153_X_EXPORT Bool noDPMSExtension = FALSE;
154#endif
155#ifdef EVI
156_X_EXPORT Bool noEVIExtension = FALSE;
157#endif
158#ifdef FONTCACHE
159_X_EXPORT Bool noFontCacheExtension = FALSE;
160#endif
161#ifdef GLXEXT
162_X_EXPORT Bool noGlxExtension = FALSE;
163#endif
164#ifdef SCREENSAVER
165_X_EXPORT Bool noScreenSaverExtension = FALSE;
166#endif
167#ifdef MITSHM
168_X_EXPORT Bool noMITShmExtension = FALSE;
169#endif
170#ifdef MITMISC
171_X_EXPORT Bool noMITMiscExtension = FALSE;
172#endif
173#ifdef MULTIBUFFER
174_X_EXPORT Bool noMultibufferExtension = FALSE;
175#endif
176#ifdef RANDR
177_X_EXPORT Bool noRRExtension = FALSE;
178#endif
179#ifdef RENDER
180_X_EXPORT Bool noRenderExtension = FALSE;
181#endif
182#ifdef SHAPE
183_X_EXPORT Bool noShapeExtension = FALSE;
184#endif
185#ifdef XCSECURITY
186_X_EXPORT Bool noSecurityExtension = FALSE;
187#endif
188#ifdef XSYNC
189_X_EXPORT Bool noSyncExtension = FALSE;
190#endif
191#ifdef TOGCUP
192_X_EXPORT Bool noXcupExtension = FALSE;
193#endif
194#ifdef RES
195_X_EXPORT Bool noResExtension = FALSE;
196#endif
197#ifdef XAPPGROUP
198_X_EXPORT Bool noXagExtension = FALSE;
199#endif
200#ifdef XCMISC
201_X_EXPORT Bool noXCMiscExtension = FALSE;
202#endif
203#ifdef XEVIE
204/* Xevie is disabled by default for now until the
205 * interface is stable */
206_X_EXPORT Bool noXevieExtension = TRUE;
207#endif
208#ifdef XF86BIGFONT
209_X_EXPORT Bool noXFree86BigfontExtension = FALSE;
210#endif
211#ifdef XFreeXDGA
212_X_EXPORT Bool noXFree86DGAExtension = FALSE;
213#endif
214#ifdef XF86DRI
215_X_EXPORT Bool noXFree86DRIExtension = FALSE;
216#endif
217#ifdef XF86MISC
218_X_EXPORT Bool noXFree86MiscExtension = FALSE;
219#endif
220#ifdef XF86VIDMODE
221_X_EXPORT Bool noXFree86VidModeExtension = FALSE;
222#endif
223#ifdef XFIXES
224_X_EXPORT Bool noXFixesExtension = FALSE;
225#endif
226/* |noXkbExtension| is defined in xc/programs/Xserver/xkb/xkbInit.c */
227#ifdef PANORAMIX
228/* Xinerama is disabled by default unless enabled via +xinerama */
229_X_EXPORT Bool noPanoramiXExtension = TRUE;
230#endif
231#ifdef XINPUT
232_X_EXPORT Bool noXInputExtension = FALSE;
233#endif
234#ifdef XIDLE
235_X_EXPORT Bool noXIdleExtension = FALSE;
236#endif
237#ifdef XV
238_X_EXPORT Bool noXvExtension = FALSE;
239#endif
240
241#define X_INCLUDE_NETDB_H
242#include <X11/Xos_r.h>
243
244#include <errno.h>
245
246Bool CoreDump;
247
248#ifdef PANORAMIX
249Bool PanoramiXExtensionDisabledHack = FALSE;
250#endif
251
252int auditTrailLevel = 1;
253
254_X_EXPORT Bool Must_have_memory = FALSE;
255
256#ifdef AIXV3
257int SyncOn  = 0;
258extern int SelectWaitTime;
259#endif
260
261#if defined(SVR4) || defined(__linux__) || defined(CSRG_BASED)
262#define HAS_SAVED_IDS_AND_SETEUID
263#endif
264
265#ifdef MEMBUG
266#define MEM_FAIL_SCALE 100000
267long Memory_fail = 0;
268#include <stdlib.h>  /* for random() */
269#endif
270
271static char *dev_tty_from_init = NULL;	/* since we need to parse it anyway */
272
273OsSigHandlerPtr
274OsSignal(sig, handler)
275    int sig;
276    OsSigHandlerPtr handler;
277{
278#ifdef X_NOT_POSIX
279    return signal(sig, handler);
280#else
281    struct sigaction act, oact;
282
283    sigemptyset(&act.sa_mask);
284    if (handler != SIG_IGN)
285	sigaddset(&act.sa_mask, sig);
286    act.sa_flags = 0;
287    act.sa_handler = handler;
288    if (sigaction(sig, &act, &oact))
289      perror("sigaction");
290    return oact.sa_handler;
291#endif
292}
293
294#ifdef SERVER_LOCK
295/*
296 * Explicit support for a server lock file like the ones used for UUCP.
297 * For architectures with virtual terminals that can run more than one
298 * server at a time.  This keeps the servers from stomping on each other
299 * if the user forgets to give them different display numbers.
300 */
301#define LOCK_DIR "/tmp"
302#define LOCK_TMP_PREFIX "/.tX"
303#define LOCK_PREFIX "/.X"
304#define LOCK_SUFFIX "-lock"
305
306#if defined(DGUX)
307#include <limits.h>
308#include <sys/param.h>
309#endif
310
311#ifndef PATH_MAX
312#ifndef Lynx
313#include <sys/param.h>
314#else
315#include <param.h>
316#endif
317#ifndef PATH_MAX
318#ifdef MAXPATHLEN
319#define PATH_MAX MAXPATHLEN
320#else
321#define PATH_MAX 1024
322#endif
323#endif
324#endif
325
326static Bool StillLocking = FALSE;
327static char LockFile[PATH_MAX];
328static Bool nolock = FALSE;
329
330/*
331 * LockServer --
332 *      Check if the server lock file exists.  If so, check if the PID
333 *      contained inside is valid.  If so, then die.  Otherwise, create
334 *      the lock file containing the PID.
335 */
336void
337LockServer(void)
338{
339  char tmp[PATH_MAX], pid_str[12];
340  int lfd, i, haslock, l_pid, t;
341  char *tmppath = NULL;
342  int len;
343  char port[20];
344
345  if (nolock) return;
346  /*
347   * Path names
348   */
349  tmppath = LOCK_DIR;
350
351  sprintf(port, "%d", atoi(display));
352  len = strlen(LOCK_PREFIX) > strlen(LOCK_TMP_PREFIX) ? strlen(LOCK_PREFIX) :
353						strlen(LOCK_TMP_PREFIX);
354  len += strlen(tmppath) + strlen(port) + strlen(LOCK_SUFFIX) + 1;
355  if (len > sizeof(LockFile))
356    FatalError("Display name `%s' is too long\n", port);
357  (void)sprintf(tmp, "%s" LOCK_TMP_PREFIX "%s" LOCK_SUFFIX, tmppath, port);
358  (void)sprintf(LockFile, "%s" LOCK_PREFIX "%s" LOCK_SUFFIX, tmppath, port);
359
360  /*
361   * Create a temporary file containing our PID.  Attempt three times
362   * to create the file.
363   */
364  StillLocking = TRUE;
365  i = 0;
366  do {
367    i++;
368    lfd = open(tmp, O_CREAT | O_EXCL | O_WRONLY, 0644);
369    if (lfd < 0)
370       sleep(2);
371    else
372       break;
373  } while (i < 3);
374  if (lfd < 0) {
375    unlink(tmp);
376    i = 0;
377    do {
378      i++;
379      lfd = open(tmp, O_CREAT | O_EXCL | O_WRONLY, 0644);
380      if (lfd < 0)
381         sleep(2);
382      else
383         break;
384    } while (i < 3);
385  }
386  if (lfd < 0)
387    FatalError("Could not create lock file in %s\n", tmp);
388  (void) sprintf(pid_str, "%10ld\n", (long)getpid());
389  (void) write(lfd, pid_str, 11);
390#ifndef USE_CHMOD
391  (void) fchmod(lfd, 0444);
392#else
393  (void) chmod(tmp, 0444);
394#endif
395  (void) close(lfd);
396
397  /*
398   * OK.  Now the tmp file exists.  Try three times to move it in place
399   * for the lock.
400   */
401  i = 0;
402  haslock = 0;
403  while ((!haslock) && (i++ < 3)) {
404    haslock = (link(tmp,LockFile) == 0);
405    if (haslock) {
406      /*
407       * We're done.
408       */
409      break;
410    }
411    else {
412      /*
413       * Read the pid from the existing file
414       */
415      lfd = open(LockFile, O_RDONLY);
416      if (lfd < 0) {
417        unlink(tmp);
418        FatalError("Can't read lock file %s\n", LockFile);
419      }
420      pid_str[0] = '\0';
421      if (read(lfd, pid_str, 11) != 11) {
422        /*
423         * Bogus lock file.
424         */
425        unlink(LockFile);
426        close(lfd);
427        continue;
428      }
429      pid_str[11] = '\0';
430      sscanf(pid_str, "%d", &l_pid);
431      close(lfd);
432
433      /*
434       * Now try to kill the PID to see if it exists.
435       */
436      errno = 0;
437      t = kill(l_pid, 0);
438      if ((t< 0) && (errno == ESRCH)) {
439        /*
440         * Stale lock file.
441         */
442        unlink(LockFile);
443        continue;
444      }
445      else if (((t < 0) && (errno == EPERM)) || (t == 0)) {
446        /*
447         * Process is still active.
448         */
449        unlink(tmp);
450	FatalError("Server is already active for display %s\n%s %s\n%s\n",
451		   port, "\tIf this server is no longer running, remove",
452		   LockFile, "\tand start again.");
453      }
454    }
455  }
456  unlink(tmp);
457  if (!haslock)
458    FatalError("Could not create server lock file: %s\n", LockFile);
459  StillLocking = FALSE;
460}
461
462/*
463 * UnlockServer --
464 *      Remove the server lock file.
465 */
466void
467UnlockServer(void)
468{
469  if (nolock) return;
470
471  if (!StillLocking){
472
473  (void) unlink(LockFile);
474  }
475}
476#endif /* SERVER_LOCK */
477
478/* Force connections to close on SIGHUP from init */
479
480/*ARGSUSED*/
481SIGVAL
482AutoResetServer (int sig)
483{
484    int olderrno = errno;
485
486    dispatchException |= DE_RESET;
487    isItTimeToYield = TRUE;
488#ifdef GPROF
489    chdir ("/tmp");
490    exit (0);
491#endif
492#if defined(SYSV) && defined(X_NOT_POSIX)
493    OsSignal (SIGHUP, AutoResetServer);
494#endif
495    errno = olderrno;
496}
497
498/* Force connections to close and then exit on SIGTERM, SIGINT */
499
500/*ARGSUSED*/
501_X_EXPORT SIGVAL
502GiveUp(int sig)
503{
504    int olderrno = errno;
505
506    dispatchException |= DE_TERMINATE;
507    isItTimeToYield = TRUE;
508#if defined(SYSV) && defined(X_NOT_POSIX)
509    if (sig)
510	OsSignal(sig, SIG_IGN);
511#endif
512    errno = olderrno;
513}
514
515#if defined WIN32 && defined __MINGW32__
516_X_EXPORT CARD32
517GetTimeInMillis (void)
518{
519  return GetTickCount ();
520}
521#else
522_X_EXPORT CARD32
523GetTimeInMillis(void)
524{
525    struct timeval tv;
526
527#ifdef MONOTONIC_CLOCK
528    struct timespec tp;
529    if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
530        return (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000L);
531#endif
532
533    X_GETTIMEOFDAY(&tv);
534    return(tv.tv_sec * 1000) + (tv.tv_usec / 1000);
535}
536#endif
537
538_X_EXPORT void
539AdjustWaitForDelay (pointer waitTime, unsigned long newdelay)
540{
541    static struct timeval   delay_val;
542    struct timeval	    **wt = (struct timeval **) waitTime;
543    unsigned long	    olddelay;
544
545    if (*wt == NULL)
546    {
547	delay_val.tv_sec = newdelay / 1000;
548	delay_val.tv_usec = 1000 * (newdelay % 1000);
549	*wt = &delay_val;
550    }
551    else
552    {
553	olddelay = (*wt)->tv_sec * 1000 + (*wt)->tv_usec / 1000;
554	if (newdelay < olddelay)
555	{
556	    (*wt)->tv_sec = newdelay / 1000;
557	    (*wt)->tv_usec = 1000 * (newdelay % 1000);
558	}
559    }
560}
561
562void UseMsg(void)
563{
564#if !defined(AIXrt) && !defined(AIX386)
565    ErrorF("use: X [:<display>] [option]\n");
566    ErrorF("-a #                   mouse acceleration (pixels)\n");
567    ErrorF("-ac                    disable access control restrictions\n");
568#ifdef MEMBUG
569    ErrorF("-alloc int             chance alloc should fail\n");
570#endif
571    ErrorF("-audit int             set audit trail level\n");
572    ErrorF("-auth file             select authorization file\n");
573    ErrorF("-br                    create root window with black background\n");
574    ErrorF("+bs                    enable any backing store support\n");
575    ErrorF("-bs                    disable any backing store support\n");
576    ErrorF("-c                     turns off key-click\n");
577    ErrorF("c #                    key-click volume (0-100)\n");
578    ErrorF("-cc int                default color visual class\n");
579    ErrorF("-co file               color database file\n");
580#ifdef COMMANDLINE_CHALLENGED_OPERATING_SYSTEMS
581    ErrorF("-config file           read options from file\n");
582#endif
583    ErrorF("-core                  generate core dump on fatal error\n");
584    ErrorF("-dpi int               screen resolution in dots per inch\n");
585#ifdef DPMSExtension
586    ErrorF("dpms                   enables VESA DPMS monitor control\n");
587    ErrorF("-dpms                  disables VESA DPMS monitor control\n");
588#endif
589    ErrorF("-deferglyphs [none|all|16] defer loading of [no|all|16-bit] glyphs\n");
590    ErrorF("-f #                   bell base (0-100)\n");
591    ErrorF("-fc string             cursor font\n");
592    ErrorF("-fn string             default font name\n");
593    ErrorF("-fp string             default font path\n");
594    ErrorF("-help                  prints message with these options\n");
595    ErrorF("-I                     ignore all remaining arguments\n");
596#ifdef RLIMIT_DATA
597    ErrorF("-ld int                limit data space to N Kb\n");
598#endif
599#ifdef RLIMIT_NOFILE
600    ErrorF("-lf int                limit number of open files to N\n");
601#endif
602#ifdef RLIMIT_STACK
603    ErrorF("-ls int                limit stack space to N Kb\n");
604#endif
605#ifdef SERVER_LOCK
606    ErrorF("-nolock                disable the locking mechanism\n");
607#endif
608#ifndef NOLOGOHACK
609    ErrorF("-logo                  enable logo in screen saver\n");
610    ErrorF("nologo                 disable logo in screen saver\n");
611#endif
612    ErrorF("-nolisten string       don't listen on protocol\n");
613    ErrorF("-noreset               don't reset after last client exists\n");
614    ErrorF("-reset                 reset after last client exists\n");
615    ErrorF("-p #                   screen-saver pattern duration (minutes)\n");
616    ErrorF("-pn                    accept failure to listen on all ports\n");
617    ErrorF("-nopn                  reject failure to listen on all ports\n");
618    ErrorF("-r                     turns off auto-repeat\n");
619    ErrorF("r                      turns on auto-repeat \n");
620#ifdef RENDER
621    ErrorF("-render [default|mono|gray|color] set render color alloc policy\n");
622#endif
623    ErrorF("-s #                   screen-saver timeout (minutes)\n");
624#ifdef XCSECURITY
625    ErrorF("-sp file               security policy file\n");
626#endif
627#ifdef XPRINT
628    PrinterUseMsg();
629#endif
630    ErrorF("-su                    disable any save under support\n");
631    ErrorF("-t #                   mouse threshold (pixels)\n");
632    ErrorF("-terminate             terminate at server reset\n");
633    ErrorF("-to #                  connection time out\n");
634    ErrorF("-tst                   disable testing extensions\n");
635    ErrorF("ttyxx                  server started from init on /dev/ttyxx\n");
636    ErrorF("v                      video blanking for screen-saver\n");
637    ErrorF("-v                     screen-saver without video blanking\n");
638    ErrorF("-wm                    WhenMapped default backing-store\n");
639    ErrorF("-wr                    create root window with white background\n");
640    ErrorF("-x string              loads named extension at init time \n");
641    ErrorF("-maxbigreqsize         set maximal bigrequest size \n");
642#ifdef PANORAMIX
643    ErrorF("+xinerama              Enable XINERAMA extension\n");
644    ErrorF("-xinerama              Disable XINERAMA extension\n");
645#endif
646#ifdef SMART_SCHEDULE
647    ErrorF("-dumbSched             Disable smart scheduling, enable old behavior\n");
648    ErrorF("-schedInterval int     Set scheduler interval in msec\n");
649#endif
650    ErrorF("+extension name        Enable extension\n");
651    ErrorF("-extension name        Disable extension\n");
652#ifdef XDMCP
653    XdmcpUseMsg();
654#endif
655#endif /* !AIXrt && ! AIX386 */
656#ifdef XKB
657    XkbUseMsg();
658#endif
659    ddxUseMsg();
660}
661
662/*  This function performs a rudimentary sanity check
663 *  on the display name passed in on the command-line,
664 *  since this string is used to generate filenames.
665 *  It is especially important that the display name
666 *  not contain a "/" and not start with a "-".
667 *                                            --kvajk
668 */
669static int
670VerifyDisplayName(const char *d)
671{
672    if ( d == (char *)0 ) return( 0 );  /*  null  */
673    if ( *d == '\0' ) return( 0 );  /*  empty  */
674    if ( *d == '-' ) return( 0 );  /*  could be confused for an option  */
675    if ( *d == '.' ) return( 0 );  /*  must not equal "." or ".."  */
676    if ( strchr(d, '/') != (char *)0 ) return( 0 );  /*  very important!!!  */
677    return( 1 );
678}
679
680/*
681 * This function is responsible for doing initalisation of any global
682 * variables at an very early point of server startup (even before
683 * |ProcessCommandLine()|.
684 */
685void InitGlobals(void)
686{
687    ddxInitGlobals();
688}
689
690
691/*
692 * This function parses the command line. Handles device-independent fields
693 * and allows ddx to handle additional fields.  It is not allowed to modify
694 * argc or any of the strings pointed to by argv.
695 */
696void
697ProcessCommandLine(int argc, char *argv[])
698{
699    int i, skip;
700
701    defaultKeyboardControl.autoRepeat = TRUE;
702
703#ifdef NO_PART_NET
704    PartialNetwork = FALSE;
705#else
706    PartialNetwork = TRUE;
707#endif
708
709    for ( i = 1; i < argc; i++ )
710    {
711	/* call ddx first, so it can peek/override if it wants */
712        if((skip = ddxProcessArgument(argc, argv, i)))
713	{
714	    i += (skip - 1);
715	}
716	else if(argv[i][0] ==  ':')
717	{
718	    /* initialize display */
719	    display = argv[i];
720	    display++;
721            if( ! VerifyDisplayName( display ) ) {
722                ErrorF("Bad display name: %s\n", display);
723                UseMsg();
724		FatalError("Bad display name, exiting: %s\n", display);
725            }
726	}
727	else if ( strcmp( argv[i], "-a") == 0)
728	{
729	    if(++i < argc)
730	        defaultPointerControl.num = atoi(argv[i]);
731	    else
732		UseMsg();
733	}
734	else if ( strcmp( argv[i], "-ac") == 0)
735	{
736	    defeatAccessControl = TRUE;
737	}
738#ifdef MEMBUG
739	else if ( strcmp( argv[i], "-alloc") == 0)
740	{
741	    if(++i < argc)
742	        Memory_fail = atoi(argv[i]);
743	    else
744		UseMsg();
745	}
746#endif
747	else if ( strcmp( argv[i], "-audit") == 0)
748	{
749	    if(++i < argc)
750	        auditTrailLevel = atoi(argv[i]);
751	    else
752		UseMsg();
753	}
754	else if ( strcmp( argv[i], "-auth") == 0)
755	{
756	    if(++i < argc)
757	        InitAuthorization (argv[i]);
758	    else
759		UseMsg();
760	}
761	else if ( strcmp( argv[i], "-br") == 0)
762	    blackRoot = TRUE;
763	else if ( strcmp( argv[i], "+bs") == 0)
764	    enableBackingStore = TRUE;
765	else if ( strcmp( argv[i], "-bs") == 0)
766	    disableBackingStore = TRUE;
767	else if ( strcmp( argv[i], "c") == 0)
768	{
769	    if(++i < argc)
770	        defaultKeyboardControl.click = atoi(argv[i]);
771	    else
772		UseMsg();
773	}
774	else if ( strcmp( argv[i], "-c") == 0)
775	{
776	    defaultKeyboardControl.click = 0;
777	}
778	else if ( strcmp( argv[i], "-cc") == 0)
779	{
780	    if(++i < argc)
781	        defaultColorVisualClass = atoi(argv[i]);
782	    else
783		UseMsg();
784	}
785	else if ( strcmp( argv[i], "-co") == 0)
786	{
787	    if(++i < argc)
788	        rgbPath = argv[i];
789	    else
790		UseMsg();
791	}
792	else if ( strcmp( argv[i], "-core") == 0)
793	{
794	    CoreDump = TRUE;
795#if !defined(WIN32) || !defined(__MINGW32__)
796	    struct rlimit   core_limit;
797	    getrlimit (RLIMIT_CORE, &core_limit);
798	    core_limit.rlim_cur = core_limit.rlim_max;
799	    setrlimit (RLIMIT_CORE, &core_limit);
800#endif
801	}
802	else if ( strcmp( argv[i], "-dpi") == 0)
803	{
804	    if(++i < argc)
805	        monitorResolution = atoi(argv[i]);
806	    else
807		UseMsg();
808	}
809#ifdef DPMSExtension
810	else if ( strcmp( argv[i], "dpms") == 0)
811	    DPMSEnabledSwitch = TRUE;
812	else if ( strcmp( argv[i], "-dpms") == 0)
813	    DPMSDisabledSwitch = TRUE;
814#endif
815	else if ( strcmp( argv[i], "-deferglyphs") == 0)
816	{
817	    if(++i >= argc || !ParseGlyphCachingMode(argv[i]))
818		UseMsg();
819	}
820	else if ( strcmp( argv[i], "-f") == 0)
821	{
822	    if(++i < argc)
823	        defaultKeyboardControl.bell = atoi(argv[i]);
824	    else
825		UseMsg();
826	}
827	else if ( strcmp( argv[i], "-fc") == 0)
828	{
829	    if(++i < argc)
830	        defaultCursorFont = argv[i];
831	    else
832		UseMsg();
833	}
834	else if ( strcmp( argv[i], "-fn") == 0)
835	{
836	    if(++i < argc)
837	        defaultTextFont = argv[i];
838	    else
839		UseMsg();
840	}
841	else if ( strcmp( argv[i], "-fp") == 0)
842	{
843	    if(++i < argc)
844	    {
845	        defaultFontPath = argv[i];
846	    }
847	    else
848		UseMsg();
849	}
850	else if ( strcmp( argv[i], "-help") == 0)
851	{
852	    UseMsg();
853	    exit(0);
854	}
855#ifdef XKB
856        else if ( (skip=XkbProcessArguments(argc,argv,i))!=0 ) {
857	    if (skip>0)
858		 i+= skip-1;
859	    else UseMsg();
860	}
861#endif
862#ifdef RLIMIT_DATA
863	else if ( strcmp( argv[i], "-ld") == 0)
864	{
865	    if(++i < argc)
866	    {
867	        limitDataSpace = atoi(argv[i]);
868		if (limitDataSpace > 0)
869		    limitDataSpace *= 1024;
870	    }
871	    else
872		UseMsg();
873	}
874#endif
875#ifdef RLIMIT_NOFILE
876	else if ( strcmp( argv[i], "-lf") == 0)
877	{
878	    if(++i < argc)
879	        limitNoFile = atoi(argv[i]);
880	    else
881		UseMsg();
882	}
883#endif
884#ifdef RLIMIT_STACK
885	else if ( strcmp( argv[i], "-ls") == 0)
886	{
887	    if(++i < argc)
888	    {
889	        limitStackSpace = atoi(argv[i]);
890		if (limitStackSpace > 0)
891		    limitStackSpace *= 1024;
892	    }
893	    else
894		UseMsg();
895	}
896#endif
897#ifdef SERVER_LOCK
898	else if ( strcmp ( argv[i], "-nolock") == 0)
899	{
900#if !defined(WIN32) && !defined(__CYGWIN__)
901	  if (getuid() != 0)
902	    ErrorF("Warning: the -nolock option can only be used by root\n");
903	  else
904#endif
905	    nolock = TRUE;
906	}
907#endif
908#ifndef NOLOGOHACK
909	else if ( strcmp( argv[i], "-logo") == 0)
910	{
911	    logoScreenSaver = 1;
912	}
913	else if ( strcmp( argv[i], "nologo") == 0)
914	{
915	    logoScreenSaver = 0;
916	}
917#endif
918	else if ( strcmp( argv[i], "-nolisten") == 0)
919	{
920            if(++i < argc) {
921		if (_XSERVTransNoListen(argv[i]))
922		    FatalError ("Failed to disable listen for %s transport",
923				argv[i]);
924	   } else
925		UseMsg();
926	}
927	else if ( strcmp( argv[i], "-noreset") == 0)
928	{
929	    dispatchExceptionAtReset = 0;
930	}
931	else if ( strcmp( argv[i], "-reset") == 0)
932	{
933	    dispatchExceptionAtReset = DE_RESET;
934	}
935	else if ( strcmp( argv[i], "-p") == 0)
936	{
937	    if(++i < argc)
938	        defaultScreenSaverInterval = ((CARD32)atoi(argv[i])) *
939					     MILLI_PER_MIN;
940	    else
941		UseMsg();
942	}
943	else if ( strcmp( argv[i], "-pn") == 0)
944	    PartialNetwork = TRUE;
945	else if ( strcmp( argv[i], "-nopn") == 0)
946	    PartialNetwork = FALSE;
947	else if ( strcmp( argv[i], "r") == 0)
948	    defaultKeyboardControl.autoRepeat = TRUE;
949	else if ( strcmp( argv[i], "-r") == 0)
950	    defaultKeyboardControl.autoRepeat = FALSE;
951	else if ( strcmp( argv[i], "-s") == 0)
952	{
953	    if(++i < argc)
954	        defaultScreenSaverTime = ((CARD32)atoi(argv[i])) *
955					 MILLI_PER_MIN;
956	    else
957		UseMsg();
958	}
959	else if ( strcmp( argv[i], "-su") == 0)
960	    disableSaveUnders = TRUE;
961	else if ( strcmp( argv[i], "-t") == 0)
962	{
963	    if(++i < argc)
964	        defaultPointerControl.threshold = atoi(argv[i]);
965	    else
966		UseMsg();
967	}
968	else if ( strcmp( argv[i], "-terminate") == 0)
969	{
970	    dispatchExceptionAtReset = DE_TERMINATE;
971	}
972	else if ( strcmp( argv[i], "-to") == 0)
973	{
974	    if(++i < argc)
975		TimeOutValue = ((CARD32)atoi(argv[i])) * MILLI_PER_SECOND;
976	    else
977		UseMsg();
978	}
979	else if ( strcmp( argv[i], "-tst") == 0)
980	{
981	    noTestExtensions = TRUE;
982	}
983	else if ( strcmp( argv[i], "v") == 0)
984	    defaultScreenSaverBlanking = PreferBlanking;
985	else if ( strcmp( argv[i], "-v") == 0)
986	    defaultScreenSaverBlanking = DontPreferBlanking;
987	else if ( strcmp( argv[i], "-wm") == 0)
988	    defaultBackingStore = WhenMapped;
989        else if ( strcmp( argv[i], "-wr") == 0)
990            whiteRoot = TRUE;
991        else if ( strcmp( argv[i], "-maxbigreqsize") == 0) {
992             if(++i < argc) {
993                 long reqSizeArg = atol(argv[i]);
994
995                 /* Request size > 128MB does not make much sense... */
996                 if( reqSizeArg > 0L && reqSizeArg < 128L ) {
997                     maxBigRequestSize = (reqSizeArg * 1048576L) - 1L;
998                 }
999                 else
1000                 {
1001                     UseMsg();
1002                 }
1003             }
1004             else
1005             {
1006                 UseMsg();
1007             }
1008         }
1009#ifdef PANORAMIX
1010	else if ( strcmp( argv[i], "+xinerama") == 0){
1011	    noPanoramiXExtension = FALSE;
1012	}
1013	else if ( strcmp( argv[i], "-xinerama") == 0){
1014	    noPanoramiXExtension = TRUE;
1015	}
1016	else if ( strcmp( argv[i], "-disablexineramaextension") == 0){
1017	    PanoramiXExtensionDisabledHack = TRUE;
1018	}
1019#endif
1020	else if ( strcmp( argv[i], "-x") == 0)
1021	{
1022	    if(++i >= argc)
1023		UseMsg();
1024	    /* For U**x, which doesn't support dynamic loading, there's nothing
1025	     * to do when we see a -x.  Either the extension is linked in or
1026	     * it isn't */
1027	}
1028	else if ( strcmp( argv[i], "-I") == 0)
1029	{
1030	    /* ignore all remaining arguments */
1031	    break;
1032	}
1033	else if (strncmp (argv[i], "tty", 3) == 0)
1034	{
1035	    /* just in case any body is interested */
1036	    dev_tty_from_init = argv[i];
1037	}
1038#ifdef XDMCP
1039	else if ((skip = XdmcpOptions(argc, argv, i)) != i)
1040	{
1041	    i = skip - 1;
1042	}
1043#endif
1044#ifdef XPRINT
1045	else if ((skip = PrinterOptions(argc, argv, i)) != i)
1046	{
1047	    i = skip - 1;
1048	}
1049#endif
1050#ifdef XCSECURITY
1051	else if ((skip = XSecurityOptions(argc, argv, i)) != i)
1052	{
1053	    i = skip - 1;
1054	}
1055#endif
1056#ifdef AIXV3
1057        else if ( strcmp( argv[i], "-timeout") == 0)
1058        {
1059            if(++i < argc)
1060                SelectWaitTime = atoi(argv[i]);
1061            else
1062                UseMsg();
1063        }
1064        else if ( strcmp( argv[i], "-sync") == 0)
1065        {
1066            SyncOn++;
1067        }
1068#endif
1069#ifdef SMART_SCHEDULE
1070	else if ( strcmp( argv[i], "-dumbSched") == 0)
1071	{
1072	    SmartScheduleDisable = TRUE;
1073	}
1074	else if ( strcmp( argv[i], "-schedInterval") == 0)
1075	{
1076	    if (++i < argc)
1077	    {
1078		SmartScheduleInterval = atoi(argv[i]);
1079		SmartScheduleSlice = SmartScheduleInterval;
1080	    }
1081	    else
1082		UseMsg();
1083	}
1084	else if ( strcmp( argv[i], "-schedMax") == 0)
1085	{
1086	    if (++i < argc)
1087	    {
1088		SmartScheduleMaxSlice = atoi(argv[i]);
1089	    }
1090	    else
1091		UseMsg();
1092	}
1093#endif
1094#ifdef RENDER
1095	else if ( strcmp( argv[i], "-render" ) == 0)
1096	{
1097	    if (++i < argc)
1098	    {
1099		int policy = PictureParseCmapPolicy (argv[i]);
1100
1101		if (policy != PictureCmapPolicyInvalid)
1102		    PictureCmapPolicy = policy;
1103		else
1104		    UseMsg ();
1105	    }
1106	    else
1107		UseMsg ();
1108	}
1109#endif
1110	else if ( strcmp( argv[i], "+extension") == 0)
1111	{
1112	    if (++i < argc)
1113	    {
1114		if (!EnableDisableExtension(argv[i], TRUE))
1115		    EnableDisableExtensionError(argv[i], TRUE);
1116	    }
1117	    else
1118		UseMsg();
1119	}
1120	else if ( strcmp( argv[i], "-extension") == 0)
1121	{
1122	    if (++i < argc)
1123	    {
1124		if (!EnableDisableExtension(argv[i], FALSE))
1125		    EnableDisableExtensionError(argv[i], FALSE);
1126	    }
1127	    else
1128		UseMsg();
1129	}
1130 	else
1131 	{
1132	    ErrorF("Unrecognized option: %s\n", argv[i]);
1133	    UseMsg();
1134	    FatalError("Unrecognized option: %s\n", argv[i]);
1135        }
1136    }
1137}
1138
1139#ifdef COMMANDLINE_CHALLENGED_OPERATING_SYSTEMS
1140static void
1141InsertFileIntoCommandLine(
1142    int *resargc, char ***resargv,
1143    int prefix_argc, char **prefix_argv,
1144    char *filename,
1145    int suffix_argc, char **suffix_argv)
1146{
1147    struct stat     st;
1148    FILE           *f;
1149    char           *p;
1150    char           *q;
1151    int             insert_argc;
1152    char           *buf;
1153    int             len;
1154    int             i;
1155
1156    f = fopen(filename, "r");
1157    if (!f)
1158	FatalError("Can't open option file %s\n", filename);
1159
1160    fstat(fileno(f), &st);
1161
1162    buf = (char *) xalloc((unsigned) st.st_size + 1);
1163    if (!buf)
1164	FatalError("Out of Memory\n");
1165
1166    len = fread(buf, 1, (unsigned) st.st_size, f);
1167
1168    fclose(f);
1169
1170    if (len < 0)
1171	FatalError("Error reading option file %s\n", filename);
1172
1173    buf[len] = '\0';
1174
1175    p = buf;
1176    q = buf;
1177    insert_argc = 0;
1178
1179    while (*p)
1180    {
1181	while (isspace(*p))
1182	    p++;
1183	if (!*p)
1184	    break;
1185	if (*p == '#')
1186	{
1187	    while (*p && *p != '\n')
1188		p++;
1189	} else
1190	{
1191	    while (*p && !isspace(*p))
1192		*q++ = *p++;
1193	    /* Since p and q might still be pointing at the same place, we	 */
1194	    /* need to step p over the whitespace now before we add the null.	 */
1195	    if (*p)
1196		p++;
1197	    *q++ = '\0';
1198	    insert_argc++;
1199	}
1200    }
1201
1202    buf = (char *) xrealloc(buf, q - buf);
1203    if (!buf)
1204	FatalError("Out of memory reallocing option buf\n");
1205
1206    *resargc = prefix_argc + insert_argc + suffix_argc;
1207    *resargv = (char **) xalloc((*resargc + 1) * sizeof(char *));
1208    if (!*resargv)
1209	FatalError("Out of Memory\n");
1210
1211    memcpy(*resargv, prefix_argv, prefix_argc * sizeof(char *));
1212
1213    p = buf;
1214    for (i = 0; i < insert_argc; i++)
1215    {
1216	(*resargv)[prefix_argc + i] = p;
1217	p += strlen(p) + 1;
1218    }
1219
1220    memcpy(*resargv + prefix_argc + insert_argc,
1221	   suffix_argv, suffix_argc * sizeof(char *));
1222
1223    (*resargv)[*resargc] = NULL;
1224} /* end InsertFileIntoCommandLine */
1225
1226
1227void
1228ExpandCommandLine(int *pargc, char ***pargv)
1229{
1230    int i;
1231
1232#if !defined(WIN32) && !defined(__CYGWIN__)
1233    if (getuid() != geteuid())
1234	return;
1235#endif
1236
1237    for (i = 1; i < *pargc; i++)
1238    {
1239	if ( (0 == strcmp((*pargv)[i], "-config")) && (i < (*pargc - 1)) )
1240	{
1241	    InsertFileIntoCommandLine(pargc, pargv,
1242					  i, *pargv,
1243					  (*pargv)[i+1], /* filename */
1244					  *pargc - i - 2, *pargv + i + 2);
1245	    i--;
1246	}
1247    }
1248} /* end ExpandCommandLine */
1249#endif
1250
1251/* Implement a simple-minded font authorization scheme.  The authorization
1252   name is "hp-hostname-1", the contents are simply the host name. */
1253int
1254set_font_authorizations(char **authorizations, int *authlen, pointer client)
1255{
1256#define AUTHORIZATION_NAME "hp-hostname-1"
1257#if defined(TCPCONN) || defined(STREAMSCONN)
1258    static char *result = NULL;
1259    static char *p = NULL;
1260
1261    if (p == NULL)
1262    {
1263	char hname[1024], *hnameptr;
1264	unsigned int len;
1265#if defined(IPv6) && defined(AF_INET6)
1266	struct addrinfo hints, *ai = NULL;
1267#else
1268	struct hostent *host;
1269#ifdef XTHREADS_NEEDS_BYNAMEPARAMS
1270	_Xgethostbynameparams hparams;
1271#endif
1272#endif
1273
1274	gethostname(hname, 1024);
1275#if defined(IPv6) && defined(AF_INET6)
1276	bzero(&hints, sizeof(hints));
1277	hints.ai_flags = AI_CANONNAME;
1278	if (getaddrinfo(hname, NULL, &hints, &ai) == 0) {
1279	    hnameptr = ai->ai_canonname;
1280	} else {
1281	    hnameptr = hname;
1282	}
1283#else
1284	host = _XGethostbyname(hname, hparams);
1285	if (host == NULL)
1286	    hnameptr = hname;
1287	else
1288	    hnameptr = host->h_name;
1289#endif
1290
1291	len = strlen(hnameptr) + 1;
1292	result = xalloc(len + sizeof(AUTHORIZATION_NAME) + 4);
1293
1294	p = result;
1295        *p++ = sizeof(AUTHORIZATION_NAME) >> 8;
1296        *p++ = sizeof(AUTHORIZATION_NAME) & 0xff;
1297        *p++ = (len) >> 8;
1298        *p++ = (len & 0xff);
1299
1300	memmove(p, AUTHORIZATION_NAME, sizeof(AUTHORIZATION_NAME));
1301	p += sizeof(AUTHORIZATION_NAME);
1302	memmove(p, hnameptr, len);
1303	p += len;
1304#if defined(IPv6) && defined(AF_INET6)
1305	if (ai) {
1306	    freeaddrinfo(ai);
1307	}
1308#endif
1309    }
1310    *authlen = p - result;
1311    *authorizations = result;
1312    return 1;
1313#else /* TCPCONN */
1314    return 0;
1315#endif /* TCPCONN */
1316}
1317
1318/* XALLOC -- X's internal memory allocator.  Why does it return unsigned
1319 * long * instead of the more common char *?  Well, if you read K&R you'll
1320 * see they say that alloc must return a pointer "suitable for conversion"
1321 * to whatever type you really want.  In a full-blown generic allocator
1322 * there's no way to solve the alignment problems without potentially
1323 * wasting lots of space.  But we have a more limited problem. We know
1324 * we're only ever returning pointers to structures which will have to
1325 * be long word aligned.  So we are making a stronger guarantee.  It might
1326 * have made sense to make Xalloc return char * to conform with people's
1327 * expectations of malloc, but this makes lint happier.
1328 */
1329
1330#ifndef INTERNAL_MALLOC
1331
1332_X_EXPORT void *
1333Xalloc(unsigned long amount)
1334{
1335    register pointer  ptr;
1336
1337    if ((long)amount <= 0) {
1338	return (unsigned long *)NULL;
1339    }
1340    /* aligned extra on long word boundary */
1341    amount = (amount + (sizeof(long) - 1)) & ~(sizeof(long) - 1);
1342#ifdef MEMBUG
1343    if (!Must_have_memory && Memory_fail &&
1344	((random() % MEM_FAIL_SCALE) < Memory_fail))
1345	return (unsigned long *)NULL;
1346#endif
1347    if ((ptr = (pointer)malloc(amount))) {
1348	return (unsigned long *)ptr;
1349    }
1350    if (Must_have_memory)
1351	FatalError("Out of memory");
1352    return (unsigned long *)NULL;
1353}
1354
1355/*****************
1356 * XNFalloc
1357 * "no failure" realloc, alternate interface to Xalloc w/o Must_have_memory
1358 *****************/
1359
1360_X_EXPORT void *
1361XNFalloc(unsigned long amount)
1362{
1363    register pointer ptr;
1364
1365    if ((long)amount <= 0)
1366    {
1367        return (unsigned long *)NULL;
1368    }
1369    /* aligned extra on long word boundary */
1370    amount = (amount + (sizeof(long) - 1)) & ~(sizeof(long) - 1);
1371    ptr = (pointer)malloc(amount);
1372    if (!ptr)
1373    {
1374        FatalError("Out of memory");
1375    }
1376    return ((unsigned long *)ptr);
1377}
1378
1379/*****************
1380 * Xcalloc
1381 *****************/
1382
1383_X_EXPORT void *
1384Xcalloc(unsigned long amount)
1385{
1386    unsigned long   *ret;
1387
1388    ret = Xalloc (amount);
1389    if (ret)
1390	bzero ((char *) ret, (int) amount);
1391    return ret;
1392}
1393
1394/*****************
1395 * XNFcalloc
1396 *****************/
1397
1398_X_EXPORT void *
1399XNFcalloc(unsigned long amount)
1400{
1401    unsigned long   *ret;
1402
1403    ret = Xalloc (amount);
1404    if (ret)
1405	bzero ((char *) ret, (int) amount);
1406    else if ((long)amount > 0)
1407        FatalError("Out of memory");
1408    return ret;
1409}
1410
1411/*****************
1412 * Xrealloc
1413 *****************/
1414
1415_X_EXPORT void *
1416Xrealloc(pointer ptr, unsigned long amount)
1417{
1418#ifdef MEMBUG
1419    if (!Must_have_memory && Memory_fail &&
1420	((random() % MEM_FAIL_SCALE) < Memory_fail))
1421	return (unsigned long *)NULL;
1422#endif
1423    if ((long)amount <= 0)
1424    {
1425	if (ptr && !amount)
1426	    free(ptr);
1427	return (unsigned long *)NULL;
1428    }
1429    amount = (amount + (sizeof(long) - 1)) & ~(sizeof(long) - 1);
1430    if (ptr)
1431        ptr = (pointer)realloc((char *)ptr, amount);
1432    else
1433	ptr = (pointer)malloc(amount);
1434    if (ptr)
1435        return (unsigned long *)ptr;
1436    if (Must_have_memory)
1437	FatalError("Out of memory");
1438    return (unsigned long *)NULL;
1439}
1440
1441/*****************
1442 * XNFrealloc
1443 * "no failure" realloc, alternate interface to Xrealloc w/o Must_have_memory
1444 *****************/
1445
1446_X_EXPORT void *
1447XNFrealloc(pointer ptr, unsigned long amount)
1448{
1449    if (( ptr = (pointer)Xrealloc( ptr, amount ) ) == NULL)
1450    {
1451	if ((long)amount > 0)
1452            FatalError( "Out of memory" );
1453    }
1454    return ((unsigned long *)ptr);
1455}
1456
1457/*****************
1458 *  Xfree
1459 *    calls free
1460 *****************/
1461
1462_X_EXPORT void
1463Xfree(pointer ptr)
1464{
1465    if (ptr)
1466	free((char *)ptr);
1467}
1468
1469void
1470OsInitAllocator (void)
1471{
1472#ifdef MEMBUG
1473    static int	been_here;
1474
1475    /* Check the memory system after each generation */
1476    if (been_here)
1477	CheckMemory ();
1478    else
1479	been_here = 1;
1480#endif
1481}
1482#endif /* !INTERNAL_MALLOC */
1483
1484
1485char *
1486Xstrdup(const char *s)
1487{
1488    char *sd;
1489
1490    if (s == NULL)
1491	return NULL;
1492
1493    sd = (char *)Xalloc(strlen(s) + 1);
1494    if (sd != NULL)
1495	strcpy(sd, s);
1496    return sd;
1497}
1498
1499
1500_X_EXPORT char *
1501XNFstrdup(const char *s)
1502{
1503    char *sd;
1504
1505    if (s == NULL)
1506	return NULL;
1507
1508    sd = (char *)XNFalloc(strlen(s) + 1);
1509    strcpy(sd, s);
1510    return sd;
1511}
1512
1513#ifdef SMART_SCHEDULE
1514
1515unsigned long	SmartScheduleIdleCount;
1516Bool		SmartScheduleIdle;
1517Bool		SmartScheduleTimerStopped;
1518
1519#ifdef SIGVTALRM
1520#define SMART_SCHEDULE_POSSIBLE
1521#endif
1522
1523#ifdef SMART_SCHEDULE_POSSIBLE
1524#define SMART_SCHEDULE_SIGNAL		SIGALRM
1525#define SMART_SCHEDULE_TIMER		ITIMER_REAL
1526#endif
1527
1528static void
1529SmartScheduleStopTimer (void)
1530{
1531#ifdef SMART_SCHEDULE_POSSIBLE
1532    struct itimerval	timer;
1533
1534    timer.it_interval.tv_sec = 0;
1535    timer.it_interval.tv_usec = 0;
1536    timer.it_value.tv_sec = 0;
1537    timer.it_value.tv_usec = 0;
1538    (void) setitimer (ITIMER_REAL, &timer, 0);
1539    SmartScheduleTimerStopped = TRUE;
1540#endif
1541}
1542
1543Bool
1544SmartScheduleStartTimer (void)
1545{
1546#ifdef SMART_SCHEDULE_POSSIBLE
1547    struct itimerval	timer;
1548
1549    SmartScheduleTimerStopped = FALSE;
1550    timer.it_interval.tv_sec = 0;
1551    timer.it_interval.tv_usec = SmartScheduleInterval * 1000;
1552    timer.it_value.tv_sec = 0;
1553    timer.it_value.tv_usec = SmartScheduleInterval * 1000;
1554    return setitimer (ITIMER_REAL, &timer, 0) >= 0;
1555#endif
1556    return FALSE;
1557}
1558
1559#ifdef SMART_SCHEDULE_POSSIBLE
1560static void
1561SmartScheduleTimer (int sig)
1562{
1563    int olderrno = errno;
1564
1565    SmartScheduleTime += SmartScheduleInterval;
1566    if (SmartScheduleIdle)
1567    {
1568	SmartScheduleStopTimer ();
1569    }
1570    errno = olderrno;
1571}
1572#endif
1573
1574Bool
1575SmartScheduleInit (void)
1576{
1577#ifdef SMART_SCHEDULE_POSSIBLE
1578    struct sigaction	act;
1579
1580    if (SmartScheduleDisable)
1581	return TRUE;
1582
1583    bzero ((char *) &act, sizeof(struct sigaction));
1584
1585    /* Set up the timer signal function */
1586    act.sa_handler = SmartScheduleTimer;
1587    sigemptyset (&act.sa_mask);
1588    sigaddset (&act.sa_mask, SMART_SCHEDULE_SIGNAL);
1589    if (sigaction (SMART_SCHEDULE_SIGNAL, &act, 0) < 0)
1590    {
1591	perror ("sigaction for smart scheduler");
1592	return FALSE;
1593    }
1594    /* Set up the virtual timer */
1595    if (!SmartScheduleStartTimer ())
1596    {
1597	perror ("scheduling timer");
1598	return FALSE;
1599    }
1600    /* stop the timer and wait for WaitForSomething to start it */
1601    SmartScheduleStopTimer ();
1602    return TRUE;
1603#else
1604    return FALSE;
1605#endif
1606}
1607#endif
1608
1609#ifdef SIG_BLOCK
1610static sigset_t	PreviousSignalMask;
1611static int	BlockedSignalCount;
1612#endif
1613
1614void
1615OsBlockSignals (void)
1616{
1617#ifdef SIG_BLOCK
1618    if (BlockedSignalCount++ == 0)
1619    {
1620	sigset_t    set;
1621
1622	sigemptyset (&set);
1623#ifdef SIGALRM
1624	sigaddset (&set, SIGALRM);
1625#endif
1626#ifdef SIGVTALRM
1627	sigaddset (&set, SIGVTALRM);
1628#endif
1629#ifdef SIGWINCH
1630	sigaddset (&set, SIGWINCH);
1631#endif
1632#ifdef SIGIO
1633	sigaddset (&set, SIGIO);
1634#endif
1635#ifdef SIGTSTP
1636	sigaddset (&set, SIGTSTP);
1637#endif
1638#ifdef SIGTTIN
1639	sigaddset (&set, SIGTTIN);
1640#endif
1641#ifdef SIGTTOU
1642	sigaddset (&set, SIGTTOU);
1643#endif
1644#ifdef SIGCHLD
1645	sigaddset (&set, SIGCHLD);
1646#endif
1647	sigprocmask (SIG_BLOCK, &set, &PreviousSignalMask);
1648    }
1649#endif
1650}
1651
1652void
1653OsReleaseSignals (void)
1654{
1655#ifdef SIG_BLOCK
1656    if (--BlockedSignalCount == 0)
1657    {
1658	sigprocmask (SIG_SETMASK, &PreviousSignalMask, 0);
1659    }
1660#endif
1661}
1662
1663#if !defined(WIN32)
1664/*
1665 * "safer" versions of system(3), popen(3) and pclose(3) which give up
1666 * all privs before running a command.
1667 *
1668 * This is based on the code in FreeBSD 2.2 libc.
1669 *
1670 * XXX It'd be good to redirect stderr so that it ends up in the log file
1671 * as well.  As it is now, xkbcomp messages don't end up in the log file.
1672 */
1673
1674int
1675System(char *command)
1676{
1677    int pid, p;
1678#ifdef SIGCHLD
1679    void (*csig)(int);
1680#endif
1681    int status;
1682
1683    if (!command)
1684	return(1);
1685
1686#ifdef SIGCHLD
1687    csig = signal(SIGCHLD, SIG_DFL);
1688    if (csig == SIG_ERR) {
1689      perror("signal");
1690      return -1;
1691    }
1692#endif
1693
1694#ifdef DEBUG
1695    ErrorF("System: `%s'\n", command);
1696#endif
1697
1698    switch (pid = fork()) {
1699    case -1:	/* error */
1700	p = -1;
1701    case 0:	/* child */
1702	if (setgid(getgid()) == -1)
1703	    _exit(127);
1704	if (setuid(getuid()) == -1)
1705	    _exit(127);
1706	execl("/bin/sh", "sh", "-c", command, (char *)NULL);
1707	_exit(127);
1708    default:	/* parent */
1709	do {
1710	    p = waitpid(pid, &status, 0);
1711	} while (p == -1 && errno == EINTR);
1712
1713    }
1714
1715#ifdef SIGCHLD
1716    if (signal(SIGCHLD, csig) == SIG_ERR) {
1717      perror("signal");
1718      return -1;
1719    }
1720#endif
1721
1722    return p == -1 ? -1 : status;
1723}
1724
1725static struct pid {
1726    struct pid *next;
1727    FILE *fp;
1728    int pid;
1729} *pidlist;
1730
1731void (*old_alarm)(int) = NULL; /* XXX horrible awful hack */
1732
1733pointer
1734Popen(char *command, char *type)
1735{
1736    struct pid *cur;
1737    FILE *iop;
1738    int pdes[2], pid;
1739
1740    if (command == NULL || type == NULL)
1741	return NULL;
1742
1743    if ((*type != 'r' && *type != 'w') || type[1])
1744	return NULL;
1745
1746    if ((cur = (struct pid *)xalloc(sizeof(struct pid))) == NULL)
1747	return NULL;
1748
1749    if (pipe(pdes) < 0) {
1750	xfree(cur);
1751	return NULL;
1752    }
1753
1754    /* Ignore the smart scheduler while this is going on */
1755    old_alarm = signal(SIGALRM, SIG_IGN);
1756    if (old_alarm == SIG_ERR) {
1757      perror("signal");
1758      return NULL;
1759    }
1760
1761    switch (pid = fork()) {
1762    case -1: 	/* error */
1763	close(pdes[0]);
1764	close(pdes[1]);
1765	xfree(cur);
1766	if (signal(SIGALRM, old_alarm) == SIG_ERR)
1767	  perror("signal");
1768	return NULL;
1769    case 0:	/* child */
1770	if (setgid(getgid()) == -1)
1771	    _exit(127);
1772	if (setuid(getuid()) == -1)
1773	    _exit(127);
1774	if (*type == 'r') {
1775	    if (pdes[1] != 1) {
1776		/* stdout */
1777		dup2(pdes[1], 1);
1778		close(pdes[1]);
1779	    }
1780	    close(pdes[0]);
1781	} else {
1782	    if (pdes[0] != 0) {
1783		/* stdin */
1784		dup2(pdes[0], 0);
1785		close(pdes[0]);
1786	    }
1787	    close(pdes[1]);
1788	}
1789	execl("/bin/sh", "sh", "-c", command, (char *)NULL);
1790	_exit(127);
1791    }
1792
1793    /* Avoid EINTR during stdio calls */
1794    OsBlockSignals ();
1795
1796    /* parent */
1797    if (*type == 'r') {
1798	iop = fdopen(pdes[0], type);
1799	close(pdes[1]);
1800    } else {
1801	iop = fdopen(pdes[1], type);
1802	close(pdes[0]);
1803    }
1804
1805    cur->fp = iop;
1806    cur->pid = pid;
1807    cur->next = pidlist;
1808    pidlist = cur;
1809
1810#ifdef DEBUG
1811    ErrorF("Popen: `%s', fp = %p\n", command, iop);
1812#endif
1813
1814    return iop;
1815}
1816
1817/* fopen that drops privileges */
1818pointer
1819Fopen(char *file, char *type)
1820{
1821    FILE *iop;
1822#ifndef HAS_SAVED_IDS_AND_SETEUID
1823    struct pid *cur;
1824    int pdes[2], pid;
1825
1826    if (file == NULL || type == NULL)
1827	return NULL;
1828
1829    if ((*type != 'r' && *type != 'w') || type[1])
1830	return NULL;
1831
1832    if ((cur = (struct pid *)xalloc(sizeof(struct pid))) == NULL)
1833	return NULL;
1834
1835    if (pipe(pdes) < 0) {
1836	xfree(cur);
1837	return NULL;
1838    }
1839
1840    switch (pid = fork()) {
1841    case -1: 	/* error */
1842	close(pdes[0]);
1843	close(pdes[1]);
1844	xfree(cur);
1845	return NULL;
1846    case 0:	/* child */
1847	if (setgid(getgid()) == -1)
1848	    _exit(127);
1849	if (setuid(getuid()) == -1)
1850	    _exit(127);
1851	if (*type == 'r') {
1852	    if (pdes[1] != 1) {
1853		/* stdout */
1854		dup2(pdes[1], 1);
1855		close(pdes[1]);
1856	    }
1857	    close(pdes[0]);
1858	} else {
1859	    if (pdes[0] != 0) {
1860		/* stdin */
1861		dup2(pdes[0], 0);
1862		close(pdes[0]);
1863	    }
1864	    close(pdes[1]);
1865	}
1866	execl("/bin/cat", "cat", file, (char *)NULL);
1867	_exit(127);
1868    }
1869
1870    /* Avoid EINTR during stdio calls */
1871    OsBlockSignals ();
1872
1873    /* parent */
1874    if (*type == 'r') {
1875	iop = fdopen(pdes[0], type);
1876	close(pdes[1]);
1877    } else {
1878	iop = fdopen(pdes[1], type);
1879	close(pdes[0]);
1880    }
1881
1882    cur->fp = iop;
1883    cur->pid = pid;
1884    cur->next = pidlist;
1885    pidlist = cur;
1886
1887#ifdef DEBUG
1888    ErrorF("Fopen(%s), fp = %p\n", file, iop);
1889#endif
1890
1891    return iop;
1892#else
1893    int ruid, euid;
1894
1895    ruid = getuid();
1896    euid = geteuid();
1897
1898    if (seteuid(ruid) == -1) {
1899	    return NULL;
1900    }
1901    iop = fopen(file, type);
1902
1903    if (seteuid(euid) == -1) {
1904	    fclose(iop);
1905	    return NULL;
1906    }
1907    return iop;
1908#endif /* HAS_SAVED_IDS_AND_SETEUID */
1909}
1910
1911int
1912Pclose(pointer iop)
1913{
1914    struct pid *cur, *last;
1915    int pstat;
1916    int pid;
1917
1918#ifdef DEBUG
1919    ErrorF("Pclose: fp = %p\n", iop);
1920#endif
1921
1922    fclose(iop);
1923
1924    for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
1925	if (cur->fp == iop)
1926	    break;
1927    if (cur == NULL)
1928	return -1;
1929
1930    do {
1931	pid = waitpid(cur->pid, &pstat, 0);
1932    } while (pid == -1 && errno == EINTR);
1933
1934    if (last == NULL)
1935	pidlist = cur->next;
1936    else
1937	last->next = cur->next;
1938    xfree(cur);
1939
1940    /* allow EINTR again */
1941    OsReleaseSignals ();
1942
1943    if (old_alarm && signal(SIGALRM, old_alarm) == SIG_ERR) {
1944      perror("signal");
1945      return -1;
1946    }
1947
1948    return pid == -1 ? -1 : pstat;
1949}
1950
1951int
1952Fclose(pointer iop)
1953{
1954#ifdef HAS_SAVED_IDS_AND_SETEUID
1955    return fclose(iop);
1956#else
1957    return Pclose(iop);
1958#endif
1959}
1960
1961#endif /* !WIN32 */
1962
1963
1964/*
1965 * CheckUserParameters: check for long command line arguments and long
1966 * environment variables.  By default, these checks are only done when
1967 * the server's euid != ruid.  In 3.3.x, these checks were done in an
1968 * external wrapper utility.
1969 */
1970
1971/* Consider LD* variables insecure? */
1972#ifndef REMOVE_ENV_LD
1973#define REMOVE_ENV_LD 1
1974#endif
1975
1976/* Remove long environment variables? */
1977#ifndef REMOVE_LONG_ENV
1978#define REMOVE_LONG_ENV 1
1979#endif
1980
1981/*
1982 * Disallow stdout or stderr as pipes?  It's possible to block the X server
1983 * when piping stdout+stderr to a pipe.
1984 *
1985 * Don't enable this because it looks like it's going to cause problems.
1986 */
1987#ifndef NO_OUTPUT_PIPES
1988#define NO_OUTPUT_PIPES 0
1989#endif
1990
1991
1992/* Check args and env only if running setuid (euid == 0 && euid != uid) ? */
1993#ifndef CHECK_EUID
1994#ifndef WIN32
1995#define CHECK_EUID 1
1996#else
1997#define CHECK_EUID 0
1998#endif
1999#endif
2000
2001/*
2002 * Maybe the locale can be faked to make isprint(3) report that everything
2003 * is printable?  Avoid it by default.
2004 */
2005#ifndef USE_ISPRINT
2006#define USE_ISPRINT 0
2007#endif
2008
2009#define MAX_ARG_LENGTH          128
2010#define MAX_ENV_LENGTH          256
2011#define MAX_ENV_PATH_LENGTH     2048	/* Limit for *PATH and TERMCAP */
2012
2013#if USE_ISPRINT
2014#include <ctype.h>
2015#define checkPrintable(c) isprint(c)
2016#else
2017#define checkPrintable(c) (((c) & 0x7f) >= 0x20 && ((c) & 0x7f) != 0x7f)
2018#endif
2019
2020enum BadCode {
2021    NotBad = 0,
2022    UnsafeArg,
2023    ArgTooLong,
2024    UnprintableArg,
2025    EnvTooLong,
2026    OutputIsPipe,
2027    InternalError
2028};
2029
2030#if defined(VENDORSUPPORT)
2031#define BUGADDRESS VENDORSUPPORT
2032#elif defined(BUILDERADDR)
2033#define BUGADDRESS BUILDERADDR
2034#else
2035#define BUGADDRESS "xorg@freedesktop.org"
2036#endif
2037
2038#define ARGMSG \
2039    "\nIf the arguments used are valid, and have been rejected incorrectly\n" \
2040      "please send details of the arguments and why they are valid to\n" \
2041      "%s.  In the meantime, you can start the Xserver as\n" \
2042      "the \"super user\" (root).\n"
2043
2044#define ENVMSG \
2045    "\nIf the environment is valid, and have been rejected incorrectly\n" \
2046      "please send details of the environment and why it is valid to\n" \
2047      "%s.  In the meantime, you can start the Xserver as\n" \
2048      "the \"super user\" (root).\n"
2049
2050void
2051CheckUserParameters(int argc, char **argv, char **envp)
2052{
2053    enum BadCode bad = NotBad;
2054    int i = 0, j;
2055    char *a, *e = NULL;
2056#if defined(__QNX__) && !defined(__QNXNTO__)
2057    char cmd_name[64];
2058#endif
2059
2060#if CHECK_EUID
2061    if (geteuid() == 0 && getuid() != geteuid())
2062#endif
2063    {
2064	/* Check each argv[] */
2065	for (i = 1; i < argc; i++) {
2066	    if (strcmp(argv[i], "-fp") == 0)
2067	    {
2068		i++; /* continue with next argument. skip the length check */
2069		if (i >= argc)
2070		    break;
2071	    } else
2072	    {
2073		if (strlen(argv[i]) > MAX_ARG_LENGTH) {
2074		    bad = ArgTooLong;
2075		    break;
2076		}
2077	    }
2078	    a = argv[i];
2079	    while (*a) {
2080		if (checkPrintable(*a) == 0) {
2081		    bad = UnprintableArg;
2082		    break;
2083		}
2084		a++;
2085	    }
2086	    if (bad)
2087		break;
2088	}
2089	if (!bad) {
2090	    /* Check each envp[] */
2091	    for (i = 0; envp[i]; i++) {
2092
2093		/* Check for bad environment variables and values */
2094#if REMOVE_ENV_LD
2095		while (envp[i] && (strncmp(envp[i], "LD", 2) == 0)) {
2096#ifdef ENVDEBUG
2097		    ErrorF("CheckUserParameters: removing %s from the "
2098			   "environment\n", strtok(envp[i], "="));
2099#endif
2100		    for (j = i; envp[j]; j++) {
2101			envp[j] = envp[j+1];
2102		    }
2103		}
2104#endif
2105		if (envp[i] && (strlen(envp[i]) > MAX_ENV_LENGTH)) {
2106#if REMOVE_LONG_ENV
2107#ifdef ENVDEBUG
2108		    ErrorF("CheckUserParameters: removing %s from the "
2109			   "environment\n", strtok(envp[i], "="));
2110#endif
2111		    for (j = i; envp[j]; j++) {
2112			envp[j] = envp[j+1];
2113		    }
2114		    i--;
2115#else
2116		    char *eq;
2117		    int len;
2118
2119		    eq = strchr(envp[i], '=');
2120		    if (!eq)
2121			continue;
2122		    len = eq - envp[i];
2123		    e = malloc(len + 1);
2124		    if (!e) {
2125			bad = InternalError;
2126			break;
2127		    }
2128		    strncpy(e, envp[i], len);
2129		    e[len] = 0;
2130		    if (len >= 4 &&
2131			(strcmp(e + len - 4, "PATH") == 0 ||
2132			 strcmp(e, "TERMCAP") == 0)) {
2133			if (strlen(envp[i]) > MAX_ENV_PATH_LENGTH) {
2134			    bad = EnvTooLong;
2135			    break;
2136			} else {
2137			    free(e);
2138			}
2139		    } else {
2140			bad = EnvTooLong;
2141			break;
2142		    }
2143#endif
2144		}
2145	    }
2146	}
2147#if NO_OUTPUT_PIPES
2148	if (!bad) {
2149	    struct stat buf;
2150
2151	    if (fstat(fileno(stdout), &buf) == 0 && S_ISFIFO(buf.st_mode))
2152		bad = OutputIsPipe;
2153	    if (fstat(fileno(stderr), &buf) == 0 && S_ISFIFO(buf.st_mode))
2154		bad = OutputIsPipe;
2155	}
2156#endif
2157    }
2158    switch (bad) {
2159    case NotBad:
2160	return;
2161    case UnsafeArg:
2162	ErrorF("Command line argument number %d is unsafe\n", i);
2163	ErrorF(ARGMSG, BUGADDRESS);
2164	break;
2165    case ArgTooLong:
2166	ErrorF("Command line argument number %d is too long\n", i);
2167	ErrorF(ARGMSG, BUGADDRESS);
2168	break;
2169    case UnprintableArg:
2170	ErrorF("Command line argument number %d contains unprintable"
2171		" characters\n", i);
2172	ErrorF(ARGMSG, BUGADDRESS);
2173	break;
2174    case EnvTooLong:
2175	ErrorF("Environment variable `%s' is too long\n", e);
2176	ErrorF(ENVMSG, BUGADDRESS);
2177	break;
2178    case OutputIsPipe:
2179	ErrorF("Stdout and/or stderr is a pipe\n");
2180	break;
2181    case InternalError:
2182	ErrorF("Internal Error\n");
2183	break;
2184    default:
2185	ErrorF("Unknown error\n");
2186	ErrorF(ARGMSG, BUGADDRESS);
2187	ErrorF(ENVMSG, BUGADDRESS);
2188	break;
2189    }
2190    FatalError("X server aborted because of unsafe environment\n");
2191}
2192
2193/*
2194 * CheckUserAuthorization: check if the user is allowed to start the
2195 * X server.  This usually means some sort of PAM checking, and it is
2196 * usually only done for setuid servers (uid != euid).
2197 */
2198
2199#ifdef USE_PAM
2200#include <security/pam_appl.h>
2201#include <security/pam_misc.h>
2202#include <pwd.h>
2203#endif /* USE_PAM */
2204
2205void
2206CheckUserAuthorization(void)
2207{
2208#ifdef USE_PAM
2209    static struct pam_conv conv = {
2210	misc_conv,
2211	NULL
2212    };
2213
2214    pam_handle_t *pamh = NULL;
2215    struct passwd *pw;
2216    int retval;
2217
2218    if (getuid() != geteuid()) {
2219	pw = getpwuid(getuid());
2220	if (pw == NULL)
2221	    FatalError("getpwuid() failed for uid %d\n", getuid());
2222
2223	retval = pam_start("xserver", pw->pw_name, &conv, &pamh);
2224	if (retval != PAM_SUCCESS)
2225	    FatalError("pam_start() failed.\n"
2226			"\tMissing or mangled PAM config file or module?\n");
2227
2228	retval = pam_authenticate(pamh, 0);
2229	if (retval != PAM_SUCCESS) {
2230	    pam_end(pamh, retval);
2231	    FatalError("PAM authentication failed, cannot start X server.\n"
2232			"\tPerhaps you do not have console ownership?\n");
2233	}
2234
2235	retval = pam_acct_mgmt(pamh, 0);
2236	if (retval != PAM_SUCCESS) {
2237	    pam_end(pamh, retval);
2238	    FatalError("PAM authentication failed, cannot start X server.\n"
2239			"\tPerhaps you do not have console ownership?\n");
2240	}
2241
2242	/* this is not a session, so do not do session management */
2243	pam_end(pamh, PAM_SUCCESS);
2244    }
2245#endif
2246}
2247
2248#ifdef __SCO__
2249#include <fcntl.h>
2250
2251static void
2252lockit (int fd, short what)
2253{
2254  struct flock lck;
2255
2256  lck.l_whence = 0;
2257  lck.l_start = 0;
2258  lck.l_len = 1;
2259  lck.l_type = what;
2260
2261  (void)fcntl (fd, F_SETLKW, &lck);
2262}
2263
2264/* SCO OpenServer 5 lacks pread/pwrite. Emulate them. */
2265ssize_t
2266pread (int fd, void *buf, size_t nbytes, off_t offset)
2267{
2268  off_t saved;
2269  ssize_t ret;
2270
2271  lockit (fd, F_RDLCK);
2272  saved = lseek (fd, 0, SEEK_CUR);
2273  lseek (fd, offset, SEEK_SET);
2274  ret = read (fd, buf, nbytes);
2275  lseek (fd, saved, SEEK_SET);
2276  lockit (fd, F_UNLCK);
2277
2278  return ret;
2279}
2280
2281ssize_t
2282pwrite (int fd, const void *buf, size_t nbytes, off_t offset)
2283{
2284  off_t saved;
2285  ssize_t ret;
2286
2287  lockit (fd, F_WRLCK);
2288  saved = lseek (fd, 0, SEEK_CUR);
2289  lseek (fd, offset, SEEK_SET);
2290  ret = write (fd, buf, nbytes);
2291  lseek (fd, saved, SEEK_SET);
2292  lockit (fd, F_UNLCK);
2293
2294  return ret;
2295}
2296#endif /* __SCO__ */
2297