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.
 
 
 
 
 
 

2855 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", 5, dLocal },
{ "foreign", 7, dForeign },
};
static int
parseDisplayType( const char *string, const char **atPos )
{
struct displayMatch *d;
*atPos = 0;
for (d = displayTypes; d < displayTypes + as(displayTypes); d++) {
if (!memcmp( d->name, string, d->len ) &&
(!string[d->len] || string[d->len] == '@'))
{
if (string[d->len] == '@' && string[d->len + 1])
*atPos = string + d->len + 1;
return d->type;
}
}
return -1;
}
typedef struct serverEntry {
struct serverEntry *next;
const char *name, *class2, *console, *argvs, *arglvs;
StrList *argv, *arglv;
int type, reserve, vt;
} ServerEntry;
static void
absorb_xservers( const char *sect ATTR_UNUSED, char **value )
{
ServerEntry *se, *se1, *serverList, **serverPtr;
const char *word, *word2;
char *sdpys, *rdpys;
StrList **argp, **arglp, *ap, *ap2;
File file;
int nldpys = 0, nrdpys = 0, dpymask = 0;
int cpcmd, cpcmdl;
#ifdef HAVE_VTS
int dn, cpvt, mtty;
#endif
if (**value == '/') {
if (!readFile( &file, *value ))
return;
usedFile( *value );
} else {
file.buf = *value;
file.eof = *value + strlen( *value );
}
file.cur = file.buf;
serverPtr = &serverList;
#ifdef HAVE_VTS
bustd:
#endif
while ((word = ReadWord( &file, 0 ))) {
se = mcalloc( sizeof(*se) );
se->name = word;
if (!(word = ReadWord( &file, 1 )))
continue;
se->type = parseDisplayType( word, &se->console );
if (se->type < 0) {
se->class2 = word;
if (!(word = ReadWord( &file, 1 )))
continue;
se->type = parseDisplayType( word, &se->console );
if (se->type < 0) {
while (ReadWord( &file, 1 ));
continue;
}
}
word = ReadWord( &file, 1 );
if (word && !strcmp( word, "reserve" )) {
se->reserve = 1;
word = ReadWord( &file, 1 );
}
if (((se->type & dLocation) == dLocal) != (word != 0))
continue;
argp = &se->argv;
arglp = &se->arglv;
while (word) {
#ifdef HAVE_VTS
if (word[0] == 'v' && word[1] == 't')
se->vt = atoi( word + 2 );
else if (!strcmp( word, "-crt" )) { /* SCO style */
if (!(word = ReadWord( &file, 1 )) ||
memcmp( word, "/dev/tty", 8 ))
goto bustd;
se->vt = atoi( word + 8 );
} else
#endif
if (strcmp( word, se->name )) {
ap = mmalloc( sizeof(*ap) );
ap->str = word;
if (!strcmp( word, "-nolisten" )) {
if (!(word2 = ReadWord( &file, 1 )))
break;
ap2 = mmalloc( sizeof(*ap2) );
ap2->str = word2;
ap->next = ap2;
if (!strcmp( word2, "unix" )) {
*argp = ap;
argp = &ap2->next;
} else {
*arglp = ap;
arglp = &ap2->next;
}
} else {
*argp = ap;
argp = &ap->next;
}
}
word = ReadWord( &file, 1 );
}
*argp = *arglp = 0;
if ((se->type & dLocation) == dLocal) {
nldpys++;
dpymask |= 1 << atoi( se->name + 1 );
if (se->reserve)
nrdpys++;
}
*serverPtr = se;
serverPtr = &se->next;
}
*serverPtr = 0;
#ifdef HAVE_VTS
/* don't copy only if all local displays are ordered and have a vt */
cpvt = 0;
getInitTab();
for (se = serverList, mtty = maxTTY; se; se = se->next)
if ((se->type & dLocation) == dLocal) {
mtty++;
if (se->vt != mtty) {
cpvt = 1;
break;
}
}
#endif
for (se = serverList; se; se = se->next) {
se->argvs = joinArgs( se->argv );
se->arglvs = joinArgs( se->arglv );
}
se1 = 0, cpcmd = cpcmdl = 0;
for (se = serverList; se; se = se->next)
if ((se->type & dLocation) == dLocal) {
if (!se1)
se1 = se;
else {
if (strcmp( se1->argvs, se->argvs ))
cpcmd = 1;
if (strcmp( se1->arglvs, se->arglvs ))
cpcmdl = 1;
}
}
if (se1) {
putfqval( "X-:*-Core", "ServerCmd", se1->argvs );
putfqval( "X-:*-Core", "ServerArgsLocal", se1->arglvs );
for (se = serverList; se; se = se->next)
if ((se->type & dLocation) == dLocal) {
char sec[32];
sprintf( sec, "X-%s-Core", se->name );
if (cpcmd)
putfqval( sec, "ServerCmd", se->argvs );
if (cpcmdl)
putfqval( sec, "ServerArgsLocal", se->arglvs );
#ifdef HAVE_VTS
if (cpvt && se->vt) {
char vt[8];
sprintf( vt, "%d", se->vt );
putfqval( sec, "ServerVT", vt );
}
#else
if (se->console)
putfqval( sec, "ServerTTY", se->console );
#endif
}
}
sdpys = rdpys = 0;
for (se = serverList; se; se = se->next)
StrCat( se->reserve ? &rdpys : &sdpys,
se->class2 ? ",%s_%s" : ",%s", se->name, se->class2 );
#ifdef HAVE_VTS
/* add reserve dpys */
if (nldpys < 4 && nldpys && !nrdpys)
for (; nldpys < 4; nldpys++) {
for (dn = 0; dpymask & (1 << dn); dn++);
dpymask |= (1 << dn);
StrCat( &rdpys, ",:%d", dn );
}
#endif
putfqval( "General", "StaticServers", sdpys ? sdpys + 1 : "" );
putfqval( "General", "ReserveServers", rdpys ? rdpys + 1 : "" );
if (**value == '/' && inNewDir( *value ) && !use_destdir)
displace( *value );
}
#ifdef HAVE_VTS
static void
upd_servervts( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!ce->active) { /* there is only the Global one */
#ifdef __linux__ /* XXX actually, sysvinit */
getInitTab();
ASPrintf( (char **)&ce->value, "-%d", maxTTY + 1 );
ce->active = ce->written = 1;
#endif
}
}
static void
upd_consolettys( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!ce->active) { /* there is only the Global one */
#ifdef __linux__ /* XXX actually, sysvinit */
char *buf;
int i;
getInitTab();
for (i = 0, buf = 0; i < 16; i++)
if (TTYmask & (1 << i))
StrCat( &buf, ",tty%d", i + 1 );
if (buf) {
ce->value = buf + 1;
ce->active = ce->written = 1;
}
#endif
}
}
#endif
#ifdef XDMCP
static void
cp_keyfile( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!ce->active) /* there is only the Global one */
return;
if (old_confs)
linkfile( ce );
else
if (!copyfile( ce, "tdmkeys", 0600, 0 ))
ce->active = 0;
}
static void
mk_xaccess( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!ce->active) /* there is only the Global one */
writefile( def_Xaccess, 0644, def_xaccess );
else if (old_confs)
linkfile( ce );
else
copyfile( ce, "Xaccess", 0644, 0 ); /* don't handle error, it will disable Xdmcp automatically */
}
static void
mk_willing( Entry *ce, Section *cs ATTR_UNUSED )
{
char *fname;
if (!ce->active) /* there is only the Global one */
goto dflt;
else {
if (!(fname = strchr( ce->value, '/' )))
return; /* obviously in-line (or empty) */
if (old_scripts || inNewDir( fname ))
dlinkfile( fname );
else {
dflt:
ce->value = TDMCONF "/Xwilling";
ce->active = ce->written = 1;
writefile( ce->value, 0755, def_willing );
}
}
}
#endif
/*
static int
edit_resources( File *file )
{
// XXX remove any login*, chooser*, ... resources
return 0;
}
*/
static void
cp_resources( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!ce->active) /* the X-*-Greeter one */
return;
if (old_confs)
linkfile( ce );
else
if (!copyfile( ce, ce->value, 0644, 0/*edit_resources*/ ))
ce->active = 0;
}
static int
delstr( File *fil, const char *pat )
{
char *p, *pp, *bpp;
const char *pap, *paap;
*fil->eof = 0;
for (p = fil->buf; *p; p++) {
for (pp = p, pap = pat; ; ) {
if (!*pap) {
*p = '\n';
memcpy( p + 1, pp, fil->eof - pp + 1 );
fil->eof -= pp - p - 1;
return 1;
} else if (!memcmp( pap, "*/", 2 )) {
paap = pap += 2;
while (!isspace( *pap ))
pap++;
if (*pp != '/')
break;
for (;;)
for (bpp = ++pp; *pp != '/'; pp++)
if (!*pp || isspace( *pp ))
goto wbrk;
wbrk:
if ((pp - bpp != pap - paap) || memcmp( bpp, paap, pap - paap ))
break;
} else if (*pap == '\t') {
pap++;
while (*pp == ' ' || *pp == '\t')
pp++;
} else if (*pap == '[') {
pap++;
for (;;) {
if (!*pap) {
fprintf( stderr, "Internal error: unterminated char set\n" );
exit( 1 );
}
if (*pap == *pp) {
while (*++pap != ']')
if (!*pap) {
fprintf( stderr, "Internal error: unterminated char set\n" );
exit( 1 );
}
pap++;
pp++;
break;
}
if (*++pap == ']')
goto no;
}
} else {
if (*pap == '\n')
while (*pp == ' ' || *pp == '\t')
pp++;
if (*pap != *pp)
break;
pap++;
pp++;
}
}
no: ;
}
return 0;
}
/* XXX
the UseBackground voodoo will horribly fail, if multiple sections link
to the same Xsetup file
*/
static int mod_usebg;
static int
edit_setup( File *file )
{
int chg =
delstr( file, "\n"
"(\n"
" PIDFILE=/var/run/tdmdesktop-$DISPLAY.pid\n"
" */tdmdesktop\t&\n"
" echo $! >$PIDFILE\n"
" wait $!\n"
" rm $PIDFILE\n"
")\t&\n" ) |
delstr( file, "\n"
"*/tdmdesktop\t&\n" ) |
delstr( file, "\n"
"tdmdesktop\t&\n" ) |
delstr( file, "\n"
"tdmdesktop\n" );
putval( "UseBackground", chg ? "true" : "false" );
return chg;
}
static void
mk_setup( Entry *ce, Section *cs )
{
setsect( resect( cs->name, "Greeter" ) );
if (old_scripts || mixed_scripts) {
if (mod_usebg && *ce->value)
putval( "UseBackground", "false" );
linkfile( ce );
} else {
if (ce->active && inNewDir( ce->value )) {
if (mod_usebg)
copyfile( ce, ce->value, 0755, edit_setup );
else
linkfile( ce );
} else {
ce->value = TDMCONF "/Xsetup";
ce->active = ce->written = 1;
writefile( ce->value, 0755, def_setup );
}
}
}
static int
edit_startup( File *file )
{
int chg1 = 0, chg2 = 0;
if (mod_usebg &&
(delstr( file, "\n"
"PIDFILE=/var/run/tdmdesktop-$DISPLAY.pid\n"
"if [[] -f $PIDFILE ] ; then\n"
" kill `cat $PIDFILE`\n"
"fi\n" ) ||
delstr( file, "\n"
"PIDFILE=/var/run/tdmdesktop-$DISPLAY.pid\n"
"test -f $PIDFILE && kill `cat $PIDFILE`\n" )))
chg1 = 1;
if (oldver < 0x0203) {
chg2 =
#ifdef _AIX
delstr( file, "\n"
"# We create a pseudodevice for finger. (host:0 becomes [kx]dm/host_0)\n" );
"# Without it, finger errors out with \"Can't stat /dev/host:0\".\n"
"#\n"
"if [[] -f /usr/lib/X11/xdm/sessreg ]; then\n"
" devname=`echo $DISPLAY | /usr/bin/sed -e 's/[[]:\\.]/_/g' | /usr/bin/cut -c1-8`\n"
" hostname=`echo $DISPLAY | /usr/bin/cut -d':' -f1`\n"
"\n"
" if [[] -z \"$devname\" ]; then\n"
" devname=\"unknown\"\n"
" fi\n"
" if [[] ! -d /dev/[kx]dm ]; then\n"
" /usr/bin/mkdir /dev/[kx]dm\n"
" /usr/bin/chmod 755 /dev/[kx]dm\n"
" fi\n"
" /usr/bin/touch /dev/[kx]dm/$devname\n"
" /usr/bin/chmod 644 /dev/[kx]dm/$devname\n"
"\n"
" if [[] -z \"$hostname\" ]; then\n"
" exec /usr/lib/X11/xdm/sessreg -a -l [kx]dm/$devname $USER\n"
" else\n"
" exec /usr/lib/X11/xdm/sessreg -a -l [kx]dm/$devname -h $hostname $USER\n"
" fi\n"
"fi\n") |
#else
# ifdef BSD
# ifdef HAVE_UTMPX
delstr( file, "\n"
"exec sessreg -a -l $DISPLAY -x */Xservers $USER\n" ) |
# else
delstr( file, "\n"
"exec sessreg -a -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) |
# endif
# endif
#endif /* _AIX */
delstr( file, "\n"
"exec sessreg -a -l $DISPLAY"
#ifdef BSD
" -x */Xservers"
#endif
" $USER\n" ) |
delstr( file, "\n"
"exec sessreg -a -l $DISPLAY -u /var/run/utmp -x */Xservers $USER\n" );
putval( "UseSessReg", chg2 ? "true" : "false");
}
return chg1 | chg2;
}
static void
mk_startup( Entry *ce, Section *cs )
{
setsect( cs->name );
if (old_scripts || mixed_scripts)
linkfile( ce );
else {
if (ce->active && inNewDir( ce->value )) {
if (mod_usebg || oldver < 0x0203)
copyfile( ce, ce->value, 0755, edit_startup );
else
linkfile( ce );
} else {
ce->value = TDMCONF "/Xstartup";
ce->active = ce->written = 1;
writefile( ce->value, 0755, def_startup );
}
}
}
static int
edit_reset( File *file )
{
return
#ifdef _AIX
delstr( file, "\n"
"if [[] -f /usr/lib/X11/xdm/sessreg ]; then\n"
" devname=`echo $DISPLAY | /usr/bin/sed -e 's/[[]:\\.]/_/g' | /usr/bin/cut -c1-8`\n"
" exec /usr/lib/X11/xdm/sessreg -d -l [kx]dm/$devname $USER\n"
"fi\n" ) |
#else
# ifdef BSD
# ifdef HAVE_UTMPX
delstr( file, "\n"
"exec sessreg -d -l $DISPLAY -x */Xservers $USER\n" ) |
# else
delstr( file, "\n"
"exec sessreg -d -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) |
# endif
# endif
#endif /* _AIX */
delstr( file, "\n"
"exec sessreg -d -l $DISPLAY"
# ifdef BSD
" -x */Xservers"
# endif
" $USER\n" ) |
delstr( file, "\n"
"exec sessreg -d -l $DISPLAY -u /var/run/utmp -x */Xservers $USER\n" );
}
static void
mk_reset( Entry *ce, Section *cs ATTR_UNUSED )
{
if (old_scripts || mixed_scripts)
linkfile( ce );
else {
if (ce->active && inNewDir( ce->value )) {
if (oldver < 0x0203)
copyfile( ce, ce->value, 0755, edit_reset );
else
linkfile( ce );
} else {
ce->value = TDMCONF "/Xreset";
ce->active = ce->written = 1;
writefile( ce->value, 0755, def_reset );
}
}
}
static void
mk_session( Entry *ce, Section *cs ATTR_UNUSED )
{
char *def_session;
const char *tmpf;
if ((old_scripts || (ce->active && inNewDir( ce->value ))) &&
oldver >= 0x202)
linkfile( ce );
else {
tmpf = locate( "mktemp" ) ?
"`mktemp /tmp/xsess-env-XXXXXX`" :
locate( "tempfile" ) ?
"`tempfile`" :
"$HOME/.xsession-env-$DISPLAY";
ASPrintf( &def_session, "%s%s%s", def_session1, tmpf, def_session2 );
ce->value = TDMCONF "/Xsession";
ce->active = ce->written = 1;
writefile( ce->value, 0755, def_session );
}
}
static void
upd_language( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!strcmp( ce->value, "C" ))
ce->value = (char *)"en_US";
}
static void
upd_guistyle( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!strcmp( ce->value, "Motif+" ))
ce->value = (char *)"MotifPlus";
else if (!strcmp( ce->value, "KDE" ))
ce->value = (char *)"Default";
}
static void
upd_showusers( Entry *ce, Section *cs )
{
if (!strcmp( ce->value, "All" ))
ce->value = (char *)"NotHidden";
else if (!strcmp( ce->value, "None" )) {
if (ce->active)
putfqval( cs->name, "UserList", "false" );
ce->value = (char *)"Selected";
ce->active = 0;
ce->written = 1;
}
}
static const char *defminuid, *defmaxuid;
static void
upd_minshowuid( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!ce->active) {
ce->value = defminuid;
ce->active = ce->written = 1;
}
}
static void
upd_maxshowuid( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!ce->active) {
ce->value = defmaxuid;
ce->active = ce->written = 1;
}
}
static void
upd_hiddenusers( Entry *ce, Section *cs ATTR_UNUSED )
{
char *nv;
const char *msu, *pt, *et;
struct passwd *pw;
unsigned minuid, maxuid;
char nbuf[128];
if (!ce->active)
return;
msu = getfqval( cs->name, "MinShowUID", "0" );
sscanf( msu, "%u", &minuid );
msu = getfqval( cs->name, "MaxShowUID", "65535" );
sscanf( msu, "%u", &maxuid );
nv = 0;
pt = ce->value;
for (;;) {
et = strpbrk( pt, ";," );
if (et) {
memcpy( nbuf, pt, et - pt );
nbuf[et - pt] = 0;
} else
strcpy( nbuf, pt );
if ((pw = getpwnam( nbuf ))) {
if (!pw->pw_uid ||
(pw->pw_uid >= minuid && pw->pw_uid <= maxuid))
{
if (nv)
StrCat( &nv, ",%s", nbuf );
else
nv = mstrdup( nbuf );
}
}
if (!et)
break;
pt = et + 1;
}
ce->value = nv ? nv : "";
}
static void
upd_forgingseed( Entry *ce, Section *cs ATTR_UNUSED )
{
if (!ce->active) {
ASPrintf( (char **)&ce->value, "%d", time( 0 ) );
ce->active = ce->written = 1;
}
}
static void
upd_fifodir( Entry *ce, Section *cs ATTR_UNUSED )
{
const char *dir;
struct stat st;
if (use_destdir)
return;
dir = ce->active ? ce->value : def_FifoDir;
stat( dir, &st );
chmod( dir, st.st_mode | 0755 );
}
static void
upd_datadir( Entry *ce, Section *cs ATTR_UNUSED )
{
char *oldsts, *newsts;
const char *dir;
if (use_destdir)
return;
dir = ce->active ? ce->value : def_DataDir;
if (mkdirp( dir, 0755, "data", 0 ) && oldkde) {
ASPrintf( &oldsts, "%s/tdm/tdmsts", oldkde );
ASPrintf( &newsts, "%s/tdmsts", dir );
rename( oldsts, newsts );
}
}
static void
CopyFile( const char *from, const char *to )
{
File file;
int fd;
if (readFile( &file, from )) {
if ((fd = creat( to, 0644 )) >= 0) {
write( fd, file.buf, file.eof - file.buf );
close( fd );
}
freeBuf( &file );
}
}
static void
upd_facedir( Entry *ce, Section *cs ATTR_UNUSED )
{
char *oldpic, *newpic, *defpic, *rootpic;
const char *dir;
struct passwd *pw;
if (use_destdir)
return;
dir = ce->active ? ce->value : def_FaceDir;
if (mkdirp( dir, 0755, "user face", 0 )) {
ASPrintf( &defpic, "%s/.default.face.icon", dir );
ASPrintf( &rootpic, "%s/root.face.icon", dir );
if (oldkde) {
setpwent();
while ((pw = getpwent()))
if (strcmp( pw->pw_name, "root" )) {
ASPrintf( &oldpic, "%s/../apps/tdm/pics/users/%s.png",
oldkde, pw->pw_name );
ASPrintf( &newpic, "%s/%s.face.icon", dir, pw->pw_name );
rename( oldpic, newpic );
free( newpic );
free( oldpic );
}
endpwent();
ASPrintf( &oldpic, "%s/../apps/tdm/pics/users/default.png", oldkde );
if (!rename( oldpic, defpic ))
defpic = 0;
ASPrintf( &oldpic, "%s/../apps/tdm/pics/users/root.png", oldkde );
if (!rename( oldpic, rootpic ))
rootpic = 0;
}
if (defpic) {
ASPrintf( &oldpic, "%s/default1.png", facesrc );
CopyFile( oldpic, defpic );
}
if (rootpic) {
ASPrintf( &oldpic, "%s/root1.png", facesrc );
CopyFile( oldpic, rootpic );
}
}
}
CONF_GEN_ENTRIES
static Sect *
findSect( const char *name )
{
const char *p;
int i;
p = strrchr( name, '-' );
if (!p)
p = name;
for (i = 0; i < as(allSects); i++)
if (!strcmp( allSects[i]->name, p ))
return allSects[i];
fprintf( stderr, "Internal error: unknown section %s\n", name );
exit( 1 );
}
static Ent *
findEnt( Sect *sect, const char *key )
{
int i;
for (i = 0; i < sect->nents; i++)
if (!strcmp( sect->ents[i].key, key ))
return sect->ents + i;
fprintf( stderr, "Internal error: unknown key %s in section %s\n",
key, sect->name );
exit( 1 );
}
/*
* defaults
*/
typedef struct DEnt {
const char *key;
const char *value;
int active;
} DEnt;
typedef struct DSect {
const char *name;
DEnt *ents;
int nents;
const char *comment;
} DSect;
CONF_GEN_EXAMPLE
static void
mkdefconf( void )
{
Section *cs, **csp;
Entry *ce, **cep;
int sc, ec;
for (csp = &config, sc = 0; sc < as(dAllSects); csp = &(cs->next), sc++) {
cs = mcalloc( sizeof(*cs) );
*csp = cs;
cs->spec = findSect( dAllSects[sc].name );
cs->name = dAllSects[sc].name;
cs->comment = dAllSects[sc].comment;
for (cep = &(cs->ents), ec = 0; ec < dAllSects[sc].nents;
cep = &(ce->next), ec++)
{
ce = mcalloc( sizeof(*ce) );
*cep = ce;
ce->spec = findEnt( cs->spec, dAllSects[sc].ents[ec].key );
ce->value = dAllSects[sc].ents[ec].value;
ce->active = dAllSects[sc].ents[ec].active;
}
}
}
/*
* read rc file structure
*/
typedef struct REntry {
struct REntry *next;
const char *key;
char *value;
} REntry;
typedef struct RSection {
struct RSection *next;
const char *name;
REntry *ents;
} RSection;
static RSection *
ReadConf( const char *fname )
{
char *nstr;
char *s, *e, *st, *en, *ek, *sl;
RSection *rootsec = 0, *cursec;
REntry *curent;
int nlen;
int line, sectmoan;
File file;
if (!readFile( &file, fname ))
return 0;
usedFile( fname );
for (s = file.buf, line = 0, cursec = 0, sectmoan = 1; s < file.eof; s++) {
line++;
while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
s++;
if ((s < file.eof) && ((*s == '\n') || (*s == '#'))) {
sktoeol:
while ((s < file.eof) && (*s != '\n'))
s++;
continue;
}
sl = s;
if (*s == '[') {
while ((s < file.eof) && (*s != '\n'))
s++;
e = s - 1;
while ((e > sl) && isspace( *e ))
e--;
if (*e != ']') {
fprintf( stderr, "Invalid section header at %s:%d\n",
fname, line );
continue;
}
sectmoan = 0;
nstr = sl + 1;
nlen = e - nstr;
for (cursec = rootsec; cursec; cursec = cursec->next)
if (!memcmp( nstr, cursec->name, nlen ) &&
!cursec->name[nlen])
{
#if 0 /* not our business ... */
fprintf( stderr, "Warning: Multiple occurrences of section "
"[%.*s] in %s. Consider merging them.\n",
nlen, nstr, fname );
#endif
goto secfnd;
}
cursec = mmalloc( sizeof(*cursec) );
ASPrintf( (char **)&cursec->name, "%.*s", nlen, nstr );
cursec->ents = 0;
cursec->next = rootsec;
rootsec = cursec;
secfnd:
continue;
}
if (!cursec) {
if (sectmoan) {
sectmoan = 0;
fprintf( stderr, "Entry outside any section at %s:%d",
fname, line );
}
goto sktoeol;
}
for (; (s < file.eof) && (*s != '\n'); s++)
if (*s == '=')
goto haveeq;
fprintf( stderr, "Invalid entry (missing '=') at %s:%d\n", fname, line );
continue;
haveeq:
for (ek = s - 1;; ek--) {
if (ek < sl) {
fprintf( stderr, "Invalid entry (empty key) at %s:%d\n",
fname, line );
goto sktoeol;
}
if (!isspace( *ek ))
break;
}
s++;
while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
s++;
st = s;
while ((s < file.eof) && (*s != '\n'))
s++;
for (en = s - 1; en >= st && isspace( *en ); en--);
nstr = sl;
nlen = ek - sl + 1;
for (curent = cursec->ents; curent; curent = curent->next)
if (!memcmp( nstr, curent->key, nlen ) &&
!curent->key[nlen]) {
fprintf( stderr, "Multiple occurrences of key '%s' in section "
"[%s] of %s.\n", curent->key, cursec->name, fname );
goto keyfnd;
}
curent = mmalloc( sizeof(*curent) );
ASPrintf( (char **)&curent->key, "%.*s", nlen, nstr );
ASPrintf( (char **)&curent->value, "%.*s", en - st + 1, st );
curent->next = cursec->ents;
cursec->ents = curent;
keyfnd:
continue;
}
return rootsec;
}
static int
mergeKdmRcOld( const char *path )
{
char *p;
struct stat st;
ASPrintf( &p, "%s/tdmrc", path );
if (stat( p, &st )) {
free( p );
return 0;
}
printf( "Information: ignoring old tdmrc %s from kde < 2.2\n", p );
free( p );
return 1;
}
typedef struct {
const char *sect, *key, *def;
int (*cond)( void );
} FDefs;
static void
applydefs( FDefs *chgdef, int ndefs, const char *path )
{
char *p;
int i;
for (i = 0; i < ndefs; i++)
if (!getfqval( chgdef[i].sect, chgdef[i].key, 0 ) &&
(!chgdef[i].cond || chgdef[i].cond()))
{
ASPrintf( &p, chgdef[i].def, path );
putfqval( chgdef[i].sect, chgdef[i].key, p );
free( p );
}
}
#ifdef XDMCP
static FDefs tdmdefs_all[] = {
{ "Xdmcp", "Xaccess", "%s/tdm/Xaccess", 0 },
{ "Xdmcp", "Willing", "", 0 },
};
#endif
static FDefs tdmdefs_eq_22[] = {
{ "General", "PidFile", "/var/run/xdm.pid", 0 },
{ "X-*-Core", "Setup", "%s/tdm/Xsetup", 0 },
{ "X-*-Core", "Startup", "%s/tdm/Xstartup", 0 },
{ "X-*-Core", "Reset", "%s/tdm/Xreset", 0 },
{ "X-*-Core", "Session", "%s/tdm/Xsession", 0 },
};
#ifdef XDMCP
static int
if_xdmcp (void)
{
return isTrue( getfqval( "Xdmcp", "Enable", "true" ) );
}
static FDefs tdmdefs_le_30[] = {
{ "Xdmcp", "KeyFile", "%s/tdm/tdmkeys", if_xdmcp },
};
#endif
/* HACK: misused by is22conf() below */
static FDefs tdmdefs_ge_30[] = {
{ "X-*-Core", "Setup", "", 0 },
{ "X-*-Core", "Startup", "", 0 },
{ "X-*-Core", "Reset", "", 0 },
{ "X-*-Core", "Session", XBINDIR "/xterm -ls -T", 0 },
};
static int
if_usebg (void)
{
return isTrue( getfqval( "X-*-Greeter", "UseBackground", "true" ) );
}
static FDefs tdmdefs_ge_31[] = {
{ "X-*-Greeter","BackgroundCfg","%s/tdm/backgroundrc", if_usebg },
};
static int
is22conf( const char *path )
{
char *p;
const char *val;
int i, sl;
sl = ASPrintf( &p, "%s/tdm/", path );
/* safe bet, i guess ... */
for (i = 0; i < 4; i++) {
val = getfqval( "X-*-Core", tdmdefs_ge_30[i].key, 0 );
if (val && !memcmp( val, p, sl )) {
free( p );
return 0;
}
}
free( p );
return 1;
}
typedef struct KUpdEnt {
const char *okey, *nsec, *nkey;
void (*func)( const char *sect, char **value );
} KUpdEnt;
typedef struct KUpdSec {
const char *osec;
KUpdEnt *ents;
int nents;
} KUpdSec;
#ifdef XDMCP
static void
P_EnableChooser( const char *sect ATTR_UNUSED, char **value )
{
*value = (char *)(isTrue( *value ) ? "DefaultLocal" : "LocalOnly");
}
#endif
static void
P_UseLilo( const char *sect ATTR_UNUSED, char **value )
{
*value = (char *)(isTrue( *value ) ? "Lilo" : "None");
}
CONF_GEN_KMERGE
static int
mergeKdmRcNewer( const char *path )
{
char *p;
const char *cp, *sec, *key;
RSection *rootsect, *cs;
REntry *ce;
int i, j;
static char sname[64];
ASPrintf( &p, "%s/tdm/tdmrc", path );
if (!(rootsect = ReadConf( p ))) {
free( p );
return 0;
}
printf( "Information: reading current tdmrc %s (from kde >= 2.2.x)\n", p );
free( p );
for (cs = rootsect; cs; cs = cs->next) {
if (!strcmp( cs->name, "Desktop0" )) {
background = mstrdup( "[Desktop0]\n" );
for (ce = cs->ents; ce; ce = ce->next)
StrCat( &background, "%s=%s\n", ce->key, ce->value );
} else {
cp = strrchr( cs->name, '-' );
if (!cp)
cp = cs->name;
else if (cs->name[0] != 'X' || cs->name[1] != '-')
goto dropsec;
for (i = 0; i < as(kupsects); i++)
if (!strcmp( cp, kupsects[i].osec )) {
for (ce = cs->ents; ce; ce = ce->next) {
for (j = 0; j < kupsects[i].nents; j++)
if (!strcmp( ce->key, kupsects[i].ents[j].okey )) {
if (kupsects[i].ents[j].nsec == (char *)-1) {
kupsects[i].ents[j].func( 0, &ce->value );
goto gotkey;
}
if (!kupsects[i].ents[j].nsec)
sec = cs->name;
else {
sec = sname;
sprintf( sname, "%.*s-%s", cp - cs->name, cs->name,
kupsects[i].ents[j].nsec );
}
if (!kupsects[i].ents[j].nkey)
key = ce->key;
else
key = kupsects[i].ents[j].nkey;
if (kupsects[i].ents[j].func)
kupsects[i].ents[j].func( sec, &ce->value );
putfqval( sec, key, ce->value );
goto gotkey;
}
printf( "Information: dropping key %s from section [%s]\n",
ce->key, cs->name );
gotkey: ;
}
goto gotsec;
}
dropsec:
printf( "Information: dropping section [%s]\n", cs->name );
gotsec: ;
}
}
#ifdef XDMCP
applydefs( tdmdefs_all, as(tdmdefs_all), path );
#endif
if (!*(cp = getfqval( "General", "ConfigVersion", "" ))) { /* < 3.1 */
mod_usebg = 1;
if (is22conf( path )) {
/* work around 2.2.x defaults borkedness */
applydefs( tdmdefs_eq_22, as(tdmdefs_eq_22), path );
printf( "Information: current tdmrc is from kde 2.2\n" );
} else {
applydefs( tdmdefs_ge_30, as(tdmdefs_ge_30), path );
printf( "Information: current tdmrc is from kde 3.0\n" );
}
#ifdef XDMCP
/* work around minor <= 3.0.x defaults borkedness */
applydefs( tdmdefs_le_30, as(tdmdefs_le_30), path );
#endif
} else {
int ma, mi;
sscanf( cp, "%d.%d", &ma, &mi );
oldver = (ma << 8) | mi;
printf( "Information: current tdmrc is from kde >= 3.1 (config version %d.%d)\n", ma, mi );
applydefs( tdmdefs_ge_30, as(tdmdefs_ge_30), path );
applydefs( tdmdefs_ge_31, as(tdmdefs_ge_31), path );
}
return 1;
}
typedef struct XResEnt {
const char *xname;
const char *ksec, *kname;
void (*func)( const char *sect, char **value );
} XResEnt;
static void
handleXdmVal( const char *dpy, const char *key, char *value,
const XResEnt *ents, int nents )
{
const char *kname;
int i;
char knameb[80], sname[80];
for (i = 0; i < nents; i++)
if (!strcmp( key, ents[i].xname ) ||
(key[0] == toupper( ents[i].xname[0] ) &&
!strcmp( key + 1, ents[i].xname + 1 )))
{
if (ents[i].ksec == (char *)-1) {
ents[i].func (0, &value);
break;
}
sprintf( sname, ents[i].ksec, dpy );
if (ents[i].kname)
kname = ents[i].kname;
else {
kname = knameb;
sprintf( knameb, "%c%s",
toupper( ents[i].xname[0] ), ents[i].xname + 1 );
}
if (ents[i].func)
ents[i].func( sname, &value );
putfqval( sname, kname, value );
break;
}
}
static void
P_List( const char *sect ATTR_UNUSED, char **value )
{
int is, d, s;
char *st;
for (st = *value, is = d = s = 0; st[s]; s++)
if (st[s] == ' ' || st[s] == '\t') {
if (!is)
st[d++] = ',';
is = 1;
} else {
st[d++] = st[s];
is = 0;
}
st[d] = 0;
}
static void
P_authDir( const char *sect ATTR_UNUSED, char **value )
{
int l;
l = strlen( *value );
if (l < 4) {
*value = 0;
return;
}
if ((*value)[l-1] == '/')
(*value)[--l] = 0;
if (!strncmp( *value, "/tmp/", 5 ) ||
!strncmp( *value, "/var/tmp/", 9 ))
{
printf( "Warning: Resetting inappropriate value %s for AuthDir to default\n",
*value );
*value = 0;
return;
}
if ((l >= 4 && !strcmp( *value + l - 4, "/tmp" )) ||
(l >= 6 && !strcmp( *value + l - 6, "/xauth" )) ||
(l >= 8 && !strcmp( *value + l - 8, "/authdir" )) ||
(l >= 10 && !strcmp( *value + l - 10, "/authfiles" )))
return;
ASPrintf( value, "%s/authdir", *value );
}
static void
P_openDelay( const char *sect, char