Added translation using Weblate (Japanese)
[cygwin-setup.git] / filemanip.cc
blobca5e4ac058fd9d96427c405b37053b6a4aabf56f
1 /*
2 * Copyright (c) 2000, 2001, Red Hat, Inc.
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.
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
12 * Written by Robert Collins <rbtcollins@redhat.com>
16 /* The purpose of this file is to put all general purpose file manipulation
17 code in one place. */
19 #include <string.h>
20 #include <wchar.h>
21 #include <stdlib.h>
22 #include "filemanip.h"
23 #include "io_stream.h"
24 #include "String++.h"
25 #include "win32.h"
26 #include "ntdll.h"
27 #include "io.h"
28 #include "fcntl.h"
30 /* legacy wrapper.
31 * Clients should use io_stream.get_size() */
32 size_t
33 get_file_size (const std::string& name)
35 io_stream *theFile = io_stream::open (name, "", 0);
36 if (!theFile)
37 /* To consider: throw an exception ? */
38 return 0;
39 ssize_t rv = theFile->get_size();
40 delete theFile;
41 return rv;
44 /* returns the number of characters of path that
45 * precede the extension
47 int
48 find_tar_ext (const char *path)
50 char *p = strchr (path, '\0') - 9;
51 if (p <= path)
52 return 0;
53 if ((p = strstr (p, ".tar")) != NULL)
54 return p - path;
55 else
56 return 0;
59 /* Parse a filename into package, version, and extension components. */
60 int
61 parse_filename (const std::string &fn, fileparse & f)
63 char *p, *ver;
64 int n;
66 if (!(n = find_tar_ext (fn.c_str ())))
67 return 0;
69 f.pkg = "";
70 f.what = "";
72 f.tail = fn.substr (n, std::string::npos);
74 p = new_cstr_char_array (fn.substr (0, n));
75 char const *ext;
76 /* TODO: make const and non-const trail variant. */
77 if ((ext = trail (p, "-src")))
79 f.what = "-src";
80 *(char *)ext = '\0';
82 else if ((ext = trail (p, "-patch")))
84 f.what = "-patch";
85 *(char *)ext = '\0';
87 for (ver = p; *ver; ver++)
88 if (*ver == '-')
90 if (isdigit (ver[1]))
92 *ver++ = 0;
93 f.pkg = p;
94 break;
96 else if (strcasecmp (ver, "-src") == 0 ||
97 strcasecmp (ver, "-patch") == 0)
99 *ver++ = 0;
100 f.pkg = p;
101 f.what = strlwr (ver);
102 ver = strchr (ver, '\0');
103 break;
107 if (!f.pkg.size())
108 f.pkg = p;
110 if (!f.what.size())
112 int n;
113 char *p1 = strchr (ver, '\0');
114 if ((p1 -= 4) >= ver && strcasecmp (p1, "-src") == 0)
115 n = 4;
116 else if ((p1 -= 2) >= ver && strcasecmp (p1, "-patch") == 0)
117 n = 6;
118 else
119 n = 0;
120 if (n)
122 // get the 'src' or 'patch'.
123 f.what = p1 + 1;
127 f.ver = *ver ? ver : "0.0";
128 delete[] p;
129 return 1;
132 const char *
133 trail (const char *haystack, const char *needle)
135 /* See if the path ends with a specific suffix.
136 Just return if it doesn't. */
137 unsigned len = strlen (haystack);
138 int prefix_len = len - strlen (needle);
139 if (prefix_len < 0
140 || strcasecmp (haystack += prefix_len, needle) != 0)
141 return NULL;
142 return haystack;
145 std::string
146 backslash(const std::string& s)
148 std::string rv(s);
150 for (std::string::iterator it = rv.begin(); it != rv.end(); ++it)
151 if (*it == '/')
152 *it = '\\';
154 return rv;
157 wchar_t tfx_chars[] = {
158 0, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007,
159 0xf008, 0xf009, 0xf00a, 0xf00b, 0xf00c, 0xf00d, 0xf00e, 0xf00f,
160 0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017,
161 0xf018, 0xf019, 0xf01a, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f,
162 ' ', '!', 0xf000 | '"', '#', '$', '%', '&', 39,
163 '(', ')', 0xf000 | '*', '+', ',', '-', '.', '\\',
164 '0', '1', '2', '3', '4', '5', '6', '7',
165 '8', '9', 0xf000 | ':', ';', 0xf000 | '<', '=', 0xf000 | '>', 0xf000 | '?',
166 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
167 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
168 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
169 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
170 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
171 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
172 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
173 'x', 'y', 'z', '{', 0xf000 | '|', '}', '~', 127
177 mklongpath (wchar_t *tgt, const char *src, size_t len)
179 wchar_t *tp, *ts;
180 size_t ret, n;
181 mbstate_t mb;
183 wcscpy (tgt, L"\\\\?\\");
184 tp = tgt + 4;
185 len -= 4;
186 if (isdirsep (src[0]) && isdirsep (src[1]))
188 wcscpy (tp, L"UNC");
189 tp += 3;
190 len -= 3;
191 ts = tp;
192 ++src; /* Skip one leading backslash. */
194 else
195 ts = tp + 2; /* Skip colon in leading drive specifier. */
196 ret = tp - tgt;
197 memset (&mb, 0, sizeof mb);
198 while (len > 0)
200 n = mbrtowc (tp, src, 6, &mb);
201 if (n == (size_t) -1 || n == (size_t) -2)
202 return -1;
203 if (n == 0)
204 break;
205 src += n;
206 /* Transform char according to Cygwin rules. */
207 if (tp >= ts && *tp < 128)
208 *tp = tfx_chars[*tp];
209 /* Skip multiple backslashes. */
210 if (*tp == L'\\' && tp[-1] == L'\\')
211 continue;
212 /* Skip "." and ".." path components. They result in annoying error
213 messages. */
214 if (*tp == L'.' && tp[-1] == L'\\')
216 if (!src[0])
217 continue;
218 if (isdirsep (src[0]))
220 ++src;
221 continue;
223 if (src[0] == '.' && isdirsep (src[1]))
225 src += 2;
226 /* Set tp back to start of previous path component. */
227 if (tp > ts + 1)
230 --tp;
231 --ret;
232 ++len;
234 while (tp > ts && tp[-1] != L'\\');
235 continue;
238 ++ret;
239 ++tp;
240 --len;
242 if (len == 0)
243 return -1;
244 /* Always remove trailing backslash. */
245 if (tgt[ret - 1] == L'\\')
246 tgt[--ret] = L'\0';
247 return 0;
251 mklongrelpath (wchar_t *tgt, const char *src, size_t len)
253 wchar_t *tp;
254 size_t n;
255 mbstate_t mb;
257 tp = tgt;
258 memset (&mb, 0, sizeof mb);
260 while (len > 0)
262 n = mbrtowc (tp, src, 6, &mb);
263 if (n == (size_t) -1 || n == (size_t) -2)
264 return -1;
265 if (n == 0)
266 break;
267 src += n;
268 /* Transform char according to Cygwin rules. */
269 if (*tp < 128)
270 *tp = tfx_chars[*tp];
271 ++tp;
272 --len;
274 return 0;
277 /* Replacement functions for Win32 API functions. The joke here is that the
278 replacement functions always use the FILE_OPEN_FOR_BACKUP_INTENT flag. */
280 extern "C" DWORD WINAPI
281 GetFileAttributesW (LPCWSTR wpath)
283 NTSTATUS status;
284 HANDLE h;
285 IO_STATUS_BLOCK io;
286 UNICODE_STRING uname;
287 OBJECT_ATTRIBUTES attr;
288 DWORD ret = INVALID_FILE_ATTRIBUTES;
290 PWCHAR wname = (PWCHAR) wpath;
291 wname[1] = L'?';
292 RtlInitUnicodeString (&uname, wname);
293 InitializeObjectAttributes (&attr, &uname, OBJ_CASE_INSENSITIVE, NULL, NULL);
294 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES, &attr, &io,
295 FILE_SHARE_VALID_FLAGS,
296 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
297 wname[1] = L'\\';
298 if (NT_SUCCESS (status))
300 FILE_BASIC_INFORMATION fbi;
302 status = NtQueryInformationFile (h, &io, &fbi, sizeof fbi,
303 FileBasicInformation);
304 if (NT_SUCCESS (status))
305 ret = fbi.FileAttributes;
306 NtClose (h);
308 if (!NT_SUCCESS (status))
309 SetLastError (RtlNtStatusToDosError (status));
310 return ret;
313 extern "C" BOOL WINAPI
314 SetFileAttributesW (LPCWSTR wpath, DWORD attribs)
316 NTSTATUS status;
317 HANDLE h;
318 IO_STATUS_BLOCK io;
319 UNICODE_STRING uname;
320 OBJECT_ATTRIBUTES attr;
322 PWCHAR wname = (PWCHAR) wpath;
323 wname[1] = L'?';
324 RtlInitUnicodeString (&uname, wname);
325 InitializeObjectAttributes (&attr, &uname, OBJ_CASE_INSENSITIVE, NULL, NULL);
326 status = NtOpenFile (&h, READ_CONTROL | FILE_WRITE_ATTRIBUTES, &attr, &io,
327 FILE_SHARE_VALID_FLAGS,
328 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
329 wname[1] = L'\\';
330 if (NT_SUCCESS (status))
332 FILE_BASIC_INFORMATION fbi;
334 memset (&fbi, 0, sizeof fbi);
335 fbi.FileAttributes = attribs ?: FILE_ATTRIBUTE_NORMAL;
336 status = NtSetInformationFile (h, &io, &fbi, sizeof fbi,
337 FileBasicInformation);
338 NtClose (h);
340 if (!NT_SUCCESS (status))
341 SetLastError (RtlNtStatusToDosError (status));
342 return NT_SUCCESS (status);
345 extern "C" BOOL WINAPI
346 MoveFileW (LPCWSTR from, LPCWSTR to)
348 NTSTATUS status;
349 HANDLE h;
350 IO_STATUS_BLOCK io;
351 UNICODE_STRING uname;
352 OBJECT_ATTRIBUTES attr;
354 PWCHAR wfrom = (PWCHAR) from;
355 wfrom[1] = L'?';
356 RtlInitUnicodeString (&uname, wfrom);
357 InitializeObjectAttributes (&attr, &uname, OBJ_CASE_INSENSITIVE, NULL, NULL);
358 status = NtOpenFile (&h, READ_CONTROL | DELETE,
359 &attr, &io, FILE_SHARE_VALID_FLAGS,
360 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
361 wfrom[1] = L'\\';
362 if (NT_SUCCESS (status))
364 size_t len = wcslen (to) * sizeof (WCHAR);
365 PFILE_RENAME_INFORMATION pfri = (PFILE_RENAME_INFORMATION)
366 malloc (sizeof (FILE_RENAME_INFORMATION) + len);
367 pfri->ReplaceIfExists = TRUE;
368 pfri->RootDirectory = NULL;
369 pfri->FileNameLength = len;
370 memcpy (pfri->FileName, to, len);
371 pfri->FileName[1] = L'?';
372 status = NtSetInformationFile(h, &io, pfri,
373 sizeof (FILE_RENAME_INFORMATION) + len,
374 FileRenameInformation);
375 free (pfri);
376 NtClose (h);
378 if (!NT_SUCCESS (status))
379 SetLastError (RtlNtStatusToDosError (status));
380 return NT_SUCCESS (status);
383 static BOOL
384 unlink (LPCWSTR wpath, ULONG file_attr)
386 NTSTATUS status;
387 HANDLE h;
388 IO_STATUS_BLOCK io;
389 UNICODE_STRING uname;
390 OBJECT_ATTRIBUTES attr;
392 PWCHAR wname = (PWCHAR) wpath;
393 wname[1] = L'?';
394 RtlInitUnicodeString (&uname, wname);
395 InitializeObjectAttributes (&attr, &uname, OBJ_CASE_INSENSITIVE, NULL, NULL);
396 status = NtOpenFile (&h, READ_CONTROL | DELETE,
397 &attr, &io, FILE_SHARE_VALID_FLAGS,
398 FILE_OPEN_FOR_BACKUP_INTENT
399 | FILE_OPEN_REPARSE_POINT
400 | file_attr);
401 wname[1] = L'\\';
402 if (NT_SUCCESS (status))
404 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
405 status = NtSetInformationFile (h, &io, &fdi, sizeof fdi,
406 FileDispositionInformation);
407 NtClose (h);
409 if (!NT_SUCCESS (status))
410 SetLastError (RtlNtStatusToDosError (status));
411 return NT_SUCCESS (status);
414 extern "C" BOOL WINAPI
415 DeleteFileW (LPCWSTR wpath)
417 return unlink (wpath, FILE_NON_DIRECTORY_FILE);
420 extern "C" BOOL WINAPI
421 RemoveDirectoryW (LPCWSTR wpath)
423 return unlink (wpath, FILE_DIRECTORY_FILE);
426 /* Perms of 0 means no POSIX perms. */
427 FILE *
428 nt_wfopen (const wchar_t *wpath, const char *mode, mode_t perms)
430 NTSTATUS status;
431 HANDLE h;
432 IO_STATUS_BLOCK io;
433 UNICODE_STRING uname;
434 OBJECT_ATTRIBUTES attr;
435 SECURITY_DESCRIPTOR sd;
436 acl_t acl;
437 const char *c;
438 ULONG access, disp;
439 int oflags = 0;
441 switch (mode[0])
443 case 'r':
444 access = STANDARD_RIGHTS_READ | GENERIC_READ;
445 disp = FILE_OPEN;
446 break;
447 case 'w':
448 access = STANDARD_RIGHTS_WRITE | GENERIC_WRITE;
449 disp = FILE_OVERWRITE_IF;
450 break;
451 case 'a':
452 access = STANDARD_RIGHTS_WRITE | GENERIC_WRITE;
453 disp = FILE_OPEN_IF;
454 oflags = _O_APPEND;
455 break;
456 default:
457 errno = EINVAL;
458 return NULL;
460 for (c = mode + 1; *c; ++c)
461 switch (*c)
463 case '+':
464 access = STANDARD_RIGHTS_WRITE | GENERIC_READ | GENERIC_WRITE;
465 break;
466 case 't':
467 oflags |= _O_TEXT;
468 break;
469 case 'b':
470 oflags |= _O_BINARY;
471 break;
472 default:
473 errno = EINVAL;
474 return NULL;
476 switch (access)
478 case GENERIC_READ:
479 oflags |= _O_RDONLY;
480 break;
481 case GENERIC_WRITE:
482 oflags |= _O_WRONLY;
483 break;
484 case GENERIC_READ | GENERIC_WRITE:
485 oflags |= _O_RDWR;
486 break;
488 PWCHAR wname = (PWCHAR) wpath;
489 wname[1] = L'?';
490 RtlInitUnicodeString (&uname, wname);
491 InitializeObjectAttributes (&attr, &uname, OBJ_CASE_INSENSITIVE, NULL,
492 disp == FILE_OPEN || perms == 0
493 ? NULL
494 : nt_sec.GetPosixPerms ("", NULL, NULL,
495 perms, sd, acl));
496 status = NtCreateFile (&h, access | SYNCHRONIZE, &attr, &io, NULL,
497 FILE_ATTRIBUTE_NORMAL, FILE_SHARE_VALID_FLAGS, disp,
498 FILE_OPEN_FOR_BACKUP_INTENT
499 | FILE_OPEN_REPARSE_POINT
500 | FILE_SYNCHRONOUS_IO_NONALERT,
501 NULL, 0);
502 if (!NT_SUCCESS (status))
504 if (status == STATUS_OBJECT_NAME_NOT_FOUND
505 || status == STATUS_OBJECT_PATH_NOT_FOUND
506 || status == STATUS_NO_SUCH_FILE)
507 errno = ENOENT;
508 else if (status == STATUS_OBJECT_NAME_INVALID)
509 errno = EINVAL;
510 else
511 errno = EACCES;
512 wname[1] = L'\\';
513 return NULL;
515 wname[1] = L'\\';
516 int fd = _open_osfhandle ((intptr_t) h, oflags);
517 if (fd < 0)
518 return NULL;
519 return _fdopen (fd, mode);
522 FILE *
523 nt_fopen (const char *path, const char *mode, mode_t perms)
525 size_t len = strlen (path) + 8;
526 WCHAR wpath[len];
527 mklongpath (wpath, path, len);
528 return nt_wfopen (wpath, mode, perms);
531 /* Override C file io functions for the sake of the download side of setup.
532 These overrides eliminate the problem that the ANSI file API limits the
533 filename length to MAX_PATH. */
534 extern "C" int
535 remove (const char *path)
537 size_t len = strlen (path) + 8;
538 WCHAR wpath[len];
539 mklongpath (wpath, path, len);
540 return unlink (wpath, 0) ? 0 : -1;
543 extern "C" int
544 rename (const char *oldpath, const char *newpath)
546 size_t len = strlen (oldpath) + 8;
547 WCHAR woldpath[len];
548 mklongpath (woldpath, oldpath, len);
550 len = strlen (newpath) + 8;
551 WCHAR wnewpath[len];
552 mklongpath (wnewpath, newpath, len);
554 return MoveFileW (woldpath, wnewpath) ? 0 : -1;
557 extern "C" int
558 _access (const char *path, int /* ignored */)
560 size_t len = strlen (path) + 8;
561 WCHAR wpath[len];
562 mklongpath (wpath, path, len);
563 return (GetFileAttributesW (wpath) != INVALID_FILE_ATTRIBUTES) ? 0 : -1;