Initial revision
[chise/xemacs-chise.git] / netinstall / install.cc
1 /*
2  * Copyright (c) 2000, Red Hat, Inc.
3  *
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  *
9  *     A copy of the GNU General Public License can be found at
10  *     http://www.gnu.org/
11  *
12  * Written by DJ Delorie <dj@cygnus.com>
13  *
14  */
15
16 /* The purpose of this file is to intall all the packages selected in
17    the install list (in ini.h).  Note that we use a separate thread to
18    maintain the progress dialog, so we avoid the complexity of
19    handling two tasks in one thread.  We also create or update all the
20    files in /etc/setup and create the mount points. */
21
22 #include <io.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #ifndef WIN32_NATIVE
26 #include <unistd.h>
27 #endif
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <zlib.h>
32
33 #include "win32.h"
34 #include "commctrl.h"
35
36 #include "resource.h"
37 #include "ini.h"
38 #include "dialog.h"
39 #include "concat.h"
40 #include "geturl.h"
41 #include "mkdir.h"
42 #include "state.h"
43 #include "tar.h"
44 #include "diskfull.h"
45 #include "msg.h"
46 #include "regedit.h"
47 #include "reginfo.h"
48 #include "log.h"
49 #include "hash.h"
50
51 #include "port.h"
52
53 static HWND ins_dialog = 0;
54 static HWND ins_action = 0;
55 static HWND ins_pkgname = 0;
56 static HWND ins_filename = 0;
57 static HWND ins_pprogress = 0;
58 static HWND ins_iprogress = 0;
59 static HWND ins_diskfull = 0;
60 static HANDLE init_event;
61
62 static int total_bytes = 0;
63 static int total_bytes_sofar = 0;
64 static int package_bytes = 0;
65
66 static BOOL
67 dialog_cmd (HWND h, int id, HWND hwndctl, UINT code)
68 {
69   switch (id)
70     {
71     case IDCANCEL:
72       exit_setup (1);
73     }
74   return FALSE;
75 }
76
77 static BOOL CALLBACK
78 dialog_proc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
79 {
80   switch (message)
81     {
82     case WM_INITDIALOG:
83       ins_dialog = h;
84       ins_action = GetDlgItem (h, IDC_INS_ACTION);
85       ins_pkgname = GetDlgItem (h, IDC_INS_PKG);
86       ins_filename = GetDlgItem (h, IDC_INS_FILE);
87       ins_pprogress = GetDlgItem (h, IDC_INS_PPROGRESS);
88       ins_iprogress = GetDlgItem (h, IDC_INS_IPROGRESS);
89       ins_diskfull = GetDlgItem (h, IDC_INS_DISKFULL);
90       SetEvent (init_event);
91       return FALSE;
92     case WM_COMMAND:
93       return HANDLE_WM_COMMAND (h, wParam, lParam, dialog_cmd);
94     }
95   return FALSE;
96 }
97
98 static DWORD WINAPI
99 dialog (void *)
100 {
101   MSG m;
102   HWND new_dialog = CreateDialog (hinstance, MAKEINTRESOURCE (IDD_INSTATUS),
103                                    0, dialog_proc);
104   if (new_dialog == 0)
105     fatal ("create dialog");
106   ShowWindow (new_dialog, SW_SHOWNORMAL);
107   UpdateWindow (new_dialog);
108   while (GetMessage (&m, 0, 0, 0) > 0) {
109     TranslateMessage (&m);
110     DispatchMessage (&m);
111   }
112   return FALSE;
113 }
114
115 static void
116 init_dialog ()
117 {
118   if (ins_dialog == 0)
119     {
120       DWORD tid;
121       HANDLE thread;
122       init_event = CreateEvent (0, 0, 0, 0);
123       thread = CreateThread (0, 0, dialog, 0, 0, &tid);
124       WaitForSingleObject (init_event, 10000);
125       CloseHandle (init_event);
126       SendMessage (ins_pprogress, PBM_SETRANGE, 0, MAKELPARAM (0, 100));
127       SendMessage (ins_iprogress, PBM_SETRANGE, 0, MAKELPARAM (0, 100));
128       SendMessage (ins_diskfull, PBM_SETRANGE, 0, MAKELPARAM (0, 100));
129     }
130
131   SetWindowText (ins_pkgname, "");
132   SetWindowText (ins_filename, "");
133   SendMessage (ins_pprogress, PBM_SETPOS, (WPARAM) 0, 0);
134   SendMessage (ins_iprogress, PBM_SETPOS, (WPARAM) 0, 0);
135   SendMessage (ins_diskfull, PBM_SETPOS, (WPARAM) 0, 0);
136   ShowWindow (ins_dialog, SW_SHOWNORMAL);
137   SetForegroundWindow (ins_dialog);
138 }
139
140 static void
141 progress (int bytes)
142 {
143   int perc;
144
145   if (package_bytes > 100)
146     {
147       perc = bytes / (package_bytes / 100);
148       SendMessage (ins_pprogress, PBM_SETPOS, (WPARAM) perc, 0);
149     }
150
151   if (total_bytes > 100)
152     {
153       perc = (total_bytes_sofar + bytes) / (total_bytes / 100);
154       SendMessage (ins_iprogress, PBM_SETPOS, (WPARAM) perc, 0);
155     }
156 }
157
158 static void
159 badrename (char *o, char *n)
160 {
161   char *err = strerror (errno);
162   if (!err)
163     err = "(unknown error)";
164   note (IDS_ERR_RENAME, o, n, err);
165 }
166
167 static char *standard_dirs[] = {
168   0
169 };
170
171 void
172 hash::add_subdirs (char *path)
173 {
174   char *nonp, *pp;
175   for (nonp = path; *nonp == '\\' || *nonp == '/'; nonp++);
176   for (pp = path + strlen(path) - 1; pp>nonp; pp--)
177     if (*pp == '/' || *pp == '\\')
178       {
179         int i, s=0;
180         char c = *pp;
181         *pp = 0;
182         for (i=0; standard_dirs[i]; i++)
183           if (strcmp (standard_dirs[i]+1, path) == 0)
184             {
185               s = 1;
186               break;
187             }
188         if (s == 0)
189           add (path);
190         *pp = c;
191       }
192 }
193
194 char *
195 map_filename (char *fn, int type)
196 {
197   char *dest_file;
198   while (*fn == '/' || *fn == '\\')
199     fn++;
200   if (type == TY_GENERIC)
201     dest_file = concat (root_dir, XEMACS_PACKAGE_DIR, fn, 0);
202   else                          // TY_CYGWIN | TY_NATIVE
203     dest_file = concat (root_dir, "/", fn, 0);
204   return dest_file;
205 }
206
207 static int
208 exists (char *file)
209 {
210   if (_access (file, 0) == 0)
211     return 1;
212   return 0;
213 }
214
215           
216 static int num_installs, num_uninstalls;
217
218 static void
219 uninstall_one (char *name, int action, int type)
220 {
221   hash dirs;
222   char line[_MAX_PATH];
223   char* fname = (type == TY_GENERIC ? 
224                  concat (root_dir, XEMACS_PACKAGE_DIR, "pkginfo/MANIFEST.",
225                          name,  0) :
226                  concat (root_dir, XEMACS_SETUP_DIR, "MANIFEST.", name,  0));
227
228   FILE* lst = fopen (fname, "rb");
229
230   if (lst)
231     {
232       SetWindowText (ins_pkgname, name);
233       SetWindowText (ins_action, "Uninstalling...");
234       if (action == ACTION_UPGRADE)
235         log (0, "Uninstalling old %s", name);
236       else
237         log (0, "Uninstalling %s", name);
238
239       while (fgets (line, sizeof (line), lst))
240         {
241           if (line[strlen(line)-1] == '\n')
242             line[strlen(line)-1] = 0;
243
244           dirs.add_subdirs (line);
245
246           char *d = map_filename (line, type);
247           DWORD dw = GetFileAttributes (d);
248           if (dw != 0xffffffff && !(dw & FILE_ATTRIBUTE_DIRECTORY))
249             {
250               log (LOG_BABBLE, "unlink %s", d);
251               DeleteFile (d);
252             }
253         }
254       fclose (lst);
255
256       remove (fname);
257
258       dirs.reverse_sort ();
259       char *subdir = 0;
260       while ((subdir = dirs.enumerate (subdir)) != 0)
261         {
262           char *d = map_filename (subdir, type);
263           if (RemoveDirectory (d))
264             log (LOG_BABBLE, "rmdir %s", d);
265         }
266       num_uninstalls ++;
267     }
268 }
269
270           
271 static int
272 install_one (char *name, char *file, int file_size, int action, int type)
273 {
274   int errors = 0;
275   char *local = file, *cp, *fn, *base;
276
277   base = local;
278   for (cp=local; *cp; cp++)
279     if (*cp == '/' || *cp == '\\' || *cp == ':')
280       base = cp+1;
281   SetWindowText (ins_pkgname, base);
282
283   if (!exists (local) && exists (base))
284     local = base;
285   if (!exists (local))
286     {
287       note (IDS_ERR_OPEN_READ, local, "No such file");
288       return 1;
289     }
290
291   char* fname = (type == TY_GENERIC ? 
292                  concat (root_dir, XEMACS_PACKAGE_DIR, "pkginfo/MANIFEST.",
293                          name,  0) :
294                  concat (root_dir, XEMACS_SETUP_DIR, "MANIFEST.", name,  0));
295
296   FILE* lst = fopen (fname, "wb");
297
298   package_bytes = file_size;
299
300   switch (action)
301     {
302     case ACTION_NEW:
303       SetWindowText (ins_action, "Installing...");
304       break;
305     case ACTION_UPGRADE:
306       SetWindowText (ins_action, "Upgrading...");
307       break;
308     }
309
310   log (0, "Installing %s", local);
311   tar_open (local);
312   while ((fn = tar_next_file ()))
313     {
314       char *dest_file;
315
316       if (lst)
317         fprintf (lst, "%s\n", fn);
318
319       dest_file = map_filename (fn, type);
320
321       SetWindowText (ins_filename, dest_file);
322       log (LOG_BABBLE, "Installing file %s", dest_file);
323       if (tar_read_file (dest_file) != 0)
324         {
325           log (0, "Unable to install file %s", dest_file);
326           errors ++;
327         }
328
329       progress (tar_ftell ());
330       num_installs ++;
331     }
332   tar_close ();
333
334   total_bytes_sofar += file_size;
335   progress (0);
336
337   int df = diskfull (root_dir);
338   SendMessage (ins_diskfull, PBM_SETPOS, (WPARAM) df, 0);
339
340   if (lst)
341     fclose (lst);
342
343   return errors;
344 }
345
346 void
347 do_install (HINSTANCE h)
348 {
349   int i;
350   int errors = 0;
351
352   num_installs = 0, num_uninstalls = 0;
353
354   next_dialog = IDD_DESKTOP;
355
356   mkdir_p (1, root_dir);
357
358   for (i=0; standard_dirs[i]; i++)
359     {
360       char *p = concat (root_dir, standard_dirs[i], 0);
361       mkdir_p (1, p);
362       free (p);
363     }
364
365   dismiss_url_status_dialog ();
366
367   init_dialog ();
368
369   total_bytes = 0;
370   total_bytes_sofar = 0;
371
372   int df = diskfull (root_dir);
373   SendMessage (ins_diskfull, PBM_SETPOS, (WPARAM) df, 0);
374
375   LOOP_PACKAGES
376     {
377       total_bytes += pi.install_size;
378     }
379
380   for (i=0; i<npackages; i++)
381     {
382       if (package[i].action == ACTION_UNINSTALL
383           || (package[i].action == ACTION_UPGRADE && pi.install))
384         {
385           uninstall_one (package[i].name, package[i].action,
386                          package[i].type);
387           uninstall_one (concat (package[i].name, "-src", 0), package[i].action,
388                          package[i].type);
389         }
390
391       if ((package[i].action == ACTION_NEW
392            || package[i].action == ACTION_UPGRADE)
393           && pi.install)
394         {
395           int e = install_one (package[i].name, pi.install, pi.install_size, package[i].action,
396                                package[i].type);
397           if (package[i].srcaction == SRCACTION_YES && pi.source)
398             e += install_one (concat (package[i].name, "-src", 0), pi.source, pi.source_size,
399                               package[i].action, package[i].type);
400           if (e)
401             {
402               package[i].action = ACTION_ERROR;
403               errors++;
404             }
405         }
406     } // end of big package loop
407
408   ShowWindow (ins_dialog, SW_HIDE);
409
410   char *odbn = concat (root_dir, XEMACS_SETUP_DIR, "installed.db", 0);
411   char *ndbn = concat (root_dir, XEMACS_SETUP_DIR, "installed.db.new", 0);
412   char *sdbn = concat (root_dir, XEMACS_SETUP_DIR, "installed.db.old", 0);
413
414   mkdir_p (0, ndbn);
415
416   FILE *odb = fopen (odbn, "rt");
417   FILE *ndb = fopen (ndbn, "wb");
418
419   if (!ndb)
420     {
421       char *err = strerror (errno);
422       if (!err)
423         err = "(unknown error)";
424       fatal (IDS_ERR_OPEN_WRITE, ndb, err);
425     }
426
427   if (odb)
428     {
429       char line[1000], pkg[1000];
430       int printit;
431       while (fgets (line, 1000, odb))
432         {
433           printit = 1;
434           sscanf (line, "%s", pkg);
435           for (i=0; i<npackages; i++)
436             {
437               if (strcmp (pkg, package[i].name) == 0)
438                 switch (package[i].action)
439                   {
440                   case ACTION_NEW:
441                   case ACTION_UPGRADE:
442                   case ACTION_UNINSTALL:
443                     printit = 0;
444                     break;
445                   }
446             }
447           if (printit)
448             fputs (line, ndb);
449         }
450       
451     }
452
453   LOOP_PACKAGES
454     {
455       if (package[i].srcaction == SRCACTION_YES)
456         fprintf (ndb, "%s %s %d %s %d\n", package[i].name,
457                  pi.install, pi.install_size,
458                  pi.source, pi.source_size);
459       else
460         fprintf (ndb, "%s %s %d\n", package[i].name,
461                  pi.install, pi.install_size);
462     }
463
464   if (odb)
465     fclose (odb);
466   fclose (ndb);
467
468   remove (sdbn);
469   if (odb && rename (odbn, sdbn))
470     badrename (odbn, sdbn);
471
472   remove (odbn);
473   if (rename (ndbn, odbn))
474     badrename (ndbn, odbn);
475
476   if (num_installs == 0 && num_uninstalls == 0)
477     {
478       exit_msg = IDS_NOTHING_INSTALLED;
479       return;
480     }
481   if (num_installs == 0)
482     {
483       exit_msg = IDS_UNINSTALL_COMPLETE;
484       return;
485     }
486
487   if (errors)
488     exit_msg = IDS_INSTALL_INCOMPLETE;
489   else
490     exit_msg = IDS_INSTALL_COMPLETE;
491 }