add more spacing
[personal-kdebase.git] / workspace / kdm / kfrontend / genkdmconf.c
blob6226999d6a4703a30129e25f0ae2efd6109aed9e
1 /*
3 Create a suitable configuration for kdm taking previous xdm/kdm
4 installations into account
6 Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include <greet.h>
27 #include <X11/Xlib.h>
28 #include <X11/Xresource.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <stdarg.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <utime.h>
40 #include <dirent.h>
41 #include <errno.h>
42 #include <pwd.h>
43 #include <time.h>
44 #include <limits.h>
45 #include <sys/stat.h>
46 #include <sys/param.h>
47 #ifdef BSD
48 # include <utmp.h>
49 #endif
51 #define WANT_CONF_GEN
52 #include <config.ci>
54 #define RCVERSTR stringify(RCVERMAJOR) "." stringify(RCVERMINOR)
56 static int old_scripts, no_old_scripts, old_confs, no_old,
57 no_backup, no_in_notice, use_destdir, mixed_scripts;
58 static const char *newdir = KDMCONF, *facesrc = KDMDATA "/pics/users",
59 *oldxdm, *oldkde, *oldkdepfx;
61 static int oldver;
64 typedef struct StrList {
65 struct StrList *next;
66 const char *str;
67 } StrList;
69 typedef struct StrMap {
70 struct StrMap *next;
71 const char *key, *value;
72 } StrMap;
75 static void *
76 mmalloc( size_t sz )
78 void *ptr;
80 if (!(ptr = malloc( sz ))) {
81 fprintf( stderr, "Out of memory\n" );
82 exit( 1 );
84 return ptr;
87 static void *
88 mcalloc( size_t sz )
90 void *ptr;
92 if (!(ptr = calloc( 1, sz ))) {
93 fprintf( stderr, "Out of memory\n" );
94 exit( 1 );
96 return ptr;
99 static void *
100 mrealloc( void *optr, size_t sz )
102 void *ptr;
104 if (!(ptr = realloc( optr, sz ))) {
105 fprintf( stderr, "Out of memory\n" );
106 exit( 1 );
108 return ptr;
111 static char *
112 mstrdup( const char *optr )
114 char *ptr;
116 if (!optr)
117 return 0;
118 if (!(ptr = strdup( optr ))) {
119 fprintf( stderr, "Out of memory\n" );
120 exit( 1 );
122 return ptr;
126 #define NO_LOGGER
127 #define STATIC static
128 #include <printf.c>
130 typedef struct {
131 char *buf;
132 int clen, blen, tlen;
133 } OCABuf;
135 static void
136 outCh_OCA( void *bp, char c )
138 OCABuf *ocabp = (OCABuf *)bp;
140 ocabp->tlen++;
141 if (ocabp->clen >= ocabp->blen) {
142 ocabp->blen = ocabp->blen * 3 / 2 + 100;
143 ocabp->buf = mrealloc( ocabp->buf, ocabp->blen );
145 ocabp->buf[ocabp->clen++] = c;
148 static int
149 VASPrintf( char **strp, const char *fmt, va_list args )
151 OCABuf ocab = { 0, 0, 0, -1 };
153 doPrint( outCh_OCA, &ocab, fmt, args );
154 outCh_OCA( &ocab, 0 );
155 *strp = realloc( ocab.buf, ocab.clen );
156 if (!*strp)
157 *strp = ocab.buf;
158 return ocab.tlen;
161 static int
162 ASPrintf( char **strp, const char *fmt, ... )
164 va_list args;
165 int len;
167 va_start( args, fmt );
168 len = VASPrintf( strp, fmt, args );
169 va_end( args );
170 return len;
173 static void
174 strCat( char **strp, const char *fmt, ... )
176 char *str, *tstr;
177 va_list args;
178 int el;
180 va_start( args, fmt );
181 el = VASPrintf( &str, fmt, args );
182 va_end( args );
183 if (*strp) {
184 int ol = strlen( *strp );
185 tstr = mmalloc( el + ol + 1 );
186 memcpy( tstr, *strp, ol );
187 memcpy( tstr + ol, str, el + 1 );
188 free( *strp );
189 free( str );
190 *strp = tstr;
191 } else
192 *strp = str;
196 #define WANT_CLOSE 1
198 typedef struct File {
199 char *buf, *eof, *cur;
200 #if defined(HAVE_MMAP) && defined(WANT_CLOSE)
201 int ismapped;
202 #endif
203 } File;
205 static int
206 readFile( File *file, const char *fn )
208 off_t flen;
209 int fd;
211 if ((fd = open( fn, O_RDONLY )) < 0)
212 return False;
214 flen = lseek( fd, 0, SEEK_END );
215 #ifdef HAVE_MMAP
216 # ifdef WANT_CLOSE
217 file->ismapped = False;
218 # endif
219 file->buf = mmap( 0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 );
220 # ifdef WANT_CLOSE
221 if (file->buf)
222 file->ismapped = True;
223 else
224 # else
225 if (!file->buf)
226 # endif
227 #endif
229 file->buf = mmalloc( flen + 1 );
230 lseek( fd, 0, SEEK_SET );
231 if (read( fd, file->buf, flen ) != flen) {
232 free( file->buf );
233 close( fd );
234 fprintf( stderr, "Cannot read file\n" );
235 return False; /* maybe better abort? */
238 file->eof = file->buf + flen;
239 close( fd );
240 return True;
243 #ifdef WANT_CLOSE
244 static void
245 freeBuf( File *file )
247 # ifdef HAVE_MMAP
248 if (file->ismapped)
249 munmap( file->buf, file->eof - file->buf );
250 else
251 # endif
252 free( file->buf );
254 #endif
256 static int
257 isTrue( const char *val )
259 return !strcmp( val, "true" ) ||
260 !strcmp( val, "yes" ) ||
261 !strcmp( val, "on" ) ||
262 atoi( val );
266 static int
267 mkpdirs( const char *name, const char *what )
269 char *mfname = mstrdup( name );
270 int i;
271 struct stat st;
273 for (i = 1; mfname[i]; i++)
274 if (mfname[i] == '/') {
275 mfname[i] = 0;
276 if (stat( mfname, &st )) {
277 if (mkdir( mfname, 0755 )) {
278 fprintf( stderr, "Cannot create parent %s of %s directory %s: %s\n",
279 mfname, what, name, strerror( errno ) );
280 free( mfname );
281 return False;
283 chmod( mfname, 0755 );
285 mfname[i] = '/';
287 free( mfname );
288 return True;
291 static int
292 mkdirp( const char *name, int mode, const char *what, int existok )
294 struct stat st;
296 if (stat( name, &st )) {
297 mkpdirs( name, what );
298 if (mkdir( name, mode )) {
299 fprintf( stderr, "Cannot create %s directory %s: %s\n",
300 what, name, strerror( errno ) );
301 return False;
303 chmod( name, mode );
304 return True;
306 return existok;
310 static void
311 displace( const char *fn )
313 if (!no_backup) {
314 char bn[PATH_MAX + 4];
315 sprintf( bn, "%s.bak", fn ); /* won't overflow if only existing paths are passed */
316 rename( fn, bn );
317 } else
318 unlink( fn );
322 static char *
323 locate( const char *exe )
325 int len;
326 char *path, *pathe, *name, *thenam, nambuf[PATH_MAX+1];
328 if (!(path = getenv( "PATH" )))
329 return 0;
330 len = strlen( exe );
331 name = nambuf + PATH_MAX - len;
332 memcpy( name, exe, len + 1 );
333 *--name = '/';
334 do {
335 if (!(pathe = strchr( path, ':' )))
336 pathe = path + strlen( path );
337 len = pathe - path;
338 if (len && !(len == 1 && *path == '.')) {
339 thenam = name - len;
340 if (thenam >= nambuf) {
341 memcpy( thenam, path, len );
342 if (!access( thenam, X_OK ))
343 return mstrdup( thenam );
346 path = pathe;
347 } while (*path++ != '\0');
348 return 0;
353 * target data to be written to kdmrc
356 typedef struct Entry {
357 struct Entry *next;
358 struct Ent *spec;
359 const char *value;
360 int active:1;
361 int written:1;
362 } Entry;
364 typedef struct Section {
365 struct Section *next;
366 struct Sect *spec;
367 const char *name;
368 const char *comment;
369 Entry *ents;
370 } Section;
372 static Section *config; /* the kdmrc data to be written */
375 * Specification of the (currently possible) kdmrc entries
378 typedef struct Ent {
379 const char *key;
380 int prio;
381 void (*func)( Entry *ce, Section *cs );
382 const char *comment;
383 } Ent;
385 typedef struct Sect {
386 const char *name;
387 Ent *ents;
388 int nents;
389 } Sect;
391 static Sect *findSect( const char *name );
392 static Ent *findEnt( Sect *sect, const char *key );
395 * Functions to manipulate the current kdmrc data
398 static const char *
399 getFqVal( const char *sect, const char *key, const char *defval )
401 Section *cs;
402 Entry *ce;
404 for (cs = config; cs; cs = cs->next)
405 if (!strcmp( cs->name, sect )) {
406 for (ce = cs->ents; ce; ce = ce->next)
407 if (!strcmp( ce->spec->key, key )) {
408 if (ce->active && ce->written)
409 return ce->value;
410 break;
412 break;
414 return defval;
417 static void
418 putFqVal( const char *sect, const char *key, const char *value )
420 Section *cs, **csp;
421 Entry *ce, **cep;
423 if (!value)
424 return;
426 for (csp = &config; (cs = *csp); csp = &(cs->next))
427 if (!strcmp( sect, cs->name ))
428 goto havesec;
429 cs = mcalloc( sizeof(*cs) );
430 ASPrintf( (char **)&cs->name, "%s", sect );
431 cs->spec = findSect( sect );
432 *csp = cs;
433 havesec:
435 for (cep = &(cs->ents); (ce = *cep); cep = &(ce->next))
436 if (!strcmp( key, ce->spec->key ))
437 goto haveent;
438 ce = mcalloc( sizeof(*ce) );
439 ce->spec = findEnt( cs->spec, key );
440 *cep = ce;
441 haveent:
442 ASPrintf( (char **)&ce->value, "%s", value );
443 ce->written = ce->active = True;
446 static const char *csect;
448 #define setSect(se) csect = se
450 static void
451 putVal( const char *key, const char *value )
453 putFqVal( csect, key, value );
457 static void
458 writeKdmrc( FILE *f )
460 Section *cs;
461 Entry *ce;
462 StrList *sl = 0, *sp;
463 const char *cmt;
465 putFqVal( "General", "ConfigVersion", RCVERSTR );
466 for (cs = config; cs; cs = cs->next) {
467 fprintf( f, "%s[%s]\n",
468 cs->comment ? cs->comment : "\n", cs->name );
469 for (ce = cs->ents; ce; ce = ce->next) {
470 if (ce->spec->comment) {
471 cmt = ce->spec->comment;
472 for (sp = sl; sp; sp = sp->next)
473 if (sp->str == cmt) {
474 cmt = "# See above\n";
475 goto havit;
477 if (!(sp = malloc( sizeof(*sp) )))
478 fprintf( stderr, "Warning: Out of memory\n" );
479 else {
480 sp->str = cmt;
481 sp->next = sl; sl = sp;
483 } else
484 cmt = "";
485 havit:
486 fprintf( f, "%s%s%s=%s\n",
487 cmt, ce->active ? "" : "#", ce->spec->key, ce->value );
494 * defaults
496 #ifdef XDMCP
497 static const char def_xaccess[] =
498 "# Xaccess - Access control file for XDMCP connections\n"
499 "#\n"
500 "# To control Direct and Broadcast access:\n"
501 "#\n"
502 "# pattern\n"
503 "#\n"
504 "# To control Indirect queries:\n"
505 "#\n"
506 "# pattern list of hostnames and/or macros ...\n"
507 "#\n"
508 "# To use the chooser:\n"
509 "#\n"
510 "# pattern CHOOSER BROADCAST\n"
511 "#\n"
512 "# or\n"
513 "#\n"
514 "# pattern CHOOSER list of hostnames and/or macros ...\n"
515 "#\n"
516 "# To define macros:\n"
517 "#\n"
518 "# %name list of hosts ...\n"
519 "#\n"
520 "# The first form tells xdm which displays to respond to itself.\n"
521 "# The second form tells xdm to forward indirect queries from hosts matching\n"
522 "# the specified pattern to the indicated list of hosts.\n"
523 "# The third form tells xdm to handle indirect queries using the chooser;\n"
524 "# the chooser is directed to send its own queries out via the broadcast\n"
525 "# address and display the results on the terminal.\n"
526 "# The fourth form is similar to the third, except instead of using the\n"
527 "# broadcast address, it sends DirectQuerys to each of the hosts in the list\n"
528 "#\n"
529 "# In all cases, xdm uses the first entry which matches the terminal;\n"
530 "# for IndirectQuery messages only entries with right hand sides can\n"
531 "# match, for Direct and Broadcast Query messages, only entries without\n"
532 "# right hand sides can match.\n"
533 "#\n"
534 "\n"
535 "* #any host can get a login window\n"
536 "\n"
537 "#\n"
538 "# To hardwire a specific terminal to a specific host, you can\n"
539 "# leave the terminal sending indirect queries to this host, and\n"
540 "# use an entry of the form:\n"
541 "#\n"
542 "\n"
543 "#terminal-a host-a\n"
544 "\n"
545 "\n"
546 "#\n"
547 "# The nicest way to run the chooser is to just ask it to broadcast\n"
548 "# requests to the network - that way new hosts show up automatically.\n"
549 "# Sometimes, however, the chooser cannot figure out how to broadcast,\n"
550 "# so this may not work in all environments.\n"
551 "#\n"
552 "\n"
553 "* CHOOSER BROADCAST #any indirect host can get a chooser\n"
554 "\n"
555 "#\n"
556 "# If you would prefer to configure the set of hosts each terminal sees,\n"
557 "# then just uncomment these lines (and comment the CHOOSER line above)\n"
558 "# and edit the %hostlist line as appropriate\n"
559 "#\n"
560 "\n"
561 "#%hostlist host-a host-b\n"
562 "\n"
563 "#* CHOOSER %hostlist #\n";
564 #endif
566 #ifdef XDMCP
567 static const char def_willing[] =
568 "#! /bin/sh\n"
569 "# The output of this script is displayed in the chooser window\n"
570 "# (instead of \"Willing to manage\").\n"
571 "\n"
572 "load=`uptime|sed -e 's/^.*load[^0-9]*//'`\n"
573 "nrusers=`who|cut -c 1-8|sort -u|wc -l|sed 's/^[ \t]*//'`\n"
574 "s=\"\"; [ \"$nrusers\" != 1 ] && s=s\n"
575 "\n"
576 "echo \"${nrusers} user${s}, load: ${load}\"\n";
577 #endif
579 static const char def_setup[] =
580 "#! /bin/sh\n"
581 "# Xsetup - run as root before the login dialog appears\n"
582 "\n"
583 "#xconsole -geometry 480x130-0-0 -notify -verbose -fn fixed -exitOnFail -file /dev/xconsole &\n";
585 static const char def_startup[] =
586 "#! /bin/sh\n"
587 "# Xstartup - run as root before session starts\n"
588 "\n"
589 "# By convention, both xconsole and xterm -C check that the\n"
590 "# console is owned by the invoking user and is readable before attaching\n"
591 "# the console output. This way a random user can invoke xterm -C without\n"
592 "# causing serious grief; still, it can cause havoc, so xconsole is started\n"
593 "# by Xsetup usually.\n"
594 "# This is not required if you use PAM with the pam_console module.\n"
595 "#\n"
596 "#chown $USER /dev/console\n"
597 "\n"
598 #ifdef _AIX
599 "# We create a pseudodevice for finger. (host:0 becomes xdm/host_0)\n"
600 "# Without it, finger errors out with \"Cannot stat /dev/host:0\".\n"
601 "#\n"
602 "#devname=`echo $DISPLAY | cut -c1-8`\n"
603 "#if [ ! -d /dev/xdm ]; then\n"
604 "# mkdir /dev/xdm\n"
605 "# chmod 755 /dev/xdm\n"
606 "#fi\n"
607 "#touch /dev/xdm/$devname\n"
608 "#chmod 644 /dev/xdm/$devname\n"
609 "#exec sessreg -a -l xdm/$devname -h \"`echo $DISPLAY | cut -d: -f1`\""
610 #else
611 "#exec sessreg -a -l $DISPLAY -h \"`echo $DISPLAY | cut -d: -f1`\""
612 # ifdef BSD
613 " -x " KDMCONF "/Xservers"
614 # endif
615 #endif /* _AIX */
616 " $USER\n"
617 "\n# NOTE: The session is aborted if the last command returns non-zero.\n";
619 static const char def_reset[] =
620 "#! /bin/sh\n"
621 "# Xreset - run as root after session exits\n"
622 "\n"
623 "# Reassign ownership of the console to root, this should disallow\n"
624 "# assignment of console output to any random users's xterm. See Xstartup.\n"
625 "#\n"
626 "#chown root /dev/console\n"
627 "#chmod 622 /dev/console\n"
628 "\n"
629 #ifdef _AIX
630 "#devname=`echo $DISPLAY | cut -c1-8`\n"
631 "#exec sessreg -d -l xdm/$devname -h \"`echo $DISPLAY | cut -d: -f1`\""
632 #else
633 "#exec sessreg -d -l $DISPLAY -h \"`echo $DISPLAY | cut -d: -f1`\""
634 # ifdef BSD
635 " -x " KDMCONF "/Xservers"
636 # endif
637 #endif /* _AIX */
638 " $USER\n";
640 static const char def_session1[] =
641 "#! /bin/sh\n"
642 "# Xsession - run as user\n"
643 "\n"
644 "session=$1\n"
645 "\n"
646 "# Note that the respective logout scripts are not sourced.\n"
647 "case $SHELL in\n"
648 " */bash)\n"
649 " [ -z \"$BASH\" ] && exec $SHELL $0 \"$@\"\n"
650 " set +o posix\n"
651 " [ -f /etc/profile ] && . /etc/profile\n"
652 " if [ -f $HOME/.bash_profile ]; then\n"
653 " . $HOME/.bash_profile\n"
654 " elif [ -f $HOME/.bash_login ]; then\n"
655 " . $HOME/.bash_login\n"
656 " elif [ -f $HOME/.profile ]; then\n"
657 " . $HOME/.profile\n"
658 " fi\n"
659 " ;;\n"
660 " */zsh)\n"
661 " [ -z \"$ZSH_NAME\" ] && exec $SHELL $0 \"$@\"\n"
662 " emulate -R zsh\n"
663 " [ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc\n"
664 " zhome=${ZDOTDIR:-$HOME}\n"
665 " # zshenv is always sourced automatically.\n"
666 " [ -f $zdir/zprofile ] && . $zdir/zprofile\n"
667 " [ -f $zhome/.zprofile ] && . $zhome/.zprofile\n"
668 " [ -f $zdir/zlogin ] && . $zdir/zlogin\n"
669 " [ -f $zhome/.zlogin ] && . $zhome/.zlogin\n"
670 " ;;\n"
671 " */csh|*/tcsh)\n"
672 " # [t]cshrc is always sourced automatically.\n"
673 " # Note that sourcing csh.login after .cshrc is non-standard.\n"
674 " xsess_tmp=";
675 static const char def_session2[] =
676 "\n"
677 " $SHELL -c \"if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c export -p >! $xsess_tmp\"\n"
678 " . $xsess_tmp\n"
679 " rm -f $xsess_tmp\n"
680 " ;;\n"
681 " *) # Plain sh, ksh, and anything we do not know.\n"
682 " [ -f /etc/profile ] && . /etc/profile\n"
683 " [ -f $HOME/.profile ] && . $HOME/.profile\n"
684 " ;;\n"
685 "esac\n"
686 "\n"
687 "[ -f /etc/xprofile ] && . /etc/xprofile\n"
688 "[ -f $HOME/.xprofile ] && . $HOME/.xprofile\n"
689 "\n"
690 "case $session in\n"
691 " \"\")\n"
692 " exec xmessage -center -buttons OK:0 -default OK \"Sorry, $DESKTOP_SESSION is no valid session.\"\n"
693 " ;;\n"
694 " failsafe)\n"
695 " exec xterm -geometry 80x24-0-0\n"
696 " ;;\n"
697 " custom)\n"
698 " exec $HOME/.xsession\n"
699 " ;;\n"
700 " default)\n"
701 " exec " KDE_BINDIR "/startkde\n"
702 " ;;\n"
703 " *)\n"
704 " eval exec \"$session\"\n"
705 " ;;\n"
706 "esac\n"
707 "exec xmessage -center -buttons OK:0 -default OK \"Sorry, cannot execute $session. Check $DESKTOP_SESSION.desktop.\"\n";
709 static const char def_background[] =
710 "[Desktop0]\n"
711 "BackgroundMode=Flat\n"
712 "BlendBalance=100\n"
713 "BlendMode=NoBlending\n"
714 "ChangeInterval=60\n"
715 "Color1=0,0,200\n"
716 "Color2=192,192,192\n"
717 "CurrentWallpaper=0\n"
718 "LastChange=0\n"
719 "MinOptimizationDepth=1\n"
720 "MultiWallpaperMode=NoMulti\n"
721 "Pattern=fish\n"
722 "Program=\n"
723 "ReverseBlending=false\n"
724 "UseSHM=false\n"
725 "Wallpaper=default_blue.jpg\n"
726 "WallpaperList=\n"
727 "WallpaperMode=Scaled\n";
729 /* Create a new file in KDMCONF */
730 static FILE *
731 createFile( const char *fn, int mode )
733 char *nname;
734 FILE *f;
736 ASPrintf( &nname, "%s/%s", newdir, fn );
737 displace( nname );
738 if (!(f = fopen( nname, "w" ))) {
739 fprintf( stderr, "Cannot create %s\n", nname );
740 exit( 1 );
742 chmod( nname, mode );
743 free( nname );
744 return f;
747 /* Create a copy of a file under KDMCONF and fill it */
748 static void
749 writeCopy( const char *fn, int mode, time_t stamp, const char *buf, size_t len )
751 char *nname;
752 int fd;
753 struct utimbuf utim;
755 ASPrintf( &nname, "%s/%s", newdir, fn );
756 displace( nname );
757 mkpdirs( nname, "target" );
758 if ((fd = creat( nname, mode )) < 0) {
759 fprintf( stderr, "Cannot create %s\n", nname );
760 exit( 1 );
762 write( fd, buf, len );
763 close( fd );
764 if (stamp) {
765 utim.actime = utim.modtime = stamp;
766 utime( nname, &utim );
768 free( nname );
772 /* returns static array! */
773 static const char *
774 reSect( const char *sec, const char *name )
776 static char sname[64];
777 char *p;
779 if ((p = strrchr( sec, '-' ))) {
780 sprintf( sname, "%.*s-%s", (int)(p - sec), sec, name );
781 return sname;
782 } else
783 return name;
786 static int
787 inNewDir( const char *name )
789 return !memcmp( name, KDMCONF "/", sizeof(KDMCONF) );
792 static const char *
793 getMapping( StrMap *sm, const char *k )
795 for (; sm; sm = sm->next)
796 if (!strcmp( sm->key, k ))
797 return sm->value;
798 return 0;
801 static void
802 addMapping( StrMap **sm, const char *k, const char *v )
804 for (; *sm; sm = &(*sm)->next)
805 if (!strcmp( (*sm)->key, k ))
806 return;
807 *sm = mcalloc( sizeof(**sm) );
808 ASPrintf( (char **)&(*sm)->key, "%s", k );
809 ASPrintf( (char **)&(*sm)->value, "%s", v );
812 static int
813 inList( StrList *sp, const char *s )
815 for (; sp; sp = sp->next)
816 if (!strcmp( sp->str, s ))
817 return True;
818 return False;
821 static void
822 addStr( StrList **sp, const char *s )
824 for (; *sp; sp = &(*sp)->next)
825 if (!strcmp( (*sp)->str, s ))
826 return;
827 *sp = mcalloc( sizeof(**sp) );
828 ASPrintf( (char **)&(*sp)->str, "%s", s );
831 static StrList *
832 splitList( const char *str )
834 StrList *sp, **spp = &sp;
835 const char *e;
836 if (!*str)
837 return 0;
838 for (;;) {
839 *spp = mcalloc( sizeof(**spp) );
840 if (!(e = strchr( str, ',' )))
841 break;
842 ASPrintf( (char **)&(*spp)->str, "%.*s", (int)(e - str), str );
843 str = e + 1;
844 spp = &(*spp)->next;
846 (*spp)->str = mstrdup( str );
847 (*spp)->next = 0;
848 return sp;
851 static char *
852 joinList( StrList *sp )
854 char *s = 0;
855 if (!sp)
856 return mstrdup( "" );
857 s = mstrdup( sp->str );
858 for (;;) {
859 sp = sp->next;
860 if (!sp)
861 return s;
862 strCat( &s, ",%s", sp->str );
866 StrMap *cfmap;
867 StrList *aflist, *uflist, *eflist, *cflist, *lflist;
869 /* file is part of new config */
870 static void
871 addedFile( const char *fn )
873 addStr( &aflist, fn );
876 /* file from old config was parsed */
877 static void
878 usedFile( const char *fn )
880 addStr( &uflist, fn );
883 /* file from old config was copied with slight modifications */
884 static void
885 editedFile( const char *fn )
887 addStr( &eflist, fn );
890 /* file from old config was copied verbatim */
891 static void
892 copiedFile( const char *fn )
894 addStr( &cflist, fn );
897 /* file from old config is still being used */
898 static void
899 linkedFile( const char *fn )
901 addStr( &lflist, fn );
905 * NOTE: This code will not correctly deal with default files colliding
906 * with pre-existing files. This should be OK, as for each class of files
907 * (scripts, configs) only one origin is used, and conflicts between classes
908 * are rather unlikely.
911 /* Make a possibly modified copy of a file under KDMCONF */
912 static int
913 copyFile( Entry *ce, int mode, int (*proc)( File * ) )
915 const char *tptr;
916 char *nname;
917 File file;
918 int rt;
920 if (!*ce->value)
921 return True;
923 if ((nname = (char *)getMapping( cfmap, ce->value ))) {
924 rt = inList( aflist, nname );
925 goto doret;
927 if (oldkde) {
928 int olen = strlen( oldkde );
929 if (!memcmp( ce->value, oldkde, olen )) {
930 if (!memcmp( ce->value + olen, "/kdm/", 5 )) {
931 tptr = ce->value + olen + 4;
932 goto gotn;
934 if (ce->value[olen] == '/') {
935 tptr = ce->value + olen;
936 goto gotn;
940 if (oldxdm) {
941 int olen = strlen( oldxdm );
942 if (!memcmp( ce->value, oldxdm, olen ) && ce->value[olen] == '/') {
943 tptr = ce->value + olen;
944 goto gotn;
947 if (!(tptr = strrchr( ce->value, '/' ))) {
948 fprintf( stderr, "Warning: cannot cope with relative path %s\n", ce->value );
949 return False;
951 gotn:
952 ASPrintf( &nname, KDMCONF "%s", tptr );
953 if (inList( aflist, nname )) {
954 int cnt = 1;
955 do {
956 free( nname );
957 ASPrintf( &nname, KDMCONF "%s-%d", tptr , ++cnt );
958 } while (inList( aflist, nname ));
960 addMapping( &cfmap, ce->value, nname );
961 if (!readFile( &file, ce->value )) {
962 fprintf( stderr, "Warning: cannot copy file %s\n", ce->value );
963 rt = False;
964 } else {
965 if (!proc || !proc( &file )) {
966 if (!use_destdir && !strcmp( ce->value, nname ))
967 linkedFile( nname );
968 else {
969 struct stat st;
970 stat( ce->value, &st );
971 writeCopy( nname + sizeof(KDMCONF), mode, st.st_mtime,
972 file.buf, file.eof - file.buf );
973 copiedFile( ce->value );
975 } else {
976 writeCopy( nname + sizeof(KDMCONF), mode, 0,
977 file.buf, file.eof - file.buf );
978 editedFile( ce->value );
980 if (strcmp( ce->value, nname ) && inNewDir( ce->value ) && !use_destdir)
981 displace( ce->value );
982 addedFile( nname );
983 rt = True;
985 doret:
986 ce->value = nname;
987 return rt;
990 static void
991 doLinkFile( const char *name )
993 File file;
995 if (inList( aflist, name ))
996 return;
997 if (!readFile( &file, name )) {
998 fprintf( stderr, "Warning: cannot read file %s\n", name );
999 return;
1001 if (inNewDir( name ) && use_destdir) {
1002 struct stat st;
1003 stat( name, &st );
1004 writeCopy( name + sizeof(KDMCONF), st.st_mode, st.st_mtime,
1005 file.buf, file.eof - file.buf );
1006 copiedFile( name );
1007 } else
1008 linkedFile( name );
1009 addedFile( name );
1012 /* Incorporate an existing file */
1013 static void
1014 linkFile( Entry *ce )
1016 if (ce->written && *ce->value)
1017 doLinkFile( ce->value );
1020 /* Create a new file in KDMCONF and fill it */
1021 static void
1022 writeFile( const char *tname, int mode, const char *cont )
1024 FILE *f = createFile( tname + sizeof(KDMCONF), mode );
1025 fputs( cont, f );
1026 fclose( f );
1027 addedFile( tname );
1031 static void
1032 handleBgCfg( Entry *ce, Section *cs ATTR_UNUSED )
1034 if (!ce->active) /* can be only the X-*-Greeter one */
1035 writeFile( def_BackgroundCfg, 0644, def_background );
1036 #if 0 /* risk of kcontrol clobbering the original file */
1037 else if (old_confs)
1038 linkFile( ce );
1039 #endif
1040 else {
1041 if (!copyFile( ce, 0644, 0 )) {
1042 if (!strcmp( cs->name, "X-*-Greeter" ))
1043 writeFile( def_BackgroundCfg, 0644, def_background );
1044 ce->active = False;
1050 #ifdef HAVE_VTS
1051 static char *
1052 memMem( char *mem, int lmem, const char *smem, int lsmem )
1054 for (; lmem >= lsmem; mem++, lmem--)
1055 if (!memcmp( mem, smem, lsmem ))
1056 return mem + lsmem;
1057 return 0;
1060 static int maxTTY, TTYmask;
1062 static void
1063 getInitTab( void )
1065 File it;
1066 char *p, *eol, *ep;
1067 int tty;
1069 if (maxTTY)
1070 return;
1071 if (readFile( &it, "/etc/inittab" )) {
1072 usedFile( "/etc/inittab" );
1073 for (p = it.buf; p < it.eof; p = eol + 1) {
1074 for (eol = p; eol < it.eof && *eol != '\n'; eol++);
1075 if (*p != '#') {
1076 if ((ep = memMem( p, eol - p, " tty", 4 )) &&
1077 ep < eol && isdigit( *ep ))
1079 if (ep + 1 == eol || isspace( *(ep + 1) ))
1080 tty = *ep - '0';
1081 else if (isdigit( *(ep + 1) ) &&
1082 (ep + 2 == eol || isspace( *(ep + 2) )))
1083 tty = (*ep - '0') * 10 + (*(ep + 1) - '0');
1084 else
1085 continue;
1086 TTYmask |= 1 << (tty - 1);
1087 if (tty > maxTTY)
1088 maxTTY = tty;
1092 freeBuf( &it );
1094 if (!maxTTY) {
1095 maxTTY = 6;
1096 TTYmask = 0x3f;
1099 #endif
1102 /* TODO: handle solaris' local_uid specs */
1104 static char *
1105 readWord( File *file, int EOFatEOL )
1107 char *wordp, *wordBuffer;
1108 int quoted;
1109 char c;
1111 rest:
1112 wordp = wordBuffer = file->cur;
1113 mloop:
1114 quoted = False;
1115 qloop:
1116 if (file->cur == file->eof) {
1117 doeow:
1118 if (wordp == wordBuffer)
1119 return 0;
1120 retw:
1121 *wordp = '\0';
1122 return wordBuffer;
1124 c = *file->cur++;
1125 switch (c) {
1126 case '#':
1127 if (quoted)
1128 break;
1129 do {
1130 if (file->cur == file->eof)
1131 goto doeow;
1132 c = *file->cur++;
1133 } while (c != '\n');
1134 case '\0':
1135 case '\n':
1136 if (EOFatEOL && !quoted) {
1137 file->cur--;
1138 goto doeow;
1140 if (wordp != wordBuffer) {
1141 file->cur--;
1142 goto retw;
1144 goto rest;
1145 case ' ':
1146 case '\t':
1147 if (wordp != wordBuffer)
1148 goto retw;
1149 goto rest;
1150 case '\\':
1151 if (!quoted) {
1152 quoted = True;
1153 goto qloop;
1155 break;
1157 *wordp++ = c;
1158 goto mloop;
1161 /* backslashes are double-escaped - first parseArgs, then KConfig */
1163 static StrList *
1164 splitArgs( const char *string )
1166 const char *word;
1167 char *str;
1168 int wlen;
1169 StrList *args, **argp = &args;
1171 while (*string) {
1172 if (isspace( *string )) {
1173 string++;
1174 continue;
1176 word = string;
1177 wlen = 0;
1178 do {
1179 if (*string == '\\') {
1180 if (*++string != '\\')
1181 string--;
1182 if (*++string != '\\')
1183 string--;
1184 if (!*++string)
1185 string--;
1186 wlen++;
1187 } else if (*string == '\'') {
1188 while (*++string != '\'' && *string) {
1189 if (*string == '\\' && *++string != '\\')
1190 string--;
1191 wlen++;
1193 } else if (*string == '"') {
1194 while (*++string != '"' && *string) {
1195 if (*string == '\\') {
1196 if (*++string != '\\')
1197 string--;
1198 if (*++string != '\\')
1199 string--;
1200 if (!*++string)
1201 string--;
1203 wlen++;
1205 } else
1206 wlen++;
1207 } while (*++string && !isspace( *string ));
1208 *argp = mmalloc( sizeof(**argp) );
1209 (*argp)->str = str = mmalloc( wlen + 1 );
1210 do {
1211 if (*word == '\\') {
1212 if (*++word != '\\')
1213 word--;
1214 if (*++word != '\\')
1215 word--;
1216 if (!*++word)
1217 word--;
1218 *str++ = *word;
1219 } else if (*word == '\'') {
1220 while (*++word != '\'' && *word) {
1221 if (*word == '\\' && *++word != '\\')
1222 word--;
1223 *str++ = *word;
1225 } else if (*word == '"') {
1226 while (*++word != '"' && *word) {
1227 if (*word == '\\') {
1228 if (*++word != '\\')
1229 word--;
1230 if (*++word != '\\')
1231 word--;
1232 if (!*++word)
1233 word--;
1235 *str++ = *word;
1237 } else
1238 *str++ = *word;
1239 } while (*++word && !isspace( *word ));
1240 *str = 0;
1241 argp = &(*argp)->next;
1243 *argp = 0;
1244 return args;
1247 static const char *
1248 joinArgs( StrList *argv )
1250 StrList *av;
1251 const char *s, *rs;
1252 char *str;
1253 int slen;
1255 if (!argv)
1256 return "";
1257 for (slen = 0, av = argv; slen++, av; av = av->next) {
1258 int nq = 0;
1259 for (s = av->str; *s; s++, slen++)
1260 if (isspace( *s ) || *s == '\'')
1261 nq = 2;
1262 else if (*s == '"')
1263 slen += 2;
1264 else if (*s == '\\')
1265 slen += 3;
1266 slen += nq;
1268 rs = str = mmalloc( slen );
1269 for (av = argv; av; av = av->next) {
1270 int nq = 0;
1271 for (s = av->str; *s; s++)
1272 if (isspace( *s ) || *s == '\'')
1273 nq = 2;
1274 if (av != argv)
1275 *str++ = ' ';
1276 if (nq)
1277 *str++ = '"';
1278 for (s = av->str; *s; s++) {
1279 if (*s == '\\')
1280 *str++ = '\\';
1281 if (*s == '"' || *s == '\\') {
1282 *str++ = '\\';
1283 *str++ = '\\';
1285 *str++ = *s;
1287 if (nq)
1288 *str++ = '"';
1290 *str = 0;
1291 return rs;
1294 static struct displayMatch {
1295 const char *name;
1296 int len, local;
1297 } displayTypes[] = {
1298 { "local", 5, True },
1299 { "foreign", 7, False },
1302 static int
1303 parseDisplayType( const char *string, const char **atPos )
1305 struct displayMatch *d;
1307 *atPos = 0;
1308 for (d = displayTypes; d < displayTypes + as(displayTypes); d++) {
1309 if (!memcmp( d->name, string, d->len ) &&
1310 (!string[d->len] || string[d->len] == '@'))
1312 if (string[d->len] == '@' && string[d->len + 1])
1313 *atPos = string + d->len + 1;
1314 return d->local;
1317 return -1;
1320 typedef struct serverEntry {
1321 struct serverEntry *next;
1322 const char *name, *class2, *console, *argvs, *arglvs;
1323 StrList *argv, *arglv;
1324 int local, reserve, vt;
1325 } ServerEntry;
1327 static void
1328 absorbXservers( const char *sect ATTR_UNUSED, char **value )
1330 ServerEntry *se, *se1, *serverList, **serverPtr;
1331 const char *word, *word2;
1332 char *sdpys, *rdpys;
1333 StrList **argp, **arglp, *ap, *ap2;
1334 File file;
1335 int nldpys = 0, nrdpys = 0, dpymask = 0;
1336 int cpcmd, cpcmdl;
1337 #ifdef HAVE_VTS
1338 int dn, cpvt, mtty;
1339 #endif
1341 if (**value == '/') {
1342 if (!readFile( &file, *value ))
1343 return;
1344 usedFile( *value );
1345 } else {
1346 file.buf = *value;
1347 file.eof = *value + strlen( *value );
1349 file.cur = file.buf;
1351 serverPtr = &serverList;
1352 #ifdef HAVE_VTS
1353 bustd:
1354 #endif
1355 while ((word = readWord( &file, 0 ))) {
1356 se = mcalloc( sizeof(*se) );
1357 se->name = word;
1358 if (!(word = readWord( &file, 1 )))
1359 continue;
1360 se->local = parseDisplayType( word, &se->console );
1361 if (se->local < 0) {
1362 se->class2 = word;
1363 if (!(word = readWord( &file, 1 )))
1364 continue;
1365 se->local = parseDisplayType( word, &se->console );
1366 if (se->local < 0) {
1367 while (readWord( &file, 1 ));
1368 continue;
1371 word = readWord( &file, 1 );
1372 if (word && !strcmp( word, "reserve" )) {
1373 se->reserve = True;
1374 word = readWord( &file, 1 );
1376 if (se->local != (word != 0))
1377 continue;
1378 argp = &se->argv;
1379 arglp = &se->arglv;
1380 while (word) {
1381 #ifdef HAVE_VTS
1382 if (word[0] == 'v' && word[1] == 't')
1383 se->vt = atoi( word + 2 );
1384 else if (!strcmp( word, "-crt" )) { /* SCO style */
1385 if (!(word = readWord( &file, 1 )) ||
1386 memcmp( word, "/dev/tty", 8 ))
1387 goto bustd;
1388 se->vt = atoi( word + 8 );
1389 } else
1390 #endif
1391 if (strcmp( word, se->name )) {
1392 ap = mmalloc( sizeof(*ap) );
1393 ap->str = word;
1394 if (!strcmp( word, "-nolisten" )) {
1395 if (!(word2 = readWord( &file, 1 )))
1396 break;
1397 ap2 = mmalloc( sizeof(*ap2) );
1398 ap2->str = word2;
1399 ap->next = ap2;
1400 if (!strcmp( word2, "unix" )) {
1401 *argp = ap;
1402 argp = &ap2->next;
1403 } else {
1404 *arglp = ap;
1405 arglp = &ap2->next;
1407 } else {
1408 *argp = ap;
1409 argp = &ap->next;
1412 word = readWord( &file, 1 );
1414 *argp = *arglp = 0;
1415 if (se->local) {
1416 nldpys++;
1417 dpymask |= 1 << atoi( se->name + 1 );
1418 if (se->reserve)
1419 nrdpys++;
1421 *serverPtr = se;
1422 serverPtr = &se->next;
1424 *serverPtr = 0;
1426 #ifdef HAVE_VTS
1427 /* don't copy only if all local displays are ordered and have a vt */
1428 cpvt = False;
1429 getInitTab();
1430 for (se = serverList, mtty = maxTTY; se; se = se->next)
1431 if (se->local) {
1432 mtty++;
1433 if (se->vt != mtty) {
1434 cpvt = True;
1435 break;
1438 #endif
1440 for (se = serverList; se; se = se->next) {
1441 se->argvs = joinArgs( se->argv );
1442 se->arglvs = joinArgs( se->arglv );
1445 se1 = 0, cpcmd = cpcmdl = False;
1446 for (se = serverList; se; se = se->next)
1447 if (se->local) {
1448 if (!se1)
1449 se1 = se;
1450 else {
1451 if (strcmp( se1->argvs, se->argvs ))
1452 cpcmd = True;
1453 if (strcmp( se1->arglvs, se->arglvs ))
1454 cpcmdl = True;
1457 if (se1) {
1458 putFqVal( "X-:*-Core", "ServerCmd", se1->argvs );
1459 putFqVal( "X-:*-Core", "ServerArgsLocal", se1->arglvs );
1460 for (se = serverList; se; se = se->next)
1461 if (se->local) {
1462 char sec[32];
1463 sprintf( sec, "X-%s-Core", se->name );
1464 if (cpcmd)
1465 putFqVal( sec, "ServerCmd", se->argvs );
1466 if (cpcmdl)
1467 putFqVal( sec, "ServerArgsLocal", se->arglvs );
1468 #ifdef HAVE_VTS
1469 if (cpvt && se->vt) {
1470 char vt[8];
1471 sprintf( vt, "%d", se->vt );
1472 putFqVal( sec, "ServerVT", vt );
1474 #else
1475 if (se->console)
1476 putFqVal( sec, "ServerTTY", se->console );
1477 #endif
1481 sdpys = rdpys = 0;
1482 for (se = serverList; se; se = se->next)
1483 strCat( se->reserve ? &rdpys : &sdpys,
1484 se->class2 ? ",%s_%s" : ",%s", se->name, se->class2 );
1486 #ifdef HAVE_VTS
1487 /* add reserve dpys */
1488 if (nldpys < 4 && nldpys && !nrdpys)
1489 for (; nldpys < 4; nldpys++) {
1490 for (dn = 0; dpymask & (1 << dn); dn++);
1491 dpymask |= (1 << dn);
1492 strCat( &rdpys, ",:%d", dn );
1494 #endif
1496 putFqVal( "General", "StaticServers", sdpys ? sdpys + 1 : "" );
1497 putFqVal( "General", "ReserveServers", rdpys ? rdpys + 1 : "" );
1499 if (**value == '/' && inNewDir( *value ) && !use_destdir)
1500 displace( *value );
1503 #ifdef HAVE_VTS
1504 static void
1505 upd_servervts( Entry *ce, Section *cs ATTR_UNUSED )
1507 if (!ce->active) { /* there is only the Global one */
1508 #ifdef __linux__ /* XXX actually, sysvinit */
1509 getInitTab();
1510 ASPrintf( (char **)&ce->value, "-%d", maxTTY + 1 );
1511 ce->active = ce->written = True;
1512 #endif
1516 static void
1517 upd_consolettys( Entry *ce, Section *cs ATTR_UNUSED )
1519 if (!ce->active) { /* there is only the Global one */
1520 #ifdef __linux__ /* XXX actually, sysvinit */
1521 char *buf;
1522 int i;
1524 getInitTab();
1525 for (i = 0, buf = 0; i < 16; i++)
1526 if (TTYmask & (1 << i))
1527 strCat( &buf, ",tty%d", i + 1 );
1528 if (buf) {
1529 ce->value = buf + 1;
1530 ce->active = ce->written = True;
1532 #endif
1535 #endif
1537 static void
1538 upd_servercmd( Entry *ce, Section *cs ATTR_UNUSED )
1540 StrList *sa;
1541 FILE *fp;
1542 char *svr;
1543 char buf[20000];
1545 if (!ce->active || oldver >= 0x0204)
1546 return;
1547 if (!(sa = splitArgs( ce->value )))
1548 return;
1549 ASPrintf( &svr, "%s -help 2>&1", sa->str );
1550 if (!(fp = popen( svr, "r" )))
1551 return;
1552 buf[fread( buf, 1, sizeof(buf) - 1, fp )] = 0;
1553 pclose( fp );
1554 if (strstr( buf, "\n-br " ))
1555 addStr( &sa, "-br" );
1556 if (strstr( buf, "\n-novtswitch " ))
1557 addStr( &sa, "-novtswitch" );
1558 if (strstr( buf, "\n-quiet " ))
1559 addStr( &sa, "-quiet" );
1560 ce->value = joinArgs( sa );
1561 ce->written = True;
1564 #ifdef XDMCP
1565 static void
1566 cp_keyfile( Entry *ce, Section *cs ATTR_UNUSED )
1568 if (!ce->active) /* there is only the Global one */
1569 return;
1570 if (old_confs)
1571 linkFile( ce );
1572 else
1573 if (!copyFile( ce, 0600, 0 ))
1574 ce->active = False;
1577 static void
1578 mk_xaccess( Entry *ce, Section *cs ATTR_UNUSED )
1580 if (!ce->active) /* there is only the Global one */
1581 writeFile( def_Xaccess, 0644, def_xaccess );
1582 else if (old_confs)
1583 linkFile( ce );
1584 else
1585 copyFile( ce, 0644, 0 ); /* don't handle error, it will disable Xdmcp automatically */
1588 static void
1589 mk_willing( Entry *ce, Section *cs ATTR_UNUSED )
1591 const char *fname;
1593 if (!ce->active) /* there is only the Global one */
1594 goto dflt;
1595 else {
1596 if (!(fname = strchr( ce->value, '/' )))
1597 return; /* obviously in-line (or empty) */
1598 if (old_scripts || inNewDir( fname ))
1599 doLinkFile( fname );
1600 else {
1601 dflt:
1602 ce->value = KDMCONF "/Xwilling";
1603 ce->active = ce->written = True;
1604 writeFile( ce->value, 0755, def_willing );
1608 #endif
1611 static int
1612 edit_resources( File *file )
1614 // XXX remove any login*, chooser*, ... resources
1615 return False;
1619 static void
1620 cp_resources( Entry *ce, Section *cs ATTR_UNUSED )
1622 if (!ce->active) /* the X-*-Greeter one */
1623 return;
1624 if (old_confs)
1625 linkFile( ce );
1626 else
1627 if (!copyFile( ce, 0644, 0/*edit_resources*/ ))
1628 ce->active = False;
1631 static int
1632 delstr( File *fil, const char *pat )
1634 char *p, *pp, *bpp;
1635 const char *pap, *paap;
1637 *fil->eof = 0;
1638 for (p = fil->buf; *p; p++) {
1639 for (pp = p, pap = pat; ; ) {
1640 if (!*pap) {
1641 *p = '\n';
1642 memcpy( p + 1, pp, fil->eof - pp + 1 );
1643 fil->eof -= pp - p - 1;
1644 return True;
1645 } else if (!memcmp( pap, "*/", 2 )) {
1646 paap = pap += 2;
1647 while (!isspace( *pap ))
1648 pap++;
1649 if (*pp != '/')
1650 break;
1651 for (;;)
1652 for (bpp = ++pp; *pp != '/'; pp++)
1653 if (!*pp || isspace( *pp ))
1654 goto wbrk;
1655 wbrk:
1656 if ((pp - bpp != pap - paap) || memcmp( bpp, paap, pap - paap ))
1657 break;
1658 } else if (*pap == '\t') {
1659 pap++;
1660 while (*pp == ' ' || *pp == '\t')
1661 pp++;
1662 } else if (*pap == '[') {
1663 pap++;
1664 for (;;) {
1665 if (!*pap) {
1666 fprintf( stderr, "Internal error: unterminated char set\n" );
1667 exit( 1 );
1669 if (*pap == *pp) {
1670 while (*++pap != ']')
1671 if (!*pap) {
1672 fprintf( stderr, "Internal error: unterminated char set\n" );
1673 exit( 1 );
1675 pap++;
1676 pp++;
1677 break;
1679 if (*++pap == ']')
1680 goto no;
1682 } else {
1683 if (*pap == '\n')
1684 while (*pp == ' ' || *pp == '\t')
1685 pp++;
1686 if (*pap != *pp)
1687 break;
1688 pap++;
1689 pp++;
1692 no: ;
1694 return False;
1697 /* XXX
1698 the UseBackground voodoo will horribly fail, if multiple sections link
1699 to the same Xsetup file
1702 static int mod_usebg;
1704 static int
1705 edit_setup( File *file )
1707 int chg =
1708 delstr( file, "\n"
1709 "(\n"
1710 " PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n"
1711 " */kdmdesktop\t&\n"
1712 " echo $! >$PIDFILE\n"
1713 " wait $!\n"
1714 " rm $PIDFILE\n"
1715 ")\t&\n" ) |
1716 delstr( file, "\n"
1717 "*/kdmdesktop\t&\n" ) |
1718 delstr( file, "\n"
1719 "kdmdesktop\t&\n" ) |
1720 delstr( file, "\n"
1721 "kdmdesktop\n" );
1722 putVal( "UseBackground", chg ? "true" : "false" );
1723 return chg;
1726 static void
1727 mk_setup( Entry *ce, Section *cs )
1729 setSect( reSect( cs->name, "Greeter" ) );
1730 if (old_scripts || mixed_scripts) {
1731 if (mod_usebg && *ce->value)
1732 putVal( "UseBackground", "false" );
1733 linkFile( ce );
1734 } else {
1735 if (ce->active && inNewDir( ce->value )) {
1736 if (mod_usebg)
1737 copyFile( ce, 0755, edit_setup );
1738 else
1739 linkFile( ce );
1740 } else {
1741 ce->value = KDMCONF "/Xsetup";
1742 ce->active = ce->written = True;
1743 writeFile( ce->value, 0755, def_setup );
1748 static int
1749 edit_startup( File *file )
1751 int chg1 = False, chg2 = False;
1753 if (mod_usebg &&
1754 (delstr( file, "\n"
1755 "PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n"
1756 "if [[] -f $PIDFILE ] ; then\n"
1757 " kill `cat $PIDFILE`\n"
1758 "fi\n" ) ||
1759 delstr( file, "\n"
1760 "PIDFILE=/var/run/kdmdesktop-$DISPLAY.pid\n"
1761 "test -f $PIDFILE && kill `cat $PIDFILE`\n" )))
1762 chg1 = True;
1763 if (oldver < 0x0203) {
1764 chg2 =
1765 #ifdef _AIX
1766 delstr( file, "\n"
1767 "# We create a pseudodevice for finger. (host:0 becomes [kx]dm/host_0)\n" );
1768 "# Without it, finger errors out with \"Cannot stat /dev/host:0\".\n"
1769 "#\n"
1770 "if [[] -f /usr/lib/X11/xdm/sessreg ]; then\n"
1771 " devname=`echo $DISPLAY | /usr/bin/sed -e 's/[[]:\\.]/_/g' | /usr/bin/cut -c1-8`\n"
1772 " hostname=`echo $DISPLAY | /usr/bin/cut -d':' -f1`\n"
1773 "\n"
1774 " if [[] -z \"$devname\" ]; then\n"
1775 " devname=\"unknown\"\n"
1776 " fi\n"
1777 " if [[] ! -d /dev/[kx]dm ]; then\n"
1778 " /usr/bin/mkdir /dev/[kx]dm\n"
1779 " /usr/bin/chmod 755 /dev/[kx]dm\n"
1780 " fi\n"
1781 " /usr/bin/touch /dev/[kx]dm/$devname\n"
1782 " /usr/bin/chmod 644 /dev/[kx]dm/$devname\n"
1783 "\n"
1784 " if [[] -z \"$hostname\" ]; then\n"
1785 " exec /usr/lib/X11/xdm/sessreg -a -l [kx]dm/$devname $USER\n"
1786 " else\n"
1787 " exec /usr/lib/X11/xdm/sessreg -a -l [kx]dm/$devname -h $hostname $USER\n"
1788 " fi\n"
1789 "fi\n") |
1790 #else
1791 # ifdef BSD
1792 delstr( file, "\n"
1793 "exec sessreg -a -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) |
1794 # endif
1795 #endif /* _AIX */
1796 delstr( file, "\n"
1797 "exec sessreg -a -l $DISPLAY"
1798 #ifdef BSD
1799 " -x */Xservers"
1800 #endif
1801 " $USER\n" ) |
1802 delstr( file, "\n"
1803 "exec sessreg -a -l $DISPLAY -u /var/run/utmp -x */Xservers $USER\n" );
1804 putVal( "UseSessReg", chg2 ? "true" : "false" );
1806 return chg1 | chg2;
1809 static void
1810 mk_startup( Entry *ce, Section *cs )
1812 setSect( cs->name );
1813 if (old_scripts || mixed_scripts)
1814 linkFile( ce );
1815 else {
1816 if (ce->active && inNewDir( ce->value )) {
1817 if (mod_usebg || oldver < 0x0203)
1818 copyFile( ce, 0755, edit_startup );
1819 else
1820 linkFile( ce );
1821 } else {
1822 ce->value = KDMCONF "/Xstartup";
1823 ce->active = ce->written = True;
1824 writeFile( ce->value, 0755, def_startup );
1829 static int
1830 edit_reset( File *file )
1832 return
1833 #ifdef _AIX
1834 delstr( file, "\n"
1835 "if [[] -f /usr/lib/X11/xdm/sessreg ]; then\n"
1836 " devname=`echo $DISPLAY | /usr/bin/sed -e 's/[[]:\\.]/_/g' | /usr/bin/cut -c1-8`\n"
1837 " exec /usr/lib/X11/xdm/sessreg -d -l [kx]dm/$devname $USER\n"
1838 "fi\n" ) |
1839 #else
1840 # ifdef BSD
1841 delstr( file, "\n"
1842 "exec sessreg -d -l $DISPLAY -x */Xservers -u " _PATH_UTMP " $USER\n" ) |
1843 # endif
1844 #endif /* _AIX */
1845 delstr( file, "\n"
1846 "exec sessreg -d -l $DISPLAY"
1847 # ifdef BSD
1848 " -x */Xservers"
1849 # endif
1850 " $USER\n" ) |
1851 delstr( file, "\n"
1852 "exec sessreg -d -l $DISPLAY -u /var/run/utmp -x */Xservers $USER\n" );
1855 static void
1856 mk_reset( Entry *ce, Section *cs ATTR_UNUSED )
1858 if (old_scripts || mixed_scripts)
1859 linkFile( ce );
1860 else {
1861 if (ce->active && inNewDir( ce->value )) {
1862 if (oldver < 0x0203)
1863 copyFile( ce, 0755, edit_reset );
1864 else
1865 linkFile( ce );
1866 } else {
1867 ce->value = KDMCONF "/Xreset";
1868 ce->active = ce->written = True;
1869 writeFile( ce->value, 0755, def_reset );
1874 static void
1875 mk_session( Entry *ce, Section *cs ATTR_UNUSED )
1877 char *def_session;
1878 const char *tmpf;
1880 if ((old_scripts || (ce->active && inNewDir( ce->value ))) &&
1881 oldver >= 0x202)
1882 linkFile( ce );
1883 else {
1884 tmpf = locate( "mktemp" ) ?
1885 "`mktemp /tmp/xsess-env-XXXXXX`" :
1886 locate( "tempfile" ) ?
1887 "`tempfile`" :
1888 "$HOME/.xsession-env-$DISPLAY";
1889 ASPrintf( &def_session, "%s%s%s", def_session1, tmpf, def_session2 );
1890 ce->value = KDMCONF "/Xsession";
1891 ce->active = ce->written = True;
1892 writeFile( ce->value, 0755, def_session );
1896 static void
1897 upd_language( Entry *ce, Section *cs ATTR_UNUSED )
1899 if (!strcmp( ce->value, "C" ))
1900 ce->value = (char *)"en_US";
1903 static void
1904 upd_guistyle( Entry *ce, Section *cs ATTR_UNUSED )
1906 if (!strcmp( ce->value, "Motif+" ))
1907 ce->value = (char *)"MotifPlus";
1908 else if (!strcmp( ce->value, "KDE" ))
1909 ce->value = (char *)"Default";
1912 static void
1913 upd_showusers( Entry *ce, Section *cs )
1915 if (!strcmp( ce->value, "All" ))
1916 ce->value = (char *)"NotHidden";
1917 else if (!strcmp( ce->value, "None" )) {
1918 if (ce->active)
1919 putFqVal( cs->name, "UserList", "false" );
1920 ce->value = (char *)"Selected";
1921 ce->active = False;
1922 ce->written = True;
1926 static const char *defminuid, *defmaxuid;
1928 static void
1929 upd_minshowuid( Entry *ce, Section *cs ATTR_UNUSED )
1931 if (!ce->active) {
1932 ce->value = defminuid;
1933 ce->active = ce->written = True;
1937 static void
1938 upd_maxshowuid( Entry *ce, Section *cs ATTR_UNUSED )
1940 if (!ce->active) {
1941 ce->value = defmaxuid;
1942 ce->active = ce->written = True;
1946 static void
1947 upd_hiddenusers( Entry *ce, Section *cs ATTR_UNUSED )
1949 char *nv;
1950 const char *msu, *pt, *et;
1951 struct passwd *pw;
1952 unsigned minuid, maxuid;
1953 char nbuf[128];
1955 if (!ce->active)
1956 return;
1958 msu = getFqVal( cs->name, "MinShowUID", "0" );
1959 sscanf( msu, "%u", &minuid );
1960 msu = getFqVal( cs->name, "MaxShowUID", "65535" );
1961 sscanf( msu, "%u", &maxuid );
1963 nv = 0;
1964 pt = ce->value;
1965 for (;;) {
1966 et = strpbrk( pt, ";," );
1967 if (et) {
1968 memcpy( nbuf, pt, et - pt );
1969 nbuf[et - pt] = 0;
1970 } else
1971 strcpy( nbuf, pt );
1972 if ((pw = getpwnam( nbuf ))) {
1973 if (!pw->pw_uid ||
1974 (pw->pw_uid >= minuid && pw->pw_uid <= maxuid))
1976 if (nv)
1977 strCat( &nv, ",%s", nbuf );
1978 else
1979 nv = mstrdup( nbuf );
1982 if (!et)
1983 break;
1984 pt = et + 1;
1986 ce->value = nv ? nv : "";
1989 static void
1990 upd_forgingseed( Entry *ce, Section *cs ATTR_UNUSED )
1992 if (!ce->active) {
1993 ASPrintf( (char **)&ce->value, "%d", time( 0 ) );
1994 ce->active = ce->written = True;
1998 static void
1999 upd_fifodir( Entry *ce, Section *cs ATTR_UNUSED )
2001 const char *dir;
2002 struct stat st;
2004 if (use_destdir)
2005 return;
2006 dir = ce->active ? ce->value : def_FifoDir;
2007 stat( dir, &st );
2008 chmod( dir, st.st_mode | 0755 );
2011 static void
2012 upd_datadir( Entry *ce, Section *cs ATTR_UNUSED )
2014 char *oldsts, *newsts;
2015 const char *dir;
2017 if (use_destdir)
2018 return;
2019 dir = ce->active ? ce->value : def_DataDir;
2020 if (mkdirp( dir, 0755, "data", 0 ) && oldkde) {
2021 ASPrintf( &oldsts, "%s/kdm/kdmsts", oldkde );
2022 ASPrintf( &newsts, "%s/kdmsts", dir );
2023 rename( oldsts, newsts );
2027 static void
2028 upd_userlogfile( Entry *ce, Section *cs ATTR_UNUSED )
2030 char *p;
2032 if ((p = strstr( ce->value, "%s" )))
2033 ASPrintf( (char **)&ce->value, "%.*s%%d%s", p - ce->value, ce->value, p + 2 );
2037 * Copy single file.
2038 * Do not overwrite existing target.
2039 * Do not complain if source cannot be read.
2041 static void
2042 copyPlainFile( const char *from, const char *to )
2044 File file;
2045 int fd;
2047 if (readFile( &file, from )) {
2048 if ((fd = open( to, O_WRONLY | O_CREAT | O_EXCL, 0644 )) >= 0) {
2049 write( fd, file.buf, file.eof - file.buf );
2050 close( fd );
2051 } else if (errno != EEXIST)
2052 fprintf( stderr, "Warning: cannot create %s\n", to );
2053 freeBuf( &file );
2057 static int
2058 copyDir( const char *from, const char *to )
2060 DIR *dir;
2061 struct dirent *ent;
2062 struct stat st;
2063 char bn[PATH_MAX], bo[PATH_MAX];
2065 if (!(dir = opendir( from )))
2066 return False;
2067 while ((ent = readdir( dir ))) {
2068 if (!strcmp( ent->d_name, "." ) || !strcmp( ent->d_name, ".." ))
2069 continue;
2070 sprintf( bo, "%s/%s", from, ent->d_name );
2071 if (stat( bo, &st ) || !S_ISREG( st.st_mode ))
2072 continue;
2073 sprintf( bn, "%s/%s", to, ent->d_name );
2074 copyPlainFile( bo, bn );
2076 closedir( dir );
2077 return True;
2080 static void
2081 upd_facedir( Entry *ce, Section *cs ATTR_UNUSED )
2083 char *oldpic, *newpic, *olddir;
2084 struct passwd *pw;
2086 if (use_destdir)
2087 return;
2088 if (oldkdepfx) { /* Do we have a previous install? */
2089 /* This would be the prev install's default location */
2090 ASPrintf( &olddir, "%s/share/apps/kdm/faces", oldkdepfx );
2091 if (ce->active && strcmp( olddir, ce->value ))
2092 /* Not default location, so don't touch the setting. */
2093 return;
2094 /* Default location, so absorb it. */
2095 ce->active = False;
2096 /* Don't copy if old dir == new new. */
2097 if (!strcmp( olddir, def_FaceDir ))
2098 olddir = 0;
2099 } else
2100 olddir = 0;
2101 if (mkdirp( def_FaceDir, 0755, "user face", True )) {
2102 const char *defpic = def_FaceDir "/.default.face.icon";
2103 const char *rootpic = def_FaceDir "/root.face.icon";
2104 if (oldkde && (!olddir || !copyDir( olddir, def_FaceDir )) &&
2105 oldver < 0x0201) /* This isn't exact - didn't inc version. */
2107 setpwent();
2108 while ((pw = getpwent()))
2109 if (strcmp( pw->pw_name, "root" )) {
2110 ASPrintf( &oldpic, "%s/share/apps/kdm/pics/users/%s.png",
2111 oldkdepfx, pw->pw_name );
2112 ASPrintf( &newpic, def_FaceDir "/%s.face.icon", pw->pw_name );
2113 rename( oldpic, newpic );
2114 free( newpic );
2115 free( oldpic );
2117 endpwent();
2118 ASPrintf( &oldpic, "%s/share/apps/kdm/pics/users/default.png", oldkdepfx );
2119 if (!rename( oldpic, defpic ))
2120 defpic = 0;
2121 ASPrintf( &oldpic, "%s/share/apps/kdm/pics/users/root.png", oldkdepfx );
2122 if (!rename( oldpic, rootpic ))
2123 rootpic = 0;
2125 if (defpic) {
2126 ASPrintf( &oldpic, "%s/default1.png", facesrc );
2127 copyPlainFile( oldpic, defpic );
2129 if (rootpic) {
2130 ASPrintf( &oldpic, "%s/root1.png", facesrc );
2131 copyPlainFile( oldpic, rootpic );
2136 static void
2137 upd_sessionsdirs( Entry *ce, Section *cs ATTR_UNUSED )
2139 StrList *sl, *sp;
2140 int olen;
2141 char olddir[PATH_MAX];
2143 if (ce->written) {
2144 sprintf( olddir, "%s/share/apps/kdm/sessions", oldkdepfx );
2145 olen = strlen( oldkde );
2146 sl = splitList( ce->value );
2147 for (sp = sl; sp; sp = sp->next) {
2148 if (!strcmp( sp->str, olddir ))
2149 sp->str = def_SessionsDirs;
2150 else if (!memcmp( sp->str, oldkde, olen ) &&
2151 !memcmp( sp->str + olen, "/kdm/", 5 ))
2153 char nd[PATH_MAX];
2154 sprintf( nd, "%s%s", newdir, sp->str + olen + 4 );
2155 mkdirp( nd, 0755, "sessions", False );
2156 copyDir( sp->str, nd );
2157 ASPrintf( (char **)&sp->str, KDMCONF "%s", sp->str + olen + 4 );
2160 ce->value = joinList( sl );
2161 } else {
2162 char nd[PATH_MAX];
2163 sprintf( nd, "%s/sessions", newdir );
2164 mkdirp( nd, 0755, "sessions", False );
2168 static void
2169 upd_preloader( Entry *ce, Section *cs ATTR_UNUSED )
2171 if (ce->written) { /* implies oldkde != 0 */
2172 char *oldpl;
2173 ASPrintf( &oldpl, "%s/bin/preloadkde", oldkdepfx );
2174 if (!strcmp( ce->value, oldpl ))
2175 ce->value = (char *)KDE_BINDIR "/preloadkde";
2176 free( oldpl );
2181 CONF_GEN_ENTRIES
2183 static Sect *
2184 findSect( const char *name )
2186 const char *p;
2187 int i;
2189 p = strrchr( name, '-' );
2190 if (!p)
2191 p = name;
2192 for (i = 0; i < as(allSects); i++)
2193 if (!strcmp( allSects[i]->name, p ))
2194 return allSects[i];
2195 fprintf( stderr, "Internal error: unknown section %s\n", name );
2196 exit( 1 );
2199 static Ent *
2200 findEnt( Sect *sect, const char *key )
2202 int i;
2204 for (i = 0; i < sect->nents; i++)
2205 if (!strcmp( sect->ents[i].key, key ))
2206 return sect->ents + i;
2207 fprintf( stderr, "Internal error: unknown key %s in section %s\n",
2208 key, sect->name );
2209 exit( 1 );
2214 * defaults
2217 typedef struct DEnt {
2218 const char *key;
2219 const char *value;
2220 int active;
2221 } DEnt;
2223 typedef struct DSect {
2224 const char *name;
2225 DEnt *ents;
2226 int nents;
2227 const char *comment;
2228 } DSect;
2230 CONF_GEN_EXAMPLE
2232 static void
2233 makeDefaultConfig( void )
2235 Section *cs, **csp;
2236 Entry *ce, **cep;
2237 int sc, ec;
2239 for (csp = &config, sc = 0; sc < as(dAllSects); csp = &(cs->next), sc++) {
2240 cs = mcalloc( sizeof(*cs) );
2241 *csp = cs;
2242 cs->spec = findSect( dAllSects[sc].name );
2243 cs->name = dAllSects[sc].name;
2244 cs->comment = dAllSects[sc].comment;
2245 for (cep = &(cs->ents), ec = 0; ec < dAllSects[sc].nents;
2246 cep = &(ce->next), ec++)
2248 ce = mcalloc( sizeof(*ce) );
2249 *cep = ce;
2250 ce->spec = findEnt( cs->spec, dAllSects[sc].ents[ec].key );
2251 ce->value = dAllSects[sc].ents[ec].value;
2252 ce->active = dAllSects[sc].ents[ec].active;
2259 * read rc file structure
2262 typedef struct REntry {
2263 struct REntry *next;
2264 const char *key;
2265 char *value;
2266 } REntry;
2268 typedef struct RSection {
2269 struct RSection *next;
2270 const char *name;
2271 REntry *ents;
2272 } RSection;
2274 static RSection *
2275 readConfig( const char *fname )
2277 char *nstr;
2278 char *s, *e, *st, *en, *ek, *sl;
2279 RSection *rootsec = 0, *cursec;
2280 REntry *curent;
2281 int nlen;
2282 int line, sectmoan;
2283 File file;
2285 if (!readFile( &file, fname ))
2286 return 0;
2287 usedFile( fname );
2289 for (s = file.buf, line = 0, cursec = 0, sectmoan = 1; s < file.eof; s++) {
2290 line++;
2292 while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
2293 s++;
2295 if ((s < file.eof) && ((*s == '\n') || (*s == '#'))) {
2296 sktoeol:
2297 while ((s < file.eof) && (*s != '\n'))
2298 s++;
2299 continue;
2301 sl = s;
2303 if (*s == '[') {
2304 while ((s < file.eof) && (*s != '\n'))
2305 s++;
2306 e = s - 1;
2307 while ((e > sl) && isspace( *e ))
2308 e--;
2309 if (*e != ']') {
2310 fprintf( stderr, "Invalid section header at %s:%d\n",
2311 fname, line );
2312 continue;
2314 sectmoan = False;
2315 nstr = sl + 1;
2316 nlen = e - nstr;
2317 for (cursec = rootsec; cursec; cursec = cursec->next)
2318 if (!memcmp( nstr, cursec->name, nlen ) &&
2319 !cursec->name[nlen])
2321 #if 0 /* not our business ... */
2322 fprintf( stderr, "Warning: Multiple occurrences of section "
2323 "[%.*s] in %s. Consider merging them.\n",
2324 nlen, nstr, fname );
2325 #endif
2326 goto secfnd;
2328 cursec = mmalloc( sizeof(*cursec) );
2329 ASPrintf( (char **)&cursec->name, "%.*s", nlen, nstr );
2330 cursec->ents = 0;
2331 cursec->next = rootsec;
2332 rootsec = cursec;
2333 secfnd:
2334 continue;
2337 if (!cursec) {
2338 if (sectmoan) {
2339 sectmoan = False;
2340 fprintf( stderr, "Entry outside any section at %s:%d",
2341 fname, line );
2343 goto sktoeol;
2346 for (; (s < file.eof) && (*s != '\n'); s++)
2347 if (*s == '=')
2348 goto haveeq;
2349 fprintf( stderr, "Invalid entry (missing '=') at %s:%d\n", fname, line );
2350 continue;
2352 haveeq:
2353 for (ek = s - 1;; ek--) {
2354 if (ek < sl) {
2355 fprintf( stderr, "Invalid entry (empty key) at %s:%d\n",
2356 fname, line );
2357 goto sktoeol;
2359 if (!isspace( *ek ))
2360 break;
2363 s++;
2364 while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
2365 s++;
2366 st = s;
2367 while ((s < file.eof) && (*s != '\n'))
2368 s++;
2369 for (en = s - 1; en >= st && isspace( *en ); en--);
2371 nstr = sl;
2372 nlen = ek - sl + 1;
2373 for (curent = cursec->ents; curent; curent = curent->next)
2374 if (!memcmp( nstr, curent->key, nlen ) &&
2375 !curent->key[nlen]) {
2376 fprintf( stderr, "Multiple occurrences of key '%s' in section "
2377 "[%s] of %s.\n", curent->key, cursec->name, fname );
2378 goto keyfnd;
2380 curent = mmalloc( sizeof(*curent) );
2381 ASPrintf( (char **)&curent->key, "%.*s", nlen, nstr );
2382 ASPrintf( (char **)&curent->value, "%.*s", en - st + 1, st );
2383 curent->next = cursec->ents;
2384 cursec->ents = curent;
2385 keyfnd:
2386 continue;
2388 return rootsec;
2392 static int
2393 mergeKdmRcOld( const char *path )
2395 char *p;
2396 struct stat st;
2398 ASPrintf( &p, "%s/kdmrc", path );
2399 if (stat( p, &st )) {
2400 free( p );
2401 return False;
2403 printf( "Information: ignoring pre-existing kdmrc %s from kde < 2.2\n", p );
2404 free( p );
2405 return True;
2408 typedef struct {
2409 const char *sect, *key, *def;
2410 int (*cond)( void );
2411 } FDefs;
2414 * The idea is to determine how exactly the pre-existing config would
2415 * have been interpreted, so no default configs are created where builtin
2416 * defaults were used so far.
2418 static void
2419 applyDefs( FDefs *chgdef, int ndefs, const char *path )
2421 char *p;
2422 int i;
2424 for (i = 0; i < ndefs; i++)
2425 if (!getFqVal( chgdef[i].sect, chgdef[i].key, 0 ) &&
2426 (!chgdef[i].cond || chgdef[i].cond()))
2428 ASPrintf( &p, chgdef[i].def, path );
2429 putFqVal( chgdef[i].sect, chgdef[i].key, p );
2430 free( p );
2434 static int
2435 if_usebg (void)
2437 return isTrue( getFqVal( "X-*-Greeter", "UseBackground", "true" ) );
2440 static FDefs kdmdefs_all[] = {
2441 #ifdef XDMCP
2442 { "Xdmcp", "Xaccess", "%s/kdm/Xaccess", 0 },
2443 { "Xdmcp", "Willing", "", 0 },
2444 #endif
2445 { "X-*-Core", "Setup", "", 0 },
2446 { "X-*-Core", "Startup", "", 0 },
2447 { "X-*-Core", "Reset", "", 0 },
2448 { "X-*-Core", "Session", XBINDIR "/xterm -ls -T", 0 },
2449 { "X-*-Greeter", "BackgroundCfg", "%s/kdm/backgroundrc", if_usebg },
2452 typedef struct KUpdEnt {
2453 const char *okey, *nsec, *nkey;
2454 void (*func)( const char *sect, char **value );
2455 } KUpdEnt;
2457 typedef struct KUpdSec {
2458 const char *osec;
2459 KUpdEnt *ents;
2460 int nents;
2461 } KUpdSec;
2463 #ifdef XDMCP
2464 static void
2465 P_EnableChooser( const char *sect ATTR_UNUSED, char **value )
2467 *value = (char *)(isTrue( *value ) ? "DefaultLocal" : "LocalOnly");
2469 #endif
2471 static void
2472 P_UseLilo( const char *sect ATTR_UNUSED, char **value )
2474 *value = (char *)(isTrue( *value ) ? "Lilo" : "None");
2477 static void
2478 P_EchoMode( const char *sect ATTR_UNUSED, char **value )
2480 *value = (char *)(!strcmp( *value, "NoEcho" ) ? "false" : "true");
2483 CONF_GEN_KMERGE
2485 static int
2486 mergeKdmRcNewer( const char *path, int obsRet )
2488 char *p;
2489 const char *cp, *sec, *key;
2490 RSection *rootsect, *cs;
2491 REntry *ce;
2492 int i, j, ma, mi;
2493 static char sname[64];
2495 ASPrintf( &p, "%s/kdm/kdmrc", path );
2496 if (!(rootsect = readConfig( p ))) {
2497 free( p );
2498 return False;
2500 for (cs = rootsect; cs; cs = cs->next)
2501 if (!strcmp( cs->name, "General" ))
2502 for (ce = cs->ents; ce; ce = ce->next)
2503 if (!strcmp( ce->key, "ConfigVersion" ))
2504 goto gotcfgv;
2505 printf( "Information: ignoring pre-existing kdmrc %s from kde < 3.1\n", p );
2506 free( p );
2507 return obsRet;
2508 gotcfgv:
2509 sscanf( ce->value, "%d.%d", &ma, &mi );
2510 oldver = (ma << 8) | mi;
2511 printf( "Information: reading pre-existing kdmrc %s (config version %d.%d)\n",
2512 p, ma, mi );
2513 free( p );
2515 for (cs = rootsect; cs; cs = cs->next) {
2516 cp = strrchr( cs->name, '-' );
2517 if (!cp)
2518 cp = cs->name;
2519 else if (cs->name[0] != 'X' || cs->name[1] != '-')
2520 goto dropsec;
2521 for (i = 0; i < as(kupsects); i++)
2522 if (!strcmp( cp, kupsects[i].osec )) {
2523 for (ce = cs->ents; ce; ce = ce->next) {
2524 for (j = 0; j < kupsects[i].nents; j++)
2525 if (!strcmp( ce->key, kupsects[i].ents[j].okey )) {
2526 if (kupsects[i].ents[j].nsec == (char *)-1) {
2527 kupsects[i].ents[j].func( 0, &ce->value );
2528 goto gotkey;
2530 if (!kupsects[i].ents[j].nsec)
2531 sec = cs->name;
2532 else {
2533 sec = sname;
2534 sprintf( sname, "%.*s-%s",
2535 (int)(cp - cs->name), cs->name,
2536 kupsects[i].ents[j].nsec );
2538 if (!kupsects[i].ents[j].nkey)
2539 key = ce->key;
2540 else
2541 key = kupsects[i].ents[j].nkey;
2542 if (kupsects[i].ents[j].func)
2543 kupsects[i].ents[j].func( sec, &ce->value );
2544 putFqVal( sec, key, ce->value );
2545 goto gotkey;
2547 printf( "Information: dropping key %s from section [%s]\n",
2548 ce->key, cs->name );
2549 gotkey: ;
2551 goto gotsec;
2553 dropsec:
2554 printf( "Information: dropping section [%s]\n", cs->name );
2555 gotsec: ;
2558 applyDefs( kdmdefs_all, as(kdmdefs_all), path );
2560 return True;
2564 typedef struct XResEnt {
2565 const char *xname;
2566 const char *ksec, *kname;
2567 void (*func)( const char *sect, char **value );
2568 } XResEnt;
2570 static void
2571 handleXdmVal( const char *dpy, const char *key, char *value,
2572 const XResEnt *ents, int nents )
2574 const char *kname;
2575 int i;
2576 char knameb[80], sname[80];
2578 for (i = 0; i < nents; i++)
2579 if (!strcmp( key, ents[i].xname ) ||
2580 (key[0] == toupper( ents[i].xname[0] ) &&
2581 !strcmp( key + 1, ents[i].xname + 1 )))
2583 if (ents[i].ksec == (char *)-1) {
2584 ents[i].func( 0, &value );
2585 break;
2587 sprintf( sname, ents[i].ksec, dpy );
2588 if (ents[i].kname)
2589 kname = ents[i].kname;
2590 else {
2591 kname = knameb;
2592 sprintf( knameb, "%c%s",
2593 toupper( ents[i].xname[0] ), ents[i].xname + 1 );
2595 if (ents[i].func)
2596 ents[i].func( sname, &value );
2597 putFqVal( sname, kname, value );
2598 break;
2602 static void
2603 P_list( const char *sect ATTR_UNUSED, char **value )
2605 int is, d, s;
2606 char *st;
2608 for (st = *value, is = False, d = s = 0; st[s]; s++)
2609 if (st[s] == ' ' || st[s] == '\t') {
2610 if (!is)
2611 st[d++] = ',';
2612 is = True;
2613 } else {
2614 st[d++] = st[s];
2615 is = False;
2617 st[d] = 0;
2620 static void
2621 P_authDir( const char *sect ATTR_UNUSED, char **value )
2623 int l;
2625 l = strlen( *value );
2626 if (l < 4) {
2627 *value = 0;
2628 return;
2630 if ((*value)[l-1] == '/')
2631 (*value)[--l] = 0;
2632 if (!strncmp( *value, "/tmp/", 5 ) ||
2633 !strncmp( *value, "/var/tmp/", 9 ))
2635 printf( "Warning: Resetting inappropriate value %s for AuthDir to default\n",
2636 *value );
2637 *value = 0;
2638 return;
2640 if ((l >= 4 && !strcmp( *value + l - 4, "/tmp" )) ||
2641 (l >= 6 && !strcmp( *value + l - 6, "/xauth" )) ||
2642 (l >= 8 && !strcmp( *value + l - 8, "/authdir" )) ||
2643 (l >= 10 && !strcmp( *value + l - 10, "/authfiles" )))
2644 return;
2645 ASPrintf( value, "%s/authdir", *value );
2648 static void
2649 P_openDelay( const char *sect, char **value )
2651 putFqVal( sect, "ServerTimeout", *value );
2654 static void
2655 P_noPassUsers( const char *sect, char **value ATTR_UNUSED )
2657 putFqVal( sect, "NoPassEnable", "true" );
2660 static void
2661 P_autoUser( const char *sect, char **value ATTR_UNUSED )
2663 putFqVal( sect, "AutoLoginEnable", "true" );
2666 #ifdef XDMCP
2667 static void
2668 P_requestPort( const char *sect, char **value )
2670 if (!strcmp( *value, "0" )) {
2671 *value = 0;
2672 putFqVal( sect, "Enable", "false" );
2673 } else
2674 putFqVal( sect, "Enable", "true" );
2676 #endif
2678 static int kdmrcmode = 0644;
2680 static void
2681 P_autoPass( const char *sect ATTR_UNUSED, char **value ATTR_UNUSED )
2683 kdmrcmode = 0600;
2686 CONF_GEN_XMERGE
2688 static XrmQuark XrmQString, empty = NULLQUARK;
2690 static Bool
2691 dumpEntry( XrmDatabase *db ATTR_UNUSED,
2692 XrmBindingList bindings,
2693 XrmQuarkList quarks,
2694 XrmRepresentation *type,
2695 XrmValuePtr value,
2696 XPointer data ATTR_UNUSED )
2698 const char *dpy, *key;
2699 int el, hasu;
2700 char dpybuf[80];
2702 if (*type != XrmQString)
2703 return False;
2704 if (*bindings == XrmBindLoosely ||
2705 strcmp( XrmQuarkToString (*quarks), "DisplayManager" ))
2706 return False;
2707 bindings++, quarks++;
2708 if (!*quarks)
2709 return False;
2710 if (*bindings != XrmBindLoosely && !quarks[1]) { /* DM.foo */
2711 key = XrmQuarkToString (*quarks);
2712 handleXdmVal( 0, key, value->addr, globents, as(globents) );
2713 return False;
2714 } else if (*bindings == XrmBindLoosely && !quarks[1]) { /* DM*bar */
2715 dpy = "*";
2716 key = XrmQuarkToString (*quarks);
2717 } else if (*bindings != XrmBindLoosely && quarks[1] &&
2718 *bindings != XrmBindLoosely && !quarks[2])
2719 { /* DM.foo.bar */
2720 dpy = dpybuf + 4;
2721 strcpy( dpybuf + 4, XrmQuarkToString (*quarks) );
2722 for (hasu = False, el = 4; dpybuf[el]; el++)
2723 if (dpybuf[el] == '_')
2724 hasu = True;
2725 if (!hasu/* && isupper (dpy[0])*/) {
2726 dpy = dpybuf;
2727 memcpy( dpybuf, "*:*_", 4 );
2728 } else {
2729 for (; --el >= 0; )
2730 if (dpybuf[el] == '_') {
2731 dpybuf[el] = ':';
2732 for (; --el >= 4; )
2733 if (dpybuf[el] == '_')
2734 dpybuf[el] = '.';
2735 break;
2738 key = XrmQuarkToString (quarks[1]);
2739 } else
2740 return False;
2741 handleXdmVal( dpy, key, value->addr, dpyents, as(dpyents) );
2742 return False;
2745 static FDefs xdmdefs[] = {
2746 #ifdef XDMCP
2747 { "Xdmcp", "Xaccess", "%s/Xaccess", 0 },
2748 { "Xdmcp", "Willing", "", 0 },
2749 #endif
2750 { "X-*-Core", "Setup", "", 0 },
2751 { "X-*-Core", "Startup", "", 0 },
2752 { "X-*-Core", "Reset", "", 0 },
2753 { "X-*-Core", "Session", "", 0 },
2756 static int
2757 mergeXdmCfg( const char *path )
2759 char *p;
2760 XrmDatabase db;
2762 ASPrintf( &p, "%s/xdm-config", path );
2763 if ((db = XrmGetFileDatabase( p ))) {
2764 printf( "Information: reading xdm config file %s\n", p );
2765 usedFile( p );
2766 free( p );
2767 XrmEnumerateDatabase( db, &empty, &empty, XrmEnumAllLevels,
2768 dumpEntry, (XPointer)0 );
2769 applyDefs( xdmdefs, as(xdmdefs), path );
2770 mod_usebg = True;
2771 return True;
2773 free( p );
2774 return False;
2777 static void
2778 fprintfLineWrap( FILE *f, const char *msg, ... )
2780 char *txt, *ftxt, *line;
2781 va_list ap;
2782 int col, lword, fspace;
2784 va_start( ap, msg );
2785 VASPrintf( &txt, msg, ap );
2786 va_end( ap );
2787 ftxt = 0;
2788 for (line = txt, col = 0, lword = fspace = -1; line[col]; ) {
2789 if (line[col] == '\n') {
2790 strCat( &ftxt, "%.*s", ++col, line );
2791 line += col;
2792 col = 0;
2793 lword = fspace = -1;
2794 continue;
2795 } else if (line[col] == ' ') {
2796 if (lword >= 0) {
2797 fspace = col;
2798 lword = -1;
2800 } else {
2801 if (lword < 0)
2802 lword = col;
2803 if (col >= 78 && fspace >= 0) {
2804 strCat( &ftxt, "%.*s\n", fspace, line );
2805 line += lword;
2806 col -= lword;
2807 lword = 0;
2808 fspace = -1;
2811 col++;
2813 free( txt );
2814 if (ftxt) {
2815 fputs( ftxt, f );
2816 free( ftxt );
2821 static const char *oldkdes[] = {
2822 KDE_CONFDIR,
2823 "/opt/kde4/share/config",
2824 "/usr/local/kde4/share/config",
2826 "/opt/kde/share/config",
2827 "/usr/local/kde/share/config",
2828 "/usr/local/share/config",
2829 "/usr/share/config",
2831 "/opt/kde3/share/config",
2832 "/usr/local/kde3/share/config",
2835 static const char *oldxdms[] = {
2836 "/etc/X11/xdm",
2837 XLIBDIR "/xdm",
2840 int main( int argc, char **argv )
2842 const char **where;
2843 FILE *f;
2844 StrList *fp;
2845 Section *cs;
2846 Entry *ce, **cep;
2847 int i, ap, locals, foreigns;
2848 int no_old_xdm = 0, no_old_kde = 0;
2849 struct stat st;
2851 for (ap = 1; ap < argc; ap++) {
2852 if (!strcmp( argv[ap], "--help" )) {
2853 printf(
2854 "genkdmconf - generate configuration files for kdm\n"
2855 "\n"
2856 "If an older xdm/kdm configuration is found, its config files are \"absorbed\";\n"
2857 "if it lives in the new target directory, its scripts are reused (and possibly\n"
2858 "modified) as well, otherwise the scripts are ignored and default scripts are\n"
2859 "installed.\n"
2860 "\n"
2861 "options:\n"
2862 " --in /path/to/new/kdm-config-dir\n"
2863 " In which directory to put the new configuration. You can use this\n"
2864 " to support a $(DESTDIR), but not to change the final location of\n"
2865 " the installation - the paths inside the files are not affected.\n"
2866 " Default is " KDMCONF ".\n"
2867 " --old-xdm /path/to/old/xdm-dir\n"
2868 " Where to look for the config files of an xdm.\n"
2869 " Default is to scan /etc/X11/xdm & $XLIBDIR/xdm.\n"
2870 " Note that you possibly need to use --no-old-kde to make this take effect.\n"
2871 " --old-kde /path/to/old/kde-config-dir\n"
2872 " Where to look for the kdmrc of a previously installed kdm.\n"
2873 " Default is to scan " KDE_CONFDIR " and\n"
2874 " {/usr,/usr/local,{/opt,/usr/local}/{kde4,kde,kde3}}/share/config.\n"
2875 " --no-old\n"
2876 " Do not look at older xdm/kdm configurations, just create default config.\n"
2877 " --no-old-xdm\n"
2878 " Do not look at older xdm configurations.\n"
2879 " --no-old-kde\n"
2880 " Do not look at older kdm configurations.\n"
2881 " --old-scripts\n"
2882 " Directly use all scripts from the older xdm/kdm configuration.\n"
2883 " --no-old-scripts\n"
2884 " Do not use scripts from the older xdm/kdm configuration even if it lives\n"
2885 " in the new target directory.\n"
2886 " --old-confs\n"
2887 " Directly use all ancillary config files from the older xdm/kdm\n"
2888 " configuration. This is usually a bad idea.\n"
2889 " --no-backup\n"
2890 " Overwrite/delete old config files instead of backing them up.\n"
2891 " --no-in-notice\n"
2892 " Do not put the notice about --in being used into the generated README.\n"
2894 exit( 0 );
2896 if (!strcmp( argv[ap], "--no-old" )) {
2897 no_old = True;
2898 continue;
2900 if (!strcmp( argv[ap], "--old-scripts" )) {
2901 old_scripts = True;
2902 continue;
2904 if (!strcmp( argv[ap], "--no-old-scripts" )) {
2905 no_old_scripts = True;
2906 continue;
2908 if (!strcmp( argv[ap], "--old-confs" )) {
2909 old_confs = True;
2910 continue;
2912 if (!strcmp( argv[ap], "--no-old-xdm" )) {
2913 no_old_xdm = True;
2914 continue;
2916 if (!strcmp( argv[ap], "--no-old-kde" )) {
2917 no_old_kde = True;
2918 continue;
2920 if (!strcmp( argv[ap], "--no-backup" )) {
2921 no_backup = True;
2922 continue;
2924 if (!strcmp( argv[ap], "--no-in-notice" )) {
2925 no_in_notice = True;
2926 continue;
2928 where = 0;
2929 if (!strcmp( argv[ap], "--in" ))
2930 where = &newdir;
2931 else if (!strcmp( argv[ap], "--old-xdm" ))
2932 where = &oldxdm;
2933 else if (!strcmp( argv[ap], "--old-kde" ))
2934 where = &oldkde;
2935 else if (!strcmp( argv[ap], "--face-src" ))
2936 where = &facesrc;
2937 else {
2938 fprintf( stderr, "Unknown command line option '%s', try --help\n", argv[ap] );
2939 exit( 1 );
2941 if (ap + 1 == argc || argv[ap + 1][0] == '-') {
2942 fprintf( stderr, "Missing argument to option '%s', try --help\n", argv[ap] );
2943 exit( 1 );
2945 *where = argv[++ap];
2947 if (memcmp( newdir, KDMCONF, sizeof(KDMCONF) ))
2948 use_destdir = True;
2950 if (!mkdirp( newdir, 0755, "target", True ))
2951 exit( 1 );
2953 makeDefaultConfig();
2954 if (no_old) {
2955 DIR *dir;
2956 StrList *bfl = 0;
2957 if ((dir = opendir( newdir ))) {
2958 struct dirent *ent;
2959 char bn[PATH_MAX];
2960 while ((ent = readdir( dir ))) {
2961 int l;
2962 if (!strcmp( ent->d_name, "." ) || !strcmp( ent->d_name, ".." ))
2963 continue;
2964 l = sprintf( bn, "%s/%s", newdir, ent->d_name ); /* cannot overflow (kernel would not allow the creation of a longer path) */
2965 if (!stat( bn, &st ) && !S_ISREG( st.st_mode ))
2966 continue;
2967 if (no_backup || !memcmp( bn + l - 4, ".bak", 5 ))
2968 unlink( bn );
2969 else
2970 addStr( &bfl, bn );
2972 closedir( dir );
2973 for (; bfl; bfl = bfl->next)
2974 displace( bfl->str );
2976 } else {
2977 if (oldkde) {
2978 if (!mergeKdmRcNewer( oldkde, True ) && !mergeKdmRcOld( oldkde )) {
2979 fprintf( stderr,
2980 "Cannot read pre-existing kdmrc at specified location\n" );
2981 oldkde = 0;
2983 } else if (!no_old_kde) {
2984 for (i = 0; i < as(oldkdes); i++) {
2985 if (i && !strcmp( oldkdes[0], oldkdes[i] ))
2986 continue;
2987 if (mergeKdmRcNewer( oldkdes[i], 0 )) {
2988 oldkde = oldkdes[i];
2989 break;
2991 mergeKdmRcOld( oldkdes[i] ); /* only prints a message */
2994 if (oldkde) {
2995 #define SHR_CONF "/share/config"
2996 int olen = strlen( oldkde );
2997 if (olen < (int)sizeof(SHR_CONF) ||
2998 memcmp( oldkde + olen - sizeof(SHR_CONF) + 1,
2999 SHR_CONF, sizeof(SHR_CONF) ))
3001 fprintf( stderr,
3002 "Warning: --old-kde does not end with " SHR_CONF ". "
3003 "Might wreak havoc.\n" );
3004 oldkdepfx = oldkde;
3005 } else
3006 ASPrintf( (char **)&oldkdepfx,
3007 "%.*s", olen - sizeof(SHR_CONF) + 1, oldkde );
3008 oldxdm = 0;
3009 } else if (!no_old_xdm) {
3010 XrmInitialize();
3011 XrmQString = XrmPermStringToQuark( "String" );
3012 if (oldxdm) {
3013 if (!mergeXdmCfg( oldxdm )) {
3014 fprintf( stderr,
3015 "Cannot read xdm-config at specified location\n" );
3016 oldxdm = 0;
3018 } else
3019 for (i = 0; i < as(oldxdms); i++)
3020 if (mergeXdmCfg( oldxdms[i] )) {
3021 oldxdm = oldxdms[i];
3022 break;
3027 * How to proceed with pre-existing scripts (which are named in the config):
3028 * - old_scripts set or some scripts in new target already => keep 'em
3029 * - no_old_scripts set or all scripts outside new target => pretend that
3030 * the old config did not reference them in the first place
3032 if (no_old_scripts)
3033 goto no_old_s;
3034 if (!old_scripts) {
3035 locals = foreigns = False;
3036 for (cs = config; cs; cs = cs->next)
3037 if (!strcmp( cs->spec->name, "-Core" )) {
3038 for (ce = cs->ents; ce; ce = ce->next)
3039 if (ce->active &&
3040 (!strcmp( ce->spec->key, "Setup" ) ||
3041 !strcmp( ce->spec->key, "Startup" ) ||
3042 !strcmp( ce->spec->key, "Reset" )))
3044 if (inNewDir( ce->value ))
3045 locals = True;
3046 else
3047 foreigns = True;
3050 if (foreigns) {
3051 if (locals) {
3052 fprintf( stderr,
3053 "Warning: both local and foreign scripts referenced. "
3054 "Will not touch any.\n" );
3055 mixed_scripts = True;
3056 } else {
3057 no_old_s:
3058 for (cs = config; cs; cs = cs->next) {
3059 if (!strcmp( cs->spec->name, "Xdmcp" )) {
3060 for (ce = cs->ents; ce; ce = ce->next)
3061 if (!strcmp( ce->spec->key, "Willing" ))
3062 ce->active = ce->written = False;
3063 } else if (!strcmp( cs->spec->name, "-Core" )) {
3064 for (cep = &cs->ents; (ce = *cep); ) {
3065 if (ce->active &&
3066 (!strcmp( ce->spec->key, "Setup" ) ||
3067 !strcmp( ce->spec->key, "Startup" ) ||
3068 !strcmp( ce->spec->key, "Reset" ) ||
3069 !strcmp( ce->spec->key, "Session" )))
3071 if (!memcmp( cs->name, "X-*-", 4 ))
3072 ce->active = ce->written = False;
3073 else {
3074 *cep = ce->next;
3075 free( ce );
3076 continue;
3079 cep = &ce->next;
3086 #ifdef __linux__
3087 if (!stat( "/etc/debian_version", &st )) { /* debian */
3088 defminuid = "1000";
3089 defmaxuid = "29999";
3090 } else if (!stat( "/usr/portage", &st )) { /* gentoo */
3091 defminuid = "1000";
3092 defmaxuid = "65000";
3093 } else if (!stat( "/etc/mandrake-release", &st )) { /* mandrake - check before redhat! */
3094 defminuid = "500";
3095 defmaxuid = "65000";
3096 } else if (!stat( "/etc/redhat-release", &st )) { /* redhat */
3097 defminuid = "100";
3098 defmaxuid = "65000";
3099 } else /* if (!stat( "/etc/SuSE-release", &st )) */ { /* suse */
3100 defminuid = "500";
3101 defmaxuid = "65000";
3103 #else
3104 defminuid = "1000";
3105 defmaxuid = "65000";
3106 #endif
3107 for (i = 0; i < CONF_MAX_PRIO; i++)
3108 for (cs = config; cs; cs = cs->next)
3109 for (ce = cs->ents; ce; ce = ce->next)
3110 if (ce->spec->func && i == ce->spec->prio)
3111 ce->spec->func( ce, cs );
3112 f = createFile( "kdmrc", kdmrcmode );
3113 writeKdmrc( f );
3114 fclose( f );
3116 f = createFile( "README", 0644 );
3117 fprintf( f,
3118 "This automatically generated configuration consists of the following files:\n" );
3119 fprintf( f, "- " KDMCONF "/kdmrc\n" );
3120 for (fp = aflist; fp; fp = fp->next)
3121 fprintf( f, "- %s\n", fp->str );
3122 if (use_destdir && !no_in_notice)
3123 fprintfLineWrap( f,
3124 "All files destined for " KDMCONF " were actually saved in %s; "
3125 "this config will not be workable until moved in place.\n", newdir );
3126 if (uflist || eflist || cflist || lflist) {
3127 fprintf( f,
3128 "\n"
3129 "This config was derived from existing files. As the used algorithms are\n"
3130 "pretty dumb, it may be broken.\n" );
3131 if (uflist) {
3132 fprintf( f,
3133 "Information from these files was extracted:\n" );
3134 for (fp = uflist; fp; fp = fp->next)
3135 fprintf( f, "- %s\n", fp->str );
3137 if (lflist) {
3138 fprintf( f,
3139 "These files were directly incorporated:\n" );
3140 for (fp = lflist; fp; fp = fp->next)
3141 fprintf( f, "- %s\n", fp->str );
3143 if (cflist) {
3144 fprintf( f,
3145 "These files were copied verbatim:\n" );
3146 for (fp = cflist; fp; fp = fp->next)
3147 fprintf( f, "- %s\n", fp->str );
3149 if (eflist) {
3150 fprintf( f,
3151 "These files were copied with modifications:\n" );
3152 for (fp = eflist; fp; fp = fp->next)
3153 fprintf( f, "- %s\n", fp->str );
3155 if (!no_backup && !use_destdir)
3156 fprintf( f,
3157 "Old files that would have been overwritten were renamed to <oldname>.bak.\n" );
3159 fprintf( f,
3160 "\nTry 'genkdmconf --help' if you want to generate another configuration.\n"
3161 "\nYou may delete this README.\n" );
3162 fclose( f );
3164 return 0;