Don't generate empty values in mpdm_scanf().
[mpdm.git] / mpdm_f.c
bloba9c6f1ec27101b243f88d8d8354e72cdff6ca3cd
1 /*
3 MPDM - Minimum Profit Data Manager
4 Copyright (C) 2003/2007 Angel Ortega <angel@triptico.com>
6 mpdm_f.c - File management
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 http://www.triptico.com
26 #include "config.h"
28 #ifdef CONFOPT_CANONICALIZE_FILE_NAME
29 #define _GNU_SOURCE
30 #endif
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <wchar.h>
37 #ifdef CONFOPT_UNISTD_H
38 #include <unistd.h>
39 #endif
41 #ifdef CONFOPT_GLOB_H
42 #include <glob.h>
43 #endif
45 #ifdef CONFOPT_WIN32
47 #include <windows.h>
48 #include <commctrl.h>
49 #include <shlobj.h>
51 #endif
53 #ifdef CONFOPT_SYS_TYPES_H
54 #include <sys/types.h>
55 #endif
57 #ifdef CONFOPT_SYS_WAIT_H
58 #include <sys/wait.h>
59 #endif
61 #ifdef CONFOPT_SYS_STAT_H
62 #include <sys/stat.h>
63 #endif
65 #ifdef CONFOPT_PWD_H
66 #include <pwd.h>
67 #endif
69 #include "mpdm.h"
71 #ifdef CONFOPT_ICONV
72 #include <iconv.h>
73 #endif
75 /* file structure */
76 struct mpdm_file {
77 FILE *in;
78 FILE *out;
80 wchar_t * (* f_read) (const struct mpdm_file *, int *);
81 int (* f_write)(const struct mpdm_file *, const wchar_t *);
83 #ifdef CONFOPT_ICONV
84 iconv_t ic_enc;
85 iconv_t ic_dec;
86 #endif /* CONFOPT_ICONV */
88 #ifdef CONFOPT_WIN32
89 HANDLE hin;
90 HANDLE hout;
91 #endif /* CONFOPT_WIN32 */
94 #include <errno.h>
95 extern int errno;
98 /*******************
99 Code
100 ********************/
103 static void store_syserr(void)
104 /* stores the system error inside the global ERRNO */
106 mpdm_hset_s(mpdm_root(), L"ERRNO", MPDM_MBS(strerror(errno)));
110 static int get_char(const struct mpdm_file *f)
111 /* reads a character from a file structure */
113 int c = EOF;
115 #ifdef CONFOPT_WIN32
117 if (f->hin != NULL) {
118 char tmp;
119 DWORD n;
121 if (ReadFile(f->hin, &tmp, 1, &n, NULL) && n > 0)
122 c = (int) tmp;
125 #endif /* CONFOPT_WIN32 */
127 if (f->in != NULL) {
128 /* read (converting to positive if needed) */
129 if ((c = fgetc(f->in)) < 0 && !feof(f->in))
130 c += 256;
133 return c;
137 static int put_buf(const char *ptr, int s, const struct mpdm_file *f)
138 /* writes s bytes in the buffer in ptr to f */
140 #ifdef CONFOPT_WIN32
142 if (f->hout != NULL) {
143 DWORD n;
145 if (WriteFile(f->hout, ptr, s, &n, NULL) && n > 0)
146 s = n;
148 else
149 #endif /* CONFOPT_WIN32 */
151 if (f->out != NULL)
152 s = fwrite(ptr, s, 1, f->out);
154 return s;
158 static int put_char(int c, const struct mpdm_file *f)
159 /* writes a character in a file structure */
161 char tmp = c;
163 if (put_buf(&tmp, 1, f) != 1)
164 c = EOF;
166 return c;
170 static wchar_t *read_mbs(const struct mpdm_file *f, int *s)
171 /* reads a multibyte string from a mpdm_file into a dynamic string */
173 wchar_t *ptr = NULL;
174 char *auxptr = NULL;
175 char tmp[100];
176 int c, i = 0, n = 0;
178 while ((c = get_char(f)) != EOF) {
179 tmp[i++] = c;
181 if (c == '\n')
182 break;
184 if (i == sizeof(tmp) - 1) {
185 /* out of space; start allocating */
186 if ((auxptr = mpdm_poke(auxptr, &n, tmp, i, sizeof(char))) == NULL)
187 return NULL;
189 i = 0;
193 /* is there something to return? */
194 if (i || n) {
195 /* NULL-terminate */
196 tmp[i++] = '\0';
198 if (n) {
199 /* auxiliary space used; concat all */
200 if ((auxptr = mpdm_poke(auxptr, &n, tmp, i, sizeof(char))) == NULL)
201 return NULL;
203 /* do the conversion */
204 ptr = mpdm_mbstowcs(auxptr, s, -1);
206 free(auxptr);
208 else
209 ptr = mpdm_mbstowcs(tmp, s, -1);
212 return ptr;
216 static int write_wcs(const struct mpdm_file *f, const wchar_t * str)
217 /* writes a wide string to an struct mpdm_file */
219 int s;
220 char *ptr;
222 ptr = mpdm_wcstombs(str, &s);
223 s = put_buf(ptr, s, f);
224 free(ptr);
226 return s;
230 #ifdef CONFOPT_ICONV
232 static wchar_t *read_iconv(const struct mpdm_file *f, int *s)
233 /* reads a multibyte string transforming with iconv */
235 char tmp[128];
236 wchar_t *ptr = NULL;
237 int c, i;
238 wchar_t wc;
240 *s = i = 0;
242 /* resets the decoder */
243 iconv(f->ic_dec, NULL, NULL, NULL, NULL);
245 while ((c = get_char(f)) != EOF) {
246 size_t il, ol;
247 char *iptr, *optr;
249 tmp[i++] = c;
251 /* too big? shouldn't happen */
252 if (i == sizeof(tmp))
253 break;
255 il = i;
256 iptr = tmp;
257 ol = sizeof(wchar_t);
258 optr = (char *) &wc;
260 /* write to file */
261 if (iconv(f->ic_dec, &iptr, &il, &optr, &ol) == (size_t) -1) {
262 /* found incomplete multibyte character */
263 if (errno == EINVAL)
264 continue;
266 /* otherwise, return '?' */
267 wc = L'?';
270 i = 0;
272 if ((ptr = mpdm_poke(ptr, s, &wc, 1, sizeof(wchar_t))) == NULL)
273 break;
275 /* if it's an end of line, finish */
276 if (wc == L'\n')
277 break;
280 if (ptr != NULL) {
281 ptr = mpdm_poke(ptr, s, L"", 1, sizeof(wchar_t));
282 (*s)--;
285 return ptr;
289 static int write_iconv(const struct mpdm_file *f, const wchar_t * str)
290 /* writes a wide string to a stream using iconv */
292 char tmp[128];
293 int cnt = 0;
295 /* resets the encoder */
296 iconv(f->ic_enc, NULL, NULL, NULL, NULL);
298 /* convert char by char */
299 for (; *str != L'\0'; str++) {
300 size_t il, ol;
301 char *iptr, *optr;
302 int n;
304 il = sizeof(wchar_t);
305 iptr = (char *) str;
306 ol = sizeof(tmp);
307 optr = tmp;
309 /* write to file */
310 if (iconv(f->ic_enc, &iptr, &il, &optr, &ol) == (size_t) -1) {
311 /* error converting; convert a '?' instead */
312 wchar_t q = L'?';
314 il = sizeof(wchar_t);
315 iptr = (char *) &q;
316 ol = sizeof(tmp);
317 optr = tmp;
319 iconv(f->ic_enc, &iptr, &il, &optr, &ol);
322 for (n = 0; n < (int)(sizeof(tmp) - ol); n++, cnt++) {
323 if (put_char(tmp[n], f) == EOF)
324 return -1;
328 return cnt;
332 #endif /* CONFOPT_ICONV */
334 #define UTF8_BYTE() if((c = get_char(f)) == EOF) break
336 static wchar_t *read_utf8(const struct mpdm_file *f, int *s)
337 /* crappy, ad-hoc utf8 reader */
339 wchar_t *ptr = NULL;
340 wchar_t wc;
341 int c;
343 *s = 0;
345 for (;;) {
346 wc = L'\0';
348 UTF8_BYTE();
350 if ((c & 0x80) == 0)
351 wc = c;
352 else
353 if ((c & 0xe0) == 0xe0) {
354 wc = (c & 0x1f) << 12;
355 UTF8_BYTE();
356 wc |= (c & 0x3f) << 6;
357 UTF8_BYTE();
358 wc |= (c & 0x3f);
360 else {
361 wc = (c & 0x3f) << 6;
362 UTF8_BYTE();
363 wc |= (c & 0x3f);
366 /* store */
367 if ((ptr = mpdm_poke(ptr, s, &wc, 1, sizeof(wchar_t))) == NULL)
368 break;
370 /* if it's an end of line, finish */
371 if (wc == L'\n')
372 break;
375 if (ptr != NULL) {
376 ptr = mpdm_poke(ptr, s, L"", 1, sizeof(wchar_t));
377 (*s)--;
380 return ptr;
384 static int write_utf8(const struct mpdm_file *f, const wchar_t * str)
385 /* crappy, ad-hoc utf8 writer */
387 int cnt = 0;
388 wchar_t wc;
390 /* convert char by char */
391 for (; (wc = *str) != L'\0'; str++) {
392 if (wc < 0x80)
393 put_char((int) wc, f);
394 else
395 if (wc < 0x800) {
396 put_char((int) (0xc0 | (wc >> 6)), f);
397 put_char((int) (0x80 | (wc & 0x3f)), f);
398 cnt++;
400 else {
401 put_char((int) (0xe0 | (wc >> 12)), f);
402 put_char((int) (0x80 | ((wc >> 6) & 0x3f)), f);
403 put_char((int) (0x80 | (wc & 0x3f)), f);
404 cnt += 2;
407 cnt++;
410 return cnt;
414 static wchar_t *read_iso8859_1(const struct mpdm_file *f, int *s)
415 /* crappy, ad-hoc iso8859-1 reader */
417 wchar_t *ptr = NULL;
418 wchar_t wc;
419 int c;
421 *s = 0;
423 while ((c = get_char(f)) != EOF) {
424 wc = c;
426 /* store */
427 if ((ptr = mpdm_poke(ptr, s, &wc, 1, sizeof(wchar_t))) == NULL)
428 break;
430 /* if it's an end of line, finish */
431 if (wc == L'\n')
432 break;
435 if (ptr != NULL) {
436 ptr = mpdm_poke(ptr, s, L"", 1, sizeof(wchar_t));
437 (*s)--;
440 return ptr;
444 static int write_iso8859_1(const struct mpdm_file *f, const wchar_t * str)
445 /* crappy, ad-hoc iso8859-1 writer */
447 int cnt = 0;
448 wchar_t wc;
450 /* convert char by char */
451 for (; (wc = *str) != L'\0'; str++)
452 put_char(wc <= 0xff ? (int) wc : '?', f);
454 return cnt;
458 static wchar_t *read_utf16ae(const struct mpdm_file *f, int *s, int le)
459 /* utf16 reader, ANY ending */
461 wchar_t *ptr = NULL;
462 wchar_t wc;
463 int c1, c2;
465 *s = 0;
467 for (;;) {
468 wc = L'\0';
470 if ((c1 = get_char(f)) == EOF)
471 break;
473 if ((c2 = get_char(f)) == EOF)
474 break;
476 if (le)
477 wc = c1 | (c2 << 8);
478 else
479 wc = c2 | (c1 << 8);
481 /* store */
482 if ((ptr = mpdm_poke(ptr, s, &wc, 1, sizeof(wchar_t))) == NULL)
483 break;
485 /* if it's an end of line, finish */
486 if (wc == L'\n')
487 break;
490 if (ptr != NULL) {
491 ptr = mpdm_poke(ptr, s, L"", 1, sizeof(wchar_t));
492 (*s)--;
495 return ptr;
499 static int write_utf16ae(const struct mpdm_file *f, const wchar_t * str, int le)
500 /* utf16 writer, ANY ending */
502 int cnt = 0;
503 wchar_t wc;
505 /* convert char by char */
506 for (; (wc = *str) != L'\0'; str++) {
508 if (le) {
509 put_char(wc & 0xff, f);
510 put_char((wc & 0xff00) >> 8, f);
512 else {
513 put_char((wc & 0xff00) >> 8, f);
514 put_char(wc & 0xff, f);
518 return cnt;
522 static wchar_t *read_utf16le(const struct mpdm_file *f, int *s)
524 return read_utf16ae(f, s, 1);
528 static int write_utf16le(const struct mpdm_file *f, const wchar_t * str)
530 return write_utf16ae(f, str, 1);
534 static wchar_t *read_utf16be(const struct mpdm_file *f, int *s)
536 return read_utf16ae(f, s, 0);
540 static int write_utf16be(const struct mpdm_file *f, const wchar_t * str)
542 return write_utf16ae(f, str, 0);
546 static wchar_t *read_utf16(struct mpdm_file *f, int *s)
548 int c1, c2;
550 f->f_read = NULL;
552 /* autodetection */
553 c1 = get_char(f);
554 c2 = get_char(f);
556 if (c1 == 0xff && c2 == 0xfe)
557 f->f_read = read_utf16le;
558 else
559 if (c1 == 0xfe && c2 == 0xff)
560 f->f_read = read_utf16be;
561 else
562 return NULL;
564 return f->f_read(f, s);
568 static int write_utf16(struct mpdm_file *f, const wchar_t * str)
570 /* store the LE signature */
571 put_char(0xff, f);
572 put_char(0xfe, f);
574 /* we're 16le from now on */
575 f->f_write = write_utf16le;
577 return f->f_write(f, str);
581 static wchar_t *read_utf32ae(const struct mpdm_file *f, int *s, int le)
582 /* utf32 reader, ANY ending */
584 wchar_t *ptr = NULL;
585 wchar_t wc;
586 int c1, c2, c3, c4;
588 *s = 0;
590 for (;;) {
591 wc = L'\0';
593 if ((c1 = get_char(f)) == EOF)
594 break;
596 if ((c2 = get_char(f)) == EOF)
597 break;
599 if ((c3 = get_char(f)) == EOF)
600 break;
602 if ((c4 = get_char(f)) == EOF)
603 break;
605 if (le)
606 wc = c1 | (c2 << 8) | (c3 << 16) | (c4 << 24);
607 else
608 wc = c4 | (c3 << 8) | (c2 << 16) | (c1 << 24);
610 /* store */
611 if ((ptr = mpdm_poke(ptr, s, &wc, 1, sizeof(wchar_t))) == NULL)
612 break;
614 /* if it's an end of line, finish */
615 if (wc == L'\n')
616 break;
619 if (ptr != NULL) {
620 ptr = mpdm_poke(ptr, s, L"", 1, sizeof(wchar_t));
621 (*s)--;
624 return ptr;
628 static int write_utf32ae(const struct mpdm_file *f, const wchar_t * str, int le)
629 /* utf32 writer, ANY ending */
631 int cnt = 0;
632 wchar_t wc;
634 /* convert char by char */
635 for (; (wc = *str) != L'\0'; str++) {
637 if (le) {
638 put_char((wc & 0x000000ff), f);
639 put_char((wc & 0x0000ff00) >> 8, f);
640 put_char((wc & 0x00ff0000) >> 16, f);
641 put_char((wc & 0xff000000) >> 24, f);
643 else {
644 put_char((wc & 0xff000000) >> 24, f);
645 put_char((wc & 0x00ff0000) >> 16, f);
646 put_char((wc & 0x0000ff00) >> 8, f);
647 put_char((wc & 0x000000ff), f);
651 return cnt;
655 static wchar_t *read_utf32le(const struct mpdm_file *f, int *s)
657 return read_utf32ae(f, s, 1);
661 static int write_utf32le(const struct mpdm_file *f, const wchar_t * str)
663 return write_utf32ae(f, str, 1);
667 static wchar_t *read_utf32be(const struct mpdm_file *f, int *s)
669 return read_utf32ae(f, s, 0);
673 static int write_utf32be(const struct mpdm_file *f, const wchar_t * str)
675 return write_utf32ae(f, str, 0);
679 static wchar_t *read_utf32(struct mpdm_file *f, int *s)
681 int c1, c2, c3, c4;
683 f->f_read = NULL;
685 /* autodetection */
686 c1 = get_char(f);
687 c2 = get_char(f);
688 c3 = get_char(f);
689 c4 = get_char(f);
691 if (c1 == 0xff && c2 == 0xfe && c3 == 0 && c4 == 0)
692 f->f_read = read_utf32le;
693 else
694 if (c1 == 0 && c2 == 0 && c3 == 0xfe && c4 == 0xff)
695 f->f_read = read_utf32be;
696 else
697 return NULL;
699 return f->f_read(f, s);
703 static int write_utf32(struct mpdm_file *f, const wchar_t * str)
705 /* store the LE signature */
706 put_char(0xff, f);
707 put_char(0xfe, f);
708 put_char(0, f);
709 put_char(0, f);
711 /* we're 32le from now on */
712 f->f_write = write_utf32le;
714 return f->f_write(f, str);
718 static mpdm_t new_mpdm_file(void)
719 /* creates a new file value */
721 mpdm_t v = NULL;
722 struct mpdm_file *fs;
723 mpdm_t e;
725 if ((fs = malloc(sizeof(struct mpdm_file))) == NULL)
726 return NULL;
728 memset(fs, '\0', sizeof(struct mpdm_file));
730 /* default I/O functions */
731 fs->f_read = read_mbs;
732 fs->f_write = write_wcs;
734 #ifdef CONFOPT_ICONV
735 /* no iconv encodings by default */
736 fs->ic_enc = fs->ic_dec = (iconv_t) -1;
737 #endif
739 if ((v = mpdm_new(MPDM_FILE | MPDM_FREE, fs, sizeof(struct mpdm_file))) == NULL) {
740 free(fs);
741 return NULL;
744 if ((e = mpdm_hget_s(mpdm_root(), L"ENCODING")) != NULL) {
746 wchar_t *enc = mpdm_string(e);
748 #ifdef CONFOPT_ICONV
749 mpdm_t cs = MPDM_2MBS(e->data);
751 if ((fs->ic_enc = iconv_open((char *) cs->data, "WCHAR_T")) != (iconv_t) -1 &&
752 (fs->ic_dec = iconv_open("WCHAR_T", (char *) cs->data)) != (iconv_t) -1) {
754 fs->f_read = read_iconv;
755 fs->f_write = write_iconv;
757 return v;
759 #endif /* CONFOPT_ICONV */
761 if (wcscmp(enc, L"utf-8") == 0) {
762 fs->f_read = read_utf8;
763 fs->f_write = write_utf8;
765 else
766 if (wcscmp(enc, L"iso8859-1") == 0) {
767 fs->f_read = read_iso8859_1;
768 fs->f_write = write_iso8859_1;
770 else
771 if (wcscmp(enc, L"utf-16le") == 0) {
772 fs->f_read = read_utf16le;
773 fs->f_write = write_utf16le;
775 else
776 if (wcscmp(enc, L"utf-16be") == 0) {
777 fs->f_read = read_utf16be;
778 fs->f_write = write_utf16be;
780 else
781 if (wcscmp(enc, L"utf-16") == 0) {
782 fs->f_read = read_utf16;
783 fs->f_write = write_utf16;
785 else
786 if (wcscmp(enc, L"utf-32le") == 0) {
787 fs->f_read = read_utf32le;
788 fs->f_write = write_utf32le;
790 else
791 if (wcscmp(enc, L"utf-32be") == 0) {
792 fs->f_read = read_utf32be;
793 fs->f_write = write_utf32be;
795 else
796 if (wcscmp(enc, L"utf-32") == 0) {
797 fs->f_read = read_utf32;
798 fs->f_write = write_utf32;
802 return v;
806 static void destroy_mpdm_file(mpdm_t v)
807 /* destroys and file value */
809 struct mpdm_file *fs = (struct mpdm_file *)v->data;
811 if (fs != NULL) {
812 #ifdef CONFOPT_ICONV
813 if (fs->ic_enc != (iconv_t) - 1) {
814 iconv_close(fs->ic_enc);
815 fs->ic_enc = (iconv_t) - 1;
818 if (fs->ic_dec != (iconv_t) - 1) {
819 iconv_close(fs->ic_dec);
820 fs->ic_dec = (iconv_t) - 1;
822 #endif
824 free(fs);
825 v->data = NULL;
830 /** interface **/
832 wchar_t *mpdm_read_mbs(FILE * f, int *s)
833 /* reads a multibyte string from a stream into a dynamic string */
835 struct mpdm_file fs;
837 /* reset the structure */
838 memset(&fs, '\0', sizeof(fs));
839 fs.in = f;
841 return read_mbs(&fs, s);
845 int mpdm_write_wcs(FILE * f, const wchar_t * str)
846 /* writes a wide string to a stream */
848 struct mpdm_file fs;
850 /* reset the structure */
851 memset(&fs, '\0', sizeof(fs));
852 fs.out = f;
854 return write_wcs(&fs, str);
858 mpdm_t mpdm_new_f(FILE * f)
859 /* creates a new file value from a FILE * */
861 mpdm_t v = NULL;
863 if (f == NULL)
864 return NULL;
866 if ((v = new_mpdm_file()) != NULL) {
867 struct mpdm_file *fs = (struct mpdm_file *)v->data;
868 fs->in = fs->out = f;
871 return v;
876 * mpdm_open - Opens a file.
877 * @filename: the file name
878 * @mode: an fopen-like mode string
880 * Opens a file. If @filename can be open in the specified @mode, an
881 * mpdm_t value will be returned containing the file descriptor, or NULL
882 * otherwise.
883 * [File Management]
885 mpdm_t mpdm_open(const mpdm_t filename, const mpdm_t mode)
887 FILE *f;
888 mpdm_t fn;
889 mpdm_t m;
891 if (filename == NULL || mode == NULL)
892 return NULL;
894 /* convert to mbs,s */
895 fn = MPDM_2MBS(filename->data);
896 m = MPDM_2MBS(mode->data);
898 if ((f = fopen((char *) fn->data, (char *) m->data)) == NULL)
899 store_syserr();
900 else {
901 #if defined(CONFOPT_SYS_STAT_H) && defined(S_ISDIR) && defined(EISDIR)
902 struct stat s;
904 /* test if the open file is a directory */
905 if (fstat(fileno(f), &s) != -1 && S_ISDIR(s.st_mode)) {
906 /* it's a directory; fail */
907 errno = EISDIR;
908 store_syserr();
909 fclose(f);
910 f = NULL;
912 #endif
915 return MPDM_F(f);
920 * mpdm_close - Closes a file descriptor.
921 * @fd: the value containing the file descriptor
923 * Closes the file descriptor.
924 * [File Management]
926 mpdm_t mpdm_close(mpdm_t fd)
928 struct mpdm_file *fs = (struct mpdm_file *)fd->data;
930 if ((fd->flags & MPDM_FILE) && fs != NULL) {
931 if (fs->in != NULL)
932 fclose(fs->in);
934 if (fs->out != fs->in && fs->out != NULL)
935 fclose(fs->out);
937 destroy_mpdm_file(fd);
940 return NULL;
945 * mpdm_read - Reads a line from a file descriptor.
946 * @fd: the value containing the file descriptor
948 * Reads a line from @fd. Returns the line, or NULL on EOF.
949 * [File Management]
950 * [Character Set Conversion]
952 mpdm_t mpdm_read(const mpdm_t fd)
954 mpdm_t v = NULL;
955 wchar_t *ptr;
956 int s;
957 const struct mpdm_file *fs = fd->data;
959 if (fs == NULL)
960 return NULL;
962 ptr = fs->f_read(fs, &s);
964 if (ptr != NULL)
965 v = MPDM_ENS(ptr, s);
967 return v;
971 mpdm_t mpdm_getchar(const mpdm_t fd)
973 int c;
974 wchar_t tmp[2];
975 const struct mpdm_file *fs = fd->data;
977 if (fs == NULL || (c = get_char(fs)) == EOF)
978 return NULL;
980 /* get the char as-is */
981 tmp[0] = (wchar_t) c;
982 tmp[1] = L'\0';
984 return MPDM_S(tmp);
988 mpdm_t mpdm_putchar(const mpdm_t fd, const mpdm_t c)
990 const struct mpdm_file *fs = fd->data;
991 const wchar_t *ptr = mpdm_string(c);
993 if (fs == NULL || put_char(*ptr, fs) == -1)
994 return NULL;
996 return c;
1001 * mpdm_write - Writes a value into a file.
1002 * @fd: the file descriptor.
1003 * @v: the value to be written.
1005 * Writes the @v string value into @fd, using the current encoding.
1006 * [File Management]
1007 * [Character Set Conversion]
1009 int mpdm_write(const mpdm_t fd, const mpdm_t v)
1011 const struct mpdm_file *fs = fd->data;
1012 int ret = -1;
1014 if (fs == NULL)
1015 return -1;
1017 ret = fs->f_write(fs, mpdm_string(v));
1019 return ret;
1023 int mpdm_fseek(const mpdm_t fd, long offset, int whence)
1025 const struct mpdm_file *fs = fd->data;
1027 return fseek(fs->in, offset, whence);
1031 long mpdm_ftell(const mpdm_t fd)
1033 const struct mpdm_file *fs = fd->data;
1035 return ftell(fs->in);
1039 FILE * mpdm_get_filehandle(const mpdm_t fd)
1041 FILE * f = NULL;
1043 if (fd->flags & MPDM_FILE) {
1044 const struct mpdm_file *fs = fd->data;
1045 f = fs->in;
1048 return f;
1053 mpdm_t mpdm_bread(mpdm_t fd, int size)
1058 int mpdm_bwrite(mpdm_tfd, mpdm_t v, int size)
1064 static mpdm_t embedded_encodings(void)
1066 mpdm_t e;
1067 wchar_t *e2e[] = {
1068 L"utf-8", L"utf-8",
1069 L"utf8", NULL,
1070 L"iso8859-1", L"iso8859-1",
1071 L"iso-8859-1", NULL,
1072 L"latin1", NULL,
1073 L"latin-1", NULL,
1074 L"utf-16le", L"utf-16le",
1075 L"utf16le", NULL,
1076 L"ucs-2le", NULL,
1077 L"utf-16be", L"utf-16be",
1078 L"utf16be", NULL,
1079 L"ucs-2be", NULL,
1080 L"utf-16", L"utf-16",
1081 L"utf16", NULL,
1082 L"ucs-2", NULL,
1083 L"ucs2", NULL,
1084 L"utf-32le", L"utf-32le",
1085 L"utf32le", NULL,
1086 L"ucs-4le", NULL,
1087 L"utf-32be", L"utf-32be",
1088 L"utf32be", NULL,
1089 L"ucs-4be", NULL,
1090 L"utf-32", L"utf-32",
1091 L"utf32", NULL,
1092 L"ucs-4", NULL,
1093 L"ucs4", NULL,
1094 NULL, NULL
1097 if ((e = mpdm_hget_s(mpdm_root(), L"EMBEDDED_ENCODINGS")) == NULL) {
1098 int n;
1099 wchar_t *p = NULL;
1101 e = MPDM_H(0);
1103 for (n = 0; e2e[n] != NULL; n += 2) {
1104 mpdm_t v = MPDM_S(e2e[n]);
1106 if (e2e[n + 1] != NULL)
1107 p = e2e[n + 1];
1109 mpdm_hset(e, v, MPDM_S(p));
1110 mpdm_hset(e, mpdm_ulc(v, 1), MPDM_S(p));
1113 mpdm_hset_s(mpdm_root(), L"EMBEDDED_ENCODINGS", e);
1116 return e;
1121 * mpdm_encoding - Sets the current charset encoding for files.
1122 * @charset: the charset name.
1124 * Sets the current charset encoding for files. Future opened
1125 * files will be assumed to be encoded with @charset, which can
1126 * be any of the supported charset names (utf-8, iso-8859-1, etc.),
1127 * and converted on each read / write. If charset is NULL, it
1128 * is reverted to default charset conversion (i.e. the one defined
1129 * in the locale).
1130 * Returns a negative number if @charset is unsupported, or zero
1131 * if no errors were found.
1132 * [File Management]
1133 * [Character Set Conversion]
1135 int mpdm_encoding(mpdm_t charset)
1137 int ret = -1;
1138 mpdm_t e = embedded_encodings();
1139 mpdm_t v = NULL;
1141 /* NULL encoding? done */
1142 if (charset == NULL) {
1143 mpdm_hset_s(mpdm_root(), L"ENCODING", NULL);
1144 return 0;
1147 #ifdef CONFOPT_ICONV
1149 iconv_t ic;
1150 mpdm_t cs = MPDM_2MBS(charset->data);
1152 /* tries to create an iconv encoder and decoder for this charset */
1154 if ((ic = iconv_open("WCHAR_T", (char *) cs->data)) == (iconv_t) - 1)
1155 ret = -1;
1156 else {
1157 iconv_close(ic);
1159 if ((ic = iconv_open((char *) cs->data, "WCHAR_T")) == (iconv_t) - 1)
1160 ret = -2;
1161 else {
1162 iconv_close(ic);
1164 /* got a valid encoding */
1165 v = charset;
1166 ret = 0;
1170 #endif /* CONFOPT_ICONV */
1172 if (ret != 0 && (v = mpdm_hget(e, charset)) != NULL)
1173 ret = 0;
1175 if (ret == 0)
1176 mpdm_hset_s(mpdm_root(), L"ENCODING", v);
1178 return ret;
1183 * mpdm_unlink - Deletes a file.
1184 * @filename: file name to be deleted
1186 * Deletes a file.
1187 * [File Management]
1189 int mpdm_unlink(const mpdm_t filename)
1191 int ret;
1192 mpdm_t fn;
1194 /* convert to mbs */
1195 fn = MPDM_2MBS(filename->data);
1197 if ((ret = unlink((char *) fn->data)) == -1)
1198 store_syserr();
1200 return ret;
1205 * mpdm_stat - Gives status from a file.
1206 * @filename: file name to get the status from
1208 * Returns a 14 element array of the status (permissions, onwer, etc.)
1209 * from the desired @filename, or NULL if the file cannot be accessed.
1210 * (man 2 stat).
1212 * The values are: 0, device number of filesystem; 1, inode number;
1213 * 2, file mode; 3, number of hard links to the file; 4, uid; 5, gid;
1214 * 6, device identifier; 7, total size of file in bytes; 8, atime;
1215 * 9, mtime; 10, ctime; 11, preferred block size for system I/O;
1216 * 12, number of blocks allocated and 13, canonicalized file name.
1217 * Not all elements have necesarily meaningful values, as most are
1218 * system-dependent.
1219 * [File Management]
1221 mpdm_t mpdm_stat(const mpdm_t filename)
1223 mpdm_t r = NULL;
1225 #ifdef CONFOPT_SYS_STAT_H
1226 struct stat s;
1227 mpdm_t fn;
1229 fn = MPDM_2MBS(filename->data);
1231 if (stat((char *) fn->data, &s) != -1) {
1232 r = MPDM_A(14);
1234 mpdm_aset(r, MPDM_I(s.st_dev), 0);
1235 mpdm_aset(r, MPDM_I(s.st_ino), 1);
1236 mpdm_aset(r, MPDM_I(s.st_mode), 2);
1237 mpdm_aset(r, MPDM_I(s.st_nlink), 3);
1238 mpdm_aset(r, MPDM_I(s.st_uid), 4);
1239 mpdm_aset(r, MPDM_I(s.st_gid), 5);
1240 mpdm_aset(r, MPDM_I(s.st_rdev), 6);
1241 mpdm_aset(r, MPDM_I(s.st_size), 7);
1242 mpdm_aset(r, MPDM_I(s.st_atime), 8);
1243 mpdm_aset(r, MPDM_I(s.st_mtime), 9);
1244 mpdm_aset(r, MPDM_I(s.st_ctime), 10);
1245 mpdm_aset(r, MPDM_I(0), 11); /* s.st_blksize */
1246 mpdm_aset(r, MPDM_I(0), 12); /* s.st_blocks */
1248 #ifdef CONFOPT_CANONICALIZE_FILE_NAME
1251 char * ptr;
1253 if ((ptr = canonicalize_file_name(
1254 (char *)fn->data)) != NULL) {
1255 mpdm_aset(r, MPDM_MBS(ptr), 13);
1256 free(ptr);
1259 #endif
1261 #ifdef CONFOPT_REALPATH
1263 char tmp[2048];
1265 if (realpath((char *)fn->data, tmp) != NULL)
1266 mpdm_aset(r, MPDM_MBS(tmp), 13);
1268 #endif
1271 else
1272 store_syserr();
1274 #endif /* CONFOPT_SYS_STAT_H */
1276 return r;
1281 * mpdm_chmod - Changes a file's permissions.
1282 * @filename: the file name
1283 * @perms: permissions (element 2 from mpdm_stat())
1285 * Changes the permissions for a file.
1286 * [File Management]
1288 int mpdm_chmod(const mpdm_t filename, mpdm_t perms)
1290 int r = -1;
1292 mpdm_t fn = MPDM_2MBS(filename->data);
1294 if ((r = chmod((char *) fn->data, mpdm_ival(perms))) == -1)
1295 store_syserr();
1297 return r;
1302 * mpdm_chdir - Changes the working directory
1303 * @dir: the new path
1305 * Changes the working directory
1306 * [File Management]
1308 int mpdm_chdir(const mpdm_t dir)
1310 int r = -1;
1312 mpdm_t fn = MPDM_2MBS(dir->data);
1314 if ((r = chdir((char *) fn->data)) == -1)
1315 store_syserr();
1317 return r;
1322 * mpdm_chown - Changes a file's owner.
1323 * @filename: the file name
1324 * @uid: user id (element 4 from mpdm_stat())
1325 * @gid: group id (element 5 from mpdm_stat())
1327 * Changes the owner and group id's for a file.
1328 * [File Management]
1330 int mpdm_chown(const mpdm_t filename, mpdm_t uid, mpdm_t gid)
1332 int r = -1;
1334 #ifdef CONFOPT_CHOWN
1336 mpdm_t fn = MPDM_2MBS(filename->data);
1338 if ((r = chown((char *) fn->data, mpdm_ival(uid), mpdm_ival(gid))) == -1)
1339 store_syserr();
1341 #endif /* CONFOPT_CHOWN */
1343 return r;
1348 * mpdm_glob - Executes a file globbing.
1349 * @spec: Globbing spec
1350 * @base: Optional base directory
1352 * Executes a file globbing. @spec is system-dependent, but usually
1353 * the * and ? metacharacters work everywhere. @base can contain a
1354 * directory; if that's the case, the output strings will include it.
1355 * In any case, each returned value will be suitable for a call to
1356 * mpdm_open().
1358 * Returns an array of files that match the globbing (can be an empty
1359 * array if no file matches), or NULL if globbing is unsupported.
1360 * [File Management]
1362 mpdm_t mpdm_glob(const mpdm_t spec, const mpdm_t base)
1364 mpdm_t d = NULL;
1365 mpdm_t f = NULL;
1366 mpdm_t v = NULL;
1368 #ifdef CONFOPT_WIN32
1370 WIN32_FIND_DATA fd;
1371 HANDLE h;
1372 char *ptr;
1373 mpdm_t w;
1374 mpdm_t s = NULL;
1375 mpdm_t sp = NULL;
1377 if (mpdm_size(base))
1378 sp = mpdm_strcat(base, MPDM_LS(L"/"));
1380 sp = mpdm_strcat(sp, mpdm_size(spec) == 0 ? MPDM_LS(L"*.*") : spec);
1382 /* delete repeated directory delimiters */
1383 sp = mpdm_sregex(MPDM_LS(L"@[\\/]+@g"), sp, MPDM_LS(L"/"), 0);
1385 sp = MPDM_2MBS(sp->data);
1387 v = MPDM_A(0);
1388 d = MPDM_A(0);
1389 f = MPDM_A(0);
1391 if ((h = FindFirstFile((char *) sp->data, &fd)) != INVALID_HANDLE_VALUE) {
1392 /* if spec includes a directory, store in s */
1393 if ((ptr = strrchr((char *) sp->data, '/')) != NULL) {
1394 *(ptr + 1) = '\0';
1395 s = MPDM_MBS(sp->data);
1398 do {
1399 /* ignore . and .. */
1400 if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0)
1401 continue;
1403 /* concat base directory and file names */
1404 w = mpdm_strcat(s, MPDM_MBS(fd.cFileName));
1406 /* if it's a directory, add a / */
1407 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1408 w = mpdm_strcat(w, MPDM_LS(L"/"));
1409 mpdm_push(d, w);
1411 else
1412 mpdm_push(f, w);
1414 while (FindNextFile(h, &fd));
1416 FindClose(h);
1419 #endif
1421 #if CONFOPT_GLOB_H
1423 /* glob.h support */
1424 glob_t globbuf;
1425 const char *ptr;
1427 /* build full path */
1428 if (mpdm_size(base))
1429 v = mpdm_strcat(base, MPDM_LS(L"/"));
1431 v = mpdm_strcat(v, mpdm_size(spec) == 0 ? MPDM_LS(L"*") : spec);
1433 /* delete repeated directory delimiters */
1434 v = mpdm_sregex(MPDM_LS(L"@/{2,}@g"), v, MPDM_LS(L"/"), 0);
1436 v = MPDM_2MBS(v->data);
1438 ptr = v->data;
1440 globbuf.gl_offs = 1;
1442 v = MPDM_A(0);
1443 d = MPDM_A(0);
1444 f = MPDM_A(0);
1446 if (glob(ptr, GLOB_MARK, NULL, &globbuf) == 0) {
1447 int n;
1449 for (n = 0; globbuf.gl_pathv[n] != NULL; n++) {
1450 char *ptr = globbuf.gl_pathv[n];
1451 mpdm_t t = MPDM_MBS(ptr);
1453 /* if last char is /, add to directories */
1454 if (ptr[strlen(ptr) - 1] == '/')
1455 mpdm_push(d, t);
1456 else
1457 mpdm_push(f, t);
1461 globfree(&globbuf);
1463 #else
1465 /* no win32 nor glob.h; try workaround */
1466 /* ... */
1468 #endif
1470 if (v != NULL) {
1471 int n;
1473 d = mpdm_sort(d, 1);
1474 f = mpdm_sort(f, 1);
1476 /* transfer all data in d and f */
1477 for (n = 0; n < mpdm_size(d); n++)
1478 mpdm_push(v, mpdm_aget(d, n));
1479 for (n = 0; n < mpdm_size(f); n++)
1480 mpdm_push(v, mpdm_aget(f, n));
1483 return v;
1487 #ifdef CONFOPT_WIN32
1489 static void win32_pipe(HANDLE * h, int n)
1491 SECURITY_ATTRIBUTES sa;
1492 HANDLE cp, t;
1494 memset(&sa, '\0', sizeof(sa));
1495 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
1496 sa.bInheritHandle = TRUE;
1497 sa.lpSecurityDescriptor = NULL;
1499 cp = GetCurrentProcess();
1501 CreatePipe(&h[0], &h[1], &sa, 0);
1502 DuplicateHandle(cp, h[n], cp, &t, 0, FALSE, DUPLICATE_SAME_ACCESS);
1503 CloseHandle(h[n]);
1504 h[n] = t;
1508 static int sysdep_popen(mpdm_t v, char *prg, int rw)
1509 /* win32-style pipe */
1511 HANDLE pr[2];
1512 HANDLE pw[2];
1513 PROCESS_INFORMATION pi;
1514 STARTUPINFO si;
1515 int ret;
1516 struct mpdm_file *fs = v->data;
1518 /* init all */
1519 pr[0] = pr[1] = pw[0] = pw[1] = NULL;
1521 if (rw & 0x01)
1522 win32_pipe(pr, 0);
1523 if (rw & 0x02)
1524 win32_pipe(pw, 1);
1526 /* spawn new process */
1527 memset(&pi, '\0', sizeof(pi));
1528 memset(&si, '\0', sizeof(si));
1530 si.cb = sizeof(STARTUPINFO);
1531 si.hStdError = pr[1];
1532 si.hStdOutput = pr[1];
1533 si.hStdInput = pw[0];
1534 si.dwFlags |= STARTF_USESTDHANDLES;
1536 ret = CreateProcess(NULL, prg, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
1538 if (rw & 0x01)
1539 CloseHandle(pr[1]);
1540 if (rw & 0x02)
1541 CloseHandle(pw[0]);
1543 fs->hin = pr[0];
1544 fs->hout = pw[1];
1546 return ret;
1550 static int sysdep_pclose(const mpdm_t v)
1552 const struct mpdm_file *fs = v->data;
1554 if (fs->hin != NULL)
1555 CloseHandle(fs->hin);
1557 if (fs->hout != NULL)
1558 CloseHandle(fs->hout);
1560 /* how to know process exit code? */
1561 return 0;
1565 #else /* CONFOPT_WIN32 */
1567 static int sysdep_popen(mpdm_t v, char *prg, int rw)
1568 /* unix-style pipe open */
1570 int pr[2], pw[2];
1571 struct mpdm_file *fs = (struct mpdm_file *)v->data;
1573 /* init all */
1574 pr[0] = pr[1] = pw[0] = pw[1] = -1;
1576 if (rw & 0x01)
1577 pipe(pr);
1578 if (rw & 0x02)
1579 pipe(pw);
1581 if (fork() == 0) {
1582 /* child process */
1583 if (rw & 0x01) {
1584 close(1);
1585 dup(pr[1]);
1586 close(pr[0]);
1588 if (rw & 0x02) {
1589 close(0);
1590 dup(pw[0]);
1591 close(pw[1]);
1594 /* redirect stderr to stdout */
1595 close(2);
1596 dup(1);
1598 /* run the program */
1599 execlp("/bin/sh", "/bin/sh", "-c", prg, NULL);
1600 execlp(prg, prg, NULL);
1602 /* still here? exec failed; close pipes and exit */
1603 close(0);
1604 close(1);
1605 exit(0);
1608 /* create the pipes as non-buffered streams */
1609 if (rw & 0x01) {
1610 fs->in = fdopen(pr[0], "r");
1611 setvbuf(fs->in, NULL, _IONBF, 0);
1612 close(pr[1]);
1615 if (rw & 0x02) {
1616 fs->out = fdopen(pw[1], "w");
1617 setvbuf(fs->out, NULL, _IONBF, 0);
1618 close(pw[0]);
1621 return 1;
1625 static int sysdep_pclose(const mpdm_t v)
1626 /* unix-style pipe close */
1628 int s;
1629 const struct mpdm_file *fs = v->data;
1631 if (fs->in != NULL)
1632 fclose(fs->in);
1634 if (fs->out != fs->in && fs->out != NULL)
1635 fclose(fs->out);
1637 wait(&s);
1639 return s;
1643 #endif /* CONFOPT_WIN32 */
1647 * mpdm_popen - Opens a pipe.
1648 * @prg: the program to pipe
1649 * @mode: an fopen-like mode string
1651 * Opens a pipe to a program. If @prg can be open in the specified @mode, an
1652 * mpdm_t value will be returned containing the file descriptor, or NULL
1653 * otherwise.
1654 * [File Management]
1656 mpdm_t mpdm_popen(const mpdm_t prg, const mpdm_t mode)
1658 mpdm_t v, pr, md;
1659 char *m;
1660 int rw = 0;
1662 if (prg == NULL || mode == NULL)
1663 return NULL;
1665 if ((v = new_mpdm_file()) == NULL)
1666 return NULL;
1668 /* convert to mbs,s */
1669 pr = MPDM_2MBS(prg->data);
1670 md = MPDM_2MBS(mode->data);
1672 /* get the mode */
1673 m = (char *) md->data;
1675 /* set the mode */
1676 if (m[0] == 'r')
1677 rw = 0x01;
1678 if (m[0] == 'w')
1679 rw = 0x02;
1680 if (m[1] == '+')
1681 rw = 0x03; /* r+ or w+ */
1683 if (!sysdep_popen(v, (char *) pr->data, rw)) {
1684 destroy_mpdm_file(v);
1685 v = NULL;
1688 return v;
1693 * mpdm_pclose - Closes a pipe.
1694 * @fd: the value containing the file descriptor
1696 * Closes a pipe.
1697 * [File Management]
1699 mpdm_t mpdm_pclose(mpdm_t fd)
1701 mpdm_t r = NULL;
1703 if ((fd->flags & MPDM_FILE) && fd->data != NULL) {
1704 r = MPDM_I(sysdep_pclose(fd));
1705 destroy_mpdm_file(fd);
1708 return r;
1713 * mpdm_home_dir - Returns the home user directory.
1715 * Returns a system-dependent directory where the user can write
1716 * documents and create subdirectories.
1717 * [File Management]
1719 mpdm_t mpdm_home_dir(void)
1721 mpdm_t r = NULL;
1722 char *ptr;
1723 char tmp[512] = "";
1725 #ifdef CONFOPT_WIN32
1727 LPITEMIDLIST pidl;
1729 /* get the 'My Documents' folder */
1730 SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl);
1731 SHGetPathFromIDList(pidl, tmp);
1732 strcat(tmp, "\\");
1734 #endif
1736 #ifdef CONFOPT_PWD_H
1738 struct passwd *p;
1740 /* get home dir from /etc/passwd entry */
1741 if (tmp[0] == '\0' && (p = getpwuid(getpid())) != NULL) {
1742 strcpy(tmp, p->pw_dir);
1743 strcat(tmp, "/");
1746 #endif
1748 /* still none? try the ENV variable $HOME */
1749 if (tmp[0] == '\0' && (ptr = getenv("HOME")) != NULL) {
1750 strcpy(tmp, ptr);
1751 strcat(tmp, "/");
1754 if (tmp[0] != '\0')
1755 r = MPDM_MBS(tmp);
1757 return r;
1762 * mpdm_app_dir - Returns the applications directory.
1764 * Returns a system-dependent directory where the applications store
1765 * their private data, as components or resources.
1766 * [File Management]
1768 mpdm_t mpdm_app_dir(void)
1770 mpdm_t r = NULL;
1772 #ifdef CONFOPT_WIN32
1774 HKEY hkey;
1775 char tmp[MAX_PATH];
1776 LPITEMIDLIST pidl;
1778 /* get the 'Program Files' folder (can fail) */
1779 tmp[0] = '\0';
1780 if (SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidl) == S_OK)
1781 SHGetPathFromIDList(pidl, tmp);
1783 /* if it's still empty, get from the registry */
1784 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1785 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
1786 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) {
1787 int n = sizeof(tmp);
1789 if (RegQueryValueEx(hkey, "ProgramFilesDir",
1790 NULL, NULL, tmp, (LPDWORD) & n) != ERROR_SUCCESS)
1791 tmp[0] = '\0';
1794 if (tmp[0] != '\0') {
1795 strcat(tmp, "\\");
1796 r = MPDM_MBS(tmp);
1798 #endif
1800 /* still none? get the configured directory */
1801 if (r == NULL)
1802 r = MPDM_MBS(CONFOPT_PREFIX "/share/");
1804 return r;