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
)
1101 if (filename
!= NULL
&& mode
!= NULL
) {
1103 /* convert to mbs,s */
1104 fn
= mpdm_ref(MPDM_2MBS(filename
->data
));
1105 m
= mpdm_ref(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_unref(filename
);
1136 * mpdm_close - Closes a file descriptor.
1137 * @fd: the value containing the file descriptor
1139 * Closes the file descriptor.
1142 mpdm_t
mpdm_close(mpdm_t fd
)
1144 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1148 if ((fd
->flags
& MPDM_FILE
) && fs
!= NULL
) {
1152 if (fs
->out
!= fs
->in
&& fs
->out
!= NULL
)
1155 destroy_mpdm_file(fd
);
1158 fd
= mpdm_unref(fd
);
1165 * mpdm_read - Reads a line from a file descriptor.
1166 * @fd: the value containing the file descriptor
1168 * Reads a line from @fd. Returns the line, or NULL on EOF.
1170 * [Character Set Conversion]
1172 mpdm_t
mpdm_read(const mpdm_t fd
)
1177 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1182 ptr
= fs
->f_read(fs
, &s
);
1185 v
= MPDM_ENS(ptr
, s
);
1191 mpdm_t
mpdm_getchar(const mpdm_t fd
)
1196 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1198 if (fs
!= NULL
&& (c
= get_char(fs
)) != EOF
) {
1199 /* get the char as-is */
1200 tmp
[0] = (wchar_t) c
;
1210 int mpdm_putchar(const mpdm_t fd
, const mpdm_t c
)
1212 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1213 const wchar_t *ptr
= mpdm_string(c
);
1218 if (fs
== NULL
|| put_char(*ptr
, fs
) == -1)
1228 * mpdm_write - Writes a value into a file.
1229 * @fd: the file descriptor.
1230 * @v: the value to be written.
1232 * Writes the @v string value into @fd, using the current encoding.
1234 * [Character Set Conversion]
1236 int mpdm_write(const mpdm_t fd
, const mpdm_t v
)
1238 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1244 ret
= fs
->f_write(fs
, mpdm_string(v
));
1252 int mpdm_fseek(const mpdm_t fd
, long offset
, int whence
)
1254 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1256 return fseek(fs
->in
, offset
, whence
);
1260 long mpdm_ftell(const mpdm_t fd
)
1262 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1264 return ftell(fs
->in
);
1268 FILE * mpdm_get_filehandle(const mpdm_t fd
)
1272 if (fd
->flags
& MPDM_FILE
&& fd
->data
!= NULL
) {
1273 struct mpdm_file
*fs
= (struct mpdm_file
*)fd
->data
;
1282 mpdm_t mpdm_bread(mpdm_t fd, int size)
1287 int mpdm_bwrite(mpdm_tfd, mpdm_t v, int size)
1293 static mpdm_t
embedded_encodings(void)
1299 L
"iso8859-1", L
"iso8859-1",
1300 L
"iso-8859-1", NULL
,
1304 L
"utf-16le", L
"utf-16le",
1307 L
"utf-16be", L
"utf-16be",
1310 L
"utf-16", L
"utf-16",
1314 L
"utf-32le", L
"utf-32le",
1317 L
"utf-32be", L
"utf-32be",
1320 L
"utf-32", L
"utf-32",
1324 L
"utf-8bom", L
"utf-8bom",
1329 if ((e
= mpdm_hget_s(mpdm_root(), L
"EMBEDDED_ENCODINGS")) == NULL
) {
1333 e
= mpdm_ref(MPDM_H(0));
1335 for (n
= 0; e2e
[n
] != NULL
; n
+= 2) {
1336 mpdm_t v
= MPDM_S(e2e
[n
]);
1338 if (e2e
[n
+ 1] != NULL
)
1339 p
= MPDM_S(e2e
[n
+ 1]);
1342 mpdm_hset(e
, mpdm_ulc(v
, 1), p
);
1345 mpdm_hset_s(mpdm_root(), L
"EMBEDDED_ENCODINGS", e
);
1355 * mpdm_encoding - Sets the current charset encoding for files.
1356 * @charset: the charset name.
1358 * Sets the current charset encoding for files. Future opened
1359 * files will be assumed to be encoded with @charset, which can
1360 * be any of the supported charset names (utf-8, iso-8859-1, etc.),
1361 * and converted on each read / write. If charset is NULL, it
1362 * is reverted to default charset conversion (i.e. the one defined
1365 * This function stores the @charset value into the ENCODING item
1366 * of the mpdm_root() hash.
1368 * Returns a negative number if @charset is unsupported, or zero
1369 * if no errors were found.
1371 * [Character Set Conversion]
1373 int mpdm_encoding(mpdm_t charset
)
1376 mpdm_t e
= embedded_encodings();
1381 /* NULL encoding? done */
1382 if (mpdm_size(charset
) == 0) {
1383 mpdm_hset_s(mpdm_root(), L
"ENCODING", NULL
);
1387 #ifdef CONFOPT_ICONV
1390 mpdm_t cs
= mpdm_ref(MPDM_2MBS(charset
->data
));
1392 /* tries to create an iconv encoder and decoder for this charset */
1394 if ((ic
= iconv_open("WCHAR_T", (char *) cs
->data
)) == (iconv_t
) - 1)
1399 if ((ic
= iconv_open((char *) cs
->data
, "WCHAR_T")) == (iconv_t
) - 1)
1404 /* got a valid encoding */
1412 #endif /* CONFOPT_ICONV */
1414 if (ret
!= 0 && (v
= mpdm_hget(e
, charset
)) != NULL
)
1418 mpdm_hset_s(mpdm_root(), L
"ENCODING", v
);
1420 mpdm_unref(charset
);
1427 * mpdm_unlink - Deletes a file.
1428 * @filename: file name to be deleted
1433 int mpdm_unlink(const mpdm_t filename
)
1440 /* convert to mbs */
1441 fn
= mpdm_ref(MPDM_2MBS(filename
->data
));
1443 if ((ret
= unlink((char *) fn
->data
)) == -1)
1447 mpdm_unref(filename
);
1454 * mpdm_stat - Gives status from a file.
1455 * @filename: file name to get the status from
1457 * Returns a 14 element array of the status (permissions, onwer, etc.)
1458 * from the desired @filename, or NULL if the file cannot be accessed.
1461 * The values are: 0, device number of filesystem; 1, inode number;
1462 * 2, file mode; 3, number of hard links to the file; 4, uid; 5, gid;
1463 * 6, device identifier; 7, total size of file in bytes; 8, atime;
1464 * 9, mtime; 10, ctime; 11, preferred block size for system I/O;
1465 * 12, number of blocks allocated and 13, canonicalized file name.
1466 * Not all elements have necesarily meaningful values, as most are
1470 mpdm_t
mpdm_stat(const mpdm_t filename
)
1476 #ifdef CONFOPT_SYS_STAT_H
1480 fn
= mpdm_ref(MPDM_2MBS(filename
->data
));
1482 if (stat((char *) fn
->data
, &s
) != -1) {
1487 mpdm_aset(r
, MPDM_I(s
.st_dev
), 0);
1488 mpdm_aset(r
, MPDM_I(s
.st_ino
), 1);
1489 mpdm_aset(r
, MPDM_I(s
.st_mode
), 2);
1490 mpdm_aset(r
, MPDM_I(s
.st_nlink
), 3);
1491 mpdm_aset(r
, MPDM_I(s
.st_uid
), 4);
1492 mpdm_aset(r
, MPDM_I(s
.st_gid
), 5);
1493 mpdm_aset(r
, MPDM_I(s
.st_rdev
), 6);
1494 mpdm_aset(r
, MPDM_I(s
.st_size
), 7);
1495 mpdm_aset(r
, MPDM_I(s
.st_atime
), 8);
1496 mpdm_aset(r
, MPDM_I(s
.st_mtime
), 9);
1497 mpdm_aset(r
, MPDM_I(s
.st_ctime
), 10);
1498 mpdm_aset(r
, MPDM_I(0), 11); /* s.st_blksize */
1499 mpdm_aset(r
, MPDM_I(0), 12); /* s.st_blocks */
1501 #ifdef CONFOPT_CANONICALIZE_FILE_NAME
1506 if ((ptr
= canonicalize_file_name(
1507 (char *)fn
->data
)) != NULL
) {
1508 mpdm_aset(r
, MPDM_MBS(ptr
), 13);
1514 #ifdef CONFOPT_REALPATH
1518 if (realpath((char *)fn
->data
, tmp
) != NULL
)
1519 mpdm_aset(r
, MPDM_MBS(tmp
), 13);
1523 #ifdef CONFOPT_FULLPATH
1525 char tmp
[_MAX_PATH
+ 1];
1527 if (_fullpath(tmp
, (char *)fn
->data
, _MAX_PATH
) != NULL
)
1528 mpdm_aset(r
, MPDM_MBS(tmp
), 13);
1539 #endif /* CONFOPT_SYS_STAT_H */
1541 mpdm_unref(filename
);
1548 * mpdm_chmod - Changes a file's permissions.
1549 * @filename: the file name
1550 * @perms: permissions (element 2 from mpdm_stat())
1552 * Changes the permissions for a file.
1555 int mpdm_chmod(const mpdm_t filename
, mpdm_t perms
)
1562 mpdm_t fn
= mpdm_ref(MPDM_2MBS(filename
->data
));
1564 if ((r
= chmod((char *) fn
->data
, mpdm_ival(perms
))) == -1)
1569 mpdm_unref(filename
);
1576 * mpdm_chdir - Changes the working directory
1577 * @dir: the new path
1579 * Changes the working directory
1582 int mpdm_chdir(const mpdm_t dir
)
1587 mpdm_t fn
= mpdm_ref(MPDM_2MBS(dir
->data
));
1589 if ((r
= chdir((char *) fn
->data
)) == -1)
1600 * mpdm_chown - Changes a file's owner.
1601 * @filename: the file name
1602 * @uid: user id (element 4 from mpdm_stat())
1603 * @gid: group id (element 5 from mpdm_stat())
1605 * Changes the owner and group id's for a file.
1608 int mpdm_chown(const mpdm_t filename
, mpdm_t uid
, mpdm_t gid
)
1616 #ifdef CONFOPT_CHOWN
1618 mpdm_t fn
= mpdm_ref(MPDM_2MBS(filename
->data
));
1620 if ((r
= chown((char *) fn
->data
, mpdm_ival(uid
), mpdm_ival(gid
))) == -1)
1625 #endif /* CONFOPT_CHOWN */
1629 mpdm_unref(filename
);
1636 * mpdm_glob - Executes a file globbing.
1637 * @spec: Globbing spec
1638 * @base: Optional base directory
1640 * Executes a file globbing. @spec is system-dependent, but usually
1641 * the * and ? metacharacters work everywhere. @base can contain a
1642 * directory; if that's the case, the output strings will include it.
1643 * In any case, each returned value will be suitable for a call to
1646 * Returns an array of files that match the globbing (can be an empty
1647 * array if no file matches), or NULL if globbing is unsupported.
1650 mpdm_t
mpdm_glob(const mpdm_t spec
, const mpdm_t base
)
1659 #ifdef CONFOPT_WIN32
1669 if (mpdm_size(base
))
1670 sp
= mpdm_strcat_s(base
, L
"/");
1675 if (mpdm_size(spec
) == 0)
1676 sp
= mpdm_strcat_s(w
, L
"*.*");
1678 sp
= mpdm_strcat(w
, spec
);
1682 /* delete repeated directory delimiters */
1684 sp
= mpdm_sregex(MPDM_LS(L
"@[\\/]+@g"), w
, MPDM_LS(L
"/"), 0);
1688 sp
= MPDM_2MBS(w
->data
);
1692 d
= mpdm_ref(MPDM_A(0));
1693 f
= mpdm_ref(MPDM_A(0));
1696 if ((h
= FindFirstFile((char *) sp
->data
, &fd
)) != INVALID_HANDLE_VALUE
) {
1697 /* if spec includes a directory, store in s */
1698 if ((ptr
= strrchr((char *) sp
->data
, '/')) != NULL
) {
1700 s
= MPDM_MBS(sp
->data
);
1704 /* ignore . and .. */
1705 if (strcmp(fd
.cFileName
, ".") == 0 || strcmp(fd
.cFileName
, "..") == 0)
1708 /* concat base directory and file names */
1709 w
= mpdm_strcat(s
, MPDM_MBS(fd
.cFileName
));
1711 /* if it's a directory, add a / */
1712 if (fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1713 mpdm_t t
= mpdm_ref(w
);
1714 w
= mpdm_strcat_s(t
, L
"/");
1722 while (FindNextFile(h
, &fd
));
1733 /* glob.h support */
1738 /* build full path */
1739 if (mpdm_size(base
))
1740 v
= mpdm_strcat_s(base
, L
"/");
1744 if (mpdm_size(spec
) == 0)
1745 v
= mpdm_strcat_s(w
, L
"*");
1747 v
= mpdm_strcat(w
, spec
);
1751 /* delete repeated directory delimiters */
1753 v
= mpdm_sregex(MPDM_LS(L
"@/{2,}@g"), w
, MPDM_LS(L
"/"), 0);
1757 v
= MPDM_2MBS(w
->data
);
1762 globbuf
.gl_offs
= 1;
1765 d
= mpdm_ref(MPDM_A(0));
1766 f
= mpdm_ref(MPDM_A(0));
1768 if (glob(ptr
, GLOB_MARK
, NULL
, &globbuf
) == 0) {
1771 for (n
= 0; globbuf
.gl_pathv
[n
] != NULL
; n
++) {
1772 char *ptr
= globbuf
.gl_pathv
[n
];
1773 mpdm_t t
= MPDM_MBS(ptr
);
1775 /* if last char is /, add to directories */
1776 if (ptr
[strlen(ptr
) - 1] == '/')
1787 /* no win32 nor glob.h; try workaround */
1800 /* transfer all data in d and f */
1801 for (n
= 0; n
< mpdm_size(d
); n
++)
1802 mpdm_push(v
, mpdm_aget(d
, n
));
1803 for (n
= 0; n
< mpdm_size(f
); n
++)
1804 mpdm_push(v
, mpdm_aget(f
, n
));
1819 #ifdef CONFOPT_WIN32
1821 static void win32_pipe(HANDLE
* h
, int n
)
1823 SECURITY_ATTRIBUTES sa
;
1826 memset(&sa
, '\0', sizeof(sa
));
1827 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
1828 sa
.bInheritHandle
= TRUE
;
1829 sa
.lpSecurityDescriptor
= NULL
;
1831 cp
= GetCurrentProcess();
1833 CreatePipe(&h
[0], &h
[1], &sa
, 0);
1834 DuplicateHandle(cp
, h
[n
], cp
, &t
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
1840 static int sysdep_popen(mpdm_t v
, char *prg
, int rw
)
1841 /* win32-style pipe */
1845 PROCESS_INFORMATION pi
;
1848 struct mpdm_file
*fs
= (struct mpdm_file
*) v
->data
;
1851 pr
[0] = pr
[1] = pw
[0] = pw
[1] = NULL
;
1858 /* spawn new process */
1859 memset(&pi
, '\0', sizeof(pi
));
1860 memset(&si
, '\0', sizeof(si
));
1862 si
.cb
= sizeof(STARTUPINFO
);
1863 si
.hStdError
= pr
[1];
1864 si
.hStdOutput
= pr
[1];
1865 si
.hStdInput
= pw
[0];
1866 si
.dwFlags
|= STARTF_USESTDHANDLES
;
1868 ret
= CreateProcess(NULL
, prg
, NULL
, NULL
, TRUE
, 0, NULL
, NULL
, &si
, &pi
);
1882 static int sysdep_pclose(const mpdm_t v
)
1884 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1886 if (fs
->hin
!= NULL
)
1887 CloseHandle(fs
->hin
);
1889 if (fs
->hout
!= NULL
)
1890 CloseHandle(fs
->hout
);
1892 /* how to know process exit code? */
1897 #else /* CONFOPT_WIN32 */
1899 static int sysdep_popen(mpdm_t v
, char *prg
, int rw
)
1900 /* unix-style pipe open */
1903 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1906 pr
[0] = pr
[1] = pw
[0] = pw
[1] = -1;
1926 /* redirect stderr to stdout */
1930 /* run the program */
1931 execlp("/bin/sh", "/bin/sh", "-c", prg
, NULL
);
1932 execlp(prg
, prg
, NULL
);
1934 /* still here? exec failed; close pipes and exit */
1940 /* create the pipes as non-buffered streams */
1942 fs
->in
= fdopen(pr
[0], "r");
1943 setvbuf(fs
->in
, NULL
, _IONBF
, 0);
1948 fs
->out
= fdopen(pw
[1], "w");
1949 setvbuf(fs
->out
, NULL
, _IONBF
, 0);
1957 static int sysdep_pclose(const mpdm_t v
)
1958 /* unix-style pipe close */
1961 struct mpdm_file
*fs
= (struct mpdm_file
*)v
->data
;
1966 if (fs
->out
!= fs
->in
&& fs
->out
!= NULL
)
1975 #endif /* CONFOPT_WIN32 */
1979 * mpdm_popen - Opens a pipe.
1980 * @prg: the program to pipe
1981 * @mode: an fopen-like mode string
1983 * Opens a pipe to a program. If @prg can be open in the specified @mode, an
1984 * mpdm_t value will be returned containing the file descriptor, or NULL
1988 mpdm_t
mpdm_popen(const mpdm_t prg
, const mpdm_t mode
)
1997 if (prg
!= NULL
&& mode
!= NULL
) {
1999 v
= new_mpdm_file();
2001 /* convert to mbs,s */
2002 pr
= mpdm_ref(MPDM_2MBS(prg
->data
));
2003 md
= mpdm_ref(MPDM_2MBS(mode
->data
));
2006 m
= (char *) md
->data
;
2014 rw
= 0x03; /* r+ or w+ */
2016 if (!sysdep_popen(v
, (char *) pr
->data
, rw
)) {
2017 destroy_mpdm_file(v
);
2018 mpdm_unref(mpdm_ref(v
));
2036 * mpdm_pclose - Closes a pipe.
2037 * @fd: the value containing the file descriptor
2042 mpdm_t
mpdm_pclose(mpdm_t fd
)
2048 if ((fd
->flags
& MPDM_FILE
) && fd
->data
!= NULL
) {
2049 r
= MPDM_I(sysdep_pclose(fd
));
2050 destroy_mpdm_file(fd
);
2053 fd
= mpdm_unref(fd
);
2060 * mpdm_home_dir - Returns the home user directory.
2062 * Returns a system-dependent directory where the user can write
2063 * documents and create subdirectories.
2066 mpdm_t
mpdm_home_dir(void)
2072 #ifdef CONFOPT_WIN32
2076 /* get the 'My Documents' folder */
2077 SHGetSpecialFolderLocation(NULL
, CSIDL_PERSONAL
, &pidl
);
2078 SHGetPathFromIDList(pidl
, tmp
);
2083 #ifdef CONFOPT_PWD_H
2087 /* get home dir from /etc/passwd entry */
2088 if (tmp
[0] == '\0' && (p
= getpwuid(getpid())) != NULL
) {
2089 strcpy(tmp
, p
->pw_dir
);
2095 /* still none? try the ENV variable $HOME */
2096 if (tmp
[0] == '\0' && (ptr
= getenv("HOME")) != NULL
) {
2109 * mpdm_app_dir - Returns the applications directory.
2111 * Returns a system-dependent directory where the applications store
2112 * their private data, as components or resources.
2115 mpdm_t
mpdm_app_dir(void)
2119 #ifdef CONFOPT_WIN32
2125 /* get the 'Program Files' folder (can fail) */
2127 if (SHGetSpecialFolderLocation(NULL
, CSIDL_PROGRAM_FILES
, &pidl
) == S_OK
)
2128 SHGetPathFromIDList(pidl
, tmp
);
2130 /* if it's still empty, get from the registry */
2131 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
2132 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
2133 0, KEY_QUERY_VALUE
, &hkey
) == ERROR_SUCCESS
) {
2134 int n
= sizeof(tmp
);
2136 if (RegQueryValueEx(hkey
, "ProgramFilesDir",
2137 NULL
, NULL
, tmp
, (LPDWORD
) & n
) != ERROR_SUCCESS
)
2141 if (tmp
[0] != '\0') {
2147 /* still none? get the configured directory */
2149 r
= MPDM_MBS(CONFOPT_PREFIX
"/share/");