diff options
author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2012-01-22 01:02:36 -0600 |
---|---|---|
committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2012-01-22 01:02:36 -0600 |
commit | b81e43465b14836b17e4fe2dea91c78a2bdd29b3 (patch) | |
tree | 7815d61ce59a6ccb6e655ed44f5fea786f520985 /tdm/kfrontend/gentdmconf.c | |
parent | 7021f40c13f949b7cb5ded32d0241d648a43bf6c (diff) | |
download | tdebase-b81e43465b14836b17e4fe2dea91c78a2bdd29b3.tar.gz tdebase-b81e43465b14836b17e4fe2dea91c78a2bdd29b3.zip |
Part 2 of prior commit
Diffstat (limited to 'tdm/kfrontend/gentdmconf.c')
-rw-r--r-- | tdm/kfrontend/gentdmconf.c | 2849 |
1 files changed, 2849 insertions, 0 deletions
diff --git a/tdm/kfrontend/gentdmconf.c b/tdm/kfrontend/gentdmconf.c new file mode 100644 index 000000000..f55ffbbbc --- /dev/null +++ b/tdm/kfrontend/gentdmconf.c @@ -0,0 +1,2849 @@ +/* + +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> +#ifdef BSD +# 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=isadora.png\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 ) +{ + File it; + char *p, *eol, *ep; + int tty; + + 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 KConfig 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 + delstr( file, "\n" +"exec sessreg -a -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) | +# 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 + delstr( file, "\n" +"exec sessreg -d -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) | +# 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 **value ) +{ + putfqval( sect, "ServerTimeout", *value ); +} + +static void +P_noPassUsers( const char *sect, char **value ATTR_UNUSED ) +{ + putfqval( sect, "NoPassEnable", "true" ); +} + +static void +P_autoUser( const char *sect, char **value ATTR_UNUSED ) +{ + putfqval( sect, "AutoLoginEnable", "true" ); +} + +#ifdef XDMCP +static void +P_requestPort( const char *sect, char **value ) +{ + if (!strcmp( *value, "0" )) { + *value = 0; + putfqval( sect, "Enable", "false" ); + } else + putfqval( sect, "Enable", "true" ); +} +#endif + +static int tdmrcmode = 0644; + +static void +P_autoPass( const char *sect ATTR_UNUSED, char **value ATTR_UNUSED ) +{ + tdmrcmode = 0600; +} + +CONF_GEN_XMERGE + +static XrmQuark XrmQString, empty = NULLQUARK; + +static Bool +DumpEntry( XrmDatabase *db ATTR_UNUSED, + XrmBindingList bindings, + XrmQuarkList quarks, + XrmRepresentation *type, + XrmValuePtr value, + XPointer data ATTR_UNUSED ) +{ + const char *dpy, *key; + int el, hasu; + char dpybuf[80]; + + if (*type != XrmQString) + return False; + if (*bindings == XrmBindLoosely || + strcmp( XrmQuarkToString (*quarks), "DisplayManager" )) + return False; + bindings++, quarks++; + if (!*quarks) + return False; + if (*bindings != XrmBindLoosely && !quarks[1]) { /* DM.foo */ + key = XrmQuarkToString (*quarks); + handleXdmVal( 0, key, value->addr, globents, as(globents) ); + return False; + } else if (*bindings == XrmBindLoosely && !quarks[1]) { /* DM*bar */ + dpy = "*"; + key = XrmQuarkToString (*quarks); + } else if (*bindings != XrmBindLoosely && quarks[1] && + *bindings != XrmBindLoosely && !quarks[2]) + { /* DM.foo.bar */ + dpy = dpybuf + 4; + strcpy( dpybuf + 4, XrmQuarkToString (*quarks) ); + for (hasu = 0, el = 4; dpybuf[el]; el++) + if (dpybuf[el] == '_') + hasu = 1; + if (!hasu/* && isupper (dpy[0])*/) { + dpy = dpybuf; + memcpy( dpybuf, "*:*_", 4 ); + } else { + for (; --el >= 0; ) + if (dpybuf[el] == '_') { + dpybuf[el] = ':'; + for (; --el >= 4; ) + if (dpybuf[el] == '_') + dpybuf[el] = '.'; + break; + } + } + key = XrmQuarkToString (quarks[1]); + } else + return False; + handleXdmVal( dpy, key, value->addr, dpyents, as(dpyents) ); + return False; +} + +static FDefs xdmdefs[] = { +#ifdef XDMCP +{ "Xdmcp", "Xaccess", "%s/Xaccess", 0 }, +{ "Xdmcp", "Willing", "", 0 }, +#endif +{ "X-*-Core", "Setup", "", 0 }, +{ "X-*-Core", "Startup", "", 0 }, +{ "X-*-Core", "Reset", "", 0 }, +{ "X-*-Core", "Session", "", 0 }, +}; + +static int +mergeXdmCfg( const char *path ) +{ + char *p; + XrmDatabase db; + + ASPrintf( &p, "%s/xdm-config", path ); + if ((db = XrmGetFileDatabase( p ))) { + printf( "Information: reading current xdm config file %s\n", p ); + usedFile( p ); + free( p ); + XrmEnumerateDatabase( db, &empty, &empty, XrmEnumAllLevels, + DumpEntry, (XPointer)0 ); + applydefs( xdmdefs, as(xdmdefs), path ); + mod_usebg = 1; + return 1; + } + free( p ); + return 0; +} + +static void +fwrapprintf( FILE *f, const char *msg, ... ) +{ + char *txt, *ftxt, *line; + va_list ap; + int col, lword, fspace; + + va_start( ap, msg ); + VASPrintf( &txt, msg, ap ); + va_end( ap ); + ftxt = 0; + for (line = txt, col = 0, lword = fspace = -1; line[col]; ) { + if (line[col] == '\n') { + StrCat( &ftxt, "%.*s", ++col, line ); + line += col; + col = 0; + lword = fspace = -1; + continue; + } else if (line[col] == ' ') { + if (lword >= 0) { + fspace = col; + lword = -1; + } + } else { + if (lword < 0) + lword = col; + if (col >= 78 && fspace >= 0) { + StrCat( &ftxt, "%.*s\n", fspace, line ); + line += lword; + col -= lword; + lword = 0; + fspace = -1; + } + } + col++; + } + free( txt ); + fputs( ftxt, f ); + free( ftxt ); +} + + +static const char *oldkdes[] = { + KDE_CONFDIR, + "/opt/trinity/share/config", + "/usr/local/trinity/share/config", + + "/opt/kde/share/config", + "/usr/local/kde/share/config", + "/usr/local/share/config", + "/usr/share/config", + + "/opt/kde2/share/config", + "/usr/local/kde2/share/config", +}; + +static const char *oldxdms[] = { + "/etc/X11/xdm", + XLIBDIR "/xdm", +}; + +int main( int argc, char **argv ) +{ + const char **where; + char *newtdmrc; + FILE *f; + StrList *fp; + Section *cs; + Entry *ce, **cep; + int i, ap, newer, locals, foreigns; + int no_old_xdm = 0, no_old_kde = 0; + struct stat st; + char *nname; + + for (ap = 1; ap < argc; ap++) { + if (!strcmp( argv[ap], "--help" )) { + printf( +"gentdmconf - generate configuration files for tdm\n" +"\n" +"If an older xdm/tdm configuration is found, its config files are \"absorbed\";\n" +"if it lives in the new target directory, its scripts are reused (and possibly\n" +"modified) as well, otherwise the scripts are ignored and default scripts are\n" +"installed.\n" +"\n" +"options:\n" +" --in /path/to/new/tdm-config-dir\n" +" In which directory to put the new configuration. You can use this\n" +" to support a $(DESTDIR), but not to change the final location of\n" +" the installation - the paths inside the files are not affected.\n" +" Default is " TDMCONF ".\n" +" --old-xdm /path/to/old/xdm-dir\n" +" Where to look for the config files of an xdm/older tdm.\n" +" Default is to scan /etc/X11/tdm, $XLIBDIR/tdm, /etc/X11/xdm,\n" +" $XLIBDIR/xdm; there in turn look for tdm-config and xdm-config.\n" +" Note that you possibly need to use --no-old-kde to make this take effect.\n" +" --old-kde /path/to/old/kde-config-dir\n" +" Where to look for the tdmrc of an older tdm.\n" +" Default is to scan " KDE_CONFDIR " and\n" +" {/usr,/usr/local,{/opt,/usr/local}/{trinity,kde,kde2,kde1}}/share/config.\n" +" --no-old\n" +" Don't look at older xdm/tdm configurations, just create default config.\n" +" --no-old-xdm\n" +" Don't look at older xdm configurations.\n" +" --no-old-kde\n" +" Don't look at older tdm configurations.\n" +" --old-scripts\n" +" Directly use all scripts from the older xdm/tdm configuration.\n" +" --no-old-scripts\n" +" Don't use scripts from the older xdm/tdm configuration even if it lives\n" +" in the new target directory.\n" +" --old-confs\n" +" Directly use all ancillary config files from the older xdm/tdm\n" +" configuration. This is usually a bad idea.\n" +" --no-backup\n" +" Overwrite/delete old config files instead of backing them up.\n" +" --no-in-notice\n" +" Don't put the notice about --in being used into the generated README.\n" +); + exit( 0 ); + } + if (!strcmp( argv[ap], "--no-old" )) { + no_old = 1; + continue; + } + if (!strcmp( argv[ap], "--old-scripts" )) { + old_scripts = 1; + continue; + } + if (!strcmp( argv[ap], "--no-old-scripts" )) { + no_old_scripts = 1; + continue; + } + if (!strcmp( argv[ap], "--old-confs" )) { + old_confs = 1; + continue; + } + if (!strcmp( argv[ap], "--no-old-xdm" )) { + no_old_xdm = 1; + continue; + } + if (!strcmp( argv[ap], "--no-old-kde" )) { + no_old_kde = 1; + continue; + } + if (!strcmp( argv[ap], "--no-backup" )) { + no_backup = 1; + continue; + } + if (!strcmp( argv[ap], "--no-in-notice" )) { + no_in_notice = 1; + continue; + } + where = 0; + if (!strcmp( argv[ap], "--in" )) + where = &newdir; + else if (!strcmp( argv[ap], "--old-xdm" )) + where = &oldxdm; + else if (!strcmp( argv[ap], "--old-kde" )) + where = &oldkde; + else if (!strcmp( argv[ap], "--face-src" )) + where = &facesrc; + else { + fprintf( stderr, "Unknown command line option '%s', try --help\n", argv[ap] ); + exit( 1 ); + } + if (ap + 1 == argc || argv[ap + 1][0] == '-') { + fprintf( stderr, "Missing argument to option '%s', try --help\n", argv[ap] ); + exit( 1 ); + } + *where = argv[++ap]; + } + if (memcmp( newdir, TDMCONF, sizeof(TDMCONF) )) + use_destdir = 1; + + if (!mkdirp( newdir, 0755, "target", 1 )) + exit( 1 ); + + mkdefconf(); + newer = 0; + if (no_old) { + DIR *dir; + if ((dir = opendir( newdir ))) { + struct dirent *ent; + char bn[PATH_MAX]; + while ((ent = readdir( dir ))) { + int l; + if (!strcmp( ent->d_name, "." ) || !strcmp( ent->d_name, ".." )) + continue; + l = sprintf( bn, "%s/%s", newdir, ent->d_name ); /* cannot overflow (kernel would not allow the creation of a longer path) */ + if (!stat( bn, &st ) && !S_ISREG( st.st_mode )) + continue; + if (no_backup || !memcmp( bn + l - 4, ".bak", 5 )) + unlink( bn ); + else + displace( bn ); + } + closedir( dir ); + } + } else { + if (oldkde) { + if (!(newer = mergeKdmRcNewer( oldkde )) && !mergeKdmRcOld( oldkde )) + fprintf( stderr, + "Cannot read old tdmrc at specified location\n" ); + } else if (!no_old_kde) { + for (i = 0; i < as(oldkdes); i++) { + if ((newer = mergeKdmRcNewer( oldkdes[i] )) || + mergeKdmRcOld( oldkdes[i] )) { + oldkde = oldkdes[i]; + break; + } + } + } + if (!newer && !no_old_xdm) { + XrmInitialize(); + XrmQString = XrmPermStringToQuark( "String" ); + if (oldxdm) { + if (!mergeXdmCfg( oldxdm )) + fprintf( stderr, + "Cannot read old tdm-config/xdm-config at specified location\n" ); + } else + for (i = 0; i < as(oldxdms); i++) + if (mergeXdmCfg( oldxdms[i] )) { + oldxdm = oldxdms[i]; + break; + } + } else + oldxdm = 0; + } + if (no_old_scripts) + goto no_old_s; + if (!old_scripts) { + locals = foreigns = 0; + for (cs = config; cs; cs = cs->next) + if (!strcmp( cs->spec->name, "-Core" )) { + for (ce = cs->ents; ce; ce = ce->next) + if (ce->active && + (!strcmp( ce->spec->key, "Setup" ) || + !strcmp( ce->spec->key, "Startup" ) || + !strcmp( ce->spec->key, "Reset" ))) + { + if (inNewDir( ce->value )) + locals = 1; + else + foreigns = 1; + } + } + if (foreigns) { + if (locals) { + fprintf( stderr, + "Warning: both local and foreign scripts referenced. " + "Won't touch any.\n" ); + mixed_scripts = 1; + } else { + no_old_s: + for (cs = config; cs; cs = cs->next) { + if (!strcmp( cs->spec->name, "Xdmcp" )) { + for (ce = cs->ents; ce; ce = ce->next) + if (!strcmp( ce->spec->key, "Willing" )) + ce->active = ce->written = 0; + } else if (!strcmp( cs->spec->name, "-Core" )) { + for (cep = &cs->ents; (ce = *cep); ) { + if (ce->active && + (!strcmp( ce->spec->key, "Setup" ) || + !strcmp( ce->spec->key, "Startup" ) || + !strcmp( ce->spec->key, "Reset" ) || + !strcmp( ce->spec->key, "Session" ))) + { + if (!memcmp( cs->name, "X-*-", 4 )) + ce->active = ce->written = 0; + else { + *cep = ce->next; + free( ce ); + continue; + } + } + cep = &ce->next; + } + } + } + } + } + } +#ifdef __linux__ + if (!stat( "/etc/debian_version", &st )) { /* debian */ + defminuid = "1000"; + defmaxuid = "29999"; + } else if (!stat( "/usr/portage", &st )) { /* gentoo */ + defminuid = "1000"; + defmaxuid = "65000"; + } else if (!stat( "/etc/mandrake-release", &st )) { /* mandrake - check before redhat! */ + defminuid = "500"; + defmaxuid = "65000"; + } else if (!stat( "/etc/redhat-release", &st )) { /* redhat */ + defminuid = "100"; + defmaxuid = "65000"; + } else /* if (!stat( "/etc/SuSE-release", &st )) */ { /* suse */ + defminuid = "500"; + defmaxuid = "65000"; + } +#else + defminuid = "1000"; + defmaxuid = "65000"; +#endif + for (i = 0; i < CONF_MAX_PRIO; i++) + for (cs = config; cs; cs = cs->next) + for (ce = cs->ents; ce; ce = ce->next) + if (ce->spec->func && i == ce->spec->prio) + ce->spec->func( ce, cs ); + ASPrintf( &newtdmrc, "%s/tdmrc", newdir ); + f = Create( newtdmrc, tdmrcmode ); + wrconf( f ); + fclose( f ); + + ASPrintf( &nname, "%s/README", newdir ); + f = Create( nname, 0644 ); + fprintf( f, +"This automatically generated configuration consists of the following files:\n" ); + fprintf( f, "- " TDMCONF "/tdmrc\n" ); + for (fp = aflist; fp; fp = fp->next) + fprintf( f, "- %s\n", fp->str ); + if (use_destdir && !no_in_notice) + fwrapprintf( f, +"All files destined for " TDMCONF " were actually saved in %s; " +"this config won't be workable until moved in place.\n", newdir ); + if (uflist || eflist || cflist || lflist) { + fprintf( f, +"\n" +"This config was derived from existing files. As the used algorithms are\n" +"pretty dumb, it may be broken.\n" ); + if (uflist) { + fprintf( f, +"Information from these files was extracted:\n" ); + for (fp = uflist; fp; fp = fp->next) + fprintf( f, "- %s\n", fp->str ); + } + if (lflist) { + fprintf( f, +"These files were directly incorporated:\n" ); + for (fp = lflist; fp; fp = fp->next) + fprintf( f, "- %s\n", fp->str ); + } + if (cflist) { + fprintf( f, +"These files were copied verbatim:\n" ); + for (fp = cflist; fp; fp = fp->next) + fprintf( f, "- %s\n", fp->str ); + } + if (eflist) { + fprintf( f, +"These files were copied with modifications:\n" ); + for (fp = eflist; fp; fp = fp->next) + fprintf( f, "- %s\n", fp->str ); + } + if (!no_backup && !use_destdir) + fprintf( f, +"Old files that would have been overwritten were renamed to <oldname>.bak.\n" ); + } + fprintf( f, +"\nTry 'gentdmconf --help' if you want to generate another configuration.\n" +"\nYou may delete this README.\n" ); + fclose( f ); + + return 0; +} |