2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /* includable WITHOUT dependency on c-client */
16 #include "../../c-client/mail.h"
17 #include "../../c-client/utf8.h"
20 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
27 #include "../../c-client/fs.h"
29 /* includable WITHOUT dependency on pico */
30 #include "../../pico/keydefs.h"
32 #include "../../pico/osdep/mswin.h"
39 #define bad_char ((UCS) '?')
43 * Make it easier to use the convert_to_locale function for filenames
44 * and directory names. Note, only one at a time because there's only
46 * This isn't being freed as it stands now.
49 fname_to_locale(char *fname
)
51 static char *fname_locale_buf
= NULL
;
52 static size_t fname_locale_len
= 0;
53 char *converted_fname
, *p
;
55 if(fname
== NULL
){ /* special call to free memory */
56 if(fname_locale_buf
) fs_give((void **) &fname_locale_buf
);
61 p
= convert_to_locale(fname
);
65 converted_fname
= fname
;
68 if(strlen(converted_fname
)+1 > fname_locale_len
){
70 fs_give((void **) &fname_locale_buf
);
72 fname_locale_len
= strlen(converted_fname
)+1;
73 fname_locale_buf
= (char *) fs_get(fname_locale_len
* sizeof(char));
76 strncpy(fname_locale_buf
, converted_fname
, fname_locale_len
);
77 fname_locale_buf
[fname_locale_len
-1] = '\0';
80 if(fname_locale_len
== 0){
82 fname_locale_buf
= (char *) fs_get(fname_locale_len
* sizeof(char));
85 fname_locale_buf
[0] = '\0';
89 fs_give((void **) &p
);
91 return(fname_locale_buf
);
96 * Make it easier to use the convert_to_utf8 function for filenames
97 * and directory names. Note, only one at a time because there's only
99 * This isn't being freed as it stands now.
102 fname_to_utf8(char *fname
)
104 static char *fname_utf8_buf
= NULL
;
105 static size_t fname_utf8_len
= 0;
106 char *converted_fname
, *p
;
108 if(fname
== NULL
){ /* special call to free memory */
109 if(fname_utf8_buf
) fs_give((void **) &fname_utf8_buf
);
114 p
= convert_to_utf8(fname
, NULL
, 0);
118 converted_fname
= fname
;
121 if(strlen(converted_fname
)+1 > fname_utf8_len
){
123 fs_give((void **) &fname_utf8_buf
);
125 fname_utf8_len
= strlen(converted_fname
)+1;
126 fname_utf8_buf
= (char *) fs_get(fname_utf8_len
* sizeof(char));
129 strncpy(fname_utf8_buf
, converted_fname
, fname_utf8_len
);
130 fname_utf8_buf
[fname_utf8_len
-1] = '\0';
133 if(fname_utf8_len
== 0){
135 fname_utf8_buf
= (char *) fs_get(fname_utf8_len
* sizeof(char));
138 fname_utf8_buf
[0] = '\0';
142 fs_give((void **) &p
);
144 return(fname_utf8_buf
);
149 * The fp file pointer is open for read on a file which has contents
150 * that are encoded in the user's locale charset. That multibyte stream
151 * of characters is converted to wide characters and returned one at
154 * Not sure what to do if an uninterpretable character happens. Returning
155 * the bad character now.
158 read_a_wide_char(FILE *fp
,
159 void *input_cs
) /* input_cs ignored in Windows */
170 unsigned long octets_so_far
, remaining_octets
;
171 unsigned char *inputp
;
172 unsigned char inputbuf
[20];
181 * Read enough bytes to make up a character and convert it to UCS-4.
183 memset(inputbuf
, 0, sizeof(inputbuf
));
184 inputbuf
[0] = (unsigned char) c
;
187 remaining_octets
= octets_so_far
;
189 ucs
= mbtow(input_cs
, &inputp
, &remaining_octets
);
195 if(octets_so_far
>= sizeof(inputbuf
))
202 inputbuf
[octets_so_far
++] = (unsigned char) c
;
206 /* got a good UCS-4 character */
217 write_a_wide_char(UCS ucs
, FILE *fp
)
224 if(_fputtc(w
, fp
) == _TEOF
)
231 unsigned char obuf
[MAX(MB_LEN_MAX
,32)];
234 obuf
[0] = (unsigned char) ucs
;
238 outchars
= wtomb((char *) obuf
, ucs
);
241 obuf
[0] = bad_char
; /* ??? */
245 for(i
= 0; i
< outchars
; i
++)
246 if(fputc(obuf
[i
], fp
) == EOF
){
257 our_stat(char *filename
, struct stat
*sbuf
)
264 f
= utf8_to_lptstr((LPSTR
) filename
);
268 sbuf
->st_dev
= s
.st_dev
;
269 sbuf
->st_ino
= s
.st_ino
;
270 sbuf
->st_mode
= s
.st_mode
;
271 sbuf
->st_nlink
= s
.st_nlink
;
272 sbuf
->st_uid
= s
.st_uid
;
273 sbuf
->st_gid
= s
.st_gid
;
274 sbuf
->st_rdev
= s
.st_rdev
;
275 sbuf
->st_size
= s
.st_size
;
276 sbuf
->st_atime
= (time_t) s
.st_atime
;
277 sbuf
->st_mtime
= (time_t) s
.st_mtime
;
278 sbuf
->st_ctime
= (time_t) s
.st_ctime
;
280 fs_give((void **) &f
);
285 return(stat(fname_to_locale(filename
), sbuf
));
291 our_lstat(char *filename
, struct stat
*sbuf
)
294 assert(0); /* lstat not used in Windows */
297 return(lstat(fname_to_locale(filename
), sbuf
));
303 our_fopen(char *path
, char *mode
)
306 LPTSTR p
= NULL
, m
= NULL
;
308 char *mode_with_ccs
= NULL
;
312 if(mode
&& (*mode
== 'r' || *mode
== 'a')){
313 char *force_bom_check
= ", ccs=UNICODE";
315 if(strchr(mode
, 'b'))
316 mode_with_ccs
= mode
;
319 * The docs seem to say that we don't need the ccs parameter and
320 * if the file has a BOM at the beginning it will notice that and
321 * use it. However, we're not seeing that. Instead, what we see is
322 * that giving a parameter of UNICODE causes the desired behavior.
323 * This causes it to check for a BOM and if it finds one it uses it.
324 * If it doesn't find one, it treats the file as ANSI, which is what
327 if((len
= strlen(mode
) + strlen(force_bom_check
)) < sizeof(buf
)){
332 mode_with_ccs
= (char *) MemAlloc((len
+1) * sizeof(char));
335 snprintf(mode_with_ccs
, len
+1, "%s%s", mode
, force_bom_check
);
337 mode_with_ccs
= mode
; /* can't happen */
340 else if(mode
&& (*mode
== 'w')){
341 char *force_utf8
= ", ccs=UTF-8";
343 if(strchr(mode
, 'b'))
344 mode_with_ccs
= mode
;
346 if((len
= strlen(mode
) + strlen(force_utf8
)) < sizeof(buf
)){
351 mode_with_ccs
= (char *) MemAlloc((len
+1) * sizeof(char));
354 snprintf(mode_with_ccs
, len
+1, "%s%s", mode
, force_utf8
);
356 mode_with_ccs
= mode
; /* can't happen */
360 p
= utf8_to_lptstr((LPSTR
) path
);
363 m
= utf8_to_lptstr((LPSTR
) mode_with_ccs
);
369 fs_give((void **) &p
);
372 if(mode_with_ccs
&& mode_with_ccs
!= buf
&& mode_with_ccs
!= mode
)
373 MemFree((void *) mode_with_ccs
);
377 return(fopen(fname_to_locale(path
), mode
));
383 our_open(char *path
, int flags
, mode_t mode
)
390 * Setting the _O_WTEXT flag when opening a file for reading
391 * will cause us to read the first few bytes to check for
392 * a BOM and to translate from that encoding if we find it.
393 * This only works with stream I/O, not low-level read/write.
395 * When opening for writing the flag _O_U8TEXT will cause
396 * us to put a UTF-8 BOM at the start of the file.
398 * O_TEXT will cause LF -> CRLF on output, opposite on input
399 * O_BINARY suppresses that.
400 * _O_U8TEXT implies O_TEXT.
403 p
= utf8_to_lptstr((LPSTR
) path
);
406 ret
= _topen(p
, flags
, mode
);
407 fs_give((void **) &p
);
412 return(open(fname_to_locale(path
), flags
, mode
));
418 our_creat(char *path
, mode_t mode
)
424 p
= utf8_to_lptstr((LPSTR
) path
);
427 ret
= _tcreat(p
, mode
);
428 fs_give((void **) &p
);
433 return(creat(fname_to_locale(path
), mode
));
439 our_mkdir(char *path
, mode_t mode
)
442 /* mode is a noop for _WINDOWS */
446 p
= utf8_to_lptstr((LPSTR
) path
);
450 fs_give((void **) &p
);
455 return(mkdir(fname_to_locale(path
), mode
));
461 our_rename(char *oldpath
, char *newpath
)
464 LPTSTR pold
= NULL
, pnew
= NULL
;
467 pold
= utf8_to_lptstr((LPSTR
) oldpath
);
468 pnew
= utf8_to_lptstr((LPSTR
) newpath
);
471 ret
= _trename(pold
, pnew
);
474 fs_give((void **) &pold
);
476 fs_give((void **) &pnew
);
484 p
= fname_to_locale(oldpath
);
487 pold
= (char *) fs_get((len
+1) * sizeof(char));
488 strncpy(pold
, p
, len
+1);
491 ret
= rename(pold
, fname_to_locale(newpath
));
492 fs_give((void **) &pold
);
500 our_rmdir(char *path
)
506 p
= utf8_to_lptstr((LPSTR
) path
);
510 fs_give((void **) &p
);
515 return(rmdir(fname_to_locale(path
)));
520 our_unlink(char *path
)
526 p
= utf8_to_lptstr((LPSTR
) path
);
530 fs_give((void **) &p
);
535 return(unlink(fname_to_locale(path
)));
541 our_link(char *oldpath
, char *newpath
)
544 assert(0); /* link not used in Windows */
551 p
= fname_to_locale(oldpath
);
554 pold
= (char *) fs_get((len
+1) * sizeof(char));
555 strncpy(pold
, p
, len
+1);
558 ret
= link(pold
, fname_to_locale(newpath
));
559 fs_give((void **) &pold
);
568 our_truncate(char *path
, off_t size
)
571 #if defined(_WINDOWS) || !defined(HAVE_TRUNCATE)
576 if((fdes
= our_open(path
, O_RDWR
| O_CREAT
| S_IREAD
| S_IWRITE
| _O_U8TEXT
, 0600)) != -1){
577 if(chsize(fdes
, size
) == 0)
586 ret
= truncate(fname_to_locale(path
), size
);
587 #else /* !HAVE_TRUNCATE */
589 if((fdes
= our_open(path
, O_RDWR
, 0600)) != -1){
590 ret
= chsize(fdes
, size
) ;
595 #endif /* !HAVE_TRUNCATE */
603 our_chmod(char *path
, mode_t mode
)
609 p
= utf8_to_lptstr((LPSTR
) path
);
611 ret
= _tchmod(p
, mode
);
612 fs_give((void **) &p
);
617 return(chmod(fname_to_locale(path
), mode
));
623 our_chown(char *path
, uid_t owner
, gid_t group
)
628 return(chown(fname_to_locale(path
), owner
, group
));
634 our_utime(char *path
, struct utimbuf
*buf
)
640 p
= utf8_to_lptstr((LPSTR
) path
);
643 ret
= _tutime(p
, buf
);
644 fs_give((void **) &p
);
649 return(utime(fname_to_locale(path
), buf
));
654 * Return a malloc'd utf8-encoded char * of the provided environment
655 * variable. The env_variable argument is assumed not to be UTF-8. Returns
656 * NULL if no such environment variable.
658 * We'll pretty much swap out getenv's where convenient. Windows pretty
659 * much doesn't want to do getenv once we do unicode
662 our_getenv(char *env_variable
)
665 TCHAR lptstr_env_variable
[MAXPATH
+1], *p
;
668 for(i
= 0; env_variable
[i
] && i
< MAXPATH
; i
++)
669 lptstr_env_variable
[i
] = env_variable
[i
];
670 lptstr_env_variable
[i
] = '\0';
671 if(p
= _tgetenv(lptstr_env_variable
))
672 return(lptstr_to_utf8(p
));
675 #else /* !_WINDOWS */
676 char *p
, *utf8_p
, *env_cpy
;
678 if((p
= getenv(env_variable
)) != NULL
){
679 /* all this when what we want is a cpystr */
680 utf8_p
= fname_to_utf8(p
);
681 len
= strlen(utf8_p
);
682 env_cpy
= (char *)fs_get((len
+1)*sizeof(char));
683 strncpy(env_cpy
, utf8_p
, len
+1);
690 #endif /* !_WINDOWS */
694 our_access(char *path
, int mode
)
700 p
= utf8_to_lptstr((LPSTR
) path
);
702 ret
= _taccess(p
, mode
);
703 fs_give((void **) &p
);
708 return(access(fname_to_locale(path
), mode
));
714 * Fgets that doesn't do any character encoding translation or any
715 * of that Windows stuff.
718 fgets_binary(char *s
, int size
, FILE *fp
)
726 * Use fread low-level input instead of fgets.
727 * Maybe if we understood better we wouldn't need this.
733 while(p
-s
< size
-1 && (r
=fread(&c
, sizeof(c
), (size_t) 1, fp
)) == 1 && c
!= '\n')
736 if(p
-s
< size
-1 && r
== 1){
737 /* must have gotten to end of line */
745 return(fgets(s
, size
, fp
));