]> cygwin.com Git - cygwin-apps/setup.git/blob - setup.c
* setup.c (main): Change version number output.
[cygwin-apps/setup.git] / setup.c
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 Ron Parker <parkerrd@hotmail.com>
13 *
14 */
15
16 #include <windows.h>
17 #include <wininet.h>
18 #include <assert.h>
19 #include <ctype.h>
20 #include <direct.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <io.h>
24 #include <shellapi.h>
25 #include <shlguid.h>
26 #include <shlobj.h>
27 #include <stdio.h>
28 #include <time.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <stdarg.h>
32
33 #include "setup.h"
34 #include "strarry.h"
35 #include "zlib/zlib.h"
36
37 #define CYGNUS_KEY "Software\\Cygnus Solutions"
38 #define DEF_ROOT "C:\\cygwin"
39 #define DOWNLOAD_SUBDIR "latest/"
40 #define SCREEN_LINES 25
41 #define COMMAND9X "command.com /E:4096 /c "
42
43 #ifndef NFILE_LIST
44 #define NFILE_LIST 10000
45 #endif
46
47 #ifndef NFILE_SLOP
48 #define NFILE_SLOP 20
49 #endif
50
51 char *wd = NULL;
52 static char *tarpgm;
53
54 static int downloaddir (const char *url);
55
56 static SA files = {NULL, 0, 0};
57 static FILE *logfp = NULL;
58 static HANDLE devnull = NULL;
59 static HINTERNET session = NULL;
60
61 void
62 warning (const char *fmt, ...)
63 {
64 va_list args;
65 va_start (args, fmt);
66 vfprintf (stderr, fmt, args);
67 if (logfp)
68 vfprintf (logfp, fmt, args);
69 }
70
71 static int
72 create_shortcut (const char *target, const char *shortcut)
73 {
74 HRESULT hres;
75 IShellLink *sl;
76 char *path, *args;
77
78 if (!SUCCEEDED (CoInitialize (NULL)))
79 return 0;
80
81 hres =
82 CoCreateInstance (&CLSID_ShellLink, NULL,
83 CLSCTX_INPROC_SERVER, &IID_IShellLink, (LPVOID *) & sl);
84 if (SUCCEEDED (hres))
85 {
86 IPersistFile *pf;
87 int quoted = 0;
88 char *c;
89
90 /* Get the command only. */
91 path = xstrdup (target);
92 for (c = path; quoted || (*c != ' ' && *c); ++c)
93 {
94 if (*c == '\"')
95 quoted = !quoted;
96 }
97 if (*c)
98 {
99 *c = '\0';
100 args = c + 1;
101 }
102 else
103 args = "";
104
105 sl->lpVtbl->SetPath (sl, path);
106 sl->lpVtbl->SetArguments (sl, args);
107 xfree (path);
108
109 hres = sl->lpVtbl->QueryInterface (sl, &IID_IPersistFile, &pf);
110
111 if (SUCCEEDED (hres))
112 {
113 WCHAR widepath[_MAX_PATH];
114
115 // Ensure that the string is Unicode.
116 MultiByteToWideChar (CP_ACP, 0, shortcut, -1, widepath, MAX_PATH);
117
118 // Save the link by calling IPersistFile::Save.
119 hres = pf->lpVtbl->Save (pf, widepath, TRUE);
120 pf->lpVtbl->Release (pf);
121 }
122 sl->lpVtbl->Release (sl);
123 }
124
125 CoUninitialize ();
126
127 return SUCCEEDED (hres);
128 }
129
130
131 BOOL CALLBACK
132 output_file (HMODULE h, LPCTSTR type, LPTSTR name, LONG lparam)
133 {
134 HRSRC rsrc;
135 HGLOBAL res;
136 char *data;
137 FILE *out;
138 BOOL retval = FALSE;
139
140 size_t bytes_needed;
141 if ((rsrc = FindResource (NULL, name, "FILE"))
142 && (res = LoadResource (NULL, rsrc))
143 && (data = (char *) LockResource (res)) && (out = fopen (strlwr (name), "w+b")))
144 {
145 gzFile gzf;
146 char *buffer;
147 size_t bytes = SizeofResource (NULL, rsrc);
148
149 if (bytes != fwrite (data, 1, bytes, out))
150 warning ("Unable to write %s: %s", name, _strerror (""));
151
152 bytes_needed = *(int *) ((char *) data + bytes - sizeof (int));
153 buffer = (char *) xmalloc (bytes_needed);
154
155 rewind (out);
156 gzf = gzdopen (_dup (fileno (out)), "rb");
157 if (gzf && (size_t) gzread (gzf, buffer, bytes_needed) == bytes_needed)
158 {
159 gzclose (gzf);
160 if (fseek (out, 0, SEEK_SET)
161 || fwrite (buffer, 1, bytes_needed, out) != bytes_needed)
162 {
163 warning ("Unable to write decompressed file to %s: %s",
164 name, _strerror (""));
165 }
166 else
167 retval = TRUE;
168 }
169 else
170 {
171 int errnum;
172 const char *msg = gzerror (gzf, &errnum);
173 warning ("bytes_needed = %d, ", bytes_needed);
174 warning ("Unable to decompress %s: Error #%d, %s\n", name,
175 errnum, msg);
176 }
177 xfree (buffer);
178 fclose (out);
179 }
180 else
181 {
182 warning ("Unable to write %s: %s", name, _strerror (""));
183 }
184
185 return retval;
186 }
187
188 static void
189 xumount (const char *mountexedir, const char *unixpath)
190 {
191 char *umount = pathcat (mountexedir, "umount");
192 char buffer[1024];
193
194 sprintf (buffer, "%s %s", umount, unixpath);
195 (void) xcreate_process (1, NULL, devnull, devnull, buffer);
196 xfree (umount);
197 }
198
199 static int
200 tarx (const char *dir, const char *fn)
201 {
202 char *path, *dpath;
203 char buffer0[2049];
204 char *buffer = buffer0 + 1;
205 int hpipe[2];
206 HANDLE hin;
207 FILE *fp;
208 int filehere;
209
210 dpath = pathcat (dir, fn);
211 path = dtoupath (dpath);
212 sprintf (buffer, "%s xvfUz \"%s\"", tarpgm, path);
213 xfree (path);
214 xfree (dpath);
215
216 printf ("Installing %s\n", fn);
217
218 if (_pipe (hpipe, 256, O_TEXT) == -1)
219 return 0;
220
221 hin = (HANDLE) _get_osfhandle (hpipe[1]);
222 if (xcreate_process (0, NULL, hin, hin, buffer) == 0)
223 {
224 warning ("Unable to extract \"%s\": %s", fn, _strerror (""));
225 return 0;
226 }
227 _close (hpipe[1]);
228 fp = fdopen (hpipe[0], "rt");
229
230 filehere = files.index;
231 while (fgets (buffer, sizeof (buffer0), fp))
232 {
233 char *s = strchr (buffer, '\n');
234
235 if (s)
236 *s = '\0';
237
238 if (strchr (buffer, ':') != NULL)
239 {
240 s = buffer;
241 fprintf (stderr, "%s\n", s);
242 }
243 else
244 {
245 char *e;
246 if (++files.index >= files.count)
247 files.array = realloc (files.array,
248 NFILE_SLOP + (files.count += NFILE_LIST));
249 s = buffer;
250 if (*s != '/')
251 *--s = '/';
252 #if 0 /* too simplistic */
253 e = strchr (s, '\0') - 1;
254 if (e > s && *e == '/')
255 {
256 *e = '\0';
257 xumount (wd, s);
258 }
259 #endif
260
261 s = files.array[files.index] = utodpath (s);
262 }
263
264 fprintf (logfp, "%s\n", s);
265 }
266 fclose (fp);
267
268 while (++filehere <= files.index)
269 if (chmod (files.array[filehere], 0777))
270 warning ("Unable to reset protection on '%s' - %s\n",
271 files.array[filehere], _strerror (""));
272
273 return 1;
274 }
275
276 static int
277 recurse_dirs (const char *dir)
278 {
279 int err = 0;
280 int retval = 0;
281
282 char *pattern = pathcat (dir, "*");
283 if (pattern)
284 {
285 WIN32_FIND_DATA find_data;
286 HANDLE handle;
287
288 handle = FindFirstFile (pattern, &find_data);
289 if (handle != INVALID_HANDLE_VALUE)
290 {
291 /* Recurse through all subdirectories */
292 do
293 {
294 if (strcmp (find_data.cFileName, ".") == 0
295 || strcmp (find_data.cFileName, "..") == 0)
296 continue;
297
298 if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
299 /* && strlen(find_data.cFileName) */ )
300 {
301 char *subdir = pathcat (dir, find_data.cFileName);
302 if (subdir)
303 {
304 if (!recurse_dirs (subdir))
305 {
306 xfree (subdir);
307 err = 1;
308 break;
309 }
310
311 xfree (subdir);
312 }
313 else
314 lowmem ();
315 }
316 }
317 while (FindNextFile (handle, &find_data) && !err);
318 FindClose (handle);
319
320 /* Look for .tar.gz files */
321 if (!err)
322 {
323 xfree (pattern);
324 pattern = pathcat (dir, "*.tar.gz");
325 handle = FindFirstFile (pattern, &find_data);
326 if (handle != INVALID_HANDLE_VALUE)
327 {
328 int err = 0;
329
330 do
331 {
332 /* Skip source archives and meta-directories */
333 if (strstr (find_data.cFileName, "-src.tar.gz")
334 || strstr (find_data.cFileName, "-src-")
335 || strcmp (find_data.cFileName, ".") == 0
336 || strcmp (find_data.cFileName, "..") == 0)
337 {
338 continue;
339 }
340
341 if (!tarx (dir, find_data.cFileName))
342 {
343 err = 1;
344 break;
345 }
346 }
347 while (FindNextFile (handle, &find_data));
348 FindClose (handle);
349 }
350 if (!err)
351 retval = 1;
352 }
353 }
354
355 xfree (pattern);
356 }
357 else
358 lowmem ();
359
360 return retval;
361 }
362
363
364 static void
365 setpath (const char *element)
366 {
367 char *buffer = xmalloc (strlen (element) + 7);
368
369 sprintf (buffer, "PATH=%s", element);
370 putenv (buffer);
371
372 xfree (buffer);
373 }
374
375 static char *
376 prompt (const char *text, const char *def)
377 {
378 char buffer[_MAX_PATH];
379
380
381 printf ((def ? "%s? [%s] " : "%s? "), text, def);
382 fgets (buffer, sizeof (buffer), stdin);
383 buffer[strcspn (buffer, "\r\n")] = '\0';
384
385 /* Duplicate the entered value or the default if nothing was entered. */
386 return xstrdup (strlen (buffer) ? buffer : def ? def : "");
387 }
388
389 static int
390 optionprompt (const char *text, SA * options)
391 {
392 size_t n, lbound, response;
393 char buf[5];
394
395 n = 0;
396
397 do
398 {
399 char *or;
400 size_t base = n;
401 enum
402 { CONTINUE, REPEAT, ALL }
403 mode;
404
405 if (!base)
406 puts (text);
407
408 for (n = 0; n < base + SCREEN_LINES - 2 && n < options->count; ++n)
409 printf ("\t%d. %s\n", n + 1, options->array[n]);
410
411 lbound = n - (SCREEN_LINES - (base ? 2 : 3));
412 if (n < options->count)
413 {
414 mode = CONTINUE;
415 or = " or [continue]";
416 }
417 else if (options->count > SCREEN_LINES - 2)
418 {
419 mode = REPEAT;
420 or = " or [repeat]";
421 }
422 else
423 {
424 mode = ALL;
425 or = "";
426 }
427 printf ("Select an option from %d-%d%s: ", lbound, n, or);
428 if (!fgets (buf, sizeof (buf), stdin))
429 continue;
430
431 if (mode == CONTINUE && (!isalnum (*buf) || strchr ("cC", *buf)))
432 continue;
433 else if (mode == REPEAT && (!isalnum (*buf) || strchr ("rR", *buf)))
434 {
435 n = 0;
436 continue;
437 }
438
439 response = atoi (buf);
440 }
441 while (response < lbound || response > n);
442
443 return response - 1;
444 }
445
446 static int
447 geturl (const char *url, const char *file, int verbose)
448 {
449 DWORD type, size;
450 int authenticated = 0;
451 int retval = 0;
452 static HINTERNET connect;
453 int tries = 20;
454 int is_ftp = strnicmp (url, "ftp", 3) == 0;
455 static int saw_first_ftp = 0;
456 char connect_buffer[sizeof ("Connecting to http site... ")];
457
458 if (saw_first_ftp)
459 verbose = 0;
460 else if (verbose)
461 {
462 char *p;
463 int n;
464
465 p = strchr (url, ':');
466 if (!p)
467 n = 0;
468 else
469 n = p - url;
470 sprintf (connect_buffer, "Connecting to %.*s%ssite...", n, url, n ? " " : "");
471 fputs (connect_buffer, stdout);
472 fflush (stdout);
473 if (is_ftp)
474 saw_first_ftp = 1;
475 }
476
477 for (tries = 1; tries <= 40; tries++)
478 {
479 DWORD flags = INTERNET_FLAG_DONT_CACHE |
480 INTERNET_FLAG_KEEP_CONNECTION |
481 INTERNET_FLAG_PRAGMA_NOCACHE |
482 INTERNET_FLAG_RELOAD;
483
484 if (is_ftp)
485 flags |= INTERNET_FLAG_EXISTING_CONNECT |
486 INTERNET_FLAG_PASSIVE;
487
488 connect = InternetOpenUrl (session, url, NULL, 0, flags, 0);
489 if (connect)
490 break;
491 if (!verbose || tries == 1)
492 /* nothing */;
493 else if (tries > 2)
494 printf ("\r%s(try %d) \b\b", connect_buffer, tries);
495 else
496 printf ("\r%s(try %d)", connect_buffer, tries);
497 }
498
499 if (!connect)
500 {
501 puts ("\nCouldn't connect to ftp site."); fflush (stdout);
502 winerror ();
503 }
504 else
505 {
506 if (verbose)
507 {
508 if (tries > 1)
509 printf ("\r%s \b\b\b\b\b\b\b\b", connect_buffer);
510 printf ("Done.\n"); fflush (stdout);
511 }
512 while (!authenticated)
513 {
514 size = sizeof (type);
515 if (!InternetQueryOption
516 (connect, INTERNET_OPTION_HANDLE_TYPE, &type, &size))
517 {
518 winerror ();
519 return 0;
520 }
521 else
522 switch (type)
523 {
524 case INTERNET_HANDLE_TYPE_HTTP_REQUEST:
525 case INTERNET_HANDLE_TYPE_CONNECT_HTTP:
526 size = sizeof (DWORD);
527 if (!HttpQueryInfo
528 (connect, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
529 &type, &size, NULL))
530 {
531 winerror ();
532 return 0;
533 }
534 else if (type == HTTP_STATUS_PROXY_AUTH_REQ)
535 {
536 DWORD len;
537
538 if (!InternetQueryDataAvailable (connect, &len, 0, 0))
539 {
540 winerror ();
541 return 0;
542 }
543 else
544 {
545 char *user, *password;
546
547 /* Have to read any pending data, WININET peculiarity. */
548 char *buffer = xmalloc (len);
549 do
550 {
551 InternetReadFile (connect, buffer, len, &size);
552 }
553 while (size);
554 xfree (buffer);
555
556 puts ("Proxy authentication is required.\n");
557
558 user = prompt ("Proxy username", NULL);
559 if (!InternetSetOption
560 (connect, INTERNET_OPTION_PROXY_USERNAME, user,
561 strlen (user)))
562 {
563 xfree (user);
564 winerror ();
565 return 0;
566 }
567 else
568 {
569 xfree (user);
570 password = prompt ("Proxy password", NULL);
571 if (!InternetSetOption
572 (connect, INTERNET_OPTION_PROXY_PASSWORD,
573 password,
574 strlen (password))
575 || !HttpSendRequest (connect, NULL, 0, NULL, 0))
576 {
577 xfree (password);
578 winerror ();
579 return 0;
580 }
581 xfree (password);
582 }
583 }
584 }
585 else if (type != HTTP_STATUS_OK)
586 {
587 warning ("Error retrieving \"%s\".\n", url);
588 return 0;
589 }
590 else
591 authenticated = 1;
592 break;
593
594 default:
595 authenticated = 1;
596 break;
597 }
598
599 /* Now that authentication is complete read the file. */
600 if (!InternetQueryDataAvailable (connect, &size, 0, 0))
601 winerror ();
602 else
603 {
604 char *buffer = xmalloc (size);
605
606 FILE *out = fopen (file, "wb");
607 if (!out)
608 warning ("Unable to open \"%s\" for output: %s\n", file,
609 _strerror (""));
610 else
611 {
612 for (;;)
613 {
614 DWORD readbytes;
615
616 if (!InternetReadFile (connect, buffer, size, &readbytes))
617 winerror ();
618 else if (!readbytes)
619 {
620 retval = 1;
621 break;
622 }
623 else if (fwrite (buffer, 1, readbytes, out) != readbytes)
624 {
625 warning ("Error writing \"%s\": %s\n", file,
626 _strerror (""));
627 break;
628 }
629 }
630 fclose (out);
631 }
632 xfree (buffer);
633 }
634
635 if (1 || !is_ftp)
636 {
637 InternetCloseHandle (connect);
638 connect = NULL;
639 }
640 }
641 }
642
643 return retval;
644 }
645
646 static char *
647 findhref (char *buffer, char *date, size_t *filesize)
648 {
649 char *ref = NULL;
650 char *anchor;
651 char *p = buffer;
652
653 while ((p = strchr (p, '<')) != NULL)
654 {
655 char *q = p;
656 char ch = *++p;
657
658 if (tolower (ch) != 'a')
659 continue;
660
661 ch = *++p;
662 if (!isspace (ch))
663 continue;
664
665 for (++p; isspace (*p); p++)
666 continue;
667
668 if (strnicmp (p, "href=", 5) == 0)
669 {
670 ref = p;
671 anchor = q;
672 }
673 }
674
675 if (ref)
676 {
677 int eatspace;
678 char *p, *q;
679 char digits[20];
680 char *diglast = digits + sizeof (digits) - 1;
681 int len;
682
683 ref += ref[5] == '"' ? 6 : 5;
684
685 len = strcspn (ref, "\" >");
686
687 ref[len] = '\0';
688 if (!filesize)
689 return ref;
690
691 *filesize = 0;
692
693 if (anchor == buffer || !isspace (anchor[-1]))
694 return ref;
695
696 eatspace = 1;
697 *diglast = '\0';
698 for (p = anchor, q = diglast; --p >= buffer; )
699 if (!isspace (*p))
700 {
701 eatspace = 0;
702 if (isdigit (*p))
703 *--q = *p;
704 }
705 else if (!eatspace)
706 break;
707
708 if (q < diglast)
709 *filesize = atoi (q);
710 }
711
712 return ref;
713 }
714
715 static int
716 needfile (const char *filename, char *date, size_t filesize)
717 {
718 struct _stat st;
719
720 if (!filesize || _stat (filename, &st))
721 return 1; /* file doesn't exist or is somehow not accessible */
722 return st.st_size != filesize;
723 }
724
725 static int
726 processdirlisting (const char *urlbase, const char *file)
727 {
728 int retval;
729 char buffer[256];
730 static enum {UNKNOWN, ALWAYS, NEVER} download_when = {UNKNOWN};
731
732 FILE *in = fopen (file, "rt");
733
734 while (fgets (buffer, sizeof (buffer), in))
735 {
736 size_t filesize;
737 char filedate[80];
738 char *ref = findhref (buffer[0] ? buffer : buffer + 1, filedate, &filesize);
739 char url[256];
740 DWORD urlspace = sizeof (url);
741 struct _stat st;
742
743 if (!ref || strnicmp (ref, "http:", 5) == 0)
744 continue;
745
746 if (!InternetCombineUrl
747 (urlbase, ref, url, &urlspace,
748 ICU_BROWSER_MODE | ICU_ENCODE_SPACES_ONLY | ICU_NO_META))
749 {
750 warning ("Unable to download from %s", ref);
751 winerror ();
752 }
753 else if (ref[strlen (ref) - 1] == '/')
754 {
755 if (strcmp (url + strlen (url) - 2, "./") != 0)
756 downloaddir (url);
757 }
758 else if (strstr (url, ".tar.gz") && !strstr (url, "-src"))
759 {
760 int download = 0;
761 char *filename = strrchr (url, '/') + 1;
762 if (download_when == ALWAYS || needfile (filename, filedate, filesize))
763 download = 1;
764 else
765 {
766 char text[_MAX_PATH];
767 char *answer;
768
769 if (download_when == NEVER)
770 answer = xstrdup ("N");
771 else
772 {
773 sprintf (text, "Replace %s from the Internet (ynAN)", filename);
774 answer = prompt (text, "y");
775 }
776
777 if (answer)
778 {
779 switch (*answer)
780 {
781 case 'a':
782 case 'A':
783 download_when = ALWAYS;
784 /* purposely fall through */
785 case 'y':
786 case 'Y':
787 download = 1;
788 break;
789 case 'N':
790 download_when = NEVER;
791 warning ("Skipping %s\n", filename);
792 case 'n':
793 default:
794 download = 0;
795 }
796 xfree (answer);
797 }
798 }
799
800 if (!download)
801 continue;
802
803 for (;;) /* file retrieval loop */
804 {
805 int res;
806 warning ("Downloading: %s...", filename);
807 fflush (stdout);
808 res = geturl (url, filename, 0);
809 if ((res && !filesize) || !needfile (filename, filedate, filesize))
810 warning ("Done.\n");
811 else
812 {
813 for (;;) /* prompt loop */
814 {
815 char a;
816 char *answer;
817 if (!res)
818 warning ("Download failed.\n");
819 else
820 fprintf (logfp, "Downloaded file size does not match (%ld).\n",
821 filesize);
822 answer = prompt (res ? "Downloaded file size does not match (Abort, Retry, Fail)" :
823 "Download failed (Abort, Retry, Fail)", "R");
824 a = toupper (*answer);
825 xfree (answer);
826 switch (a)
827 {
828 case 'R':
829 break; /* try it again */
830 case 'A':
831 exit (1); /* abort program */
832 case 'F':
833 warning ("Deleting %s.\n", filename);
834 unlink (filename);
835 goto noget; /* Keep trying to download the rest */
836 default:
837 continue; /* erroneous response */
838 }
839
840 break; /* from prompt loop */
841 }
842 }
843
844 noget:
845 break; /* Leave from file retrieval for loop.
846 Either we successfully downloaded the file
847 or the user said don't bother. */
848 }
849 }
850 }
851
852 fflush (stdout);
853 retval = feof (in);
854
855 fclose (in);
856
857 return retval;
858 }
859
860 static char *
861 tmpfilename ()
862 {
863 return _tempnam (NULL, "su");
864 }
865
866 static int
867 downloaddir (const char *url)
868 {
869 int retval = 0;
870 char *file = tmpfilename ();
871
872 if (geturl (url, file, 1))
873 {
874 retval = processdirlisting (url, file);
875 unlink (file);
876 }
877 xfree (file);
878
879 return retval;
880 }
881
882
883 static HINTERNET
884 opensession ()
885 {
886 return InternetOpen ("Cygwin Setup", INTERNET_OPEN_TYPE_PRECONFIG, NULL,
887 NULL, 0);
888 }
889
890 static int
891 downloadfrom (const char *url)
892 {
893 int retval = 0;
894 char *file = tmpfilename ();
895
896 if (geturl (url, file, 1))
897 {
898 retval = processdirlisting (url, file);
899 unlink (file);
900 }
901
902 xfree (file);
903
904 return retval;
905 }
906
907 static int
908 reverse_sort (const void *arg1, const void *arg2)
909 {
910 return -strcmp (*(char **) arg1, *(char **) arg2);
911 }
912
913 static int
914 create_uninstall (const char *wd, const char *folder, const char *shellscut,
915 const char *shortcut)
916 {
917 int retval = 0;
918 char buffer[MAX_PATH];
919 clock_t start;
920 HINSTANCE lib;
921
922 warning ("Creating the uninstall file...");
923 fflush (stdout);
924 if (files.array)
925 {
926 size_t n;
927 FILE *uninst;
928 char cwd[MAX_PATH];
929 char *uninstfile;
930
931 getcwd (cwd, sizeof (cwd));
932 uninstfile = pathcat (cwd, "uninst.bat");
933 uninst = fopen (uninstfile, "wt");
934
935 if (uninst)
936 {
937 unsigned percent = 0;
938 struct _stat st;
939
940 files.array[++files.index] = pathcat (cwd, "bin\\cygwin.bat");
941 files.count = files.index + 1;
942 qsort (files.array, files.count, sizeof (char *), reverse_sort);
943
944 fprintf (uninst,
945 "@echo off\n" "%c:\n" "cd \"%s\"\n", *cwd, cwd);
946 for (n = 0; n < files.count; ++n)
947 {
948 char *dpath;
949
950 if (n && !strcmp (files.array[n], files.array[n - 1]))
951 continue;
952
953 dpath = files.array[n];
954
955 if (_stat (dpath, &st) == 0 && st.st_mode & _S_IFDIR)
956 fprintf (uninst, "rmdir \"%s\"\n", dpath);
957 else
958 {
959 if (access (dpath, 6) != 0)
960 fprintf (uninst, "attrib -r \"%s\"\n", dpath);
961 fprintf (uninst, "del \"%s\"\n", dpath);
962 }
963 }
964 fprintf (uninst,
965 "del \"%s\"\n"
966 "del \"%s\"\n"
967 "rmdir \"%s\"\n"
968 "del %s\n", shortcut, shellscut,
969 folder, uninstfile);
970 fclose (uninst);
971
972 create_shortcut (uninstfile, shortcut);
973 }
974 sa_cleanup (&files);
975 retval = 1;
976 }
977
978 if (lib)
979 FreeLibrary (lib);
980
981 warning ("Done.\n");
982 return retval;
983 }
984
985
986 /* Writes the startup batch file. */
987 static int
988 do_start_menu (const char *root)
989 {
990 FILE *batch;
991 char *batch_name = pathcat (root, "bin\\cygwin.bat");
992 int retval = 0;
993
994 /* Create the batch file for the start menu. */
995 if (batch_name)
996 {
997 batch = fopen (batch_name, "wt");
998 if (batch)
999 {
1000 LPITEMIDLIST progfiles;
1001 char pfilespath[_MAX_PATH];
1002 char *folder;
1003
1004 fprintf (batch,
1005 "@echo off\n"
1006 "SET MAKE_MODE=unix\n"
1007 "SET PATH=%s\\bin;%s\\usr\\local\\bin;%%PATH%%\n"
1008 "bash\n", root, root);
1009 fclose (batch);
1010
1011 /* Create a shortcut to the batch file */
1012 SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &progfiles);
1013 SHGetPathFromIDList (progfiles, pfilespath);
1014
1015 folder = pathcat (pfilespath, "Cygnus Solutions");
1016 if (folder)
1017 {
1018 char *shortcut;
1019 mkdir (folder); /* Ignore the result, it may exist. */
1020
1021 shortcut = pathcat (folder, "Cygwin 1.1.0.lnk");
1022 if (shortcut)
1023 {
1024 char *cmdline;
1025 OSVERSIONINFO verinfo;
1026 verinfo.dwOSVersionInfoSize = sizeof (verinfo);
1027
1028 /* If we are running Win9x, build a command line. */
1029 GetVersionEx (&verinfo);
1030 if (verinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
1031 cmdline = xstrdup (batch_name);
1032 else
1033 {
1034 char *pccmd;
1035 char windir[MAX_PATH];
1036 GetWindowsDirectory (windir, sizeof (windir));
1037
1038 pccmd = pathcat (windir, COMMAND9X);
1039 cmdline =
1040 xmalloc (strlen (pccmd) + strlen (batch_name) + 1);
1041 strcat (strcpy (cmdline, pccmd), batch_name);
1042 xfree (pccmd);
1043 }
1044
1045 if (create_shortcut (cmdline, shortcut))
1046 {
1047 char *uninstscut =
1048 pathcat (folder, "Uninstall Cygwin 1.1.0.lnk");
1049 if (uninstscut)
1050 {
1051 if (create_uninstall
1052 (wd, folder, shortcut, uninstscut))
1053 retval = 1;
1054 xfree (uninstscut);
1055 }
1056 }
1057 xfree (cmdline);
1058 xfree (shortcut);
1059 }
1060 xfree (folder);
1061 }
1062 }
1063
1064 xfree (batch_name);
1065 }
1066 return retval;
1067 }
1068
1069 static char *
1070 getdownloadsource ()
1071 {
1072 char *retval = NULL;
1073 char *filename = tmpfilename ();
1074
1075 /* Initialize session one time and one time only */
1076 session = opensession ();
1077
1078 if (!session)
1079 {
1080 winerror ();
1081 exit (1);
1082 }
1083
1084 if (!geturl ("http://sourceware.cygnus.com/cygwin/mirrors.html",
1085 filename, 1))
1086 fputs ("Unable to retrieve the list of cygwin mirrors.\n", stderr);
1087 else
1088 {
1089 FILE *in = fopen (filename, "rt");
1090
1091 if (!in)
1092 warning ("Unable to open %s for input.\n", filename);
1093 else
1094 {
1095 size_t option;
1096 int ready4urls = 0;
1097 char buf[256];
1098 SA urls, names; /* These must stay sync'd. */
1099
1100 sa_init (&urls);
1101 sa_init (&names);
1102
1103 while (fgets (buf, sizeof (buf), in))
1104 {
1105 if (!ready4urls)
1106 {
1107 if (strstr (buf, "Mirror Sites:"))
1108 ready4urls = 1;
1109 }
1110 else
1111 {
1112 char *ref = findhref (buf, NULL, NULL);
1113
1114 if (ref)
1115 {
1116 size_t len = strlen (ref);
1117
1118 if (ref[len - 1] == '/')
1119 {
1120 char *name;
1121 char *url = xmalloc (len + 13);
1122
1123 strcat (strcpy (url, ref), DOWNLOAD_SUBDIR);
1124 sa_add (&urls, url);
1125
1126 /* Get just the sites name. */
1127 name = strstr (url, "//");
1128 if (name)
1129 name += 2;
1130 else
1131 name = url;
1132 *strchr (name, '/') = '\0';
1133 sa_add (&names, url);
1134
1135 xfree (url);
1136 }
1137 }
1138 }
1139 }
1140
1141 sa_add (&urls, "Other");
1142 sa_add (&names, "Other");
1143 option =
1144 optionprompt ("Select a download location close to you:", &names);
1145 if (option == urls.count - 1)
1146 retval = prompt ("Download url", NULL);
1147 else
1148 retval = xstrdup (urls.array[option]);
1149
1150 sa_cleanup (&urls);
1151 sa_cleanup (&names);
1152 }
1153 }
1154 unlink (filename);
1155
1156 return retval;
1157 }
1158
1159
1160 /* Basically a mkdir -p /somedir function. */
1161 static void
1162 mkdirp (const char *dir)
1163 {
1164 if (mkdir (dir) == -1 && errno != EEXIST)
1165 {
1166 char *parent = strdup (dir);
1167 char *slash = strrchr (parent, '\\');
1168
1169 if (slash)
1170 {
1171 *slash = '\0';
1172 mkdirp (parent);
1173 }
1174
1175 xfree (parent);
1176
1177 mkdir (dir);
1178 }
1179 }
1180
1181 /* This routine assumes that the cwd is the root directory. */
1182 static int
1183 mkmount (const char *mountexedir, const char *root, const char *dospath,
1184 const char *unixpath, int force)
1185 {
1186 char *mount, *bslashed, *fulldospath, *p;
1187 char buffer[1024];
1188
1189 if (*root == '\0')
1190 fulldospath = xstrdup (dospath);
1191 else
1192 {
1193 xumount (mountexedir, unixpath);
1194 /* Make sure the mount point exists. */
1195 mount = utodpath (unixpath);
1196 mkdirp (mount);
1197 xfree (mount);
1198 fulldospath = pathcat (root, dospath);
1199 }
1200
1201 /* Make sure the target path exists. */
1202 mkdirp (fulldospath);
1203
1204 /* Mount the directory. */
1205 mount = pathcat (mountexedir, "mount");
1206 sprintf (buffer, "%s %s -b \"%s\" %s", mount, force ? "-f" : "",
1207 fulldospath, unixpath);
1208 xfree (mount);
1209 xfree (fulldospath);
1210
1211 return xcreate_process (1, NULL, NULL, NULL, buffer) != 0;
1212 }
1213
1214 static char rev[] = "$Revision$ ";
1215
1216 int
1217 main ()
1218 {
1219 int retval = 1; /* Default to error code */
1220 clock_t start;
1221 char *logpath = NULL;
1222 char *revn, *p;
1223 int fd = _open ("nul", _O_WRONLY | _O_BINARY);
1224
1225 devnull = (HANDLE) _get_osfhandle (fd);
1226
1227 setbuf (stdout, NULL);
1228 SetEnvironmentVariable ("CYGWIN", NULL);
1229 revn = strchr (rev, ':');
1230 if (!revn || (p = strchr (revn + 2, ' ')) == NULL)
1231 revn = "";
1232 else
1233 {
1234 revn--;
1235 memcpy (revn, " (v", 3);
1236 *p = ')';
1237 p[1] = '\0';
1238 }
1239
1240 printf ( "\n\n\n\n"
1241 "This is the Cygwin setup utility%s,\n"
1242 "built on " __DATE__ " " __TIME__ ".\n\n"
1243 "Use this program to install the latest version of the Cygwin Utilities\n"
1244 "from the Internet.\n\n"
1245 "Alternatively, if you already have already downloaded the appropriate files\n"
1246 "to the current directory (and subdirectories below it), this program can use
1247 those as the basis for your installation.\n\n"
1248 "If you are installing from the Internet, please run this program in an empty\n"
1249 "temporary directory.\n\n", revn);
1250
1251 start = clock ();
1252 if (!EnumResourceNames (NULL, "FILE", output_file, 0))
1253 {
1254 winerror ();
1255 }
1256 else
1257 {
1258 char *defroot, *update;
1259 char *root;
1260 int done;
1261 HKEY cu = NULL, lm = NULL;
1262
1263 wd = _getcwd (NULL, 0);
1264 setpath (wd);
1265
1266 logpath = pathcat (wd, "setup.log");
1267 tarpgm = pathcat (wd, "tar.exe");
1268
1269 if (logpath)
1270 logfp = fopen (logpath, "wt");
1271
1272 if (logfp == NULL)
1273 {
1274 fprintf (stderr, "Unable to open log file '%s' for writing - %s\n",
1275 logpath, _strerror (""));
1276 exit (1);
1277 }
1278
1279 /* Begin prompting user for setup requirements. */
1280 printf ("Press <enter> to accept the default value.\n");
1281
1282 /* If some Cygnus software has been installed, assume there is a root
1283 mount in the registry. Otherwise use C:\cygwin for the default root
1284 directory. */
1285 if (RegOpenKey (HKEY_CURRENT_USER, CYGNUS_KEY, &cu) == ERROR_SUCCESS
1286 || RegOpenKey (HKEY_LOCAL_MACHINE, CYGNUS_KEY,
1287 &lm) == ERROR_SUCCESS)
1288 {
1289 defroot = utodpath ("/");
1290 if (cu)
1291 RegCloseKey (cu);
1292 if (lm)
1293 RegCloseKey (lm);
1294 }
1295 else
1296 defroot = xstrdup (DEF_ROOT);
1297
1298 /* Get the root directory and warn the user if there are any spaces in
1299 the path. */
1300 for (done = 0; !done;)
1301 {
1302 root = prompt ("Root directory", defroot);
1303 if (strcmpi (root, wd) == 0)
1304 {
1305 printf ("Please do not use the current directory as the root directory.\n"
1306 "You should run setup.exe from a temporary directory.\n");
1307 continue;
1308 }
1309 if (strchr (root, ' '))
1310 {
1311 char *temp;
1312 temp =
1313 prompt
1314 ("Using spaces in the root directory path may cause problems."
1315 " Continue anyway", "no");
1316 if (toupper (*temp) == 'Y')
1317 done = 1;
1318 xfree (temp);
1319 }
1320 else
1321 done = 1;
1322 }
1323 xfree (defroot);
1324
1325 /* Create the root directory. */
1326 mkdir (root); /* Ignore any return value since it may
1327 already exist. */
1328 mkmount (wd, "", root, "/", 1);
1329
1330 update =
1331 prompt ("Install from the current directory (d) or from the Internet (i)", "i");
1332 if (toupper (*update) == 'I')
1333 {
1334 char *dir = getdownloadsource ();
1335
1336 if (!dir)
1337 {
1338 fprintf (stderr, "Couldn't connect to download site.\n");
1339 exit (1);
1340 }
1341
1342 downloadfrom (dir);
1343 InternetCloseHandle (session);
1344 xfree (dir);
1345 }
1346 xfree (update);
1347
1348 /* Make the root directory the current directory so that recurse_dirs
1349 will * extract the packages into the correct path. */
1350 if (chdir (root) == -1)
1351 {
1352 warning ("Unable to make \"%s\" the current directory: %s\n",
1353 root, _strerror (""));
1354 }
1355 else
1356 {
1357 FILE *fp;
1358
1359 _chdrive (toupper (*root) - 'A' + 1);
1360
1361 xumount (wd, "/usr");
1362 xumount (wd, "/var");
1363 xumount (wd, "/lib");
1364 xumount (wd, "/bin");
1365
1366 /* Make /bin point to /usr/bin and /lib point to /usr/lib. */
1367 mkmount (wd, root, "bin", "/usr/bin", 1);
1368 mkmount (wd, root, "lib", "/usr/lib", 1);
1369
1370 mkdirp ("var\\run");
1371 /* Create /var/run/utmp */
1372 fp = fopen ("var\\run\\utmp", "wb");
1373 if (fp)
1374 fclose (fp);
1375
1376 files.count = NFILE_LIST;
1377 files.array = calloc (sizeof (char *), NFILE_LIST + NFILE_SLOP);
1378 files.index = -1;
1379
1380 /* Extract all of the packages that are stored with setup or in
1381 subdirectories of its location */
1382 if (recurse_dirs (wd))
1383 {
1384 char *mount;
1385 char buffer[1024];
1386
1387 /* Mount the new root directory. */
1388 mount = pathcat (wd, "mount");
1389 sprintf (buffer, "%s -f -b \"%s\" /", mount, root);
1390 xfree (mount);
1391 if (xsystem (buffer))
1392 {
1393 printf
1394 ("Unable to mount \"%s\" as the root directory: %s",
1395 root, _strerror (""));
1396 }
1397 else
1398 {
1399 char **a;
1400 /* bash expects a /tmp */
1401 char *tmpdir = pathcat (root, "tmp");
1402
1403 if (tmpdir)
1404 {
1405 files.array[++files.index] = tmpdir;
1406 mkdir (tmpdir); /* Ignore the result, it may
1407 exist. */
1408 }
1409
1410 files.array[++files.index] = pathcat (root, "usr\\local");
1411 files.array[++files.index] = pathcat (root, "usr\\local\\bin");
1412 files.array[++files.index] = pathcat (root, "usr\\local\\lib");
1413 files.array[++files.index] = pathcat (root, "usr\\local\\etc");
1414 mkdirp (files.array[files.index]);
1415 mkdir (files.array[files.index - 1]);
1416
1417 if (do_start_menu (root))
1418 retval = 0; /* Everything worked return
1419 successful code */
1420 }
1421 }
1422 }
1423
1424 xfree (root);
1425
1426 chdir (wd);
1427 _chdrive (toupper (*wd) - 'A' + 1);
1428 xfree (wd);
1429 }
1430
1431 printf ("\nInstallation took %.0f seconds.\n",
1432 (double) (clock () - start) / CLK_TCK);
1433
1434 if (logpath)
1435 {
1436 fclose (logfp);
1437 xfree (logpath);
1438 }
1439
1440 return retval;
1441 }
This page took 0.097124 seconds and 5 git commands to generate.