1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: filesys.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2006-2007 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /* includable WITHOUT dependency on c-client */
20 #include "../../c-client/mail.h"
21 #include "../../c-client/utf8.h"
24 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
31 #include "../../c-client/fs.h"
33 /* includable WITHOUT dependency on pico */
34 #include "../../pico/keydefs.h"
36 #include "../../pico/osdep/mswin.h"
43 #define bad_char ((UCS) '?')
47 * Make it easier to use the convert_to_locale function for filenames
48 * and directory names. Note, only one at a time because there's only
50 * This isn't being freed as it stands now.
53 fname_to_locale(char *fname
)
55 static char *fname_locale_buf
= NULL
;
56 static size_t fname_locale_len
= 0;
57 char *converted_fname
, *p
;
59 if(fname
== NULL
){ /* special call to free memory */
60 if(fname_locale_buf
) fs_give((void **) &fname_locale_buf
);
65 p
= convert_to_locale(fname
);
69 converted_fname
= fname
;
72 if(strlen(converted_fname
)+1 > fname_locale_len
){
74 fs_give((void **) &fname_locale_buf
);
76 fname_locale_len
= strlen(converted_fname
)+1;
77 fname_locale_buf
= (char *) fs_get(fname_locale_len
* sizeof(char));
80 strncpy(fname_locale_buf
, converted_fname
, fname_locale_len
);
81 fname_locale_buf
[fname_locale_len
-1] = '\0';
84 if(fname_locale_len
== 0){
86 fname_locale_buf
= (char *) fs_get(fname_locale_len
* sizeof(char));
89 fname_locale_buf
[0] = '\0';
93 fs_give((void **) &p
);
95 return(fname_locale_buf
);
100 * Make it easier to use the convert_to_utf8 function for filenames
101 * and directory names. Note, only one at a time because there's only
103 * This isn't being freed as it stands now.
106 fname_to_utf8(char *fname
)
108 static char *fname_utf8_buf
= NULL
;
109 static size_t fname_utf8_len
= 0;
110 char *converted_fname
, *p
;
112 if(fname
== NULL
){ /* special call to free memory */
113 if(fname_utf8_buf
) fs_give((void **) &fname_utf8_buf
);
118 p
= convert_to_utf8(fname
, NULL
, 0);
122 converted_fname
= fname
;
125 if(strlen(converted_fname
)+1 > fname_utf8_len
){
127 fs_give((void **) &fname_utf8_buf
);
129 fname_utf8_len
= strlen(converted_fname
)+1;
130 fname_utf8_buf
= (char *) fs_get(fname_utf8_len
* sizeof(char));
133 strncpy(fname_utf8_buf
, converted_fname
, fname_utf8_len
);
134 fname_utf8_buf
[fname_utf8_len
-1] = '\0';
137 if(fname_utf8_len
== 0){
139 fname_utf8_buf
= (char *) fs_get(fname_utf8_len
* sizeof(char));
142 fname_utf8_buf
[0] = '\0';
146 fs_give((void **) &p
);
148 return(fname_utf8_buf
);
153 * The fp file pointer is open for read on a file which has contents
154 * that are encoded in the user's locale charset. That multibyte stream
155 * of characters is converted to wide characters and returned one at
158 * Not sure what to do if an uninterpretable character happens. Returning
159 * the bad character now.
162 read_a_wide_char(FILE *fp
,
163 void *input_cs
) /* input_cs ignored in Windows */
174 unsigned long octets_so_far
, remaining_octets
;
175 unsigned char *inputp
;
176 unsigned char inputbuf
[20];
185 * Read enough bytes to make up a character and convert it to UCS-4.
187 memset(inputbuf
, 0, sizeof(inputbuf
));
188 inputbuf
[0] = (unsigned char) c
;
191 remaining_octets
= octets_so_far
;
193 ucs
= mbtow(input_cs
, &inputp
, &remaining_octets
);
199 if(octets_so_far
>= sizeof(inputbuf
))
206 inputbuf
[octets_so_far
++] = (unsigned char) c
;
210 /* got a good UCS-4 character */
221 write_a_wide_char(UCS ucs
, FILE *fp
)
228 if(_fputtc(w
, fp
) == _TEOF
)
235 unsigned char obuf
[MAX(MB_LEN_MAX
,32)];
238 obuf
[0] = (unsigned char) ucs
;
242 outchars
= wtomb((char *) obuf
, ucs
);
245 obuf
[0] = bad_char
; /* ??? */
249 for(i
= 0; i
< outchars
; i
++)
250 if(fputc(obuf
[i
], fp
) == EOF
){
261 our_stat(char *filename
, struct stat
*sbuf
)
268 f
= utf8_to_lptstr((LPSTR
) filename
);
272 sbuf
->st_dev
= s
.st_dev
;
273 sbuf
->st_ino
= s
.st_ino
;
274 sbuf
->st_mode
= s
.st_mode
;
275 sbuf
->st_nlink
= s
.st_nlink
;
276 sbuf
->st_uid
= s
.st_uid
;
277 sbuf
->st_gid
= s
.st_gid
;
278 sbuf
->st_rdev
= s
.st_rdev
;
279 sbuf
->st_size
= s
.st_size
;
280 sbuf
->st_atime
= (time_t) s
.st_atime
;
281 sbuf
->st_mtime
= (time_t) s
.st_mtime
;
282 sbuf
->st_ctime
= (time_t) s
.st_ctime
;
284 fs_give((void **) &f
);
289 return(stat(fname_to_locale(filename
), sbuf
));
295 our_lstat(char *filename
, struct stat
*sbuf
)
298 assert(0); /* lstat not used in Windows */
301 return(lstat(fname_to_locale(filename
), sbuf
));
307 our_fopen(char *path
, char *mode
)
310 LPTSTR p
= NULL
, m
= NULL
;
312 char *mode_with_ccs
= NULL
;
316 if(mode
&& (*mode
== 'r' || *mode
== 'a')){
317 char *force_bom_check
= ", ccs=UNICODE";
319 if(strchr(mode
, 'b'))
320 mode_with_ccs
= mode
;
323 * The docs seem to say that we don't need the ccs parameter and
324 * if the file has a BOM at the beginning it will notice that and
325 * use it. However, we're not seeing that. Instead, what we see is
326 * that giving a parameter of UNICODE causes the desired behavior.
327 * This causes it to check for a BOM and if it finds one it uses it.
328 * If it doesn't find one, it treats the file as ANSI, which is what
331 if((len
= strlen(mode
) + strlen(force_bom_check
)) < sizeof(buf
)){
336 mode_with_ccs
= (char *) MemAlloc((len
+1) * sizeof(char));
339 snprintf(mode_with_ccs
, len
+1, "%s%s", mode
, force_bom_check
);
341 mode_with_ccs
= mode
; /* can't happen */
344 else if(mode
&& (*mode
== 'w')){
345 char *force_utf8
= ", ccs=UTF-8";
347 if(strchr(mode
, 'b'))
348 mode_with_ccs
= mode
;
350 if((len
= strlen(mode
) + strlen(force_utf8
)) < sizeof(buf
)){
355 mode_with_ccs
= (char *) MemAlloc((len
+1) * sizeof(char));
358 snprintf(mode_with_ccs
, len
+1, "%s%s", mode
, force_utf8
);
360 mode_with_ccs
= mode
; /* can't happen */
364 p
= utf8_to_lptstr((LPSTR
) path
);
367 m
= utf8_to_lptstr((LPSTR
) mode_with_ccs
);
373 fs_give((void **) &p
);
376 if(mode_with_ccs
&& mode_with_ccs
!= buf
&& mode_with_ccs
!= mode
)
377 MemFree((void *) mode_with_ccs
);
381 return(fopen(fname_to_locale(path
), mode
));
387 our_open(char *path
, int flags
, mode_t mode
)
394 * Setting the _O_WTEXT flag when opening a file for reading
395 * will cause us to read the first few bytes to check for
396 * a BOM and to translate from that encoding if we find it.
397 * This only works with stream I/O, not low-level read/write.
399 * When opening for writing the flag _O_U8TEXT will cause
400 * us to put a UTF-8 BOM at the start of the file.
402 * O_TEXT will cause LF -> CRLF on output, opposite on input
403 * O_BINARY suppresses that.
404 * _O_U8TEXT implies O_TEXT.
407 p
= utf8_to_lptstr((LPSTR
) path
);
410 ret
= _topen(p
, flags
, mode
);
411 fs_give((void **) &p
);
416 return(open(fname_to_locale(path
), flags
, mode
));
422 our_creat(char *path
, mode_t mode
)
428 p
= utf8_to_lptstr((LPSTR
) path
);
431 ret
= _tcreat(p
, mode
);
432 fs_give((void **) &p
);
437 return(creat(fname_to_locale(path
), mode
));
443 our_mkdir(char *path
, mode_t mode
)
446 /* mode is a noop for _WINDOWS */
450 p
= utf8_to_lptstr((LPSTR
) path
);
454 fs_give((void **) &p
);
459 return(mkdir(fname_to_locale(path
), mode
));
465 our_rename(char *oldpath
, char *newpath
)
468 LPTSTR pold
= NULL
, pnew
= NULL
;
471 pold
= utf8_to_lptstr((LPSTR
) oldpath
);
472 pnew
= utf8_to_lptstr((LPSTR
) newpath
);
475 ret
= _trename(pold
, pnew
);
478 fs_give((void **) &pold
);
480 fs_give((void **) &pnew
);
488 p
= fname_to_locale(oldpath
);
491 pold
= (char *) fs_get((len
+1) * sizeof(char));
492 strncpy(pold
, p
, len
+1);
495 ret
= rename(pold
, fname_to_locale(newpath
));
496 fs_give((void **) &pold
);
505 our_unlink(char *path
)
511 p
= utf8_to_lptstr((LPSTR
) path
);
515 fs_give((void **) &p
);
520 return(unlink(fname_to_locale(path
)));
526 our_link(char *oldpath
, char *newpath
)
529 assert(0); /* link not used in Windows */
536 p
= fname_to_locale(oldpath
);
539 pold
= (char *) fs_get((len
+1) * sizeof(char));
540 strncpy(pold
, p
, len
+1);
543 ret
= link(pold
, fname_to_locale(newpath
));
544 fs_give((void **) &pold
);
553 our_truncate(char *path
, off_t size
)
556 #if defined(_WINDOWS) || !defined(HAVE_TRUNCATE)
561 if((fdes
= our_open(path
, O_RDWR
| O_CREAT
| S_IREAD
| S_IWRITE
| _O_U8TEXT
, 0600)) != -1){
562 if(chsize(fdes
, size
) == 0)
571 ret
= truncate(fname_to_locale(path
), size
);
572 #else /* !HAVE_TRUNCATE */
574 if((fdes
= our_open(path
, O_RDWR
, 0600)) != -1){
575 ret
= chsize(fdes
, size
) ;
580 #endif /* !HAVE_TRUNCATE */
588 our_chmod(char *path
, mode_t mode
)
594 p
= utf8_to_lptstr((LPSTR
) path
);
596 ret
= _tchmod(p
, mode
);
597 fs_give((void **) &p
);
602 return(chmod(fname_to_locale(path
), mode
));
608 our_chown(char *path
, uid_t owner
, gid_t group
)
613 return(chown(fname_to_locale(path
), owner
, group
));
619 our_utime(char *path
, struct utimbuf
*buf
)
625 p
= utf8_to_lptstr((LPSTR
) path
);
628 ret
= _tutime(p
, buf
);
629 fs_give((void **) &p
);
634 return(utime(fname_to_locale(path
), buf
));
639 * Return a malloc'd utf8-encoded char * of the provided environment
640 * variable. The env_variable argument is assumed not to be UTF-8. Returns
641 * NULL if no such environment variable.
643 * We'll pretty much swap out getenv's where convenient. Windows pretty
644 * much doesn't want to do getenv once we do unicode
647 our_getenv(char *env_variable
)
650 TCHAR lptstr_env_variable
[MAXPATH
+1], *p
;
653 for(i
= 0; env_variable
[i
] && i
< MAXPATH
; i
++)
654 lptstr_env_variable
[i
] = env_variable
[i
];
655 lptstr_env_variable
[i
] = '\0';
656 if(p
= _tgetenv(lptstr_env_variable
))
657 return(lptstr_to_utf8(p
));
660 #else /* !_WINDOWS */
661 char *p
, *utf8_p
, *env_cpy
;
663 if((p
= getenv(env_variable
)) != NULL
){
664 /* all this when what we want is a cpystr */
665 utf8_p
= fname_to_utf8(p
);
666 len
= strlen(utf8_p
);
667 env_cpy
= (char *)fs_get((len
+1)*sizeof(char));
668 strncpy(env_cpy
, utf8_p
, len
+1);
675 #endif /* !_WINDOWS */
679 our_access(char *path
, int mode
)
685 p
= utf8_to_lptstr((LPSTR
) path
);
687 ret
= _taccess(p
, mode
);
688 fs_give((void **) &p
);
693 return(access(fname_to_locale(path
), mode
));
699 * Fgets that doesn't do any character encoding translation or any
700 * of that Windows stuff.
703 fgets_binary(char *s
, int size
, FILE *fp
)
711 * Use fread low-level input instead of fgets.
712 * Maybe if we understood better we wouldn't need this.
718 while(p
-s
< size
-1 && (r
=fread(&c
, sizeof(c
), (size_t) 1, fp
)) == 1 && c
!= '\n')
721 if(p
-s
< size
-1 && r
== 1){
722 /* must have gotten to end of line */
730 return(fgets(s
, size
, fp
));