* Crashing bug when suspending Alpine and attempting to return to
[alpine.git] / pith / charconv / filesys.c
bloba346f3239c7838e2da659333fe6f0ac79486177a
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: filesys.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $";
3 #endif
5 /*
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"
23 #ifdef _WINDOWS
24 /* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
25 #undef ERROR
26 #endif
28 #include <system.h>
29 #include <general.h>
31 #include "../../c-client/fs.h"
33 /* includable WITHOUT dependency on pico */
34 #include "../../pico/keydefs.h"
35 #ifdef _WINDOWS
36 #include "../../pico/osdep/mswin.h"
37 #endif
39 #include "filesys.h"
40 #include "utf8.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
49 * one buffer.
50 * This isn't being freed as it stands now.
52 char *
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);
61 fname_locale_len = 0;
62 return NULL;
65 p = convert_to_locale(fname);
66 if(p)
67 converted_fname = p;
68 else
69 converted_fname = fname;
71 if(converted_fname){
72 if(strlen(converted_fname)+1 > fname_locale_len){
73 if(fname_locale_buf)
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';
83 else{
84 if(fname_locale_len == 0){
85 fname_locale_len = 1;
86 fname_locale_buf = (char *) fs_get(fname_locale_len * sizeof(char));
89 fname_locale_buf[0] = '\0';
92 if(p)
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
102 * one buffer.
103 * This isn't being freed as it stands now.
105 char *
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);
114 fname_utf8_len = 0;
115 return NULL;
118 p = convert_to_utf8(fname, NULL, 0);
119 if(p)
120 converted_fname = p;
121 else
122 converted_fname = fname;
124 if(converted_fname){
125 if(strlen(converted_fname)+1 > fname_utf8_len){
126 if(fname_utf8_buf)
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';
136 else{
137 if(fname_utf8_len == 0){
138 fname_utf8_len = 1;
139 fname_utf8_buf = (char *) fs_get(fname_utf8_len * sizeof(char));
142 fname_utf8_buf[0] = '\0';
145 if(p)
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
156 * a time.
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 */
165 #ifdef _WINDOWS
166 _TINT val;
168 val = _fgettc(fp);
169 if(val == _TEOF)
170 return(CCONV_EOF);
172 return((UCS) val);
173 #else /* UNIX */
174 unsigned long octets_so_far, remaining_octets;
175 unsigned char *inputp;
176 unsigned char inputbuf[20];
177 int c;
178 UCS ucs;
180 c = fgetc(fp);
181 if(c == EOF)
182 return(CCONV_EOF);
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;
189 octets_so_far = 1;
190 for(;;){
191 remaining_octets = octets_so_far;
192 inputp = inputbuf;
193 ucs = mbtow(input_cs, &inputp, &remaining_octets);
194 switch(ucs){
195 case CCONV_BADCHAR:
196 return(bad_char);
198 case CCONV_NEEDMORE:
199 if(octets_so_far >= sizeof(inputbuf))
200 return(bad_char);
202 c = fgetc(fp);
203 if(c == EOF)
204 return(CCONV_EOF);
206 inputbuf[octets_so_far++] = (unsigned char) c;
207 break;
209 default:
210 /* got a good UCS-4 character */
211 return(ucs);
215 return(bad_char);
216 #endif /* UNIX */
221 write_a_wide_char(UCS ucs, FILE *fp)
223 #ifdef _WINDOWS
224 int rv = 1;
225 TCHAR w;
227 w = (TCHAR) ucs;
228 if(_fputtc(w, fp) == _TEOF)
229 rv = EOF;
231 return(rv);
232 #else /* UNIX */
233 int rv = 1;
234 int i, outchars;
235 unsigned char obuf[MAX(MB_LEN_MAX,32)];
237 if(ucs < 0x80){
238 obuf[0] = (unsigned char) ucs;
239 outchars = 1;
241 else{
242 outchars = wtomb((char *) obuf, ucs);
243 if(outchars < 0){
244 outchars = 1;
245 obuf[0] = bad_char; /* ??? */
249 for(i = 0; i < outchars; i++)
250 if(fputc(obuf[i], fp) == EOF){
251 rv = EOF;
252 break;
255 return(rv);
256 #endif /* UNIX */
261 our_stat(char *filename, struct stat *sbuf)
263 #ifdef _WINDOWS
264 LPTSTR f = NULL;
265 int ret = -1;
266 struct _stat s;
268 f = utf8_to_lptstr((LPSTR) filename);
269 if(f){
270 ret = _tstat(f, &s);
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);
287 return ret;
288 #else /* UNIX */
289 return(stat(fname_to_locale(filename), sbuf));
290 #endif /* UNIX */
295 our_lstat(char *filename, struct stat *sbuf)
297 #ifdef _WINDOWS
298 assert(0); /* lstat not used in Windows */
299 return(-1);
300 #else /* UNIX */
301 return(lstat(fname_to_locale(filename), sbuf));
302 #endif /* UNIX */
306 FILE *
307 our_fopen(char *path, char *mode)
309 #ifdef _WINDOWS
310 LPTSTR p = NULL, m = NULL;
311 FILE *ret = NULL;
312 char *mode_with_ccs = NULL;
313 char buf[500];
314 size_t len;
316 if(mode && (*mode == 'r' || *mode == 'a')){
317 char *force_bom_check = ", ccs=UNICODE";
319 if(strchr(mode, 'b'))
320 mode_with_ccs = mode;
321 else{
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
329 * we want.
331 if((len = strlen(mode) + strlen(force_bom_check)) < sizeof(buf)){
332 len = sizeof(buf)-1;
333 mode_with_ccs = buf;
335 else
336 mode_with_ccs = (char *) MemAlloc((len+1) * sizeof(char));
338 if(mode_with_ccs)
339 snprintf(mode_with_ccs, len+1, "%s%s", mode, force_bom_check);
340 else
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;
349 else{
350 if((len = strlen(mode) + strlen(force_utf8)) < sizeof(buf)){
351 len = sizeof(buf)-1;
352 mode_with_ccs = buf;
354 else
355 mode_with_ccs = (char *) MemAlloc((len+1) * sizeof(char));
357 if(mode_with_ccs)
358 snprintf(mode_with_ccs, len+1, "%s%s", mode, force_utf8);
359 else
360 mode_with_ccs = mode; /* can't happen */
364 p = utf8_to_lptstr((LPSTR) path);
366 if(p){
367 m = utf8_to_lptstr((LPSTR) mode_with_ccs);
368 if(m){
369 ret = _tfopen(p, m);
370 MemFree((void *) m);
373 fs_give((void **) &p);
376 if(mode_with_ccs && mode_with_ccs != buf && mode_with_ccs != mode)
377 MemFree((void *) mode_with_ccs);
379 return ret;
380 #else /* UNIX */
381 return(fopen(fname_to_locale(path), mode));
382 #endif /* UNIX */
387 our_open(char *path, int flags, mode_t mode)
389 #ifdef _WINDOWS
390 LPTSTR p = NULL;
391 int ret = -1;
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);
409 if(p){
410 ret = _topen(p, flags, mode);
411 fs_give((void **) &p);
414 return ret;
415 #else /* UNIX */
416 return(open(fname_to_locale(path), flags, mode));
417 #endif /* UNIX */
422 our_creat(char *path, mode_t mode)
424 #ifdef _WINDOWS
425 LPTSTR p = NULL;
426 int ret = -1;
428 p = utf8_to_lptstr((LPSTR) path);
430 if(p){
431 ret = _tcreat(p, mode);
432 fs_give((void **) &p);
435 return ret;
436 #else /* UNIX */
437 return(creat(fname_to_locale(path), mode));
438 #endif /* UNIX */
443 our_mkdir(char *path, mode_t mode)
445 #ifdef _WINDOWS
446 /* mode is a noop for _WINDOWS */
447 LPTSTR p = NULL;
448 int ret = -1;
450 p = utf8_to_lptstr((LPSTR) path);
452 if(p){
453 ret = _tmkdir(p);
454 fs_give((void **) &p);
457 return ret;
458 #else /* UNIX */
459 return(mkdir(fname_to_locale(path), mode));
460 #endif /* UNIX */
465 our_rename(char *oldpath, char *newpath)
467 #ifdef _WINDOWS
468 LPTSTR pold = NULL, pnew = NULL;
469 int ret = -1;
471 pold = utf8_to_lptstr((LPSTR) oldpath);
472 pnew = utf8_to_lptstr((LPSTR) newpath);
474 if(pold && pnew)
475 ret = _trename(pold, pnew);
477 if(pold)
478 fs_give((void **) &pold);
479 if(pnew)
480 fs_give((void **) &pnew);
482 return ret;
483 #else /* UNIX */
484 char *p, *pold;
485 size_t len;
486 int ret = -1;
488 p = fname_to_locale(oldpath);
489 if(p){
490 len = strlen(p);
491 pold = (char *) fs_get((len+1) * sizeof(char));
492 strncpy(pold, p, len+1);
493 pold[len] = '\0';
495 ret = rename(pold, fname_to_locale(newpath));
496 fs_give((void **) &pold);
499 return ret;
500 #endif /* UNIX */
505 our_unlink(char *path)
507 #ifdef _WINDOWS
508 LPTSTR p = NULL;
509 int ret = -1;
511 p = utf8_to_lptstr((LPSTR) path);
513 if(p){
514 ret = _tunlink(p);
515 fs_give((void **) &p);
518 return ret;
519 #else /* UNIX */
520 return(unlink(fname_to_locale(path)));
521 #endif /* UNIX */
526 our_link(char *oldpath, char *newpath)
528 #ifdef _WINDOWS
529 assert(0); /* link not used in Windows */
530 return(-1);
531 #else /* UNIX */
532 char *p, *pold;
533 size_t len;
534 int ret = -1;
536 p = fname_to_locale(oldpath);
537 if(p){
538 len = strlen(p);
539 pold = (char *) fs_get((len+1) * sizeof(char));
540 strncpy(pold, p, len+1);
541 pold[len] = '\0';
543 ret = link(pold, fname_to_locale(newpath));
544 fs_give((void **) &pold);
547 return ret;
548 #endif /* UNIX */
553 our_truncate(char *path, off_t size)
555 int ret = -1;
556 #if defined(_WINDOWS) || !defined(HAVE_TRUNCATE)
557 int fdes;
558 #endif
560 #ifdef _WINDOWS
561 if((fdes = our_open(path, O_RDWR | O_CREAT | S_IREAD | S_IWRITE | _O_U8TEXT, 0600)) != -1){
562 if(chsize(fdes, size) == 0)
563 ret = 0;
565 close(fdes);
568 #else /* UNIX */
570 #ifdef HAVE_TRUNCATE
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) ;
577 if(close(fdes))
578 ret = -1;
580 #endif /* !HAVE_TRUNCATE */
581 #endif /* UNIX */
583 return ret;
588 our_chmod(char *path, mode_t mode)
590 #ifdef _WINDOWS
591 LPTSTR p = NULL;
592 int ret = -1;
594 p = utf8_to_lptstr((LPSTR) path);
595 if(p){
596 ret = _tchmod(p, mode);
597 fs_give((void **) &p);
600 return ret;
601 #else /* UNIX */
602 return(chmod(fname_to_locale(path), mode));
603 #endif /* UNIX */
608 our_chown(char *path, uid_t owner, gid_t group)
610 #ifdef _WINDOWS
611 return 0;
612 #else /* UNIX */
613 return(chown(fname_to_locale(path), owner, group));
614 #endif /* UNIX */
619 our_utime(char *path, struct utimbuf *buf)
621 #ifdef _WINDOWS
622 LPTSTR p = NULL;
623 int ret = -1;
625 p = utf8_to_lptstr((LPSTR) path);
627 if(p){
628 ret = _tutime(p, buf);
629 fs_give((void **) &p);
632 return ret;
633 #else /* UNIX */
634 return(utime(fname_to_locale(path), buf));
635 #endif /* UNIX */
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
646 char *
647 our_getenv(char *env_variable)
649 #ifdef _WINDOWS
650 TCHAR lptstr_env_variable[MAXPATH+1], *p;
651 int i;
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));
658 else
659 return(NULL);
660 #else /* !_WINDOWS */
661 char *p, *utf8_p, *env_cpy;
662 size_t len;
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);
669 env_cpy[len] = '\0';
671 return(env_cpy);
673 else
674 return(NULL);
675 #endif /* !_WINDOWS */
679 our_access(char *path, int mode)
681 #ifdef _WINDOWS
682 LPTSTR p = NULL;
683 int ret = -1;
685 p = utf8_to_lptstr((LPSTR) path);
686 if(p){
687 ret = _taccess(p, mode);
688 fs_give((void **) &p);
691 return ret;
692 #else /* UNIX */
693 return(access(fname_to_locale(path), mode));
694 #endif /* UNIX */
699 * Fgets that doesn't do any character encoding translation or any
700 * of that Windows stuff.
702 char *
703 fgets_binary(char *s, int size, FILE *fp)
705 #ifdef _WINDOWS
706 char *p;
707 char c;
708 int r;
711 * Use fread low-level input instead of fgets.
712 * Maybe if we understood better we wouldn't need this.
714 if(!s)
715 return s;
717 p = s;
718 while(p-s < size-1 && (r=fread(&c, sizeof(c), (size_t) 1, fp)) == 1 && c != '\n')
719 *p++ = c;
721 if(p-s < size-1 && r == 1){
722 /* must have gotten to end of line */
723 *p++ = '\n';
726 *p = '\0';
727 return(s);
729 #else /* UNIX */
730 return(fgets(s, size, fp));
731 #endif /* UNIX */