3 MPDM - Minimum Profit Data Manager
4 Copyright (C) 2003/2010 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 */
101 static void store_syserr(void)
102 /* stores the system error inside the global ERRNO */
104 mpdm_hset_s(mpdm_root(), L
"ERRNO", MPDM_MBS(strerror(errno
)));
108 static int get_char(struct mpdm_file
*f
)
109 /* reads a character from a file structure */
115 if (f
->hin
!= NULL
) {
119 if (ReadFile(f
->hin
, &tmp
, 1, &n
, NULL
) && n
> 0)
123 #endif /* CONFOPT_WIN32 */
126 /* read (converting to positive if needed) */
127 if ((c
= fgetc(f
->in
)) < 0 && !feof(f
->in
))
135 static int put_buf(const char *ptr
, int s
, struct mpdm_file
*f
)
136 /* writes s bytes in the buffer in ptr to f */
140 if (f
->hout
!= NULL
) {
143 if (WriteFile(f
->hout
, ptr
, s
, &n
, NULL
) && n
> 0)
147 #endif /* CONFOPT_WIN32 */
150 s
= fwrite(ptr
, s
, 1, f
->out
);
156 static int put_char(int c
, struct mpdm_file
*f
)
157 /* writes a character in a file structure */
161 if (put_buf(&tmp
, 1, f
) != 1)
168 static wchar_t *read_mbs(struct mpdm_file
*f
, int *s
)
169 /* reads a multibyte string from a mpdm_file into a dynamic string */
176 while ((c
= get_char(f
)) != EOF
) {
182 if (i
== sizeof(tmp
) - 1) {
183 /* out of space; start allocating */
184 if ((auxptr
= mpdm_poke(auxptr
, &n
, tmp
, i
, sizeof(char))) == NULL
)
191 /* is there something to return? */
197 /* auxiliary space used; concat all */
198 if ((auxptr
= mpdm_poke(auxptr
, &n
, tmp
, i
, sizeof(char))) == NULL
)
201 /* do the conversion */
202 ptr
= mpdm_mbstowcs(auxptr
, s
, -1);
207 ptr
= mpdm_mbstowcs(tmp
, s
, -1);
214 static int write_wcs(struct mpdm_file
*f
, const wchar_t * str
)
215 /* writes a wide string to an struct mpdm_file */
220 ptr
= mpdm_wcstombs(str
, &s
);
221 s
= put_buf(ptr
, s
, f
);
230 static wchar_t *read_iconv(struct mpdm_file
*f
, int *s
)
231 /* reads a multibyte string transforming with iconv */
240 /* resets the decoder */
241 iconv(f
->ic_dec
, NULL
, NULL
, NULL
, NULL
);
243 while ((c
= get_char(f
)) != EOF
) {
249 /* too big? shouldn't happen */
250 if (i
== sizeof(tmp
))
255 ol
= sizeof(wchar_t);
259 if (iconv(f
->ic_dec
, &iptr
, &il
, &optr
, &ol
) == (size_t) -1) {
260 /* found incomplete multibyte character */
264 /* otherwise, return '?' */
270 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
273 /* if it's an end of line, finish */
279 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
287 static int write_iconv(struct mpdm_file
*f
, const wchar_t * str
)
288 /* writes a wide string to a stream using iconv */
293 /* resets the encoder */
294 iconv(f
->ic_enc
, NULL
, NULL
, NULL
, NULL
);
296 /* convert char by char */
297 for (; *str
!= L
'\0'; str
++) {
302 il
= sizeof(wchar_t);
308 if (iconv(f
->ic_enc
, &iptr
, &il
, &optr
, &ol
) == (size_t) -1) {
309 /* error converting; convert a '?' instead */
312 il
= sizeof(wchar_t);
317 iconv(f
->ic_enc
, &iptr
, &il
, &optr
, &ol
);
320 for (n
= 0; n
< (int)(sizeof(tmp
) - ol
); n
++, cnt
++) {
321 if (put_char(tmp
[n
], f
) == EOF
)
330 #endif /* CONFOPT_ICONV */
332 #define UTF8_BYTE() if((c = get_char(f)) == EOF) break
334 static wchar_t *read_utf8(struct mpdm_file
*f
, int *s
)
351 if ((c
& 0xe0) == 0xe0) {
352 wc
= (c
& 0x1f) << 12;
354 wc
|= (c
& 0x3f) << 6;
359 wc
= (c
& 0x3f) << 6;
365 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
368 /* if it's an end of line, finish */
374 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
382 static int write_utf8(struct mpdm_file
*f
, const wchar_t * str
)
388 /* convert char by char */
389 for (; (wc
= *str
) != L
'\0'; str
++) {
391 put_char((int) wc
, f
);
394 put_char((int) (0xc0 | (wc
>> 6)), f
);
395 put_char((int) (0x80 | (wc
& 0x3f)), f
);
399 put_char((int) (0xe0 | (wc
>> 12)), f
);
400 put_char((int) (0x80 | ((wc
>> 6) & 0x3f)), f
);
401 put_char((int) (0x80 | (wc
& 0x3f)), f
);
412 static wchar_t *read_utf8_bom(struct mpdm_file
*f
, int *s
)
413 /* utf-8 reader with BOM detection */
420 if (get_char(f
) == 0xef && get_char(f
) == 0xbb && get_char(f
) == 0xbf)
424 fseek(f
->in
, 0, SEEK_SET
);
427 mpdm_hset_s(mpdm_root(), L
"DETECTED_ENCODING", MPDM_LS(enc
));
429 /* we're utf-8 from now on */
430 f
->f_read
= read_utf8
;
432 return f
->f_read(f
, s
);
436 static int write_utf8_bom(struct mpdm_file
*f
, const wchar_t * str
)
437 /* utf-8 writer with BOM */
444 /* we're utf-8 from now on */
445 f
->f_write
= write_utf8
;
447 return f
->f_write(f
, str
);
451 static wchar_t *read_iso8859_1(struct mpdm_file
*f
, int *s
)
452 /* iso8859-1 reader */
460 while ((c
= get_char(f
)) != EOF
) {
464 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
467 /* if it's an end of line, finish */
473 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
481 static int write_iso8859_1(struct mpdm_file
*f
, const wchar_t * str
)
482 /* iso8859-1 writer */
487 /* convert char by char */
488 for (; (wc
= *str
) != L
'\0'; str
++)
489 put_char(wc
<= 0xff ? (int) wc
: '?', f
);
495 static wchar_t *read_utf16ae(struct mpdm_file
*f
, int *s
, int le
)
496 /* utf16 reader, ANY ending */
507 if ((c1
= get_char(f
)) == EOF
)
510 if ((c2
= get_char(f
)) == EOF
)
519 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
522 /* if it's an end of line, finish */
528 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
536 static int write_utf16ae(struct mpdm_file
*f
, const wchar_t * str
, int le
)
537 /* utf16 writer, ANY ending */
542 /* convert char by char */
543 for (; (wc
= *str
) != L
'\0'; str
++) {
546 put_char(wc
& 0xff, f
);
547 put_char((wc
& 0xff00) >> 8, f
);
550 put_char((wc
& 0xff00) >> 8, f
);
551 put_char(wc
& 0xff, f
);
559 static wchar_t *read_utf16le(struct mpdm_file
*f
, int *s
)
561 return read_utf16ae(f
, s
, 1);
565 static int write_utf16le(struct mpdm_file
*f
, const wchar_t * str
)
567 return write_utf16ae(f
, str
, 1);
571 static wchar_t *read_utf16be(struct mpdm_file
*f
, int *s
)
573 return read_utf16ae(f
, s
, 0);
577 static int write_utf16be(struct mpdm_file
*f
, const wchar_t * str
)
579 return write_utf16ae(f
, str
, 0);
583 static wchar_t *read_utf16(struct mpdm_file
*f
, int *s
)
586 wchar_t *enc
= L
"utf-16le";
588 /* assume little-endian */
589 f
->f_read
= read_utf16le
;
595 if (c1
== 0xfe && c2
== 0xff) {
597 f
->f_read
= read_utf16be
;
600 if (c1
!= 0xff || c2
!= 0xfe) {
601 /* no BOM; rewind and hope */
602 fseek(f
->in
, 0, SEEK_SET
);
605 mpdm_hset_s(mpdm_root(), L
"DETECTED_ENCODING", MPDM_LS(enc
));
607 return f
->f_read(f
, s
);
611 static int write_utf16le_bom(struct mpdm_file
*f
, const wchar_t * str
)
613 /* store the LE signature */
617 /* we're 16le from now on */
618 f
->f_write
= write_utf16le
;
620 return f
->f_write(f
, str
);
624 static int write_utf16be_bom(struct mpdm_file
*f
, const wchar_t * str
)
626 /* store the BE signature */
630 /* we're 16be from now on */
631 f
->f_write
= write_utf16be
;
633 return f
->f_write(f
, str
);
637 static wchar_t *read_utf32ae(struct mpdm_file
*f
, int *s
, int le
)
638 /* utf32 reader, ANY ending */
649 if ((c1
= get_char(f
)) == EOF
)
652 if ((c2
= get_char(f
)) == EOF
)
655 if ((c3
= get_char(f
)) == EOF
)
658 if ((c4
= get_char(f
)) == EOF
)
662 wc
= c1
| (c2
<< 8) | (c3
<< 16) | (c4
<< 24);
664 wc
= c4
| (c3
<< 8) | (c2
<< 16) | (c1
<< 24);
667 if ((ptr
= mpdm_poke(ptr
, s
, &wc
, 1, sizeof(wchar_t))) == NULL
)
670 /* if it's an end of line, finish */
676 ptr
= mpdm_poke(ptr
, s
, L
"", 1, sizeof(wchar_t));
684 static int write_utf32ae(struct mpdm_file
*f
, const wchar_t * str
, int le
)
685 /* utf32 writer, ANY ending */
690 /* convert char by char */
691 for (; (wc
= *str
) != L
'\0'; str
++) {
694 put_char((wc
& 0x000000ff), f
);
695 put_char((wc
& 0x0000ff00) >> 8, f
);
696 put_char((wc
& 0x00ff0000) >> 16, f
);
697 put_char((wc
& 0xff000000) >> 24, f
);
700 put_char((wc
& 0xff000000) >> 24, f
);
701 put_char((wc
& 0x00ff0000) >> 16, f
);
702 put_char((wc
& 0x0000ff00) >> 8, f
);
703 put_char((wc
& 0x000000ff), f
);
711 static wchar_t *read_utf32le(struct mpdm_file
*f
, int *s
)
713 return read_utf32ae(f
, s
, 1);
717 static int write_utf32le(struct mpdm_file
*f
, const wchar_t * str
)
719 return write_utf32ae(f
, str
, 1);
723 static wchar_t *read_utf32be(struct mpdm_file
*f
, int *s
)
725 return read_utf32ae(f
, s
, 0);
729 static int write_utf32be(struct mpdm_file
*f
, const wchar_t * str
)
731 return write_utf32ae(f
, str
, 0);
735 static wchar_t *read_utf32(struct mpdm_file
*f
, int *s
)
738 wchar_t *enc
= L
"utf-32le";
740 f
->f_read
= read_utf32le
;
748 if (c1
== 0 && c2
== 0 && c3
== 0xfe && c4
== 0xff) {
750 f
->f_read
= read_utf32be
;
752 if (c1
!= 0xff || c2
!= 0xfe || c3
!= 0 || c4
!= 0) {
753 /* no BOM; assume le and hope */
754 fseek(f
->in
, 0, SEEK_SET
);
757 mpdm_hset_s(mpdm_root(), L
"DETECTED_ENCODING", MPDM_LS(enc
));
759 return f
->f_read(f
, s
);
763 static int write_utf32le_bom(struct mpdm_file
*f
, const wchar_t * str
)
765 /* store the LE signature */
771 /* we're 32le from now on */
772 f
->f_write
= write_utf32le
;
774 return f
->f_write(f
, str
);
778 static int write_utf32be_bom(struct mpdm_file
*f
, const wchar_t * str
)
780 /* store the BE signature */
786 /* we're 32be from now on */
787 f
->f_write
= write_utf32be
;
789 return f
->f_write(f
, str
);
793 static wchar_t *read_auto(struct mpdm_file
*f
, int *s
)
794 /* autodetects different encodings based on the BOM */
798 /* by default, multibyte reading */
799 f
->f_read
= read_mbs
;
801 /* ensure seeking is possible */
802 if (f
->in
!= NULL
&& fseek(f
->in
, 0, SEEK_CUR
) != -1) {
808 /* can be utf32le or utf16le */
809 if (get_char(f
) == 0xfe) {
810 /* if next 2 chars are 0x00, it's utf32; otherwise utf16 */
811 if (get_char(f
) == 0x00 && get_char(f
) == 0x00) {
813 f
->f_read
= read_utf32le
;
817 /* rewind to 3rd character */
818 fseek(f
->in
, 2, SEEK_SET
);
821 f
->f_read
= read_utf16le
;
829 if (get_char(f
) == 0x00 && get_char(f
) == 0xfe && get_char(f
) == 0xff) {
831 f
->f_read
= read_utf32be
;
838 if (get_char(f
) == 0xff) {
840 f
->f_read
= read_utf16be
;
846 /* can be utf8 with BOM */
847 if (get_char(f
) == 0xbb && get_char(f
) == 0xbf) {
849 f
->f_read
= read_utf8
;
854 /* try if a first bunch of chars are valid UTF-8 */
859 while (--n
&& (c
= get_char(f
)) != EOF
) {
860 if ((c
& 0xc0) == 0x80) {
861 if ((p
& 0xc0) == 0xc0)
864 if ((p
& 0x80) == 0x00) {
870 if ((p
& 0xc0) == 0xc0) {
879 /* invalid utf-8; fall back to 8bit */
881 f
->f_read
= read_iso8859_1
;
885 /* utf-8 sequences found */
887 f
->f_read
= read_utf8
;
890 /* 7 bit ASCII: do nothing */
893 /* none of the above; restart */
894 fseek(f
->in
, 0, SEEK_SET
);
898 mpdm_hset_s(mpdm_root(), L
"DETECTED_ENCODING", MPDM_LS(enc
));
900 return f
->f_read(f
, s
);
904 static mpdm_t
new_mpdm_file(void)
905 /* creates a new file value */
908 struct mpdm_file
*fs
;
911 if ((fs
= malloc(sizeof(struct mpdm_file
))) == NULL
)
914 memset(fs
, '\0', sizeof(struct mpdm_file
));
916 /* default I/O functions */
917 fs
->f_read
= read_auto
;
918 fs
->f_write
= write_wcs
;
921 /* no iconv encodings by default */
922 fs
->ic_enc
= fs
->ic_dec
= (iconv_t
) -1;
925 if ((v
= mpdm_new(MPDM_FILE
| MPDM_FREE
, fs
, sizeof(struct mpdm_file
))) == NULL
) {
930 e
= mpdm_hget_s(mpdm_root(), L
"ENCODING");
932 if (mpdm_size(e
) == 0)
933 e
= mpdm_hget_s(mpdm_root(), L
"TEMP_ENCODING");
937 wchar_t *enc
= mpdm_string(e
);
939 if (wcscmp(enc
, L
"utf-8") == 0) {
940 fs
->f_read
= read_utf8_bom
;
941 fs
->f_write
= write_utf8
;
944 if (wcscmp(enc
, L
"utf-8bom") == 0) {
945 fs
->f_read
= read_utf8_bom
;
946 fs
->f_write
= write_utf8_bom
;
949 if (wcscmp(enc
, L
"iso8859-1") == 0 ||
950 wcscmp(enc
, L
"8bit") == 0) {
951 fs
->f_read
= read_iso8859_1
;
952 fs
->f_write
= write_iso8859_1
;
955 if (wcscmp(enc
, L
"utf-16le") == 0) {
956 fs
->f_read
= read_utf16le
;
957 fs
->f_write
= write_utf16le_bom
;
960 if (wcscmp(enc
, L
"utf-16be") == 0) {
961 fs
->f_read
= read_utf16be
;
962 fs
->f_write
= write_utf16be_bom
;
965 if (wcscmp(enc
, L
"utf-16") == 0) {
966 fs
->f_read
= read_utf16
;
967 fs
->f_write
= write_utf16le_bom
;
970 if (wcscmp(enc
, L
"utf-32le") == 0) {
971 fs
->f_read
= read_utf32le
;
972 fs
->f_write
= write_utf32le_bom
;
975 if (wcscmp(enc
, L
"utf-32be") == 0) {
976 fs
->f_read
= read_utf32be
;
977 fs
->f_write
= write_utf32be_bom
;
980 if (wcscmp(enc
, L
"utf-32") == 0) {
981 fs
->f_read
= read_utf32
;
982 fs
->f_write
= write_utf32le_bom
;
986 mpdm_t cs
= MPDM_2MBS(e
->data
);
988 if ((fs
->ic_enc
= iconv_open((char *) cs
->data
, "WCHAR_T")) != (iconv_t
) -1 &&
989 (fs
->ic_dec
= iconv_open("WCHAR_T", (char *) cs
->data
)) != (iconv_t
) -1) {
991 fs
->f_read
= read_iconv
;
992 fs
->f_write
= write_iconv
;
994 #endif /* CONFOPT_ICONV */
997 mpdm_hset_s(mpdm_root(), L
"TEMP_ENCODING", NULL
);
1004 static void destroy_mpdm_file(mpdm_t v
)
1005 /* destroys and file value */
1007 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1010 #ifdef CONFOPT_ICONV
1011 if (fs
->ic_enc
!= (iconv_t
) - 1) {
1012 iconv_close(fs
->ic_enc
);
1013 fs
->ic_enc
= (iconv_t
) - 1;
1016 if (fs
->ic_dec
!= (iconv_t
) - 1) {
1017 iconv_close(fs
->ic_dec
);
1018 fs
->ic_dec
= (iconv_t
) - 1;
1030 wchar_t *mpdm_read_mbs(FILE * f
, int *s
)
1031 /* reads a multibyte string from a stream into a dynamic string */
1033 struct mpdm_file fs
;
1035 /* reset the structure */
1036 memset(&fs
, '\0', sizeof(fs
));
1039 return read_mbs(&fs
, s
);
1043 int mpdm_write_wcs(FILE * f
, const wchar_t * str
)
1044 /* writes a wide string to a stream */
1046 struct mpdm_file fs
;
1048 /* reset the structure */
1049 memset(&fs
, '\0', sizeof(fs
));
1052 return write_wcs(&fs
, str
);
1056 mpdm_t
mpdm_new_f(FILE * f
)
1057 /* creates a new file value from a FILE * */
1064 if ((v
= new_mpdm_file()) != NULL
) {
1065 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1066 fs
->in
= fs
->out
= f
;
1074 * mpdm_open - Opens a file.
1075 * @filename: the file name
1076 * @mode: an fopen-like mode string
1078 * Opens a file. If @filename can be open in the specified @mode, an
1079 * mpdm_t value will be returned containing the file descriptor, or NULL
1082 * If the file is open for reading, some charset detection methods are
1083 * used. If any of them is successful, its name is stored in the
1084 * DETECTED_ENCODING element of the mpdm_root() hash. This value is
1085 * suitable to be copied over ENCODING or TEMP_ENCODING.
1087 * If the file is open for writing, the encoding to be used is read from
1088 * the ENCODING element of mpdm_root() and, if not set, from the
1089 * TEMP_ENCODING one. The latter will always be deleted afterwards.
1092 mpdm_t
mpdm_open(const mpdm_t filename
, const mpdm_t mode
)
1098 if (filename
== NULL
|| mode
== NULL
)
1101 /* convert to mbs,s */
1102 fn
= mpdm_ref(MPDM_2MBS(filename
->data
));
1103 m
= mpdm_ref(MPDM_2MBS(mode
->data
));
1105 if ((f
= fopen((char *) fn
->data
, (char *) m
->data
)) == NULL
)
1108 #if defined(CONFOPT_SYS_STAT_H) && defined(S_ISDIR) && defined(EISDIR)
1111 /* test if the open file is a directory */
1112 if (fstat(fileno(f
), &s
) != -1 && S_ISDIR(s
.st_mode
)) {
1113 /* it's a directory; fail */
1130 * mpdm_close - Closes a file descriptor.
1131 * @fd: the value containing the file descriptor
1133 * Closes the file descriptor.
1136 mpdm_t
mpdm_close(mpdm_t fd
)
1138 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1140 if ((fd
->flags
& MPDM_FILE
) && fs
!= NULL
) {
1144 if (fs
->out
!= fs
->in
&& fs
->out
!= NULL
)
1147 destroy_mpdm_file(fd
);
1155 * mpdm_read - Reads a line from a file descriptor.
1156 * @fd: the value containing the file descriptor
1158 * Reads a line from @fd. Returns the line, or NULL on EOF.
1160 * [Character Set Conversion]
1162 mpdm_t
mpdm_read(const mpdm_t fd
)
1167 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1172 ptr
= fs
->f_read(fs
, &s
);
1175 v
= MPDM_ENS(ptr
, s
);
1181 mpdm_t
mpdm_getchar(const mpdm_t fd
)
1185 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1187 if (fs
== NULL
|| (c
= get_char(fs
)) == EOF
)
1190 /* get the char as-is */
1191 tmp
[0] = (wchar_t) c
;
1198 mpdm_t
mpdm_putchar(const mpdm_t fd
, const mpdm_t c
)
1200 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1201 const wchar_t *ptr
= mpdm_string(c
);
1203 if (fs
== NULL
|| put_char(*ptr
, fs
) == -1)
1211 * mpdm_write - Writes a value into a file.
1212 * @fd: the file descriptor.
1213 * @v: the value to be written.
1215 * Writes the @v string value into @fd, using the current encoding.
1217 * [Character Set Conversion]
1219 int mpdm_write(const mpdm_t fd
, const mpdm_t v
)
1221 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1227 ret
= fs
->f_write(fs
, mpdm_string(v
));
1233 int mpdm_fseek(const mpdm_t fd
, long offset
, int whence
)
1235 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1237 return fseek(fs
->in
, offset
, whence
);
1241 long mpdm_ftell(const mpdm_t fd
)
1243 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1245 return ftell(fs
->in
);
1249 FILE * mpdm_get_filehandle(const mpdm_t fd
)
1253 if (fd
->flags
& MPDM_FILE
) {
1254 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1263 mpdm_t mpdm_bread(mpdm_t fd, int size)
1268 int mpdm_bwrite(mpdm_tfd, mpdm_t v, int size)
1274 static mpdm_t
embedded_encodings(void)
1280 L
"iso8859-1", L
"iso8859-1",
1281 L
"iso-8859-1", NULL
,
1285 L
"utf-16le", L
"utf-16le",
1288 L
"utf-16be", L
"utf-16be",
1291 L
"utf-16", L
"utf-16",
1295 L
"utf-32le", L
"utf-32le",
1298 L
"utf-32be", L
"utf-32be",
1301 L
"utf-32", L
"utf-32",
1305 L
"utf-8bom", L
"utf-8bom",
1310 if ((e
= mpdm_hget_s(mpdm_root(), L
"EMBEDDED_ENCODINGS")) == NULL
) {
1316 for (n
= 0; e2e
[n
] != NULL
; n
+= 2) {
1317 mpdm_t v
= MPDM_S(e2e
[n
]);
1319 if (e2e
[n
+ 1] != NULL
)
1320 p
= MPDM_S(e2e
[n
+ 1]);
1323 mpdm_hset(e
, mpdm_ulc(v
, 1), p
);
1326 mpdm_hset_s(mpdm_root(), L
"EMBEDDED_ENCODINGS", e
);
1334 * mpdm_encoding - Sets the current charset encoding for files.
1335 * @charset: the charset name.
1337 * Sets the current charset encoding for files. Future opened
1338 * files will be assumed to be encoded with @charset, which can
1339 * be any of the supported charset names (utf-8, iso-8859-1, etc.),
1340 * and converted on each read / write. If charset is NULL, it
1341 * is reverted to default charset conversion (i.e. the one defined
1344 * This function stores the @charset value into the ENCODING item
1345 * of the mpdm_root() hash.
1347 * Returns a negative number if @charset is unsupported, or zero
1348 * if no errors were found.
1350 * [Character Set Conversion]
1352 int mpdm_encoding(mpdm_t charset
)
1355 mpdm_t e
= embedded_encodings();
1358 /* NULL encoding? done */
1359 if (mpdm_size(charset
) == 0) {
1360 mpdm_hset_s(mpdm_root(), L
"ENCODING", NULL
);
1364 #ifdef CONFOPT_ICONV
1367 mpdm_t cs
= mpdm_ref(MPDM_2MBS(charset
->data
));
1369 /* tries to create an iconv encoder and decoder for this charset */
1371 if ((ic
= iconv_open("WCHAR_T", (char *) cs
->data
)) == (iconv_t
) - 1)
1376 if ((ic
= iconv_open((char *) cs
->data
, "WCHAR_T")) == (iconv_t
) - 1)
1381 /* got a valid encoding */
1389 #endif /* CONFOPT_ICONV */
1391 if (ret
!= 0 && (v
= mpdm_hget(e
, charset
)) != NULL
)
1395 mpdm_hset_s(mpdm_root(), L
"ENCODING", v
);
1402 * mpdm_unlink - Deletes a file.
1403 * @filename: file name to be deleted
1408 int mpdm_unlink(const mpdm_t filename
)
1413 /* convert to mbs */
1414 fn
= mpdm_ref(MPDM_2MBS(filename
->data
));
1416 if ((ret
= unlink((char *) fn
->data
)) == -1)
1426 * mpdm_stat - Gives status from a file.
1427 * @filename: file name to get the status from
1429 * Returns a 14 element array of the status (permissions, onwer, etc.)
1430 * from the desired @filename, or NULL if the file cannot be accessed.
1433 * The values are: 0, device number of filesystem; 1, inode number;
1434 * 2, file mode; 3, number of hard links to the file; 4, uid; 5, gid;
1435 * 6, device identifier; 7, total size of file in bytes; 8, atime;
1436 * 9, mtime; 10, ctime; 11, preferred block size for system I/O;
1437 * 12, number of blocks allocated and 13, canonicalized file name.
1438 * Not all elements have necesarily meaningful values, as most are
1442 mpdm_t
mpdm_stat(const mpdm_t filename
)
1446 #ifdef CONFOPT_SYS_STAT_H
1450 fn
= mpdm_ref(MPDM_2MBS(filename
->data
));
1452 if (stat((char *) fn
->data
, &s
) != -1) {
1455 mpdm_aset(r
, MPDM_I(s
.st_dev
), 0);
1456 mpdm_aset(r
, MPDM_I(s
.st_ino
), 1);
1457 mpdm_aset(r
, MPDM_I(s
.st_mode
), 2);
1458 mpdm_aset(r
, MPDM_I(s
.st_nlink
), 3);
1459 mpdm_aset(r
, MPDM_I(s
.st_uid
), 4);
1460 mpdm_aset(r
, MPDM_I(s
.st_gid
), 5);
1461 mpdm_aset(r
, MPDM_I(s
.st_rdev
), 6);
1462 mpdm_aset(r
, MPDM_I(s
.st_size
), 7);
1463 mpdm_aset(r
, MPDM_I(s
.st_atime
), 8);
1464 mpdm_aset(r
, MPDM_I(s
.st_mtime
), 9);
1465 mpdm_aset(r
, MPDM_I(s
.st_ctime
), 10);
1466 mpdm_aset(r
, MPDM_I(0), 11); /* s.st_blksize */
1467 mpdm_aset(r
, MPDM_I(0), 12); /* s.st_blocks */
1469 #ifdef CONFOPT_CANONICALIZE_FILE_NAME
1474 if ((ptr
= canonicalize_file_name(
1475 (char *)fn
->data
)) != NULL
) {
1476 mpdm_aset(r
, MPDM_MBS(ptr
), 13);
1482 #ifdef CONFOPT_REALPATH
1486 if (realpath((char *)fn
->data
, tmp
) != NULL
)
1487 mpdm_aset(r
, MPDM_MBS(tmp
), 13);
1491 #ifdef CONFOPT_FULLPATH
1493 char tmp
[_MAX_PATH
+ 1];
1495 if (_fullpath(tmp
, (char *)fn
->data
, _MAX_PATH
) != NULL
)
1496 mpdm_aset(r
, MPDM_MBS(tmp
), 13);
1505 #endif /* CONFOPT_SYS_STAT_H */
1512 * mpdm_chmod - Changes a file's permissions.
1513 * @filename: the file name
1514 * @perms: permissions (element 2 from mpdm_stat())
1516 * Changes the permissions for a file.
1519 int mpdm_chmod(const mpdm_t filename
, mpdm_t perms
)
1523 mpdm_t fn
= mpdm_ref(MPDM_2MBS(filename
->data
));
1525 if ((r
= chmod((char *) fn
->data
, mpdm_ival(perms
))) == -1)
1535 * mpdm_chdir - Changes the working directory
1536 * @dir: the new path
1538 * Changes the working directory
1541 int mpdm_chdir(const mpdm_t dir
)
1545 mpdm_t fn
= mpdm_ref(MPDM_2MBS(dir
->data
));
1547 if ((r
= chdir((char *) fn
->data
)) == -1)
1557 * mpdm_chown - Changes a file's owner.
1558 * @filename: the file name
1559 * @uid: user id (element 4 from mpdm_stat())
1560 * @gid: group id (element 5 from mpdm_stat())
1562 * Changes the owner and group id's for a file.
1565 int mpdm_chown(const mpdm_t filename
, mpdm_t uid
, mpdm_t gid
)
1569 #ifdef CONFOPT_CHOWN
1571 mpdm_t fn
= mpdm_ref(MPDM_2MBS(filename
->data
));
1573 if ((r
= chown((char *) fn
->data
, mpdm_ival(uid
), mpdm_ival(gid
))) == -1)
1578 #endif /* CONFOPT_CHOWN */
1585 * mpdm_glob - Executes a file globbing.
1586 * @spec: Globbing spec
1587 * @base: Optional base directory
1589 * Executes a file globbing. @spec is system-dependent, but usually
1590 * the * and ? metacharacters work everywhere. @base can contain a
1591 * directory; if that's the case, the output strings will include it.
1592 * In any case, each returned value will be suitable for a call to
1595 * Returns an array of files that match the globbing (can be an empty
1596 * array if no file matches), or NULL if globbing is unsupported.
1599 mpdm_t
mpdm_glob(const mpdm_t spec
, const mpdm_t base
)
1605 #ifdef CONFOPT_WIN32
1615 if (mpdm_size(base
))
1616 sp
= mpdm_strcat_s(base
, L
"/");
1621 if (mpdm_size(spec
) == 0)
1622 sp
= mpdm_strcat_s(w
, L
"*.*");
1624 sp
= mpdm_strcat(w
, spec
);
1628 /* delete repeated directory delimiters */
1630 sp
= mpdm_sregex(MPDM_LS(L
"@[\\/]+@g"), w
, MPDM_LS(L
"/"), 0);
1634 sp
= MPDM_2MBS(w
->data
);
1638 d
= mpdm_ref(MPDM_A(0));
1639 f
= mpdm_ref(MPDM_A(0));
1642 if ((h
= FindFirstFile((char *) sp
->data
, &fd
)) != INVALID_HANDLE_VALUE
) {
1643 /* if spec includes a directory, store in s */
1644 if ((ptr
= strrchr((char *) sp
->data
, '/')) != NULL
) {
1646 s
= MPDM_MBS(sp
->data
);
1650 /* ignore . and .. */
1651 if (strcmp(fd
.cFileName
, ".") == 0 || strcmp(fd
.cFileName
, "..") == 0)
1654 /* concat base directory and file names */
1655 w
= mpdm_strcat(s
, MPDM_MBS(fd
.cFileName
));
1657 /* if it's a directory, add a / */
1658 if (fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1659 mpdm_t t
= mpdm_ref(w
);
1660 w
= mpdm_strcat_s(t
, L
"/");
1668 while (FindNextFile(h
, &fd
));
1679 /* glob.h support */
1684 /* build full path */
1685 if (mpdm_size(base
))
1686 v
= mpdm_strcat_s(base
, L
"/");
1690 if (mpdm_size(spec
) == 0)
1691 v
= mpdm_strcat_s(w
, L
"*");
1693 v
= mpdm_strcat(w
, spec
);
1697 /* delete repeated directory delimiters */
1699 v
= mpdm_sregex(MPDM_LS(L
"@/{2,}@g"), w
, MPDM_LS(L
"/"), 0);
1703 v
= MPDM_2MBS(w
->data
);
1708 globbuf
.gl_offs
= 1;
1711 d
= mpdm_ref(MPDM_A(0));
1712 f
= mpdm_ref(MPDM_A(0));
1714 if (glob(ptr
, GLOB_MARK
, NULL
, &globbuf
) == 0) {
1717 for (n
= 0; globbuf
.gl_pathv
[n
] != NULL
; n
++) {
1718 char *ptr
= globbuf
.gl_pathv
[n
];
1719 mpdm_t t
= MPDM_MBS(ptr
);
1721 /* if last char is /, add to directories */
1722 if (ptr
[strlen(ptr
) - 1] == '/')
1733 /* no win32 nor glob.h; try workaround */
1744 /* transfer all data in d and f */
1745 for (n
= 0; n
< mpdm_size(d
); n
++)
1746 mpdm_push(v
, mpdm_aget(d
, n
));
1747 for (n
= 0; n
< mpdm_size(f
); n
++)
1748 mpdm_push(v
, mpdm_aget(f
, n
));
1758 #ifdef CONFOPT_WIN32
1760 static void win32_pipe(HANDLE
* h
, int n
)
1762 SECURITY_ATTRIBUTES sa
;
1765 memset(&sa
, '\0', sizeof(sa
));
1766 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
1767 sa
.bInheritHandle
= TRUE
;
1768 sa
.lpSecurityDescriptor
= NULL
;
1770 cp
= GetCurrentProcess();
1772 CreatePipe(&h
[0], &h
[1], &sa
, 0);
1773 DuplicateHandle(cp
, h
[n
], cp
, &t
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
1779 static int sysdep_popen(mpdm_t v
, char *prg
, int rw
)
1780 /* win32-style pipe */
1784 PROCESS_INFORMATION pi
;
1787 struct mpdm_file
*fs
= (struct mpdm_file
*) v
->data
;
1790 pr
[0] = pr
[1] = pw
[0] = pw
[1] = NULL
;
1797 /* spawn new process */
1798 memset(&pi
, '\0', sizeof(pi
));
1799 memset(&si
, '\0', sizeof(si
));
1801 si
.cb
= sizeof(STARTUPINFO
);
1802 si
.hStdError
= pr
[1];
1803 si
.hStdOutput
= pr
[1];
1804 si
.hStdInput
= pw
[0];
1805 si
.dwFlags
|= STARTF_USESTDHANDLES
;
1807 ret
= CreateProcess(NULL
, prg
, NULL
, NULL
, TRUE
, 0, NULL
, NULL
, &si
, &pi
);
1821 static int sysdep_pclose(const mpdm_t v
)
1823 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1825 if (fs
->hin
!= NULL
)
1826 CloseHandle(fs
->hin
);
1828 if (fs
->hout
!= NULL
)
1829 CloseHandle(fs
->hout
);
1831 /* how to know process exit code? */
1836 #else /* CONFOPT_WIN32 */
1838 static int sysdep_popen(mpdm_t v
, char *prg
, int rw
)
1839 /* unix-style pipe open */
1842 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1845 pr
[0] = pr
[1] = pw
[0] = pw
[1] = -1;
1865 /* redirect stderr to stdout */
1869 /* run the program */
1870 execlp("/bin/sh", "/bin/sh", "-c", prg
, NULL
);
1871 execlp(prg
, prg
, NULL
);
1873 /* still here? exec failed; close pipes and exit */
1879 /* create the pipes as non-buffered streams */
1881 fs
->in
= fdopen(pr
[0], "r");
1882 setvbuf(fs
->in
, NULL
, _IONBF
, 0);
1887 fs
->out
= fdopen(pw
[1], "w");
1888 setvbuf(fs
->out
, NULL
, _IONBF
, 0);
1896 static int sysdep_pclose(const mpdm_t v
)
1897 /* unix-style pipe close */
1900 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1905 if (fs
->out
!= fs
->in
&& fs
->out
!= NULL
)
1914 #endif /* CONFOPT_WIN32 */
1918 * mpdm_popen - Opens a pipe.
1919 * @prg: the program to pipe
1920 * @mode: an fopen-like mode string
1922 * Opens a pipe to a program. If @prg can be open in the specified @mode, an
1923 * mpdm_t value will be returned containing the file descriptor, or NULL
1927 mpdm_t
mpdm_popen(const mpdm_t prg
, const mpdm_t mode
)
1933 if (prg
== NULL
|| mode
== NULL
)
1936 if ((v
= new_mpdm_file()) == NULL
)
1939 /* convert to mbs,s */
1940 pr
= mpdm_ref(MPDM_2MBS(prg
->data
));
1941 md
= mpdm_ref(MPDM_2MBS(mode
->data
));
1944 m
= (char *) md
->data
;
1952 rw
= 0x03; /* r+ or w+ */
1954 if (!sysdep_popen(v
, (char *) pr
->data
, rw
)) {
1955 destroy_mpdm_file(v
);
1967 * mpdm_pclose - Closes a pipe.
1968 * @fd: the value containing the file descriptor
1973 mpdm_t
mpdm_pclose(mpdm_t fd
)
1977 if ((fd
->flags
& MPDM_FILE
) && fd
->data
!= NULL
) {
1978 r
= MPDM_I(sysdep_pclose(fd
));
1979 destroy_mpdm_file(fd
);
1987 * mpdm_home_dir - Returns the home user directory.
1989 * Returns a system-dependent directory where the user can write
1990 * documents and create subdirectories.
1993 mpdm_t
mpdm_home_dir(void)
1999 #ifdef CONFOPT_WIN32
2003 /* get the 'My Documents' folder */
2004 SHGetSpecialFolderLocation(NULL
, CSIDL_PERSONAL
, &pidl
);
2005 SHGetPathFromIDList(pidl
, tmp
);
2010 #ifdef CONFOPT_PWD_H
2014 /* get home dir from /etc/passwd entry */
2015 if (tmp
[0] == '\0' && (p
= getpwuid(getpid())) != NULL
) {
2016 strcpy(tmp
, p
->pw_dir
);
2022 /* still none? try the ENV variable $HOME */
2023 if (tmp
[0] == '\0' && (ptr
= getenv("HOME")) != NULL
) {
2036 * mpdm_app_dir - Returns the applications directory.
2038 * Returns a system-dependent directory where the applications store
2039 * their private data, as components or resources.
2042 mpdm_t
mpdm_app_dir(void)
2046 #ifdef CONFOPT_WIN32
2052 /* get the 'Program Files' folder (can fail) */
2054 if (SHGetSpecialFolderLocation(NULL
, CSIDL_PROGRAM_FILES
, &pidl
) == S_OK
)
2055 SHGetPathFromIDList(pidl
, tmp
);
2057 /* if it's still empty, get from the registry */
2058 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
2059 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
2060 0, KEY_QUERY_VALUE
, &hkey
) == ERROR_SUCCESS
) {
2061 int n
= sizeof(tmp
);
2063 if (RegQueryValueEx(hkey
, "ProgramFilesDir",
2064 NULL
, NULL
, tmp
, (LPDWORD
) & n
) != ERROR_SUCCESS
)
2068 if (tmp
[0] != '\0') {
2074 /* still none? get the configured directory */
2076 r
= MPDM_MBS(CONFOPT_PREFIX
"/share/");