3 MPDM - Minimum Profit Data Manager
4 Copyright (C) 2003/2009 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
28 #ifdef CONFOPT_CANONICALIZE_FILE_NAME
37 #ifdef CONFOPT_UNISTD_H
53 #ifdef CONFOPT_SYS_TYPES_H
54 #include <sys/types.h>
57 #ifdef CONFOPT_SYS_WAIT_H
61 #ifdef CONFOPT_SYS_STAT_H
80 wchar_t * (* f_read
) (struct mpdm_file
*, int *);
81 int (* f_write
)(struct mpdm_file
*, const wchar_t *);
86 #endif /* CONFOPT_ICONV */
91 #endif /* CONFOPT_WIN32 */
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(struct mpdm_file
*f
)
111 /* reads a character from a file structure */
117 if (f
->hin
!= NULL
) {
121 if (ReadFile(f
->hin
, &tmp
, 1, &n
, NULL
) && n
> 0)
125 #endif /* CONFOPT_WIN32 */
128 /* read (converting to positive if needed) */
129 if ((c
= fgetc(f
->in
)) < 0 && !feof(f
->in
))
137 static int put_buf(const char *ptr
, int s
, struct mpdm_file
*f
)
138 /* writes s bytes in the buffer in ptr to f */
142 if (f
->hout
!= NULL
) {
145 if (WriteFile(f
->hout
, ptr
, s
, &n
, NULL
) && n
> 0)
149 #endif /* CONFOPT_WIN32 */
152 s
= fwrite(ptr
, s
, 1, f
->out
);
158 static int put_char(int c
, struct mpdm_file
*f
)
159 /* writes a character in a file structure */
163 if (put_buf(&tmp
, 1, f
) != 1)
170 static wchar_t *read_mbs(struct mpdm_file
*f
, int *s
)
171 /* reads a multibyte string from a mpdm_file into a dynamic string */
178 while ((c
= get_char(f
)) != EOF
) {
184 if (i
== sizeof(tmp
) - 1) {
185 /* out of space; start allocating */
186 if ((auxptr
= mpdm_poke(auxptr
, &n
, tmp
, i
, sizeof(char))) == NULL
)
193 /* is there something to return? */
199 /* auxiliary space used; concat all */
200 if ((auxptr
= mpdm_poke(auxptr
, &n
, tmp
, i
, sizeof(char))) == NULL
)
203 /* do the conversion */
204 ptr
= mpdm_mbstowcs(auxptr
, s
, -1);
209 ptr
= mpdm_mbstowcs(tmp
, s
, -1);
216 static int write_wcs(struct mpdm_file
*f
, const wchar_t * str
)
217 /* writes a wide string to an struct mpdm_file */
222 ptr
= mpdm_wcstombs(str
, &s
);
223 s
= put_buf(ptr
, s
, f
);
232 static wchar_t *read_iconv(struct mpdm_file
*f
, int *s
)
233 /* reads a multibyte string transforming with iconv */
242 /* resets the decoder */
243 iconv(f
->ic_dec
, NULL
, NULL
, NULL
, NULL
);
245 while ((c
= get_char(f
)) != EOF
) {
251 /* too big? shouldn't happen */
252 if (i
== sizeof(tmp
))
257 ol
= sizeof(wchar_t);
261 if (iconv(f
->ic_dec
, &iptr
, &il
, &optr
, &ol
) == (size_t) -1) {
262 /* found incomplete multibyte character */
266 /* otherwise, return '?' */
272 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
275 /* if it's an end of line, finish */
281 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
289 static int write_iconv(struct mpdm_file
*f
, const wchar_t * str
)
290 /* writes a wide string to a stream using iconv */
295 /* resets the encoder */
296 iconv(f
->ic_enc
, NULL
, NULL
, NULL
, NULL
);
298 /* convert char by char */
299 for (; *str
!= L
'\0'; str
++) {
304 il
= sizeof(wchar_t);
310 if (iconv(f
->ic_enc
, &iptr
, &il
, &optr
, &ol
) == (size_t) -1) {
311 /* error converting; convert a '?' instead */
314 il
= sizeof(wchar_t);
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
)
332 #endif /* CONFOPT_ICONV */
334 #define UTF8_BYTE() if((c = get_char(f)) == EOF) break
336 static wchar_t *read_utf8(struct mpdm_file
*f
, int *s
)
353 if ((c
& 0xe0) == 0xe0) {
354 wc
= (c
& 0x1f) << 12;
356 wc
|= (c
& 0x3f) << 6;
361 wc
= (c
& 0x3f) << 6;
367 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
370 /* if it's an end of line, finish */
376 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
384 static int write_utf8(struct mpdm_file
*f
, const wchar_t * str
)
390 /* convert char by char */
391 for (; (wc
= *str
) != L
'\0'; str
++) {
393 put_char((int) wc
, f
);
396 put_char((int) (0xc0 | (wc
>> 6)), f
);
397 put_char((int) (0x80 | (wc
& 0x3f)), f
);
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
);
414 static wchar_t *read_utf8_bom(struct mpdm_file
*f
, int *s
)
415 /* utf-8 reader with BOM detection */
422 if (get_char(f
) == 0xef && get_char(f
) == 0xbb && get_char(f
) == 0xbf)
426 fseek(f
->in
, 0, SEEK_SET
);
429 mpdm_hset_s(mpdm_root(), L
"DETECTED_ENCODING", MPDM_LS(enc
));
431 /* we're utf-8 from now on */
432 f
->f_read
= read_utf8
;
434 return f
->f_read(f
, s
);
438 static int write_utf8_bom(struct mpdm_file
*f
, const wchar_t * str
)
439 /* utf-8 writer with BOM */
446 /* we're utf-8 from now on */
447 f
->f_write
= write_utf8
;
449 return f
->f_write(f
, str
);
453 static wchar_t *read_iso8859_1(struct mpdm_file
*f
, int *s
)
454 /* iso8859-1 reader */
462 while ((c
= get_char(f
)) != EOF
) {
466 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
469 /* if it's an end of line, finish */
475 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
483 static int write_iso8859_1(struct mpdm_file
*f
, const wchar_t * str
)
484 /* iso8859-1 writer */
489 /* convert char by char */
490 for (; (wc
= *str
) != L
'\0'; str
++)
491 put_char(wc
<= 0xff ? (int) wc
: '?', f
);
497 static wchar_t *read_utf16ae(struct mpdm_file
*f
, int *s
, int le
)
498 /* utf16 reader, ANY ending */
509 if ((c1
= get_char(f
)) == EOF
)
512 if ((c2
= get_char(f
)) == EOF
)
521 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
524 /* if it's an end of line, finish */
530 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
538 static int write_utf16ae(struct mpdm_file
*f
, const wchar_t * str
, int le
)
539 /* utf16 writer, ANY ending */
544 /* convert char by char */
545 for (; (wc
= *str
) != L
'\0'; str
++) {
548 put_char(wc
& 0xff, f
);
549 put_char((wc
& 0xff00) >> 8, f
);
552 put_char((wc
& 0xff00) >> 8, f
);
553 put_char(wc
& 0xff, f
);
561 static wchar_t *read_utf16le(struct mpdm_file
*f
, int *s
)
563 return read_utf16ae(f
, s
, 1);
567 static int write_utf16le(struct mpdm_file
*f
, const wchar_t * str
)
569 return write_utf16ae(f
, str
, 1);
573 static wchar_t *read_utf16be(struct mpdm_file
*f
, int *s
)
575 return read_utf16ae(f
, s
, 0);
579 static int write_utf16be(struct mpdm_file
*f
, const wchar_t * str
)
581 return write_utf16ae(f
, str
, 0);
585 static wchar_t *read_utf16(struct mpdm_file
*f
, int *s
)
588 wchar_t *enc
= L
"utf-16le";
590 /* assume little-endian */
591 f
->f_read
= read_utf16le
;
597 if (c1
== 0xfe && c2
== 0xff) {
599 f
->f_read
= read_utf16be
;
602 if (c1
!= 0xff || c2
!= 0xfe) {
603 /* no BOM; rewind and hope */
604 fseek(f
->in
, 0, SEEK_SET
);
607 mpdm_hset_s(mpdm_root(), L
"DETECTED_ENCODING", MPDM_LS(enc
));
609 return f
->f_read(f
, s
);
613 static int write_utf16le_bom(struct mpdm_file
*f
, const wchar_t * str
)
615 /* store the LE signature */
619 /* we're 16le from now on */
620 f
->f_write
= write_utf16le
;
622 return f
->f_write(f
, str
);
626 static int write_utf16be_bom(struct mpdm_file
*f
, const wchar_t * str
)
628 /* store the BE signature */
632 /* we're 16be from now on */
633 f
->f_write
= write_utf16be
;
635 return f
->f_write(f
, str
);
639 static wchar_t *read_utf32ae(struct mpdm_file
*f
, int *s
, int le
)
640 /* utf32 reader, ANY ending */
651 if ((c1
= get_char(f
)) == EOF
)
654 if ((c2
= get_char(f
)) == EOF
)
657 if ((c3
= get_char(f
)) == EOF
)
660 if ((c4
= get_char(f
)) == EOF
)
664 wc
= c1
| (c2
<< 8) | (c3
<< 16) | (c4
<< 24);
666 wc
= c4
| (c3
<< 8) | (c2
<< 16) | (c1
<< 24);
669 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
672 /* if it's an end of line, finish */
678 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
686 static int write_utf32ae(struct mpdm_file
*f
, const wchar_t * str
, int le
)
687 /* utf32 writer, ANY ending */
692 /* convert char by char */
693 for (; (wc
= *str
) != L
'\0'; str
++) {
696 put_char((wc
& 0x000000ff), f
);
697 put_char((wc
& 0x0000ff00) >> 8, f
);
698 put_char((wc
& 0x00ff0000) >> 16, f
);
699 put_char((wc
& 0xff000000) >> 24, f
);
702 put_char((wc
& 0xff000000) >> 24, f
);
703 put_char((wc
& 0x00ff0000) >> 16, f
);
704 put_char((wc
& 0x0000ff00) >> 8, f
);
705 put_char((wc
& 0x000000ff), f
);
713 static wchar_t *read_utf32le(struct mpdm_file
*f
, int *s
)
715 return read_utf32ae(f
, s
, 1);
719 static int write_utf32le(struct mpdm_file
*f
, const wchar_t * str
)
721 return write_utf32ae(f
, str
, 1);
725 static wchar_t *read_utf32be(struct mpdm_file
*f
, int *s
)
727 return read_utf32ae(f
, s
, 0);
731 static int write_utf32be(struct mpdm_file
*f
, const wchar_t * str
)
733 return write_utf32ae(f
, str
, 0);
737 static wchar_t *read_utf32(struct mpdm_file
*f
, int *s
)
740 wchar_t *enc
= L
"utf-32le";
742 f
->f_read
= read_utf32le
;
750 if (c1
== 0 && c2
== 0 && c3
== 0xfe && c4
== 0xff) {
752 f
->f_read
= read_utf32be
;
754 if (c1
!= 0xff || c2
!= 0xfe || c3
!= 0 || c4
!= 0) {
755 /* no BOM; assume le and hope */
756 fseek(f
->in
, 0, SEEK_SET
);
759 mpdm_hset_s(mpdm_root(), L
"DETECTED_ENCODING", MPDM_LS(enc
));
761 return f
->f_read(f
, s
);
765 static int write_utf32le_bom(struct mpdm_file
*f
, const wchar_t * str
)
767 /* store the LE signature */
773 /* we're 32le from now on */
774 f
->f_write
= write_utf32le
;
776 return f
->f_write(f
, str
);
780 static int write_utf32be_bom(struct mpdm_file
*f
, const wchar_t * str
)
782 /* store the BE signature */
788 /* we're 32be from now on */
789 f
->f_write
= write_utf32be
;
791 return f
->f_write(f
, str
);
795 static wchar_t *read_auto(struct mpdm_file
*f
, int *s
)
796 /* autodetects different encodings based on the BOM */
800 /* by default, multibyte reading */
801 f
->f_read
= read_mbs
;
803 /* ensure seeking is possible */
804 if (f
->in
!= NULL
&& fseek(f
->in
, 0, SEEK_CUR
) != -1) {
810 /* can be utf32le or utf16le */
811 if (get_char(f
) == 0xfe) {
812 /* if next 2 chars are 0x00, it's utf32; otherwise utf16 */
813 if (get_char(f
) == 0x00 && get_char(f
) == 0x00) {
815 f
->f_read
= read_utf32le
;
819 /* rewind to 3rd character */
820 fseek(f
->in
, 2, SEEK_SET
);
823 f
->f_read
= read_utf16le
;
831 if (get_char(f
) == 0x00 && get_char(f
) == 0xfe && get_char(f
) == 0xff) {
833 f
->f_read
= read_utf32be
;
840 if (get_char(f
) == 0xff) {
842 f
->f_read
= read_utf16be
;
848 /* can be utf8 with BOM */
849 if (get_char(f
) == 0xbb && get_char(f
) == 0xbf) {
851 f
->f_read
= read_utf8
;
856 /* try if a first bunch of chars are valid UTF-8 */
861 while (--n
&& (c
= get_char(f
)) != EOF
) {
862 if ((c
& 0xc0) == 0x80) {
863 if ((p
& 0xc0) == 0xc0)
866 if ((p
& 0x80) == 0x00) {
872 if ((p
& 0xc0) == 0xc0) {
881 /* invalid utf-8; fall back to 8bit */
883 f
->f_read
= read_iso8859_1
;
887 /* utf-8 sequences found */
889 f
->f_read
= read_utf8
;
892 /* 7 bit ASCII: do nothing */
895 /* none of the above; restart */
896 fseek(f
->in
, 0, SEEK_SET
);
900 mpdm_hset_s(mpdm_root(), L
"DETECTED_ENCODING", MPDM_LS(enc
));
902 return f
->f_read(f
, s
);
906 static mpdm_t
new_mpdm_file(void)
907 /* creates a new file value */
910 struct mpdm_file
*fs
;
913 if ((fs
= malloc(sizeof(struct mpdm_file
))) == NULL
)
916 memset(fs
, '\0', sizeof(struct mpdm_file
));
918 /* default I/O functions */
919 fs
->f_read
= read_auto
;
920 fs
->f_write
= write_wcs
;
923 /* no iconv encodings by default */
924 fs
->ic_enc
= fs
->ic_dec
= (iconv_t
) -1;
927 if ((v
= mpdm_new(MPDM_FILE
| MPDM_FREE
, fs
, sizeof(struct mpdm_file
))) == NULL
) {
932 e
= mpdm_hget_s(mpdm_root(), L
"ENCODING");
934 if (mpdm_size(e
) == 0)
935 e
= mpdm_hget_s(mpdm_root(), L
"TEMP_ENCODING");
939 wchar_t *enc
= mpdm_string(e
);
941 if (wcscmp(enc
, L
"utf-8") == 0) {
942 fs
->f_read
= read_utf8_bom
;
943 fs
->f_write
= write_utf8
;
946 if (wcscmp(enc
, L
"utf-8bom") == 0) {
947 fs
->f_read
= read_utf8_bom
;
948 fs
->f_write
= write_utf8_bom
;
951 if (wcscmp(enc
, L
"iso8859-1") == 0 ||
952 wcscmp(enc
, L
"8bit") == 0) {
953 fs
->f_read
= read_iso8859_1
;
954 fs
->f_write
= write_iso8859_1
;
957 if (wcscmp(enc
, L
"utf-16le") == 0) {
958 fs
->f_read
= read_utf16le
;
959 fs
->f_write
= write_utf16le_bom
;
962 if (wcscmp(enc
, L
"utf-16be") == 0) {
963 fs
->f_read
= read_utf16be
;
964 fs
->f_write
= write_utf16be_bom
;
967 if (wcscmp(enc
, L
"utf-16") == 0) {
968 fs
->f_read
= read_utf16
;
969 fs
->f_write
= write_utf16le_bom
;
972 if (wcscmp(enc
, L
"utf-32le") == 0) {
973 fs
->f_read
= read_utf32le
;
974 fs
->f_write
= write_utf32le_bom
;
977 if (wcscmp(enc
, L
"utf-32be") == 0) {
978 fs
->f_read
= read_utf32be
;
979 fs
->f_write
= write_utf32be_bom
;
982 if (wcscmp(enc
, L
"utf-32") == 0) {
983 fs
->f_read
= read_utf32
;
984 fs
->f_write
= write_utf32le_bom
;
988 mpdm_t cs
= MPDM_2MBS(e
->data
);
990 if ((fs
->ic_enc
= iconv_open((char *) cs
->data
, "WCHAR_T")) != (iconv_t
) -1 &&
991 (fs
->ic_dec
= iconv_open("WCHAR_T", (char *) cs
->data
)) != (iconv_t
) -1) {
993 fs
->f_read
= read_iconv
;
994 fs
->f_write
= write_iconv
;
996 #endif /* CONFOPT_ICONV */
999 mpdm_hset_s(mpdm_root(), L
"TEMP_ENCODING", NULL
);
1006 static void destroy_mpdm_file(mpdm_t v
)
1007 /* destroys and file value */
1009 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1012 #ifdef CONFOPT_ICONV
1013 if (fs
->ic_enc
!= (iconv_t
) - 1) {
1014 iconv_close(fs
->ic_enc
);
1015 fs
->ic_enc
= (iconv_t
) - 1;
1018 if (fs
->ic_dec
!= (iconv_t
) - 1) {
1019 iconv_close(fs
->ic_dec
);
1020 fs
->ic_dec
= (iconv_t
) - 1;
1032 wchar_t *mpdm_read_mbs(FILE * f
, int *s
)
1033 /* reads a multibyte string from a stream into a dynamic string */
1035 struct mpdm_file fs
;
1037 /* reset the structure */
1038 memset(&fs
, '\0', sizeof(fs
));
1041 return read_mbs(&fs
, s
);
1045 int mpdm_write_wcs(FILE * f
, const wchar_t * str
)
1046 /* writes a wide string to a stream */
1048 struct mpdm_file fs
;
1050 /* reset the structure */
1051 memset(&fs
, '\0', sizeof(fs
));
1054 return write_wcs(&fs
, str
);
1058 mpdm_t
mpdm_new_f(FILE * f
)
1059 /* creates a new file value from a FILE * */
1066 if ((v
= new_mpdm_file()) != NULL
) {
1067 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1068 fs
->in
= fs
->out
= f
;
1076 * mpdm_open - Opens a file.
1077 * @filename: the file name
1078 * @mode: an fopen-like mode string
1080 * Opens a file. If @filename can be open in the specified @mode, an
1081 * mpdm_t value will be returned containing the file descriptor, or NULL
1084 * If the file is open for reading, some charset detection methods are
1085 * used. If any of them is successful, its name is stored in the
1086 * DETECTED_ENCODING element of the mpdm_root() hash. This value is
1087 * suitable to be copied over ENCODING or TEMP_ENCODING.
1089 * If the file is open for writing, the encoding to be used is read from
1090 * the ENCODING element of mpdm_root() and, if not set, from the
1091 * TEMP_ENCODING one. The latter will always be deleted afterwards.
1094 mpdm_t
mpdm_open(const mpdm_t filename
, const mpdm_t mode
)
1100 if (filename
== NULL
|| mode
== NULL
)
1103 /* convert to mbs,s */
1104 fn
= MPDM_2MBS(filename
->data
);
1105 m
= MPDM_2MBS(mode
->data
);
1107 if ((f
= fopen((char *) fn
->data
, (char *) m
->data
)) == NULL
)
1110 #if defined(CONFOPT_SYS_STAT_H) && defined(S_ISDIR) && defined(EISDIR)
1113 /* test if the open file is a directory */
1114 if (fstat(fileno(f
), &s
) != -1 && S_ISDIR(s
.st_mode
)) {
1115 /* it's a directory; fail */
1129 * mpdm_close - Closes a file descriptor.
1130 * @fd: the value containing the file descriptor
1132 * Closes the file descriptor.
1135 mpdm_t
mpdm_close(mpdm_t fd
)
1137 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1139 if ((fd
->flags
& MPDM_FILE
) && fs
!= NULL
) {
1143 if (fs
->out
!= fs
->in
&& fs
->out
!= NULL
)
1146 destroy_mpdm_file(fd
);
1154 * mpdm_read - Reads a line from a file descriptor.
1155 * @fd: the value containing the file descriptor
1157 * Reads a line from @fd. Returns the line, or NULL on EOF.
1159 * [Character Set Conversion]
1161 mpdm_t
mpdm_read(const mpdm_t fd
)
1166 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1171 ptr
= fs
->f_read(fs
, &s
);
1174 v
= MPDM_ENS(ptr
, s
);
1180 mpdm_t
mpdm_getchar(const mpdm_t fd
)
1184 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1186 if (fs
== NULL
|| (c
= get_char(fs
)) == EOF
)
1189 /* get the char as-is */
1190 tmp
[0] = (wchar_t) c
;
1197 mpdm_t
mpdm_putchar(const mpdm_t fd
, const mpdm_t c
)
1199 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1200 const wchar_t *ptr
= mpdm_string(c
);
1202 if (fs
== NULL
|| put_char(*ptr
, fs
) == -1)
1210 * mpdm_write - Writes a value into a file.
1211 * @fd: the file descriptor.
1212 * @v: the value to be written.
1214 * Writes the @v string value into @fd, using the current encoding.
1216 * [Character Set Conversion]
1218 int mpdm_write(const mpdm_t fd
, const mpdm_t v
)
1220 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1226 ret
= fs
->f_write(fs
, mpdm_string(v
));
1232 int mpdm_fseek(const mpdm_t fd
, long offset
, int whence
)
1234 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1236 return fseek(fs
->in
, offset
, whence
);
1240 long mpdm_ftell(const mpdm_t fd
)
1242 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1244 return ftell(fs
->in
);
1248 FILE * mpdm_get_filehandle(const mpdm_t fd
)
1252 if (fd
->flags
& MPDM_FILE
) {
1253 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1262 mpdm_t mpdm_bread(mpdm_t fd, int size)
1267 int mpdm_bwrite(mpdm_tfd, mpdm_t v, int size)
1273 static mpdm_t
embedded_encodings(void)
1279 L
"iso8859-1", L
"iso8859-1",
1280 L
"iso-8859-1", NULL
,
1284 L
"utf-16le", L
"utf-16le",
1287 L
"utf-16be", L
"utf-16be",
1290 L
"utf-16", L
"utf-16",
1294 L
"utf-32le", L
"utf-32le",
1297 L
"utf-32be", L
"utf-32be",
1300 L
"utf-32", L
"utf-32",
1304 L
"utf-8bom", L
"utf-8bom",
1309 if ((e
= mpdm_hget_s(mpdm_root(), L
"EMBEDDED_ENCODINGS")) == NULL
) {
1315 for (n
= 0; e2e
[n
] != NULL
; n
+= 2) {
1316 mpdm_t v
= MPDM_S(e2e
[n
]);
1318 if (e2e
[n
+ 1] != NULL
)
1321 mpdm_hset(e
, v
, MPDM_S(p
));
1322 mpdm_hset(e
, mpdm_ulc(v
, 1), MPDM_S(p
));
1325 mpdm_hset_s(mpdm_root(), L
"EMBEDDED_ENCODINGS", e
);
1333 * mpdm_encoding - Sets the current charset encoding for files.
1334 * @charset: the charset name.
1336 * Sets the current charset encoding for files. Future opened
1337 * files will be assumed to be encoded with @charset, which can
1338 * be any of the supported charset names (utf-8, iso-8859-1, etc.),
1339 * and converted on each read / write. If charset is NULL, it
1340 * is reverted to default charset conversion (i.e. the one defined
1343 * This function stores the @charset value into the ENCODING item
1344 * of the mpdm_root() hash.
1346 * Returns a negative number if @charset is unsupported, or zero
1347 * if no errors were found.
1349 * [Character Set Conversion]
1351 int mpdm_encoding(mpdm_t charset
)
1354 mpdm_t e
= embedded_encodings();
1357 /* NULL encoding? done */
1358 if (mpdm_size(charset
) == 0) {
1359 mpdm_hset_s(mpdm_root(), L
"ENCODING", NULL
);
1363 #ifdef CONFOPT_ICONV
1366 mpdm_t cs
= MPDM_2MBS(charset
->data
);
1368 /* tries to create an iconv encoder and decoder for this charset */
1370 if ((ic
= iconv_open("WCHAR_T", (char *) cs
->data
)) == (iconv_t
) - 1)
1375 if ((ic
= iconv_open((char *) cs
->data
, "WCHAR_T")) == (iconv_t
) - 1)
1380 /* got a valid encoding */
1386 #endif /* CONFOPT_ICONV */
1388 if (ret
!= 0 && (v
= mpdm_hget(e
, charset
)) != NULL
)
1392 mpdm_hset_s(mpdm_root(), L
"ENCODING", v
);
1399 * mpdm_unlink - Deletes a file.
1400 * @filename: file name to be deleted
1405 int mpdm_unlink(const mpdm_t filename
)
1410 /* convert to mbs */
1411 fn
= MPDM_2MBS(filename
->data
);
1413 if ((ret
= unlink((char *) fn
->data
)) == -1)
1421 * mpdm_stat - Gives status from a file.
1422 * @filename: file name to get the status from
1424 * Returns a 14 element array of the status (permissions, onwer, etc.)
1425 * from the desired @filename, or NULL if the file cannot be accessed.
1428 * The values are: 0, device number of filesystem; 1, inode number;
1429 * 2, file mode; 3, number of hard links to the file; 4, uid; 5, gid;
1430 * 6, device identifier; 7, total size of file in bytes; 8, atime;
1431 * 9, mtime; 10, ctime; 11, preferred block size for system I/O;
1432 * 12, number of blocks allocated and 13, canonicalized file name.
1433 * Not all elements have necesarily meaningful values, as most are
1437 mpdm_t
mpdm_stat(const mpdm_t filename
)
1441 #ifdef CONFOPT_SYS_STAT_H
1445 fn
= MPDM_2MBS(filename
->data
);
1447 if (stat((char *) fn
->data
, &s
) != -1) {
1450 mpdm_aset(r
, MPDM_I(s
.st_dev
), 0);
1451 mpdm_aset(r
, MPDM_I(s
.st_ino
), 1);
1452 mpdm_aset(r
, MPDM_I(s
.st_mode
), 2);
1453 mpdm_aset(r
, MPDM_I(s
.st_nlink
), 3);
1454 mpdm_aset(r
, MPDM_I(s
.st_uid
), 4);
1455 mpdm_aset(r
, MPDM_I(s
.st_gid
), 5);
1456 mpdm_aset(r
, MPDM_I(s
.st_rdev
), 6);
1457 mpdm_aset(r
, MPDM_I(s
.st_size
), 7);
1458 mpdm_aset(r
, MPDM_I(s
.st_atime
), 8);
1459 mpdm_aset(r
, MPDM_I(s
.st_mtime
), 9);
1460 mpdm_aset(r
, MPDM_I(s
.st_ctime
), 10);
1461 mpdm_aset(r
, MPDM_I(0), 11); /* s.st_blksize */
1462 mpdm_aset(r
, MPDM_I(0), 12); /* s.st_blocks */
1464 #ifdef CONFOPT_CANONICALIZE_FILE_NAME
1469 if ((ptr
= canonicalize_file_name(
1470 (char *)fn
->data
)) != NULL
) {
1471 mpdm_aset(r
, MPDM_MBS(ptr
), 13);
1477 #ifdef CONFOPT_REALPATH
1481 if (realpath((char *)fn
->data
, tmp
) != NULL
)
1482 mpdm_aset(r
, MPDM_MBS(tmp
), 13);
1486 #ifdef CONFOPT_FULLPATH
1488 char tmp
[_MAX_PATH
+ 1];
1490 if (_fullpath(tmp
, (char *)fn
->data
, _MAX_PATH
) != NULL
)
1491 mpdm_aset(r
, MPDM_MBS(tmp
), 13);
1499 #endif /* CONFOPT_SYS_STAT_H */
1506 * mpdm_chmod - Changes a file's permissions.
1507 * @filename: the file name
1508 * @perms: permissions (element 2 from mpdm_stat())
1510 * Changes the permissions for a file.
1513 int mpdm_chmod(const mpdm_t filename
, mpdm_t perms
)
1517 mpdm_t fn
= MPDM_2MBS(filename
->data
);
1519 if ((r
= chmod((char *) fn
->data
, mpdm_ival(perms
))) == -1)
1527 * mpdm_chdir - Changes the working directory
1528 * @dir: the new path
1530 * Changes the working directory
1533 int mpdm_chdir(const mpdm_t dir
)
1537 mpdm_t fn
= MPDM_2MBS(dir
->data
);
1539 if ((r
= chdir((char *) fn
->data
)) == -1)
1547 * mpdm_chown - Changes a file's owner.
1548 * @filename: the file name
1549 * @uid: user id (element 4 from mpdm_stat())
1550 * @gid: group id (element 5 from mpdm_stat())
1552 * Changes the owner and group id's for a file.
1555 int mpdm_chown(const mpdm_t filename
, mpdm_t uid
, mpdm_t gid
)
1559 #ifdef CONFOPT_CHOWN
1561 mpdm_t fn
= MPDM_2MBS(filename
->data
);
1563 if ((r
= chown((char *) fn
->data
, mpdm_ival(uid
), mpdm_ival(gid
))) == -1)
1566 #endif /* CONFOPT_CHOWN */
1573 * mpdm_glob - Executes a file globbing.
1574 * @spec: Globbing spec
1575 * @base: Optional base directory
1577 * Executes a file globbing. @spec is system-dependent, but usually
1578 * the * and ? metacharacters work everywhere. @base can contain a
1579 * directory; if that's the case, the output strings will include it.
1580 * In any case, each returned value will be suitable for a call to
1583 * Returns an array of files that match the globbing (can be an empty
1584 * array if no file matches), or NULL if globbing is unsupported.
1587 mpdm_t
mpdm_glob(const mpdm_t spec
, const mpdm_t base
)
1593 #ifdef CONFOPT_WIN32
1602 if (mpdm_size(base
))
1603 sp
= mpdm_strcat(base
, MPDM_LS(L
"/"));
1605 sp
= mpdm_strcat(sp
, mpdm_size(spec
) == 0 ? MPDM_LS(L
"*.*") : spec
);
1607 /* delete repeated directory delimiters */
1608 sp
= mpdm_sregex(MPDM_LS(L
"@[\\/]+@g"), sp
, MPDM_LS(L
"/"), 0);
1610 sp
= MPDM_2MBS(sp
->data
);
1616 if ((h
= FindFirstFile((char *) sp
->data
, &fd
)) != INVALID_HANDLE_VALUE
) {
1617 /* if spec includes a directory, store in s */
1618 if ((ptr
= strrchr((char *) sp
->data
, '/')) != NULL
) {
1620 s
= MPDM_MBS(sp
->data
);
1624 /* ignore . and .. */
1625 if (strcmp(fd
.cFileName
, ".") == 0 || strcmp(fd
.cFileName
, "..") == 0)
1628 /* concat base directory and file names */
1629 w
= mpdm_strcat(s
, MPDM_MBS(fd
.cFileName
));
1631 /* if it's a directory, add a / */
1632 if (fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1633 w
= mpdm_strcat(w
, MPDM_LS(L
"/"));
1639 while (FindNextFile(h
, &fd
));
1648 /* glob.h support */
1652 /* build full path */
1653 if (mpdm_size(base
))
1654 v
= mpdm_strcat(base
, MPDM_LS(L
"/"));
1656 v
= mpdm_strcat(v
, mpdm_size(spec
) == 0 ? MPDM_LS(L
"*") : spec
);
1658 /* delete repeated directory delimiters */
1659 v
= mpdm_sregex(MPDM_LS(L
"@/{2,}@g"), v
, MPDM_LS(L
"/"), 0);
1661 v
= MPDM_2MBS(v
->data
);
1665 globbuf
.gl_offs
= 1;
1671 if (glob(ptr
, GLOB_MARK
, NULL
, &globbuf
) == 0) {
1674 for (n
= 0; globbuf
.gl_pathv
[n
] != NULL
; n
++) {
1675 char *ptr
= globbuf
.gl_pathv
[n
];
1676 mpdm_t t
= MPDM_MBS(ptr
);
1678 /* if last char is /, add to directories */
1679 if (ptr
[strlen(ptr
) - 1] == '/')
1690 /* no win32 nor glob.h; try workaround */
1698 d
= mpdm_sort(d
, 1);
1699 f
= mpdm_sort(f
, 1);
1701 /* transfer all data in d and f */
1702 for (n
= 0; n
< mpdm_size(d
); n
++)
1703 mpdm_push(v
, mpdm_aget(d
, n
));
1704 for (n
= 0; n
< mpdm_size(f
); n
++)
1705 mpdm_push(v
, mpdm_aget(f
, n
));
1712 #ifdef CONFOPT_WIN32
1714 static void win32_pipe(HANDLE
* h
, int n
)
1716 SECURITY_ATTRIBUTES sa
;
1719 memset(&sa
, '\0', sizeof(sa
));
1720 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
1721 sa
.bInheritHandle
= TRUE
;
1722 sa
.lpSecurityDescriptor
= NULL
;
1724 cp
= GetCurrentProcess();
1726 CreatePipe(&h
[0], &h
[1], &sa
, 0);
1727 DuplicateHandle(cp
, h
[n
], cp
, &t
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
1733 static int sysdep_popen(mpdm_t v
, char *prg
, int rw
)
1734 /* win32-style pipe */
1738 PROCESS_INFORMATION pi
;
1741 struct mpdm_file
*fs
= v
->data
;
1744 pr
[0] = pr
[1] = pw
[0] = pw
[1] = NULL
;
1751 /* spawn new process */
1752 memset(&pi
, '\0', sizeof(pi
));
1753 memset(&si
, '\0', sizeof(si
));
1755 si
.cb
= sizeof(STARTUPINFO
);
1756 si
.hStdError
= pr
[1];
1757 si
.hStdOutput
= pr
[1];
1758 si
.hStdInput
= pw
[0];
1759 si
.dwFlags
|= STARTF_USESTDHANDLES
;
1761 ret
= CreateProcess(NULL
, prg
, NULL
, NULL
, TRUE
, 0, NULL
, NULL
, &si
, &pi
);
1775 static int sysdep_pclose(const mpdm_t v
)
1777 struct mpdm_file
*fs
= v
->data
;
1779 if (fs
->hin
!= NULL
)
1780 CloseHandle(fs
->hin
);
1782 if (fs
->hout
!= NULL
)
1783 CloseHandle(fs
->hout
);
1785 /* how to know process exit code? */
1790 #else /* CONFOPT_WIN32 */
1792 static int sysdep_popen(mpdm_t v
, char *prg
, int rw
)
1793 /* unix-style pipe open */
1796 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1799 pr
[0] = pr
[1] = pw
[0] = pw
[1] = -1;
1819 /* redirect stderr to stdout */
1823 /* run the program */
1824 execlp("/bin/sh", "/bin/sh", "-c", prg
, NULL
);
1825 execlp(prg
, prg
, NULL
);
1827 /* still here? exec failed; close pipes and exit */
1833 /* create the pipes as non-buffered streams */
1835 fs
->in
= fdopen(pr
[0], "r");
1836 setvbuf(fs
->in
, NULL
, _IONBF
, 0);
1841 fs
->out
= fdopen(pw
[1], "w");
1842 setvbuf(fs
->out
, NULL
, _IONBF
, 0);
1850 static int sysdep_pclose(const mpdm_t v
)
1851 /* unix-style pipe close */
1854 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1859 if (fs
->out
!= fs
->in
&& fs
->out
!= NULL
)
1868 #endif /* CONFOPT_WIN32 */
1872 * mpdm_popen - Opens a pipe.
1873 * @prg: the program to pipe
1874 * @mode: an fopen-like mode string
1876 * Opens a pipe to a program. If @prg can be open in the specified @mode, an
1877 * mpdm_t value will be returned containing the file descriptor, or NULL
1881 mpdm_t
mpdm_popen(const mpdm_t prg
, const mpdm_t mode
)
1887 if (prg
== NULL
|| mode
== NULL
)
1890 if ((v
= new_mpdm_file()) == NULL
)
1893 /* convert to mbs,s */
1894 pr
= MPDM_2MBS(prg
->data
);
1895 md
= MPDM_2MBS(mode
->data
);
1898 m
= (char *) md
->data
;
1906 rw
= 0x03; /* r+ or w+ */
1908 if (!sysdep_popen(v
, (char *) pr
->data
, rw
)) {
1909 destroy_mpdm_file(v
);
1918 * mpdm_pclose - Closes a pipe.
1919 * @fd: the value containing the file descriptor
1924 mpdm_t
mpdm_pclose(mpdm_t fd
)
1928 if ((fd
->flags
& MPDM_FILE
) && fd
->data
!= NULL
) {
1929 r
= MPDM_I(sysdep_pclose(fd
));
1930 destroy_mpdm_file(fd
);
1938 * mpdm_home_dir - Returns the home user directory.
1940 * Returns a system-dependent directory where the user can write
1941 * documents and create subdirectories.
1944 mpdm_t
mpdm_home_dir(void)
1950 #ifdef CONFOPT_WIN32
1954 /* get the 'My Documents' folder */
1955 SHGetSpecialFolderLocation(NULL
, CSIDL_PERSONAL
, &pidl
);
1956 SHGetPathFromIDList(pidl
, tmp
);
1961 #ifdef CONFOPT_PWD_H
1965 /* get home dir from /etc/passwd entry */
1966 if (tmp
[0] == '\0' && (p
= getpwuid(getpid())) != NULL
) {
1967 strcpy(tmp
, p
->pw_dir
);
1973 /* still none? try the ENV variable $HOME */
1974 if (tmp
[0] == '\0' && (ptr
= getenv("HOME")) != NULL
) {
1987 * mpdm_app_dir - Returns the applications directory.
1989 * Returns a system-dependent directory where the applications store
1990 * their private data, as components or resources.
1993 mpdm_t
mpdm_app_dir(void)
1997 #ifdef CONFOPT_WIN32
2003 /* get the 'Program Files' folder (can fail) */
2005 if (SHGetSpecialFolderLocation(NULL
, CSIDL_PROGRAM_FILES
, &pidl
) == S_OK
)
2006 SHGetPathFromIDList(pidl
, tmp
);
2008 /* if it's still empty, get from the registry */
2009 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
2010 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
2011 0, KEY_QUERY_VALUE
, &hkey
) == ERROR_SUCCESS
) {
2012 int n
= sizeof(tmp
);
2014 if (RegQueryValueEx(hkey
, "ProgramFilesDir",
2015 NULL
, NULL
, tmp
, (LPDWORD
) & n
) != ERROR_SUCCESS
)
2019 if (tmp
[0] != '\0') {
2025 /* still none? get the configured directory */
2027 r
= MPDM_MBS(CONFOPT_PREFIX
"/share/");