14 #include <sys/types.h>
19 #include "zlib/zlib.h"
21 #define CYGNUS_KEY "Software\\Cygnus Solutions"
22 #define DEF_ROOT "C:\\cygwin"
23 #define DOWNLOAD_SUBDIR "latest"
24 #define SCREEN_LINES 25
25 #define COMMAND9X "command.com /E:4096 /c "
28 #define NFILE_LIST 10000
37 int downloaddir (HINTERNET session
, const char *url
);
39 static SA files
= {NULL
, 0, 0};
42 create_shortcut (const char *target
, const char *shortcut
)
48 if (!SUCCEEDED (CoInitialize (NULL
)))
52 CoCreateInstance (&CLSID_ShellLink
, NULL
,
53 CLSCTX_INPROC_SERVER
, &IID_IShellLink
, (LPVOID
*) & sl
);
60 /* Get the command only. */
61 path
= xstrdup (target
);
62 for (c
= path
; quoted
|| (*c
!= ' ' && *c
); ++c
)
75 sl
->lpVtbl
->SetPath (sl
, path
);
76 sl
->lpVtbl
->SetArguments (sl
, args
);
79 hres
= sl
->lpVtbl
->QueryInterface (sl
, &IID_IPersistFile
, &pf
);
83 WCHAR widepath
[_MAX_PATH
];
85 // Ensure that the string is Unicode.
86 MultiByteToWideChar (CP_ACP
, 0, shortcut
, -1, widepath
, MAX_PATH
);
88 // Save the link by calling IPersistFile::Save.
89 hres
= pf
->lpVtbl
->Save (pf
, widepath
, TRUE
);
90 pf
->lpVtbl
->Release (pf
);
92 sl
->lpVtbl
->Release (sl
);
97 return SUCCEEDED (hres
);
102 output_file (HMODULE h
, LPCTSTR type
, LPTSTR name
, LONG lparam
)
111 if ((rsrc
= FindResource (NULL
, name
, "FILE"))
112 && (res
= LoadResource (NULL
, rsrc
))
113 && (data
= (char *) LockResource (res
)) && (out
= fopen (strlwr (name
), "w+b")))
117 size_t bytes
= SizeofResource (NULL
, rsrc
);
119 if (bytes
!= fwrite (data
, 1, bytes
, out
))
120 printf ("Unable to write %s: %s", name
, _strerror (""));
122 bytes_needed
= *(int *) ((char *) data
+ bytes
- sizeof (int));
123 buffer
= (char *) xmalloc (bytes_needed
);
126 gzf
= gzdopen (_dup (fileno (out
)), "rb");
127 if (gzf
&& (size_t) gzread (gzf
, buffer
, bytes_needed
) == bytes_needed
)
130 if (fseek (out
, 0, SEEK_SET
)
131 || fwrite (buffer
, 1, bytes_needed
, out
) != bytes_needed
)
133 printf ("Unable to write decompressed file to %s: %s",
134 name
, _strerror (""));
142 const char *msg
= gzerror (gzf
, &errnum
);
143 printf ("bytes_needed = %d, ", bytes_needed
);
144 printf ("Unable to decompress %s: Error #%d, %s\n", name
,
152 printf ("Unable to write %s: %s", name
, _strerror (""));
159 tarx (const char *dir
, const char *fn
, FILE *logfp
)
163 char *buffer
= buffer0
+ 1;
169 dpath
= pathcat (dir
, fn
);
170 path
= dtoupath (dpath
);
171 sprintf (buffer
, "tar xvfUz \"%s\"", path
);
175 printf ("Installing %s\n", fn
);
177 if (_pipe (hpipe
, 256, O_TEXT
) == -1)
180 hin
= (HANDLE
) _get_osfhandle (hpipe
[1]);
181 if (xcreate_process (0, NULL
, hin
, hin
, buffer
) == 0)
183 printf ("Unable to extract \"%s\": %s", fn
, _strerror (""));
187 fp
= fdopen (hpipe
[0], "rt");
189 filehere
= files
.index
;
190 while (fgets (buffer
, sizeof (buffer0
), fp
))
192 char *s
= strchr (buffer
, '\n');
197 if (strchr (buffer
, ':') != NULL
)
200 fprintf (stderr
, "%s\n", s
);
204 if (++files
.index
>= files
.count
)
205 files
.array
= realloc (files
.array
,
206 NFILE_SLOP
+ (files
.count
+= NFILE_LIST
));
210 s
= files
.array
[files
.index
] = utodpath (s
);
213 fprintf (logfp
, "%s\n", s
);
217 while (++filehere
<= files
.index
)
218 (void) chmod (files
.array
[files
.index
], 0777);
224 recurse_dirs (const char *dir
, FILE *logfp
)
229 char *pattern
= pathcat (dir
, "*");
232 WIN32_FIND_DATA find_data
;
235 handle
= FindFirstFile (pattern
, &find_data
);
236 if (handle
!= INVALID_HANDLE_VALUE
)
238 /* Recurse through all subdirectories */
241 if (strcmp (find_data
.cFileName
, ".") == 0
242 || strcmp (find_data
.cFileName
, "..") == 0)
245 if (find_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
246 /* && strlen(find_data.cFileName) */ )
248 char *subdir
= pathcat (dir
, find_data
.cFileName
);
251 if (!recurse_dirs (subdir
, logfp
))
264 while (FindNextFile (handle
, &find_data
) && !err
);
267 /* Look for .tar.gz files */
271 pattern
= pathcat (dir
, "*.tar.gz");
272 handle
= FindFirstFile (pattern
, &find_data
);
273 if (handle
!= INVALID_HANDLE_VALUE
)
279 /* Skip source archives and meta-directories */
280 if (strstr (find_data
.cFileName
, "-src.tar.gz")
281 || strstr (find_data
.cFileName
, "-src-")
282 || strcmp (find_data
.cFileName
, ".") == 0
283 || strcmp (find_data
.cFileName
, "..") == 0)
288 if (!tarx (dir
, find_data
.cFileName
, logfp
))
294 while (FindNextFile (handle
, &find_data
));
312 setpath (const char *element
)
314 char *buffer
= xmalloc (strlen (element
) + 7);
316 sprintf (buffer
, "PATH=%s", element
);
323 prompt (const char *text
, const char *def
)
325 char buffer
[_MAX_PATH
];
328 printf ((def
? "%s? [%s] " : "%s? "), text
, def
);
329 fgets (buffer
, sizeof (buffer
), stdin
);
330 buffer
[strcspn (buffer
, "\r\n")] = '\0';
332 /* Duplicate the entered value or the default if nothing was entered. */
333 return xstrdup (strlen (buffer
) ? buffer
: def
? def
: "");
337 optionprompt (const char *text
, SA
* options
)
339 size_t n
, lbound
, response
;
349 { CONTINUE
, REPEAT
, ALL
}
355 for (n
= 0; n
< base
+ SCREEN_LINES
- 2 && n
< options
->count
; ++n
)
356 printf ("\t%d. %s\n", n
+ 1, options
->array
[n
]);
358 lbound
= n
- (SCREEN_LINES
- (base
? 2 : 3));
359 if (n
< options
->count
)
362 or = " or [continue]";
364 else if (options
->count
> SCREEN_LINES
- 2)
374 printf ("Select an option from %d-%d%s: ", lbound
, n
, or);
375 if (!fgets (buf
, sizeof (buf
), stdin
))
378 if (mode
== CONTINUE
&& (!isalnum (*buf
) || strchr ("cC", *buf
)))
380 else if (mode
== REPEAT
&& (!isalnum (*buf
) || strchr ("rR", *buf
)))
386 response
= atoi (buf
);
388 while (response
< lbound
|| response
> n
);
394 geturl (HINTERNET session
, const char *url
, const char *file
, int verbose
)
397 int authenticated
= 0;
404 printf ("Connecting to ftp site...");
407 for (tries
= 1; tries
<= 20; tries
++)
410 InternetOpenUrl (session
, url
, NULL
, 0,
411 INTERNET_FLAG_DONT_CACHE
|
412 INTERNET_FLAG_KEEP_CONNECTION
|
413 INTERNET_FLAG_RELOAD
, 0);
416 if (!verbose
|| tries
== 1)
419 printf ("\rConnecting to ftp site...(try %d) \b\b", tries
);
421 printf ("\rConnecting to ftp site...(try %d)", tries
);
426 puts ("\nCouldn't connect to ftp site."); fflush (stdout
);
434 printf ("\rConnecting to ftp site... \b\b\b\b\b\b\b\b");
435 printf ("Done.\n"); fflush (stdout
);
437 while (!authenticated
)
439 size
= sizeof (type
);
440 if (!InternetQueryOption
441 (connect
, INTERNET_OPTION_HANDLE_TYPE
, &type
, &size
))
449 case INTERNET_HANDLE_TYPE_HTTP_REQUEST
:
450 case INTERNET_HANDLE_TYPE_CONNECT_HTTP
:
451 size
= sizeof (DWORD
);
453 (connect
, HTTP_QUERY_STATUS_CODE
| HTTP_QUERY_FLAG_NUMBER
,
459 else if (type
== HTTP_STATUS_PROXY_AUTH_REQ
)
463 if (!InternetQueryDataAvailable (connect
, &len
, 0, 0))
470 char *user
, *password
;
472 /* Have to read any pending data, WININET peculiarity. */
473 char *buffer
= xmalloc (len
);
476 InternetReadFile (connect
, buffer
, len
, &size
);
481 puts ("Proxy authentication is required.\n");
483 user
= prompt ("Proxy username", NULL
);
484 if (!InternetSetOption
485 (connect
, INTERNET_OPTION_PROXY_USERNAME
, user
,
495 password
= prompt ("Proxy password", NULL
);
496 if (!InternetSetOption
497 (connect
, INTERNET_OPTION_PROXY_PASSWORD
,
500 || !HttpSendRequest (connect
, NULL
, 0, NULL
, 0))
510 else if (type
!= HTTP_STATUS_OK
)
512 printf ("Error retrieving \"%s\".\n", url
);
524 /* Now that authentication is complete read the file. */
525 if (!InternetQueryDataAvailable (connect
, &size
, 0, 0))
529 char *buffer
= xmalloc (size
);
531 FILE *out
= fopen (file
, "wb");
533 printf ("Unable to open \"%s\" for output: %s\n", file
,
541 if (!InternetReadFile (connect
, buffer
, size
, &readbytes
))
548 else if (fwrite (buffer
, 1, readbytes
, out
) != readbytes
)
550 printf ("Error writing \"%s\": %s\n", file
,
559 InternetCloseHandle (connect
);
567 findhref (char *buffer
)
569 char *ref
= strstr (buffer
, "href=");
572 ref
= strstr (buffer
, "HREF=");
577 ref
+= ref
[5] == '"' ? 6 : 5;
579 len
= strcspn (ref
, "\" >");
588 processdirlisting (HINTERNET session
, const char *urlbase
, const char *file
)
592 static enum {UNKNOWN
, ALWAYS
, NEVER
} download_when
= {UNKNOWN
};
594 FILE *in
= fopen (file
, "rt");
596 while (fgets (buffer
, sizeof (buffer
), in
))
598 char *ref
= findhref (buffer
);
603 DWORD urlspace
= sizeof (url
);
605 if (!InternetCombineUrl
606 (urlbase
, ref
, url
, &urlspace
,
607 ICU_BROWSER_MODE
| ICU_ENCODE_SPACES_ONLY
| ICU_NO_META
))
609 printf ("Unable to download from %s", ref
);
612 else if (ref
[strlen (ref
) - 1] == '/')
614 if (strcmp (url
+ strlen (url
) - 2, "./") != 0)
615 downloaddir (session
, url
);
617 else if (strstr (url
, ".tar.gz") && !strstr (url
, "-src"))
620 char *filename
= strrchr (url
, '/') + 1;
621 if (download_when
== ALWAYS
|| _access (filename
, 0) == -1)
625 char text
[_MAX_PATH
];
628 if (download_when
== NEVER
)
632 sprintf (text
, "Replace %s from the net (ynAN)", filename
);
633 answer
= prompt (text
, "y");
642 download_when
= ALWAYS
;
643 /* purposely fall through */
649 download_when
= NEVER
;
650 fprintf (stderr
, "Skipping %s\n", filename
);
661 printf ("Downloading: %s...", filename
);
663 if (geturl (session
, url
, filename
, 0))
669 printf ("\nUnable to retrieve %s\n", url
);
687 return xstrdup (tmpnam (NULL
));
691 downloaddir (HINTERNET session
, const char *url
)
694 char *file
= tmpfilename ();
696 if (geturl (session
, url
, file
, 1))
697 retval
= processdirlisting (session
, url
, file
);
704 HINTERNET
opensession ()
706 return InternetOpen ("Cygwin Setup", INTERNET_OPEN_TYPE_PRECONFIG
, NULL
,
711 downloadfrom (const char *url
)
715 HINTERNET session
= opensession ();
721 char *file
= tmpfilename ();
723 if (geturl (session
, url
, file
, 1))
724 retval
= processdirlisting (session
, url
, file
);
728 InternetCloseHandle (session
);
735 reverse_sort (const void *arg1
, const void *arg2
)
737 return -strcmp (*(char **) arg1
, *(char **) arg2
);
741 create_uninstall (const char *wd
, const char *folder
, const char *shellscut
,
742 const char *shortcut
)
745 char buffer
[MAX_PATH
];
749 printf ("Creating the uninstall file...");
758 getcwd (cwd
, sizeof (cwd
));
759 uninstfile
= pathcat (cwd
, "uninst.bat");
760 uninst
= fopen (uninstfile
, "wt");
764 unsigned percent
= 0;
767 files
.array
[++files
.index
] = pathcat (cwd
, "bin\\cygwin.bat");
768 files
.count
= files
.index
+ 1;
769 qsort (files
.array
, files
.count
, sizeof (char *), reverse_sort
);
772 "@echo off\n" "%c:\n" "cd \"%s\"\n", *cwd
, cwd
);
773 for (n
= 0; n
< files
.count
; ++n
)
777 if (n
&& !strcmp (files
.array
[n
], files
.array
[n
- 1]))
780 dpath
= files
.array
[n
];
782 if (_stat (dpath
, &st
) == 0 && st
.st_mode
& _S_IFDIR
)
783 fprintf (uninst
, "rmdir \"%s\"\n", dpath
);
786 if (access (dpath
, 6) != 0)
787 fprintf (uninst
, "attrib -r \"%s\"\n", dpath
);
788 fprintf (uninst
, "del \"%s\"\n", dpath
);
795 "del %s\n", shortcut
, shellscut
,
799 create_shortcut (uninstfile
, shortcut
);
813 /* Writes the startup batch file. */
815 do_start_menu (const char *root
)
818 char *batch_name
= pathcat (root
, "bin\\cygwin.bat");
821 /* Create the batch file for the start menu. */
824 batch
= fopen (batch_name
, "wt");
827 LPITEMIDLIST progfiles
;
828 char pfilespath
[_MAX_PATH
];
833 "SET MAKE_MODE=unix\n"
834 "SET PATH=%s\\bin;%s\\usr\\local\\bin;%%PATH%%\n"
835 "bash\n", root
, root
);
838 /* Create a shortcut to the batch file */
839 SHGetSpecialFolderLocation (NULL
, CSIDL_PROGRAMS
, &progfiles
);
840 SHGetPathFromIDList (progfiles
, pfilespath
);
842 folder
= pathcat (pfilespath
, "Cygnus Solutions");
846 mkdir (folder
); /* Ignore the result, it may exist. */
848 shortcut
= pathcat (folder
, "Cygwin 1.1.0.lnk");
852 OSVERSIONINFO verinfo
;
853 verinfo
.dwOSVersionInfoSize
= sizeof (verinfo
);
855 /* If we are running Win9x, build a command line. */
856 GetVersionEx (&verinfo
);
857 if (verinfo
.dwPlatformId
== VER_PLATFORM_WIN32_NT
)
858 cmdline
= xstrdup (batch_name
);
862 char windir
[MAX_PATH
];
863 GetWindowsDirectory (windir
, sizeof (windir
));
865 pccmd
= pathcat (windir
, COMMAND9X
);
867 xmalloc (strlen (pccmd
) + strlen (batch_name
) + 1);
868 strcat (strcpy (cmdline
, pccmd
), batch_name
);
872 if (create_shortcut (cmdline
, shortcut
))
875 pathcat (folder
, "Uninstall Cygwin 1.1.0.lnk");
879 (wd
, folder
, shortcut
, uninstscut
))
900 HINTERNET session
= opensession ();
901 char *filename
= tmpfilename ();
906 (session
, "http://sourceware.cygnus.com/cygwin/mirrors.html",
908 fputs ("Unable to retrieve the list of cygwin mirrors.\n", stderr
);
911 FILE *in
= fopen (filename
, "rt");
914 fprintf (stderr
, "Unable to open %s for input.\n", filename
);
920 SA urls
, names
; /* These must stay sync'd. */
925 while (fgets (buf
, sizeof (buf
), in
))
929 if (strstr (buf
, "Mirror Sites:"))
934 char *ref
= findhref (buf
);
938 size_t len
= strlen (ref
);
940 if (ref
[len
- 1] == '/')
943 char *url
= xmalloc (len
+ 13);
945 strcat (strcpy (url
, ref
), DOWNLOAD_SUBDIR
);
948 /* Get just the sites name. */
949 name
= strstr (url
, "//");
954 *strchr (name
, '/') = '\0';
955 sa_add (&names
, url
);
963 sa_add (&urls
, "Other");
964 sa_add (&names
, "Other");
966 optionprompt ("Select a download location close to you:", &names
);
967 if (option
== urls
.count
- 1)
968 retval
= prompt ("Download url", NULL
);
970 retval
= xstrdup (urls
.array
[option
]);
982 /* Basically a mkdir -p /somedir function. */
984 mkdirp (const char *dir
)
986 if (mkdir (dir
) == -1 && errno
!= EEXIST
)
988 char *parent
= strdup (dir
);
989 char *slash
= strrchr (parent
, '\\');
1004 /* This routine assumes that the cwd is the root directory. */
1006 mkmount (const char *mountexedir
, const char *root
, const char *dospath
,
1007 const char *unixpath
, int force
)
1009 char *mount
, *bslashed
, *fulldospath
, *p
;
1013 fulldospath
= xstrdup (dospath
);
1016 /* Make sure the mount point exists. */
1017 mount
= utodpath (unixpath
);
1020 fulldospath
= pathcat (root
, dospath
);
1023 /* Make sure the target path exists. */
1024 mkdirp (fulldospath
);
1026 /* Mount the directory. */
1027 mount
= pathcat (mountexedir
, "mount");
1028 sprintf (buffer
, "%s %s -b \"%s\" %s", mount
, force
? "-f" : "",
1029 fulldospath
, unixpath
);
1031 xfree (fulldospath
);
1033 return xsystem (buffer
) == 0;
1039 int retval
= 1; /* Default to error code */
1043 "This is the Cygwin setup utility.\n\n"
1044 "Use this program to install the latest version of the Cygwin Utilities\n"
1045 "from the Internet.\n\n"
1046 "Alternatively, if you already have already downloaded the appropriate files\n"
1047 "to the current directory, this program can use those as the basis for your\n"
1051 if (!EnumResourceNames (NULL
, "FILE", output_file
, 0))
1057 char *defroot
, *update
;
1060 HKEY cu
= NULL
, lm
= NULL
;
1062 wd
= _getcwd (NULL
, 0);
1065 /* Begin prompting user for setup requirements. */
1066 printf ("Press <enter> to accept the default value.\n");
1068 /* If some Cygnus software has been installed, assume there is a root
1069 mount in the registry. Otherwise use C:\cygwin for the default root
1071 if (RegOpenKey (HKEY_CURRENT_USER
, CYGNUS_KEY
, &cu
) == ERROR_SUCCESS
1072 || RegOpenKey (HKEY_LOCAL_MACHINE
, CYGNUS_KEY
,
1073 &lm
) == ERROR_SUCCESS
)
1075 defroot
= utodpath ("/");
1082 defroot
= xstrdup (DEF_ROOT
);
1084 /* Get the root directory and warn the user if there are any spaces in
1086 for (done
= 0; !done
;)
1088 root
= prompt ("Root directory", defroot
);
1089 if (strchr (root
, ' '))
1094 ("Using spaces in the root directory path may cause problems."
1095 " Continue anyway", "no");
1096 if (toupper (*temp
) == 'Y')
1105 /* Create the root directory. */
1106 mkdir (root
); /* Ignore any return value since it may
1108 mkmount (wd
, "", root
, "/", 1);
1111 prompt ("Install from the current directory (d) or from the Internet (i)", "i");
1112 if (toupper (*update
) == 'I')
1114 char *dir
= getdownloadsource ();
1124 /* Make the root directory the current directory so that recurse_dirs
1125 will * extract the packages into the correct path. */
1126 if (chdir (root
) == -1)
1128 printf ("Unable to make \"%s\" the current directory: %s\n",
1129 root
, _strerror (""));
1133 char *logpath
= pathcat (wd
, "setup.log");
1137 FILE *logfp
= fopen (logpath
, "w+t");
1141 fprintf (stderr
, "Unable to open log file '%s' for writing - %s\n",
1142 logpath
, _strerror (""));
1146 _chdrive (toupper (*root
) - 'A' + 1);
1148 /* Make /bin point to /usr/bin and /lib point to /usr/lib. */
1149 mkmount (wd
, root
, "bin", "/usr/bin", 1);
1150 mkmount (wd
, root
, "lib", "/usr/lib", 1);
1152 files
.count
= NFILE_LIST
;
1153 files
.array
= calloc (sizeof (char *), NFILE_LIST
+ NFILE_SLOP
);
1156 /* Extract all of the packages that are stored with setup or in
1157 subdirectories of its location */
1158 if (recurse_dirs (wd
, logfp
))
1163 /* Mount the new root directory. */
1164 mount
= pathcat (wd
, "mount");
1165 sprintf (buffer
, "%s -f -b \"%s\" /", mount
, root
);
1167 if (xsystem (buffer
))
1170 ("Unable to mount \"%s\" as the root directory: %s",
1171 root
, _strerror (""));
1176 /* bash expects a /tmp */
1177 char *tmpdir
= pathcat (root
, "tmp");
1181 files
.array
[++files
.index
] = tmpdir
;
1182 mkdir (tmpdir
); /* Ignore the result, it may
1186 files
.array
[++files
.index
] = pathcat (root
, "usr\\local");
1187 files
.array
[++files
.index
] = pathcat (root
, "usr\\local\\bin");
1188 files
.array
[++files
.index
] = pathcat (root
, "usr\\local\\lib");
1189 mkdirp (files
.array
[files
.index
]);
1190 mkdir (files
.array
[files
.index
- 1]);
1192 if (do_start_menu (root
))
1193 retval
= 0; /* Everything worked return
1206 _chdrive (toupper (*wd
) - 'A' + 1);
1210 printf ("\nInstallation took %.0f seconds.\n",
1211 (double) (clock () - start
) / CLK_TCK
);