TDE base libraries and programs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/tdm/kfrontend/gentdmconf.c

2856 lines
63 KiB

/*
Create a suitable configuration for tdm taking old xdm/tdm
installations into account
Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <utime.h>
#include <dirent.h>
#include <errno.h>
#include <pwd.h>
#include <time.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/param.h>
#if defined(BSD) && !defined(HAVE_UTMPX)
# include <utmp.h>
#endif
#include "config.ci"
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_UNUSED __attribute__((unused))
#else
# define ATTR_UNUSED
#endif
#if defined(__sun) && !defined(__sun__)
# define __sun__
#endif
#define as(ar) ((int)(sizeof(ar)/sizeof(ar[0])))
#define __stringify(x) #x
#define stringify(x) __stringify(x)
#define RCVERSTR stringify(RCVERMAJOR) "." stringify(RCVERMINOR)
static int old_scripts, no_old_scripts, old_confs, no_old,
no_backup, no_in_notice, use_destdir, mixed_scripts;
static const char *newdir = TDMCONF, *facesrc = TDMDATA "/pics/users",
*oldxdm, *oldkde;
static int oldver;
typedef struct StrList {
struct StrList *next;
const char *str;
} StrList;
static void *
mmalloc( size_t sz )
{
void *ptr;
if (!(ptr = malloc( sz ))) {
fprintf( stderr, "Out of memory\n" );
exit( 1 );
}
return ptr;
}
static void *
mcalloc( size_t sz )
{
void *ptr;
if (!(ptr = calloc( 1, sz ))) {
fprintf( stderr, "Out of memory\n" );
exit( 1 );
}
return ptr;
}
static void *
mrealloc( void *optr, size_t sz )
{
void *ptr;
if (!(ptr = realloc( optr, sz ))) {
fprintf( stderr, "Out of memory\n" );
exit( 1 );
}
return ptr;
}
static char *
mstrdup( const char *optr )
{
char *ptr;
if (!optr)
return 0;
if (!(ptr = strdup( optr ))) {
fprintf( stderr, "Out of memory\n" );
exit( 1 );
}
return ptr;
}
#define NO_LOGGER
#define STATIC static
#include <printf.c>
typedef struct {
char *buf;
int clen, blen, tlen;
} OCABuf;
static void
OutCh_OCA( void *bp, char c )
{
OCABuf *ocabp = (OCABuf *)bp;
ocabp->tlen++;
if (ocabp->clen >= ocabp->blen) {
ocabp->blen = ocabp->blen * 3 / 2 + 100;
ocabp->buf = mrealloc( ocabp->buf, ocabp->blen );
}
ocabp->buf[ocabp->clen++] = c;
}
static int
VASPrintf( char **strp, const char *fmt, va_list args )
{
OCABuf ocab = { 0, 0, 0, -1 };
DoPr( OutCh_OCA, &ocab, fmt, args );
OutCh_OCA( &ocab, 0 );
*strp = realloc( ocab.buf, ocab.clen );
if (!*strp)
*strp = ocab.buf;
return ocab.tlen;
}
static int
ASPrintf( char **strp, const char *fmt, ... )
{
va_list args;
int len;
va_start( args, fmt );
len = VASPrintf( strp, fmt, args );
va_end( args );
return len;
}
static void
StrCat( char **strp, const char *fmt, ... )
{
char *str, *tstr;
va_list args;
int el;
va_start( args, fmt );
el = VASPrintf( &str, fmt, args );
va_end( args );
if (*strp) {
int ol = strlen( *strp );
tstr = mmalloc( el + ol + 1 );
memcpy( tstr, *strp, ol );
memcpy( tstr + ol, str, el + 1 );
free( *strp );
free( str );
*strp = tstr;
} else
*strp = str;
}
#define WANT_CLOSE 1
typedef struct File {
char *buf, *eof, *cur;
#if defined(HAVE_MMAP) && defined(WANT_CLOSE)
int ismapped;
#endif
} File;
static int
readFile( File *file, const char *fn )
{
off_t flen;
int fd;
if ((fd = open( fn, O_RDONLY )) < 0)
return 0;
flen = lseek( fd, 0, SEEK_END );
#ifdef HAVE_MMAP
# ifdef WANT_CLOSE
file->ismapped = 0;
# endif
file->buf = mmap( 0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 );
# ifdef WANT_CLOSE
if (file->buf)
file->ismapped = 1;
else
# else
if (!file->buf)
# endif
#endif
{
file->buf = mmalloc( flen + 1 );
lseek( fd, 0, SEEK_SET );
if (read( fd, file->buf, flen ) != flen) {
free( file->buf );
close( fd );
fprintf( stderr, "Cannot read file\n" );
return 0; /* maybe better abort? */
}
}
file->eof = file->buf + flen;
close( fd );
return 1;
}
#ifdef WANT_CLOSE
static void
freeBuf( File *file )
{
# ifdef HAVE_MMAP
if (file->ismapped)
munmap( file->buf, file->eof - file->buf );
else
# endif
free( file->buf );
}
#endif
static int
isTrue( const char *val )
{
return !strcmp( val, "true" ) ||
!strcmp( val, "yes" ) ||
!strcmp( val, "on" ) ||
atoi( val );
}
static int
mkdirp( const char *name, int mode, const char *what, int existok )
{
char *mfname = mstrdup( name );
int i;
struct stat st;
for (i = 1; mfname[i]; i++)
if (mfname[i] == '/') {
mfname[i] = 0;
if (stat( mfname, &st )) {
if (mkdir( mfname, 0755 )) {
fprintf( stderr, "Cannot create parent %s of %s directory %s: %s\n",
mfname, what, name, strerror( errno ) );
free( mfname );
return 0;
}
chmod( mfname, 0755 );
}
mfname[i] = '/';
}
free( mfname );
if (stat( name, &st )) {
if (mkdir( name, mode )) {
fprintf( stderr, "Cannot create %s directory %s: %s\n",
what, name, strerror( errno ) );
return 0;
}
chmod( name, mode );
return 1;
}
return existok;
}
static void
displace( const char *fn )
{
if (!no_backup) {
char bn[PATH_MAX + 4];
sprintf( bn, "%s.bak", fn ); /* won't overflow if only existing paths are passed */
rename( fn, bn );
} else
unlink( fn );
}
static char *
locate( const char *exe )
{
int len;
char *path, *name, *thenam, nambuf[PATH_MAX+1];
char *pathe;
if (!(path = getenv( "PATH" )))
return 0;
len = strlen( exe );
name = nambuf + PATH_MAX - len;
memcpy( name, exe, len + 1 );
*--name = '/';
do {
if (!(pathe = strchr( path, ':' )))
pathe = path + strlen( path );
len = pathe - path;
if (len && !(len == 1 && *path == '.')) {
thenam = name - len;
if (thenam >= nambuf) {
memcpy( thenam, path, len );
if (!access( thenam, X_OK ))
return mstrdup( thenam );
}
}
path = pathe;
} while (*path++ != '\0');
return 0;
}
/*
* target data to be written to tdmrc
*/
typedef struct Entry {
struct Entry *next;
struct Ent *spec;
const char *value;
int active:1;
int written:1;
} Entry;
typedef struct Section {
struct Section *next;
struct Sect *spec;
const char *name;
const char *comment;
Entry *ents;
} Section;
static Section *config; /* the tdmrc data to be written */
/*
* Specification of the (currently possible) tdmrc entries
*/
typedef struct Ent {
const char *key;
int prio;
void (*func)( Entry *ce, Section *cs );
const char *comment;
} Ent;
typedef struct Sect {
const char *name;
Ent *ents;
int nents;
} Sect;
static Sect *findSect( const char *name );
static Ent *findEnt( Sect *sect, const char *key );
/*
* Functions to manipulate the current tdmrc data
*/
static const char *
getfqval( const char *sect, const char *key, const char *defval )
{
Section *cs;
Entry *ce;
for (cs = config; cs; cs = cs->next)
if (!strcmp( cs->name, sect )) {
for (ce = cs->ents; ce; ce = ce->next)
if (!strcmp( ce->spec->key, key )) {
if (ce->active && ce->written)
return ce->value;
break;
}
break;
}
return defval;
}
static void
putfqval( const char *sect, const char *key, const char *value )
{
Section *cs, **csp;
Entry *ce, **cep;
if (!value)
return;
for (csp = &config; (cs = *csp); csp = &(cs->next))
if (!strcmp( sect, cs->name ))
goto havesec;
cs = mcalloc( sizeof(*cs) );
ASPrintf( (char **)&cs->name, "%s", sect );
cs->spec = findSect( sect );
*csp = cs;
havesec:
for (cep = &(cs->ents); (ce = *cep); cep = &(ce->next))
if (!strcmp( key, ce->spec->key ))
goto haveent;
ce = mcalloc( sizeof(*ce) );
ce->spec = findEnt( cs->spec, key );
*cep = ce;
haveent:
ASPrintf( (char **)&ce->value, "%s", value );
ce->written = ce->active = 1;
}
static const char *csect;
#define setsect(se) csect = se
static void
putval( const char *key, const char *value )
{
putfqval( csect, key, value );
}
static void
wrconf( FILE *f )
{
Section *cs;
Entry *ce;
StrList *sl = 0, *sp;
const char *cmt;
putfqval( "General", "ConfigVersion", RCVERSTR );
for (cs = config; cs; cs = cs->next) {
fprintf( f, "%s[%s]\n",
cs->comment ? cs->comment : "\n", cs->name );
for (ce = cs->ents; ce; ce = ce->next) {
if (ce->spec->comment) {
cmt = ce->spec->comment;
for (sp = sl; sp; sp = sp->next)
if (sp->str == cmt) {
cmt = "# See above\n";
goto havit;
}
if (!(sp = malloc( sizeof(*sp) )))
fprintf( stderr, "Warning: Out of memory\n" );
else {
sp->str = cmt;
sp->next = sl; sl = sp;
}
} else
cmt = "";
havit:
fprintf( f, "%s%s%s=%s\n",
cmt, ce->active ? "" : "#", ce->spec->key, ce->value );
}
}
}
/*
* defaults
*/
#ifdef XDMCP
static const char def_xaccess[] =
"# Xaccess - Access control file for XDMCP connections\n"
"#\n"
"# To control Direct and Broadcast access:\n"
"#\n"
"# pattern\n"
"#\n"
"# To control Indirect queries:\n"
"#\n"
"# pattern list of hostnames and/or macros ...\n"
"#\n"
"# To use the chooser:\n"
"#\n"
"# pattern CHOOSER BROADCAST\n"
"#\n"
"# or\n"
"#\n"
"# pattern CHOOSER list of hostnames and/or macros ...\n"
"#\n"
"# To define macros:\n"
"#\n"
"# %name list of hosts ...\n"
"#\n"
"# The first form tells xdm which displays to respond to itself.\n"
"# The second form tells xdm to forward indirect queries from hosts matching\n"
"# the specified pattern to the indicated list of hosts.\n"
"# The third form tells xdm to handle indirect queries using the chooser;\n"
"# the chooser is directed to send its own queries out via the broadcast\n"
"# address and display the results on the terminal.\n"
"# The fourth form is similar to the third, except instead of using the\n"
"# broadcast address, it sends DirectQuerys to each of the hosts in the list\n"
"#\n"
"# In all cases, xdm uses the first entry which matches the terminal;\n"
"# for IndirectQuery messages only entries with right hand sides can\n"
"# match, for Direct and Broadcast Query messages, only entries without\n"
"# right hand sides can match.\n"
"#\n"
"\n"
"#* #any host can get a login window\n"
"\n"
"#\n"
"# To hardwire a specific terminal to a specific host, you can\n"
"# leave the terminal sending indirect queries to this host, and\n"
"# use an entry of the form:\n"
"#\n"
"\n"
"#terminal-a host-a\n"
"\n"
"\n"
"#\n"
"# The nicest way to run the chooser is to just ask it to broadcast\n"
"# requests to the network - that way new hosts show up automatically.\n"
"# Sometimes, however, the chooser can't figure out how to broadcast,\n"
"# so this may not work in all environments.\n"
"#\n"
"\n"
"#* CHOOSER BROADCAST #any indirect host can get a chooser\n"
"\n"
"#\n"
"# If you'd prefer to configure the set of hosts each terminal sees,\n"
"# then just uncomment these lines (and comment the CHOOSER line above)\n"
"# and edit the %hostlist line as appropriate\n"
"#\n"
"\n"
"#%hostlist host-a host-b\n"
"\n"
"#* CHOOSER %hostlist #\n";
#endif
#ifdef XDMCP
static const char def_willing[] =
"#! /bin/sh\n"
"# The output of this script is displayed in the chooser window\n"
"# (instead of \"Willing to manage\").\n"
"\n"
"load=`uptime|sed -e 's/^.*load[^0-9]*//'`\n"
"nrusers=`who|cut -c 1-8|sort -u|wc -l|sed 's/^[ \t]*//'`\n"
"s=\"\"; [ \"$nrusers\" != 1 ] && s=s\n"
"\n"
"echo \"${nrusers} user${s}, load: ${load}\"\n";
#endif
static const char def_setup[] =
"#! /bin/sh\n"
"# Xsetup - run as root before the login dialog appears\n"
"\n"
"#xconsole -geometry 480x130-0-0 -notify -verbose -fn fixed -exitOnFail -file /dev/xconsole &\n";
static const char def_startup[] =
"#! /bin/sh\n"
"# Xstartup - run as root before session starts\n"
"\n"
"\n"
"\n"
"if [ -e /etc/nologin ]; then\n"
" # always display the nologin message, if possible\n"
" if [ -s /etc/nologin ] && which xmessage > /dev/null 2>&1; then\n"
" xmessage -file /etc/nologin -geometry 640x480\n"
" fi\n"
" if [ \"$(id -u)\" != \"0\" ] && \\\n"
" ! grep -qs '^ignore-nologin' /etc/trinity/tdm/tdm.options; then\n"
" exit 1\n"
" fi\n"
"fi\n"
"\n"
"if grep -qs '^use-sessreg' /etc/trinity/tdm/tdm.options && \\\n"
" which sessreg > /dev/null 2>&1; then\n"
" exec sessreg -a -l \"$DISPLAY\" -u /var/run/utmp \\\n"
" -h \"`echo $DISPLAY | cut -d: -f1`\" \"$USER\"\n"
" # NOTREACHED\n"
"fi\n";
static const char def_reset[] =
"#! /bin/sh\n"
"# Xreset - run as root after session exits\n"
"\n"
"# Reassign ownership of the console to root, this should disallow\n"
"# assignment of console output to any random users's xterm. See Xstartup.\n"
"#\n"
"#chown root /dev/console\n"
"#chmod 622 /dev/console\n"
"\n"
#ifdef _AIX
"#devname=`echo $DISPLAY | cut -c1-8`\n"
"#exec sessreg -d -l xdm/$devname -h \"`echo $DISPLAY | cut -d: -f1`\""
#else
"if grep -qs '^use-sessreg' /etc/trinity/tdm/tdm.options && \\\n"
" which sessreg > /dev/null 2>&1; then\n"
" exec sessreg -d -l \"$DISPLAY\" -u /var/run/utmp \\\n"
" -h \"`echo $DISPLAY | cut -d: -f1`\" \"$USER\"\n"
" # NOTREACHED\n"
"fi\n";
#endif /* _AIX */
static const char def_session1[] =
"#! /bin/sh\n"
"# Xsession - run as user\n"
"\n"
"session=$1\n"
"\n"
"# Note that the respective logout scripts are not sourced.\n"
"case $SHELL in\n"
" */bash)\n"
" [ -z \"$BASH\" ] && exec $SHELL $0 \"$@\"\n"
" set +o posix\n"
" [ -f /etc/profile ] && . /etc/profile\n"
" if [ -f $HOME/.bash_profile ]; then\n"
" . $HOME/.bash_profile\n"
" elif [ -f $HOME/.bash_login ]; then\n"
" . $HOME/.bash_login\n"
" elif [ -f $HOME/.profile ]; then\n"
" . $HOME/.profile\n"
" fi\n"
" ;;\n"
" */zsh)\n"
" [ -z \"$ZSH_NAME\" ] && exec $SHELL $0 \"$@\"\n"
" emulate -R zsh\n"
" [ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc\n"
" zhome=${ZDOTDIR:-$HOME}\n"
" # zshenv is always sourced automatically.\n"
" [ -f $zdir/zprofile ] && . $zdir/zprofile\n"
" [ -f $zhome/.zprofile ] && . $zhome/.zprofile\n"
" [ -f $zdir/zlogin ] && . $zdir/zlogin\n"
" [ -f $zhome/.zlogin ] && . $zhome/.zlogin\n"
" setopt shwordsplit noextendedglob\n"
" ;;\n"
" */csh|*/tcsh)\n"
" # [t]cshrc is always sourced automatically.\n"
" # Note that sourcing csh.login after .cshrc is non-standard.\n"
" xsess_tmp=";
static const char def_session2[] =
"\n"
" $SHELL -c \"if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c export -p >! $xsess_tmp\"\n"
" . $xsess_tmp\n"
" rm -f $xsess_tmp\n"
" ;;\n"
" *) # Plain sh, ksh, and anything we don't know.\n"
" [ -f /etc/profile ] && . /etc/profile\n"
" [ -f $HOME/.profile ] && . $HOME/.profile\n"
" ;;\n"
"esac\n"
"# invoke global X session script\n"
". /etc/X11/Xsession\n";
static const char def_background[] =
"[Desktop0]\n"
"BackgroundMode=Flat\n"
"BlendBalance=100\n"
"BlendMode=NoBlending\n"
"ChangeInterval=60\n"
"Color1=0,0,200\n"
"Color2=192,192,192\n"
"CurrentWallpaper=0\n"
"LastChange=0\n"
"MinOptimizationDepth=1\n"
"MultiWallpaperMode=NoMulti\n"
"Pattern=fish\n"
"Program=\n"
"ReverseBlending=false\n"
"UseSHM=false\n"
"Wallpaper=Trinity-lineart.svg\n"
"WallpaperList=\n"
"WallpaperMode=Scaled\n";
static char *
prepName( const char *fn )
{
const char *tname;
char *nname;
tname = strrchr( fn, '/' );
ASPrintf( &nname, "%s/%s", newdir, tname ? tname + 1 : fn );
displace( nname );
return nname;
}
static FILE *
Create( const char *fn, int mode )
{
char *nname;
FILE *f;
nname = prepName( fn );
if (!(f = fopen( nname, "w" ))) {
fprintf( stderr, "Cannot create %s\n", nname );
exit( 1 );
}
chmod( nname, mode );
free( nname );
return f;
}
static void
WriteOut( const char *fn, int mode, time_t stamp, const char *buf, size_t len )
{
char *nname;
int fd;
struct utimbuf utim;
nname = prepName( fn );
if ((fd = creat( nname, mode )) < 0) {
fprintf( stderr, "Cannot create %s\n", nname );
exit( 1 );
}
write( fd, buf, len );
close( fd );
if (stamp) {
utim.actime = utim.modtime = stamp;
utime( nname, &utim );
}
free( nname );
}
/* returns static array! */
static const char *
resect( const char *sec, const char *name )
{
static char sname[64];
char *p;
if ((p = strrchr( sec, '-' ))) {
sprintf( sname, "%.*s-%s", p - sec, sec, name );
return sname;
} else
return name;
}
static int
inNewDir( const char *name )
{
return !memcmp( name, TDMCONF "/", sizeof(TDMCONF) );
}
static int
inList( StrList *sp, const char *s )
{
for (; sp; sp = sp->next)
if (!strcmp( sp->str, s ))
return 1;
return 0;
}
static void
addStr( StrList **sp, const char *s )
{
for (; *sp; sp = &(*sp)->next)
if (!strcmp( (*sp)->str, s ))
return;
*sp = mcalloc( sizeof(**sp) );
ASPrintf( (char **)&(*sp)->str, "%s", s );
}
StrList *aflist, *uflist, *eflist, *cflist, *lflist;
/* file is part of new config */
static void
addedFile( const char *fn )
{
addStr( &aflist, fn );
}
/* file from old config was parsed */
static void
usedFile( const char *fn )
{
addStr( &uflist, fn );
}
/* file from old config was copied with slight modifications */
static void
editedFile( const char *fn )
{
addStr( &eflist, fn );
}
/* file from old config was copied verbatim */
static void
copiedFile( const char *fn )
{
addStr( &cflist, fn );
}
/* file from old config is still being used */
static void
linkedFile( const char *fn )
{
addStr( &lflist, fn );
}
/*
* XXX this stuff is highly borked. it does not handle collisions at all.
*/
static int
copyfile( Entry *ce, const char *tname, int mode, int (*proc)( File * ) )
{
const char *tptr;
char *nname;
File file;
int rt;
if (!*ce->value)
return 1;
tptr = strrchr( tname, '/' );
ASPrintf( &nname, TDMCONF "/%s", tptr ? tptr + 1 : tname );
if (inList( cflist, ce->value ) ||
inList( eflist, ce->value ) ||
inList( lflist, ce->value ))
{
rt = 1;
goto doret;
}
if (!readFile( &file, ce->value )) {
fprintf( stderr, "Warning: cannot copy file %s\n", ce->value );
rt = 0;
} else {
if (!proc || !proc( &file )) {
if (!use_destdir && !strcmp( ce->value, nname ))
linkedFile( nname );
else {
struct stat st;
stat( ce->value, &st );
WriteOut( nname, mode, st.st_mtime, file.buf, file.eof - file.buf );
copiedFile( ce->value );
}
} else {
WriteOut( nname, mode, 0, file.buf, file.eof - file.buf );
editedFile( ce->value );
}
if (strcmp( ce->value, nname ) && inNewDir( ce->value ) && !use_destdir)
displace( ce->value );
addedFile( nname );
rt = 1;
}
doret:
ce->value = nname;
return rt;
}
static void
dlinkfile( const char *name )
{
File file;
if (!readFile( &file, name )) {
fprintf( stderr, "Warning: cannot read file %s\n", name );
return;
}
if (inNewDir( name ) && use_destdir) {
struct stat st;
stat( name, &st );
WriteOut( name, st.st_mode, st.st_mtime, file.buf, file.eof - file.buf );
copiedFile( name );
} else
linkedFile( name );
addedFile( name );
}
static void
linkfile( Entry *ce )
{
if (ce->written && *ce->value)
dlinkfile( ce->value );
}
static void
writefile( const char *tname, int mode, const char *cont )
{
WriteOut( tname, mode, 0, cont, strlen( cont ) );
addedFile( tname );
}
char *background;
static void
handBgCfg( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!ce->active) /* can be only the X-*-Greeter one */
writefile( def_BackgroundCfg, 0644,
background ? background : def_background );
#if 0 /* risk of kcontrol clobbering the original file */
else if (old_confs)
linkfile( ce );
#endif
else {
if (!copyfile( ce, ce->value, 0644, 0 )) {
if (!strcmp( cs->name, "X-*-Greeter" ))
writefile( def_BackgroundCfg, 0644, def_background );
ce->active = 0;
}
}
}
#ifdef HAVE_VTS
static char *
mem_mem( char *mem, int lmem, const char *smem, int lsmem )
{
for (; lmem >= lsmem; mem++, lmem--)
if (!memcmp( mem, smem, lsmem ))
return mem + lsmem;
return 0;
}
static int maxTTY, TTYmask;
static void
getInitTab( void )
{
if (maxTTY)
return;
if (!maxTTY) {
maxTTY = 6;
TTYmask = 0x3f;
}
}
#endif
/* TODO: handle solaris' local_uid specs */
static char *
ReadWord( File *file, int EOFatEOL )
{
char *wordp, *wordBuffer;
int quoted;
char c;
rest:
wordp = wordBuffer = file->cur;
mloop:
quoted = 0;
qloop:
if (file->cur == file->eof) {
doeow:
if (wordp == wordBuffer)
return 0;
retw:
*wordp = '\0';
return wordBuffer;
}
c = *file->cur++;
switch (c) {
case '#':
if (quoted)
break;
do {
if (file->cur == file->eof)
goto doeow;
c = *file->cur++;
} while (c != '\n');
case '\0':
case '\n':
if (EOFatEOL && !quoted) {
file->cur--;
goto doeow;
}
if (wordp != wordBuffer) {
file->cur--;
goto retw;
}
goto rest;
case ' ':
case '\t':
if (wordp != wordBuffer)
goto retw;
goto rest;
case '\\':
if (!quoted) {
quoted = 1;
goto qloop;
}
break;
}
*wordp++ = c;
goto mloop;
}
/* backslashes are double-escaped - for TDEConfig and for parseArgs */
static const char *
joinArgs( StrList *argv )
{
StrList *av;
const char *s, *rs;
char *str;
int slen;
if (!argv)
return "";
for (slen = 0, av = argv; slen++, av; av = av->next) {
int nq = 0;
for (s = av->str; *s; s++, slen++)
if (isspace( *s ) || *s == '\'')
nq = 2;
else if (*s == '"')
slen += 2;
else if (*s == '\\')
slen += 3;
slen += nq;
}
rs = str = mmalloc( slen );
for (av = argv; av; av = av->next) {
int nq = 0;
for (s = av->str; *s; s++)
if (isspace( *s ) || *s == '\'')
nq = 2;
if (av != argv)
*str++ = ' ';
if (nq)
*str++ = '"';
for (s = av->str; *s; s++) {
if (*s == '\\')
*str++ = '\\';
if (*s == '"' || *s == '\\') {
*str++ = '\\';
*str++ = '\\';
}
*str++ = *s;
}
if (nq)
*str++ = '"';
}
*str = 0;
return rs;
}
# define dLocation 1
# define dLocal 0
# define dForeign 1
static struct displayMatch {
const char *name;
int len, type;
} displayTypes[] = {
{ "local"