(U+6215): Apply new conventions for glyph granularity.
[chise/xemacs-chise.git.1] / 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 #include "desktop.h"
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       // remove shortcuts and registry entries
235       if (type != TY_GENERIC)
236         remove_xemacs_setup();
237
238       if (action == ACTION_UPGRADE)
239         log (0, "Uninstalling old %s", name);
240       else
241         log (0, "Uninstalling %s", name);
242
243       while (fgets (line, sizeof (line), lst))
244         {
245           if (line[strlen(line)-1] == '\n')
246             line[strlen(line)-1] = 0;
247
248           dirs.add_subdirs (line);
249
250           char *d = map_filename (line, type);
251           DWORD dw = GetFileAttributes (d);
252           if (dw != 0xffffffff && !(dw & FILE_ATTRIBUTE_DIRECTORY))
253             {
254               log (LOG_BABBLE, "unlink %s", d);
255               DeleteFile (d);
256             }
257         }
258       fclose (lst);
259
260       remove (fname);
261
262       dirs.reverse_sort ();
263       char *subdir = 0;
264       while ((subdir = dirs.enumerate (subdir)) != 0)
265         {
266           char *d = map_filename (subdir, type);
267           if (RemoveDirectory (d))
268             log (LOG_BABBLE, "rmdir %s", d);
269         }
270       num_uninstalls ++;
271     }
272 }
273
274           
275 static int
276 install_one (char *name, char *file, int file_size, int action, int type)
277 {
278   int errors = 0;
279   char *local = file, *cp, *fn, *base;
280
281   base = local;
282   for (cp=local; *cp; cp++)
283     if (*cp == '/' || *cp == '\\' || *cp == ':')
284       base = cp+1;
285
286   SetWindowText (ins_pkgname, base);
287
288   if (!exists (local) && exists (base))
289     local = base;
290   if (!exists (local))
291     {
292       note (IDS_ERR_OPEN_READ, local, "No such file");
293       return 1;
294     }
295
296   char* fname = (type == TY_GENERIC ? 
297                  concat (root_dir, XEMACS_PACKAGE_DIR, "pkginfo/MANIFEST.",
298                          name,  0) :
299                  concat (root_dir, XEMACS_SETUP_DIR, "MANIFEST.", name,  0));
300
301   FILE* lst = fopen (fname, "wb");
302
303   package_bytes = file_size;
304
305   switch (action)
306     {
307     case ACTION_NEW:
308       SetWindowText (ins_action, "Installing...");
309       break;
310     case ACTION_UPGRADE:
311       SetWindowText (ins_action, "Upgrading...");
312       break;
313     }
314
315   log (0, "Installing %s", local);
316   tar_open (local);
317   while ((fn = tar_next_file ()))
318     {
319       char *dest_file, *disp_file;
320       int len;
321
322       if (lst)
323         fprintf (lst, "%s\n", fn);
324
325       dest_file = map_filename (fn, type);
326       
327       // The installer uses a variable width font. Assume roughly 32 chars
328       // will fit and munge the file accordingly.
329 #define MAX_DISP_SIZE 50
330       disp_file = strdup(dest_file);
331       if ((len = strlen(dest_file)) > MAX_DISP_SIZE) {
332         disp_file += (len - MAX_DISP_SIZE);
333         disp_file[0] = '.';
334         disp_file[1] = '.';
335         disp_file[2] = '.';
336       }
337 #undef MAX_DISP_SIZE
338       SetWindowText (ins_filename, disp_file);
339
340       log (LOG_BABBLE, "Installing file %s", dest_file);
341       if (tar_read_file (dest_file) != 0)
342         {
343           log (0, "Unable to install file %s", dest_file);
344           errors ++;
345         }
346
347       progress (tar_ftell ());
348       num_installs ++;
349     }
350   tar_close ();
351
352   total_bytes_sofar += file_size;
353   progress (0);
354
355   int df = diskfull (root_dir);
356   SendMessage (ins_diskfull, PBM_SETPOS, (WPARAM) df, 0);
357
358   if (lst)
359     fclose (lst);
360
361   return errors;
362 }
363
364 void
365 do_install (HINSTANCE h)
366 {
367   int i;
368   int errors = 0;
369
370   num_installs = 0, num_uninstalls = 0;
371
372   next_dialog = IDD_DESKTOP;
373
374   mkdir_p (1, root_dir);
375
376   for (i=0; standard_dirs[i]; i++)
377     {
378       char *p = concat (root_dir, standard_dirs[i], 0);
379       mkdir_p (1, p);
380       free (p);
381     }
382
383   dismiss_url_status_dialog ();
384
385   init_dialog ();
386
387   total_bytes = 0;
388   total_bytes_sofar = 0;
389
390   int df = diskfull (root_dir);
391   SendMessage (ins_diskfull, PBM_SETPOS, (WPARAM) df, 0);
392
393   LOOP_PACKAGES
394     {
395       total_bytes += pi.install_size;
396     }
397
398   for (i=0; i<npackages; i++)
399     {
400       if (package[i].action == ACTION_UNINSTALL
401           || (package[i].action == ACTION_UPGRADE && pi.install))
402         {
403           uninstall_one (package[i].name, package[i].action,
404                          package[i].type);
405           uninstall_one (concat (package[i].name, "-src", 0), package[i].action,
406                          package[i].type);
407         }
408
409       if ((package[i].action == ACTION_NEW
410            || package[i].action == ACTION_UPGRADE)
411           && pi.install)
412         {
413           int e = install_one (package[i].name, pi.install, pi.install_size, package[i].action,
414                                package[i].type);
415           if (package[i].srcaction == SRCACTION_YES && pi.source)
416             e += install_one (concat (package[i].name, "-src", 0), pi.source, pi.source_size,
417                               package[i].action, package[i].type);
418           if (e)
419             {
420               package[i].action = ACTION_ERROR;
421               errors++;
422             }
423         }
424     } // end of big package loop
425
426   ShowWindow (ins_dialog, SW_HIDE);
427
428   char *odbn = concat (root_dir, XEMACS_SETUP_DIR, "installed.db", 0);
429   char *ndbn = concat (root_dir, XEMACS_SETUP_DIR, "installed.db.new", 0);
430   char *sdbn = concat (root_dir, XEMACS_SETUP_DIR, "installed.db.old", 0);
431
432   mkdir_p (0, ndbn);
433
434   FILE *odb = fopen (odbn, "rt");
435   FILE *ndb = fopen (ndbn, "wb");
436
437   if (!ndb)
438     {
439       char *err = strerror (errno);
440       if (!err)
441         err = "(unknown error)";
442       fatal (IDS_ERR_OPEN_WRITE, ndb, err);
443     }
444
445   if (odb)
446     {
447       char line[1000], pkg[1000];
448       int printit;
449       while (fgets (line, 1000, odb))
450         {
451           printit = 1;
452           sscanf (line, "%s", pkg);
453           for (i=0; i<npackages; i++)
454             {
455               if (strcmp (pkg, package[i].name) == 0)
456                 switch (package[i].action)
457                   {
458                   case ACTION_NEW:
459                   case ACTION_UPGRADE:
460                   case ACTION_UNINSTALL:
461                     printit = 0;
462                     break;
463                   }
464             }
465           if (printit)
466             fputs (line, ndb);
467         }
468       
469     }
470
471   LOOP_PACKAGES
472     {
473       if (package[i].srcaction == SRCACTION_YES)
474         fprintf (ndb, "%s %s %d %s %d\n", package[i].name,
475                  pi.install, pi.install_size,
476                  pi.source, pi.source_size);
477       else
478         fprintf (ndb, "%s %s %d\n", package[i].name,
479                  pi.install, pi.install_size);
480     }
481
482   if (odb)
483     fclose (odb);
484   fclose (ndb);
485
486   remove (sdbn);
487   if (odb && rename (odbn, sdbn))
488     badrename (odbn, sdbn);
489
490   remove (odbn);
491   if (rename (ndbn, odbn))
492     badrename (ndbn, odbn);
493
494   if (num_installs == 0 && num_uninstalls == 0)
495     {
496       exit_msg = IDS_NOTHING_INSTALLED;
497       return;
498     }
499   if (num_installs == 0)
500     {
501       exit_msg = IDS_UNINSTALL_COMPLETE;
502       return;
503     }
504
505   if (errors)
506     exit_msg = IDS_INSTALL_INCOMPLETE;
507   else
508     exit_msg = IDS_INSTALL_COMPLETE;
509 }