Do not try to chflags() a symbolic link when copying an underlying filesytem
[dragonfly.git] / release / sysinstall / dist.c
1 /*
2  * The new sysinstall program.
3  *
4  * This is probably the last program in the `sysinstall' line - the next
5  * generation being essentially a complete rewrite.
6  *
7  * $FreeBSD: src/release/sysinstall/dist.c,v 1.175.2.31 2003/03/03 09:31:42 murray Exp $
8  * $DragonFly: src/release/sysinstall/Attic/dist.c,v 1.3 2003/08/05 07:45:42 asmodai Exp $
9  *
10  * Copyright (c) 1995
11  *      Jordan Hubbard.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer,
18  *    verbatim and that no modifications are made prior to this
19  *    point in the file.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  *
24  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  */
37
38 #include "sysinstall.h"
39 #include <sys/time.h>
40 #include <signal.h>
41 #include <libutil.h>
42
43 unsigned int Dists;
44 unsigned int CRYPTODists;
45 unsigned int SrcDists;
46 unsigned int XF86Dists;
47 unsigned int XF86ServerDists;
48 unsigned int XF86FontDists;
49
50 typedef struct _dist {
51     char *my_name;
52     char *my_dir;
53     unsigned int *my_mask;
54     unsigned int my_bit;
55     struct _dist *my_dist;
56 } Distribution;
57
58 extern Distribution DistTable[];
59 extern Distribution CRYPTODistTable[];
60 extern Distribution SrcDistTable[];
61 extern Distribution XF86DistTable[];
62 extern Distribution XF86FontDistTable[];
63 extern Distribution XF86ServerDistTable[];
64
65 /* The top-level distribution categories */
66 static Distribution DistTable[] = {
67 { "bin",        "/",                    &Dists,         DIST_BIN,               NULL            },
68 { "doc",        "/",                    &Dists,         DIST_DOC,               NULL            },
69 { "games",      "/",                    &Dists,         DIST_GAMES,             NULL            },
70 { "manpages",   "/",                    &Dists,         DIST_MANPAGES,          NULL            },
71 { "catpages",   "/",                    &Dists,         DIST_CATPAGES,          NULL            },
72 { "proflibs",   "/",                    &Dists,         DIST_PROFLIBS,          NULL            },
73 { "dict",       "/",                    &Dists,         DIST_DICT,              NULL            },
74 { "info",       "/",                    &Dists,         DIST_INFO,              NULL            },
75 { "src",        "/",                    &Dists,         DIST_SRC,               SrcDistTable    },
76 { "crypto",     "/",                    &Dists,         DIST_CRYPTO,            CRYPTODistTable },
77 #ifdef __i386__
78 { "compat1x",   "/",                    &Dists,         DIST_COMPAT1X,          NULL            },
79 { "compat20",   "/",                    &Dists,         DIST_COMPAT20,          NULL            },
80 { "compat21",   "/",                    &Dists,         DIST_COMPAT21,          NULL            },
81 { "compat22",   "/",                    &Dists,         DIST_COMPAT22,          NULL            },
82 { "compat3x",   "/",                    &Dists,         DIST_COMPAT3X,          NULL            },
83 { "compat4x",   "/",                    &Dists,         DIST_COMPAT4X,          NULL            },
84 #endif
85 { "ports",      "/usr",                 &Dists,         DIST_PORTS,             NULL            },
86 { "local",      "/",                    &Dists,         DIST_LOCAL,             NULL            },
87 { "XF86336",    "/usr",                 &Dists,         DIST_XF86,              XF86DistTable   },
88 { NULL },
89 };
90
91 /* The CRYPTO distribution */
92 static Distribution CRYPTODistTable[] = {
93 { "crypto",     "/",                    &CRYPTODists,   DIST_CRYPTO_CRYPTO,             NULL            },
94 { "krb5",       "/",                    &CRYPTODists,   DIST_CRYPTO_KERBEROS5,  NULL            },
95 { "ssecure",    "/usr/src",             &CRYPTODists,   DIST_CRYPTO_SSECURE,    NULL            },
96 { "scrypto",    "/usr/src",             &CRYPTODists,   DIST_CRYPTO_SCRYPTO,    NULL            },
97 { "skrb5",      "/usr/src",             &CRYPTODists,   DIST_CRYPTO_SKERBEROS5, NULL            },
98 { NULL },
99 };
100
101 /* The /usr/src distribution */
102 static Distribution SrcDistTable[] = {
103 { "sbase",      "/usr/src",             &SrcDists,      DIST_SRC_BASE,          NULL            },
104 { "scontrib",   "/usr/src",             &SrcDists,      DIST_SRC_CONTRIB,       NULL            },
105 { "sgnu",       "/usr/src",             &SrcDists,      DIST_SRC_GNU,           NULL            },
106 { "setc",       "/usr/src",             &SrcDists,      DIST_SRC_ETC,           NULL            },
107 { "sgames",     "/usr/src",             &SrcDists,      DIST_SRC_GAMES,         NULL            },
108 { "sinclude",   "/usr/src",             &SrcDists,      DIST_SRC_INCLUDE,       NULL            },
109 { "slib",       "/usr/src",             &SrcDists,      DIST_SRC_LIB,           NULL            },
110 { "slibexec",   "/usr/src",             &SrcDists,      DIST_SRC_LIBEXEC,       NULL            },
111 { "srelease",   "/usr/src",             &SrcDists,      DIST_SRC_RELEASE,       NULL            },
112 { "sbin",       "/usr/src",             &SrcDists,      DIST_SRC_BIN,           NULL            },
113 { "ssbin",      "/usr/src",             &SrcDists,      DIST_SRC_SBIN,          NULL            },
114 { "sshare",     "/usr/src",             &SrcDists,      DIST_SRC_SHARE,         NULL            },
115 { "ssys",       "/usr/src",             &SrcDists,      DIST_SRC_SYS,           NULL            },
116 { "subin",      "/usr/src",             &SrcDists,      DIST_SRC_UBIN,          NULL            },
117 { "susbin",     "/usr/src",             &SrcDists,      DIST_SRC_USBIN,         NULL            },
118 { "stools",     "/usr/src",             &SrcDists,      DIST_SRC_TOOLS,         NULL            },
119 { NULL },
120 };
121
122 /* The XFree86 distribution */
123 static Distribution XF86DistTable[] = {
124 { "XF86336",    "/usr/X11R6",           &XF86Dists,     DIST_XF86_FONTS,        XF86FontDistTable },
125 { "XF86336",    "/usr/X11R6",           &XF86Dists,     DIST_XF86_SERVER,       XF86ServerDistTable },
126 { "Xbin",       "/usr/X11R6",           &XF86Dists,     DIST_XF86_BIN,          NULL            },
127 { "Xcfg",       "/usr/X11R6",           &XF86Dists,     DIST_XF86_CFG,          NULL            },
128 { "Xdoc",       "/usr/X11R6",           &XF86Dists,     DIST_XF86_DOC,          NULL            },
129 { "Xhtml",      "/usr/X11R6",           &XF86Dists,     DIST_XF86_HTML,         NULL            },
130 { "Xlib",       "/usr/X11R6",           &XF86Dists,     DIST_XF86_LIB,          NULL            },
131 #if defined(__i386__) && defined(PC98)
132 { "Xlk98",      "/usr/X11R6",           &XF86Dists,     DIST_XF86_LKIT98,       NULL            },
133 #endif
134 { "Xlkit",      "/usr/X11R6",           &XF86Dists,     DIST_XF86_LKIT,         NULL            },
135 { "Xman",       "/usr/X11R6",           &XF86Dists,     DIST_XF86_MAN,          NULL            },
136 { "Xprog",      "/usr/X11R6",           &XF86Dists,     DIST_XF86_PROG,         NULL            },
137 { "Xps",        "/usr/X11R6",           &XF86Dists,     DIST_XF86_PS,           NULL            },
138 { "Xset",       "/usr/X11R6",           &XF86Dists,     DIST_XF86_SET,          NULL            },
139 #if defined(__i386__) && defined(PC98)
140 { "X9set",      "/usr/X11R6",           &XF86Dists,     DIST_XF86_9SET,         NULL            },
141 #endif
142 { NULL },
143 };
144
145 /* The XFree86 server distribution */
146 static Distribution XF86ServerDistTable[] = {
147 #if defined(__i386__) && defined(PC98)
148 { "PC98-Servers/X9480", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9480,  NULL            },
149 { "PC98-Servers/X9EGC", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9EGC,  NULL            },
150 { "PC98-Servers/X9GA9", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9GA9,  NULL            },
151 { "PC98-Servers/X9GAN", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9GAN,  NULL            },
152 { "PC98-Servers/X9LPW", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9LPW,  NULL            },
153 { "PC98-Servers/X9MGA", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9MGA,  NULL            },
154 { "PC98-Servers/X9NKV", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9NKV,  NULL            },
155 { "PC98-Servers/X9NS3", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9NS3,  NULL            },
156 { "PC98-Servers/X9SPW", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9SPW,  NULL            },
157 { "PC98-Servers/X9SVG", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9SVG,  NULL            },
158 { "PC98-Servers/X9TGU", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9TGU,  NULL            },
159 { "PC98-Servers/X9WEP", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9WEP,  NULL            },
160 { "PC98-Servers/X9WS",  "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9WS,   NULL            },
161 { "PC98-Servers/X9WSN", "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_9WSN,  NULL            },
162 #endif
163 { "Servers/X3DL",       "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_3DL,   NULL            },
164 #ifdef __i386__
165 { "Servers/X8514",      "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_8514,  NULL            },
166 { "Servers/XAGX",       "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_AGX,   NULL            },
167 #endif
168 { "Servers/XI128",      "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_I128,  NULL            },
169 #ifdef __i386__
170 { "Servers/XMa8",       "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_MACH8, NULL            },
171 { "Servers/XMa32",      "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_MACH32,NULL            },
172 #endif
173 { "Servers/XMa64",      "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_MACH64,NULL            },
174 { "Servers/XMono",      "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_MONO,  NULL            },
175 { "Servers/XP9K",       "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_P9000, NULL            },
176 { "Servers/XS3",        "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_S3,    NULL            },
177 { "Servers/XS3V",       "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_S3V,   NULL            },
178 { "Servers/XSVGA",      "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_SVGA,  NULL            },
179 #ifdef __i386__
180 { "Servers/XVG16",      "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_VGA16, NULL            },
181 { "Servers/XW32",       "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_W32,   NULL            },
182 #endif
183 #ifdef __alpha__
184 { "Servers/XTGA",       "/usr/X11R6",           &XF86ServerDists,       DIST_XF86_SERVER_TGA,   NULL            },
185 #endif
186 { NULL },
187 };
188
189 /* The XFree86 font distribution */
190 static Distribution XF86FontDistTable[] = {
191 { "Xfnts",      "/usr/X11R6",           &XF86FontDists,         DIST_XF86_FONTS_MISC,   NULL            },
192 { "Xf100",      "/usr/X11R6",           &XF86FontDists,         DIST_XF86_FONTS_100,    NULL            },
193 { "Xfcyr",      "/usr/X11R6",           &XF86FontDists,         DIST_XF86_FONTS_CYR,    NULL            },
194 { "Xfscl",      "/usr/X11R6",           &XF86FontDists,         DIST_XF86_FONTS_SCALE,  NULL            },
195 { "Xfnon",      "/usr/X11R6",           &XF86FontDists,         DIST_XF86_FONTS_NON,    NULL            },
196 { "Xfsrv",      "/usr/X11R6",           &XF86FontDists,         DIST_XF86_FONTS_SERVER, NULL            },
197 { NULL },
198 };
199
200 static int      distMaybeSetPorts(dialogMenuItem *self);
201
202 static void
203 distVerifyFlags(void)
204 {
205     if (SrcDists)
206         Dists |= DIST_SRC;
207     if (CRYPTODists) {
208         if (CRYPTODists & DIST_CRYPTO_KERBEROS5)
209             CRYPTODists |= DIST_CRYPTO_CRYPTO;
210         Dists |= DIST_CRYPTO;
211     }
212     else if ((Dists & DIST_CRYPTO) && !CRYPTODists)
213         CRYPTODists |= DIST_CRYPTO_ALL;
214     if (XF86Dists & DIST_XF86_SET)
215         XF86ServerDists |= DIST_XF86_SERVER_VGA16;
216     if (XF86ServerDists)
217         XF86Dists |= DIST_XF86_SERVER;
218     if (XF86FontDists)
219         XF86Dists |= DIST_XF86_FONTS;
220     if (XF86Dists || XF86ServerDists || XF86FontDists) {
221         Dists |= DIST_XF86;
222 #ifdef __i386__
223         Dists |= DIST_COMPAT22; /* For certain old X applications */
224 #endif
225     }
226     if (isDebug())
227         msgDebug("Dist Masks: Dists: %0x, CRYPTO: %0x, Srcs: %0x\nXServer: %0x, XFonts: %0x, XDists: %0x\n",
228                  Dists, CRYPTODists, SrcDists, XF86ServerDists, XF86FontDists, XF86Dists);
229 }
230
231 int
232 distReset(dialogMenuItem *self)
233 {
234     Dists = 0;
235     CRYPTODists = 0;
236     SrcDists = 0;
237     XF86Dists = 0;
238     XF86ServerDists = 0;
239     XF86FontDists = 0;
240     return DITEM_SUCCESS | DITEM_REDRAW;
241 }
242
243 int
244 distConfig(dialogMenuItem *self)
245 {
246     char *cp;
247
248     distReset(NULL);
249
250     if ((cp = variable_get(VAR_DIST_MAIN)) != NULL)
251         Dists = atoi(cp);
252
253     if ((cp = variable_get(VAR_DIST_CRYPTO)) != NULL)
254         CRYPTODists = atoi(cp);
255
256     if ((cp = variable_get(VAR_DIST_SRC)) != NULL)
257         SrcDists = atoi(cp);
258
259     if ((cp = variable_get(VAR_DIST_X11)) != NULL)
260         XF86Dists = atoi(cp);
261
262     if ((cp = variable_get(VAR_DIST_XSERVER)) != NULL)
263         XF86ServerDists = atoi(cp);
264
265     if ((cp = variable_get(VAR_DIST_XFONTS)) != NULL)
266         XF86FontDists = atoi(cp);
267     distVerifyFlags();
268     return DITEM_SUCCESS | DITEM_REDRAW;
269 }
270
271 static int
272 distSetX(void)
273 {
274     Dists |= DIST_XF86;
275     XF86Dists = DIST_XF86_BIN | DIST_XF86_SET | DIST_XF86_CFG | DIST_XF86_LIB | DIST_XF86_PROG | DIST_XF86_MAN | DIST_XF86_DOC | DIST_XF86_SERVER | DIST_XF86_FONTS;
276     XF86ServerDists = DIST_XF86_SERVER_SVGA | DIST_XF86_SERVER_VGA16;
277     XF86FontDists = DIST_XF86_FONTS_MISC;
278 #ifndef X_AS_PKG
279     return distSetXF86(NULL);
280 #endif
281     return DITEM_SUCCESS;
282 }
283
284 int
285 distSetDeveloper(dialogMenuItem *self)
286 {
287     int i;
288
289     distReset(NULL);
290     Dists = _DIST_DEVELOPER;
291     SrcDists = DIST_SRC_ALL;
292     CRYPTODists = DIST_CRYPTO_ALL;
293     i = distMaybeSetPorts(self);
294     distVerifyFlags();
295     return i;
296 }
297
298 int
299 distSetXDeveloper(dialogMenuItem *self)
300 {
301     int i;
302
303     i = distSetDeveloper(self);
304     i |= distSetX();
305     distVerifyFlags();
306     return i;
307 }
308
309 int
310 distSetKernDeveloper(dialogMenuItem *self)
311 {
312     int i;
313
314     distReset(NULL);
315     Dists = _DIST_DEVELOPER;
316     SrcDists = DIST_SRC_SYS;
317     CRYPTODists |= DIST_CRYPTO_BIN;
318     i = distMaybeSetPorts(self);
319     distVerifyFlags();
320     return i;
321 }
322
323 int
324 distSetXKernDeveloper(dialogMenuItem *self)
325 {
326     int i;
327
328     i = distSetKernDeveloper(self);
329     i |= distSetX();
330     distVerifyFlags();
331     return i;
332 }
333
334 int
335 distSetUser(dialogMenuItem *self)
336 {
337     int i;
338
339     distReset(NULL);
340     Dists = _DIST_USER;
341     CRYPTODists |= DIST_CRYPTO_CRYPTO;
342     i = distMaybeSetPorts(self);
343     distVerifyFlags();
344     return i;
345 }
346
347 int
348 distSetXUser(dialogMenuItem *self)
349 {
350     int i;
351
352     i = distSetUser(self);
353     i |= distSetX();
354     distVerifyFlags();
355     return i;
356 }
357
358 int
359 distSetMinimum(dialogMenuItem *self)
360 {
361     distReset(NULL);
362     Dists = DIST_BIN | DIST_CRYPTO;
363     CRYPTODists |= DIST_CRYPTO_CRYPTO;
364     distVerifyFlags();
365     return DITEM_SUCCESS | DITEM_REDRAW;
366 }
367
368 int
369 distSetEverything(dialogMenuItem *self)
370 {
371     int i;
372
373     Dists = DIST_ALL | DIST_XF86;
374     SrcDists = DIST_SRC_ALL;
375     CRYPTODists = DIST_CRYPTO_ALL;
376     XF86Dists = DIST_XF86_ALL;
377     XF86ServerDists = DIST_XF86_SERVER_ALL;
378     XF86FontDists = DIST_XF86_FONTS_ALL;
379     i = distMaybeSetPorts(self);
380     distVerifyFlags();
381     return i;
382 }
383
384 static int
385 distMaybeSetPorts(dialogMenuItem *self)
386 {
387     dialog_clear_norefresh();
388     if (!msgYesNo("Would you like to install the FreeBSD ports collection?\n\n"
389                   "This will give you ready access to over 8200 ported software packages,\n"
390                   "at a cost of around 180MB of disk space when \"clean\" and possibly\n"
391                   "much more than that when a lot of the distribution tarballs are loaded\n"
392                   "(unless you have the extra discs available from a FreeBSD CD/DVD distribution\n"
393                   "and can mount them on /cdrom, in which case this is far less of a problem).\n\n"
394                   "The ports collection is a very valuable resource and well worth having\n"
395                   "on your /usr partition, so it is advisable to say Yes to this option.\n\n"
396                   "For more information on the ports collection & the latest ports, visit:\n"
397                   "    http://www.freebsd.org/ports\n"))
398         Dists |= DIST_PORTS;
399     else
400         Dists &= ~DIST_PORTS;
401     return DITEM_SUCCESS | DITEM_RESTORE | DITEM_REDRAW;
402 }
403
404 static Boolean
405 distSetByName(Distribution *dist, char *name)
406 {
407     int i, status = FALSE;
408     
409     /* Loop through current set */
410     for (i = 0; dist[i].my_name; i++) {
411         /* This is shorthand for "dist currently disabled" */
412         if (!dist[i].my_dir)
413             continue;
414         if (!strcmp(dist[i].my_name, name)) {
415             *(dist[i].my_mask) |= dist[i].my_bit;
416             status = TRUE;
417         }
418         if (dist[i].my_dist) {
419             if (distSetByName(dist[i].my_dist, name)) {
420                 status = TRUE;
421             }
422         }
423     }
424     distVerifyFlags();
425     return status;
426 }
427
428 static Boolean
429 distUnsetByName(Distribution *dist, char *name)
430 {
431     int i, status = FALSE;
432     
433     /* Loop through current set */
434     for (i = 0; dist[i].my_name; i++) {
435         /* This is shorthand for "dist currently disabled" */
436         if (!dist[i].my_dir)
437             continue;
438         if (!strcmp(dist[i].my_name, name)) {
439             *(dist[i].my_mask) &= ~(dist[i].my_bit);
440             status = TRUE;
441         }
442         if (dist[i].my_dist) {
443             if (distUnsetByName(dist[i].my_dist, name)) {
444                 status = TRUE;
445             }
446         }
447     }
448     return status;
449 }
450
451 /* Just for the dispatch stuff */
452 int
453 distSetCustom(dialogMenuItem *self)
454 {
455     char *cp, *cp2, *tmp;
456
457     if (!(tmp = variable_get(VAR_DISTS))) {
458         msgDebug("distSetCustom() called without %s variable set.\n", VAR_DISTS);
459         return DITEM_FAILURE;
460     }
461
462     cp = alloca(strlen(tmp) + 1);
463     if (!cp)
464         msgFatal("Couldn't alloca() %d bytes!\n", (int)(strlen(tmp) + 1));
465     strcpy(cp, tmp);
466     while (cp) {
467         if ((cp2 = index(cp, ' ')) != NULL)
468             *(cp2++) = '\0';
469         if (!distSetByName(DistTable, cp))
470             msgDebug("distSetCustom: Warning, no such release \"%s\"\n", cp);
471         cp = cp2;
472     }
473     distVerifyFlags();
474     return DITEM_SUCCESS;
475 }
476     
477 /* Just for the dispatch stuff */
478 int
479 distUnsetCustom(dialogMenuItem *self)
480 {
481     char *cp, *cp2, *tmp;
482
483     if (!(tmp = variable_get(VAR_DISTS))) {
484         msgDebug("distUnsetCustom() called without %s variable set.\n", VAR_DISTS);
485         return DITEM_FAILURE;
486     }
487
488     cp = alloca(strlen(tmp) + 1);
489     if (!cp)
490         msgFatal("Couldn't alloca() %d bytes!\n", (int)(strlen(tmp) + 1));
491     strcpy(cp, tmp);
492     while (cp) {
493         if ((cp2 = index(cp, ' ')) != NULL)
494             *(cp2++) = '\0';
495         if (!distUnsetByName(DistTable, cp))
496             msgDebug("distUnsetCustom: Warning, no such release \"%s\"\n", cp);
497         cp = cp2;
498     }
499     return DITEM_SUCCESS;
500 }
501
502 int
503 distSetSrc(dialogMenuItem *self)
504 {
505     int i;
506
507     dialog_clear_norefresh();
508     if (!dmenuOpenSimple(&MenuSrcDistributions, FALSE))
509         i = DITEM_FAILURE;
510     else
511         i = DITEM_SUCCESS;
512     distVerifyFlags();
513     return i | DITEM_RESTORE;
514 }
515
516 int
517 distSetXF86(dialogMenuItem *self)
518 {
519     int i = DITEM_SUCCESS;
520
521     dialog_clear_norefresh();
522     if (!dmenuOpenSimple(&MenuXF86Select, FALSE))
523         i = DITEM_FAILURE;
524     distVerifyFlags();
525     return i | DITEM_RESTORE;
526 }
527
528 static Boolean got_intr = FALSE;
529
530 /* timeout handler */
531 static void
532 handle_intr(int sig)
533 {
534     msgDebug("User generated interrupt.\n");
535     got_intr = TRUE;
536 }
537
538 static int
539 check_for_interrupt(void)
540 {
541     if (got_intr) {
542         got_intr = FALSE;
543         return TRUE;
544     }
545     return FALSE;
546 }
547
548 static Boolean
549 distExtract(char *parent, Distribution *me)
550 {
551     int i,j, status, total, intr;
552     int cpid, zpid, fd2, chunk, numchunks;
553     char *path, *dist, buf[300000];
554     const char *tmp;
555     FILE *fp;
556     WINDOW *w = savescr();
557     struct timeval start, stop;
558     struct sigaction old, new;
559     properties dist_attr = NULL;
560
561     status = TRUE;
562     if (isDebug())
563         msgDebug("distExtract: parent: %s, me: %s\n", parent ? parent : "(none)", me->my_name);
564
565     /* Make ^C fake a sudden timeout */
566     new.sa_handler = handle_intr;
567     new.sa_flags = 0;
568     (void)sigemptyset(&new.sa_mask);
569     dialog_clear_norefresh();
570     dialog_msgbox("Please Wait", "Extracting all requested distributions...", -1, -1, 0);
571     sigaction(SIGINT, &new, &old);
572
573     /* Loop through to see if we're in our parent's plans */
574     for (i = 0; me[i].my_name; i++) {
575         dist = me[i].my_name;
576         path = parent ? parent : dist;
577
578         /* If our bit isn't set, go to the next */
579         if (!(me[i].my_bit & *(me[i].my_mask)))
580             continue;
581
582         /* This is shorthand for "dist currently disabled" */
583         if (!me[i].my_dir) {
584             *(me[i].my_mask) &= ~(me[i].my_bit);
585             continue;
586         }
587
588         /* Recurse if we actually have a sub-distribution */
589         if (me[i].my_dist) {
590             if ((status = distExtract(dist, me[i].my_dist)) == TRUE)
591                 *(me[i].my_mask) &= ~(me[i].my_bit);
592             goto done;
593         }
594
595         /*
596          * Try to get distribution as multiple pieces, locating and parsing an
597          * info file which tells us how many we need for this distribution.
598          */
599         numchunks = 0;
600         snprintf(buf, sizeof buf, "%s/%s.inf", path, dist);
601
602     getinfo:
603         fp = DEVICE_GET(mediaDevice, buf, TRUE);
604         intr = check_for_interrupt();
605         if (fp == (FILE *)IO_ERROR || intr || !mediaDevice) {
606             /* Hard error, can't continue */
607             if (!msgYesNo("Unable to open %s: %s.\nReinitialize media?",
608                           buf, !intr ? "I/O error." : "User interrupt.")) {
609                 DEVICE_SHUTDOWN(mediaDevice);
610                 if (!DEVICE_INIT(mediaDevice)) {
611                     status = FALSE;
612                     goto done;
613                 }
614                 else
615                     goto getinfo;
616             }
617             else {
618                 status = FALSE;
619                 goto done;
620             }
621         }
622         else if (fp > 0) {
623             if (isDebug())
624                 msgDebug("Parsing attributes file for distribution %s\n", dist);
625
626             dist_attr = properties_read(fileno(fp));
627             intr = check_for_interrupt();
628             if (intr || !dist_attr) {
629                 msgConfirm("Cannot parse information file for the %s distribution: %s\n"
630                            "Please verify that your media is valid and try again.",
631                            dist, !intr ? "I/O error" : "User interrupt");
632             }
633             else {
634                 tmp = property_find(dist_attr, "Pieces");
635                 if (tmp)
636                     numchunks = strtol(tmp, 0, 0);
637             }
638             fclose(fp);
639             if (!numchunks)
640                 continue;
641         }
642         else {
643             /* Try to get the distribution as a single file */
644             snprintf(buf, sizeof buf, "%s/%s.tgz", path, dist);
645             /*
646              * Passing TRUE as 3rd parm to get routine makes this a "probing"
647              * get, for which errors are not considered too significant.
648              */
649         getsingle:
650             fp = DEVICE_GET(mediaDevice, buf, TRUE);
651             intr = check_for_interrupt();
652             if (fp == (FILE *)IO_ERROR || intr || !mediaDevice) {
653                 /* Hard error, can't continue */
654                 if (intr)       /* result of an interrupt */
655                     msgConfirm("Unable to open %s: User interrupt", buf);
656                 else
657                     msgConfirm("Unable to open %s: I/O error", buf);
658                 DEVICE_SHUTDOWN(mediaDevice);
659                 if (!DEVICE_INIT(mediaDevice)) {
660                     status = FALSE;
661                     goto done;
662                 }
663                 else
664                     goto getsingle;
665             }
666             else if (fp > 0) {
667                 char *dir = root_bias(me[i].my_dir);
668
669                 dialog_clear_norefresh();
670                 msgNotify("Extracting %s into %s directory...", dist, dir);
671                 status = mediaExtractDist(dir, dist, fp);
672                 fclose(fp);
673                 goto done;
674             }
675             else {
676                 status = FALSE;
677                 goto done;
678             }
679         }
680
681         /* Fall through from "we got the attribute file, now get the pieces" step */
682         if (!numchunks)
683             continue;
684
685         if (isDebug())
686             msgDebug("Attempting to extract distribution from %u chunks.\n", numchunks);
687
688         total = 0;
689         (void)gettimeofday(&start, (struct timezone *)0);
690
691         /* We have one or more chunks, initialize unpackers... */
692         mediaExtractDistBegin(root_bias(me[i].my_dir), &fd2, &zpid, &cpid);
693
694         /* And go for all the chunks */
695         dialog_clear_norefresh();
696         for (chunk = 0; chunk < numchunks; chunk++) {
697             int n, retval, last_msg, chunksize, realsize;
698             char prompt[80];
699
700             last_msg = 0;
701
702         getchunk:
703             snprintf(buf, sizeof buf, "cksum.%c%c",  (chunk / 26) + 'a', (chunk % 26) + 'a');
704             tmp = property_find(dist_attr, buf);
705             chunksize = 0;
706             if (tmp) {
707                 tmp=index(tmp, ' ');
708                 chunksize = strtol(tmp, 0, 0);
709             }
710             snprintf(buf, sizeof buf, "%s/%s.%c%c", path, dist, (chunk / 26) + 'a', (chunk % 26) + 'a');
711             if (isDebug())
712                 msgDebug("trying for piece %d of %d: %s\n", chunk + 1, numchunks, buf);
713             fp = DEVICE_GET(mediaDevice, buf, FALSE);
714             intr = check_for_interrupt();
715             if (fp <= (FILE *)0 || intr) {
716                 if (fp == (FILE *)0)
717                     msgConfirm("Failed to find %s on this media.  Reinitializing media.", buf);
718                 else
719                     msgConfirm("failed to retreive piece file %s.\n"
720                                "%s: Reinitializing media.", buf, !intr ? "I/O error" : "User interrupt");
721                 DEVICE_SHUTDOWN(mediaDevice);
722                 if (!DEVICE_INIT(mediaDevice))
723                     goto punt;
724                 else
725                     goto getchunk;
726             }
727
728             snprintf(prompt, sizeof prompt, "Extracting %s into %s directory...", dist, root_bias(me[i].my_dir));
729             dialog_gauge("Progress", prompt, 8, 15, 6, 50, (int)((float)(chunk + 1) / numchunks * 100));
730
731             realsize = 0;
732             while (1) {
733                 int seconds;
734
735                 n = fread(buf + realsize, 1, BUFSIZ, fp);
736                 if (check_for_interrupt()) {
737                     msgConfirm("Media read error:  User interrupt.");
738                     fclose(fp);
739                     goto punt;
740                 }
741                 else if (n <= 0)
742                     break;
743                 total += n;
744                 realsize += n;
745
746                 /* Print statistics about how we're doing */
747                 (void) gettimeofday(&stop, (struct timezone *)0);
748                 stop.tv_sec = stop.tv_sec - start.tv_sec;
749                 stop.tv_usec = stop.tv_usec - start.tv_usec;
750                 if (stop.tv_usec < 0)
751                     stop.tv_sec--, stop.tv_usec += 1000000;
752                 seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
753                 if (!seconds)
754                     seconds = 1;
755
756                 if (seconds != last_msg) {
757                     last_msg = seconds;
758                     msgInfo("%10d bytes read from %s dist, chunk %2d of %2d @ %.1f KBytes/sec.",
759                             total, dist, chunk + 1, numchunks, (total / seconds) / 1000.0);
760                 }
761             }
762             fclose(fp);
763             
764             if (!chunksize || (realsize == chunksize)) {
765                 /* No substitution necessary */
766                 retval = write(fd2, buf, realsize);
767                 if (retval != realsize) {
768                     fclose(fp);
769                     dialog_clear_norefresh();
770                     msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", retval, realsize);
771                     goto punt;
772                 }
773             } else {
774                 for (j = 0; j < realsize; j++) {
775                     /* On finding CRLF, skip the CR; don't exceed end of buffer. */
776                     if ((buf[j] != 0x0d) || (j == total - 1) || (buf[j + 1] != 0x0a)) {
777                         retval = write(fd2, buf + j, 1);
778                         if (retval != 1) {
779                             fclose(fp);
780                             dialog_clear_norefresh();
781                             msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", j, chunksize);
782                             goto punt;
783                         }
784                     }
785                 }
786             }
787         }
788         close(fd2);
789         status = mediaExtractDistEnd(zpid, cpid);
790         goto done;
791
792     punt:
793         close(fd2);
794         mediaExtractDistEnd(zpid, cpid);
795         status = FALSE;
796
797     done:
798         if (!status) {
799             dialog_clear_norefresh();
800             if (me[i].my_dist) {
801                 msgConfirm("Unable to transfer all components of the %s distribution.\n"
802                            "You may wish to switch media types and try again.\n", me[i].my_name);
803             }
804             else if (me[i].my_bit != DIST_LOCAL) {
805                 status = msgYesNo("Unable to transfer the %s distribution from\n%s.\n\n"
806                                   "Do you want to try to retrieve it again?",
807                                   me[i].my_name, mediaDevice->name);
808                 if (!status)
809                     --i;
810             }
811         }
812         /* If extract was successful, remove ourselves from further consideration */
813         if (status)
814             *(me[i].my_mask) &= ~(me[i].my_bit);
815         else
816             continue;
817     }
818     properties_free(dist_attr);
819     sigaction(SIGINT, &old, NULL);      /* Restore signal handler */
820     restorescr(w);
821     return status;
822 }
823
824 static void
825 printSelected(char *buf, int selected, Distribution *me, int *col)
826 {
827     int i;
828
829     /* Loop through to see if we're in our parent's plans */
830     for (i = 0; me[i].my_name; i++) {
831
832         /* If our bit isn't set, go to the next */
833         if (!(me[i].my_bit & selected))
834             continue;
835
836         /* This is shorthand for "dist currently disabled" */
837         if (!me[i].my_dir)
838             continue;
839
840         *col += strlen(me[i].my_name);
841         if (*col > 50) {
842             *col = 0;
843             strcat(buf, "\n");
844         }
845         sprintf(&buf[strlen(buf)], " %s", me[i].my_name);
846         /* Recurse if have a sub-distribution */
847         if (me[i].my_dist)
848             printSelected(buf, *(me[i].my_mask), me[i].my_dist, col);
849     }
850 }
851
852 int
853 distExtractAll(dialogMenuItem *self)
854 {
855     int old_dists, retries = 0, status = DITEM_SUCCESS;
856     char buf[512];
857     WINDOW *w;
858 #ifdef X_AS_PKG
859     int want_x_package = 0;
860 #endif
861
862     /* paranoia */
863     if (!Dists) {
864         if (!dmenuOpenSimple(&MenuSubDistributions, FALSE) || !Dists)
865             return DITEM_FAILURE;
866     }
867
868     if (!mediaVerify() || !DEVICE_INIT(mediaDevice))
869         return DITEM_FAILURE;
870
871     old_dists = Dists;
872     distVerifyFlags();
873
874     dialog_clear_norefresh();
875     w = savescr();
876     msgNotify("Attempting to install all selected distributions..");
877
878 #ifdef X_AS_PKG
879     /* Clear any XFree86 dist flags, but remember they were present. */
880     if(Dists & DIST_XF86)
881         want_x_package = 1;
882     Dists &= ~DIST_XF86;
883     /*Dists &= ~(DIST_XF86 | XF86Dists | XF86ServerDists | XF86FontDists);*/
884 #endif
885     
886     /* Try for 3 times around the loop, then give up. */
887     while (Dists && ++retries < 3)
888         distExtract(NULL, DistTable);
889
890 #ifdef X_AS_PKG
891     if (want_x_package)
892         status |= installX11package(NULL);
893 #endif
894
895     dialog_clear_norefresh();
896     /* Only do bin fixup if bin dist was successfully extracted */
897     if ((old_dists & DIST_BIN) && !(Dists & DIST_BIN))
898         status |= installFixupBin(self);
899 #ifndef X_AS_PKG
900     if (old_dists & DIST_XF86)
901         status |= installFixupXFree(self);
902 #endif
903
904     /* Clear any local dist flags now */
905     Dists &= ~DIST_LOCAL;
906
907     if (Dists) {
908         int col = 0;
909
910         buf[0] = '\0';
911         dialog_clear_norefresh();
912         printSelected(buf, Dists, DistTable, &col);
913         dialog_clear_norefresh();
914         if (col) {
915             msgConfirm("Couldn't extract the following distributions.  This may\n"
916                        "be because they were not available on the installation\n"
917                        "media you've chosen:\n\n\t%s", buf);
918         }
919     }
920     restorescr(w);
921     return status;
922 }