1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
15 #include <glib/gstdio.h>
21 #include "application.h"
22 #include "deployment.h"
25 managed_stream_open (gpointer context
, const char *filename
, int mode
)
27 // minizip expects to get a FILE* here, we'll just shuffle our context around.
33 managed_stream_read (gpointer context
, gpointer stream
, void *buf
, unsigned long size
)
35 ManagedStreamCallbacks
*s
= (ManagedStreamCallbacks
*) context
;
36 unsigned long left
= size
;
37 unsigned long nread
= 0;
41 if ((n
= s
->Read (s
->handle
, (char *) buf
+ nread
, 0, MIN (left
, G_MAXINT32
))) <= 0)
46 } while (nread
< size
);
52 managed_stream_write (gpointer context
, gpointer stream
, const void *buf
, unsigned long size
)
54 ManagedStreamCallbacks
*s
= (ManagedStreamCallbacks
*) context
;
55 unsigned long nwritten
= 0;
56 unsigned long left
= size
;
60 n
= MIN (left
, G_MAXINT32
);
61 s
->Write (s
->handle
, (char *) buf
+ nwritten
, 0, n
);
64 } while (nwritten
< size
);
70 managed_stream_tell (gpointer context
, gpointer stream
)
72 ManagedStreamCallbacks
*s
= (ManagedStreamCallbacks
*) context
;
74 return s
->Position (s
->handle
);
78 managed_stream_seek (gpointer context
, gpointer stream
, unsigned long offset
, int origin
)
80 ManagedStreamCallbacks
*s
= (ManagedStreamCallbacks
*) context
;
82 s
->Seek (s
->handle
, offset
, origin
);
88 managed_stream_close (gpointer context
, gpointer stream
)
94 managed_stream_error (gpointer context
, gpointer stream
)
101 managed_unzip_stream_to_stream_first_file (ManagedStreamCallbacks
*source
, ManagedStreamCallbacks
*dest
)
103 zlib_filefunc_def funcs
;
109 funcs
.zopen_file
= managed_stream_open
;
110 funcs
.zread_file
= managed_stream_read
;
111 funcs
.zwrite_file
= managed_stream_write
;
112 funcs
.ztell_file
= managed_stream_tell
;
113 funcs
.zseek_file
= managed_stream_seek
;
114 funcs
.zclose_file
= managed_stream_close
;
115 funcs
.zerror_file
= managed_stream_error
;
116 funcs
.opaque
= source
;
118 zipFile
= unzOpen2 (NULL
, &funcs
);
123 if (unzGoToFirstFile (zipFile
) != UNZ_OK
)
126 if (unzOpenCurrentFile (zipFile
) != UNZ_OK
)
129 ret
= managed_unzip_extract_to_stream (zipFile
, dest
);
132 unzCloseCurrentFile (zipFile
);
139 managed_unzip_stream_to_stream (ManagedStreamCallbacks
*source
, ManagedStreamCallbacks
*dest
, const char *partname
)
141 zlib_filefunc_def funcs
;
147 funcs
.zopen_file
= managed_stream_open
;
148 funcs
.zread_file
= managed_stream_read
;
149 funcs
.zwrite_file
= managed_stream_write
;
150 funcs
.ztell_file
= managed_stream_tell
;
151 funcs
.zseek_file
= managed_stream_seek
;
152 funcs
.zclose_file
= managed_stream_close
;
153 funcs
.zerror_file
= managed_stream_error
;
154 funcs
.opaque
= source
;
156 zipFile
= unzOpen2 (NULL
, &funcs
);
161 if (unzLocateFile (zipFile
, partname
, 2) != UNZ_OK
)
164 if (unzOpenCurrentFile (zipFile
) != UNZ_OK
)
167 ret
= managed_unzip_extract_to_stream (zipFile
, dest
);
170 unzCloseCurrentFile (zipFile
);
177 managed_unzip_extract_to_stream (unzFile zipFile
, ManagedStreamCallbacks
*dest
)
183 if ((nread
= unzReadCurrentFile (zipFile
, buf
, sizeof (buf
))) > 0) {
184 dest
->Write (dest
->handle
, buf
, 0, nread
);
194 * @hi: the high bound
196 * Finds the midpoint between positive integer values, @lo and @hi.
198 * Notes: Typically expressed as '(@lo + @hi) / 2', this is incorrect
199 * when @lo and @hi are sufficiently large enough that combining them
200 * would overflow their integer type. To work around this, we use the
201 * formula, '@lo + ((@hi - @lo) / 2)', thus preventing this problem
204 * Returns the midpoint between @lo and @hi (rounded down).
206 #define MID(lo, hi) (lo + ((hi - lo) >> 1))
210 bsearch (GPtrArray
*array
, bool stable
, GCompareFunc cmp
, void *item
)
212 register guint lo
, hi
;
219 lo
= 0, hi
= array
->len
;
223 if ((c
= cmp (&item
, &array
->pdata
[m
])) > 0) {
240 g_ptr_array_insert_sorted (GPtrArray
*array
, GCompareFunc cmp
, void *item
)
242 guint index
= bsearch (array
, true, cmp
, item
);
244 g_ptr_array_insert (array
, index
, item
);
248 g_ptr_array_insert (GPtrArray
*array
, guint index
, void *item
)
253 if (index
>= array
->len
) {
254 g_ptr_array_add (array
, item
);
258 g_ptr_array_set_size (array
, array
->len
+ 1);
260 dest
= ((guint8
*) array
->pdata
) + (sizeof (void *) * (index
+ 1));
261 src
= ((guint8
*) array
->pdata
) + (sizeof (void *) * index
);
262 n
= array
->len
- index
- 1;
264 memmove (dest
, src
, (sizeof (void *) * n
));
265 array
->pdata
[index
] = item
;
269 write_all (int fd
, char *buf
, size_t len
)
276 n
= write (fd
, buf
+ nwritten
, len
- nwritten
);
277 } while (n
== -1 && errno
== EINTR
);
283 } while (nwritten
< len
);
289 is_dll_or_mdb (const char *filename
, int n
)
291 return n
> 4 && (!g_ascii_strcasecmp (filename
+ (n
- 4), ".dll") || !g_ascii_strcasecmp (filename
+ (n
- 4), ".mdb"));
295 CanonicalizeFilename (char *filename
, int n
, CanonMode mode
)
297 char *inptr
= filename
;
301 n
= strlen (filename
);
305 if (mode
== CanonModeXap
&& is_dll_or_mdb (filename
, n
)) {
306 // Note: We don't want to change the casing of the dll's
307 // basename since Mono requires the basename to match the
311 while (inend
> inptr
&& *inend
!= '\\' && *inend
!= '/')
314 if (*inend
== '\\') {
315 // include the trailing '\\'
320 while (inptr
< inend
) {
321 if (*inptr
!= '\\') {
322 if (mode
!= CanonModeNone
)
323 *inptr
= g_ascii_tolower (*inptr
);
325 *inptr
= G_DIR_SEPARATOR
;
334 ExtractFile (unzFile zip
, int fd
)
342 if ((nread
= unzReadCurrentFile (zip
, buf
, sizeof (buf
))) > 0) {
343 if ((n
= write_all (fd
, buf
, nread
)) == -1)
348 if (nread
!= 0 || n
== -1 /*|| fsync (fd) == -1*/) {
360 ExtractAll (unzFile zip
, const char *dir
, CanonMode mode
)
362 char *filename
, *dirname
, *path
, *altpath
;
366 if (unzGoToFirstFile (zip
) != UNZ_OK
)
370 unzGetCurrentFileInfo (zip
, &info
, NULL
, 0, NULL
, 0, NULL
, 0);
371 if (info
.external_fa
& (1 << 4))
374 if (!(filename
= (char *) g_malloc (info
.size_filename
+ 1)))
377 unzGetCurrentFileInfo (zip
, NULL
, filename
, info
.size_filename
+ 1, NULL
, 0, NULL
, 0);
379 CanonicalizeFilename (filename
, info
.size_filename
, mode
);
381 path
= g_build_filename (dir
, filename
, NULL
);
383 dirname
= g_path_get_dirname (path
);
384 if (g_mkdir_with_parents (dirname
, 0700) == -1 && errno
!= EEXIST
) {
393 if ((fd
= g_open (path
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0600)) == -1) {
399 if (unzOpenCurrentFile (zip
) != UNZ_OK
) {
406 if (!ExtractFile (zip
, fd
)) {
407 unzCloseCurrentFile (zip
);
413 unzCloseCurrentFile (zip
);
415 if (mode
== CanonModeXap
&& is_dll_or_mdb (filename
, info
.size_filename
)) {
416 CanonicalizeFilename (filename
, info
.size_filename
, CanonModeResource
);
417 altpath
= g_build_filename (dir
, filename
, NULL
);
418 if (strcmp (path
, altpath
) != 0)
419 symlink (path
, altpath
);
425 } while (unzGoToNextFile (zip
) == UNZ_OK
);
431 MakeTempDir (char *tmpdir
)
437 if ((n
= strlen (tmpdir
)) < 6) {
442 xxx
= tmpdir
+ (n
- 6);
443 if (strcmp (xxx
, "XXXXXX") != 0) {
450 if (!mktemp (tmpdir
))
453 if (g_mkdir (tmpdir
, 0700) != -1)
456 if (errno
!= EEXIST
) {
457 // don't bother trying again...
461 // that path already exists, try a new one...
462 strcpy (xxx
, "XXXXXX");
464 } while (attempts
< 100);
471 rmdir_real (GString
*path
)
473 const gchar
*dirname
;
478 if (!(dir
= g_dir_open (path
->str
, 0, NULL
)))
481 g_string_append_c (path
, G_DIR_SEPARATOR
);
484 while ((dirname
= g_dir_read_name (dir
))) {
485 if (!strcmp (dirname
, ".") || !strcmp (dirname
, ".."))
488 g_string_truncate (path
, len
);
489 g_string_append (path
, dirname
);
491 if (g_lstat (path
->str
, &st
) == -1)
494 if (S_ISDIR (st
.st_mode
))
497 g_unlink (path
->str
);
502 g_string_truncate (path
, len
- 1);
504 return g_rmdir (path
->str
);
508 // Creates a temporary directory, based on the @filename template
510 // Returns: a g-allocated string name that points to the created
511 // directory, or NULL on failure
514 CreateTempDir (const char *filename
)
519 if (!(name
= strrchr (filename
, '/')))
524 buf
= g_strdup_printf ("%s.XXXXXX", name
);
526 if (Application::GetCurrent())
527 path
= g_build_filename (Application::GetCurrent()->GetResourceRoot(), buf
, NULL
);
529 path
= g_build_filename (g_get_tmp_dir (), buf
, NULL
);
530 Deployment::GetCurrent()->TrackPath (path
);
534 if (!MakeTempDir (path
)) {
543 RemoveDir (const char *dir
)
548 path
= g_string_new (dir
);
549 rv
= rmdir_real (path
);
550 g_string_free (path
, true);
556 CopyFileTo (const char *filename
, int fd
)
562 if ((in
= g_open (filename
, O_RDONLY
)) == -1) {
569 nread
= read (in
, buf
, sizeof (buf
));
570 } while (nread
== -1 && errno
== EINTR
);
578 if (write_all (fd
, buf
, nread
) == -1)
582 if (fsync (fd
) == -1)
599 measuring_context_create (void)
601 cairo_surface_t
* surf
= cairo_image_surface_create (CAIRO_FORMAT_A1
, 1, 1);
602 return cairo_create (surf
);
606 measuring_context_destroy (cairo_t
*cr
)
608 cairo_surface_destroy (cairo_get_target (cr
));
614 read_internal (int fd
, char *buf
, size_t n
)
619 nread
= read (fd
, buf
, n
);
620 } while (nread
== -1 && errno
== EINTR
);
626 TextStream::TextStream ()
636 TextStream::~TextStream ()
641 if (cd
!= (GIConv
) -1) {
647 #define BOM ((gunichar2) 0xFEFF)
648 #define ANTIBOM ((gunichar2) 0xFFFE)
659 static const char *encoding_names
[] = { "UTF-16BE", "UTF-16LE", "UTF-32BE", "UTF-32LE", "UTF-8" };
663 TextStream::OpenBuffer (const char *buf
, int size
)
667 textbufptr
= textbuf
= (char *) buf
;
673 return ReadBOM (false);
677 TextStream::OpenFile (const char *filename
, bool force
)
684 if ((fd
= g_open (filename
, O_RDONLY
)) == -1)
687 return ReadBOM (force
);
691 TextStream::ReadBOM (bool force
)
693 Encoding encoding
= UNKNOWN
;
697 // prefetch the first chunk of data in order to determine encoding
698 if ((nread
= ReadInternal (buffer
, sizeof (buffer
))) == -1) {
708 memcpy (&bom
, buffer
, 2);
722 memcpy (&bom
, buffer
+ 2, 2);
723 if (bom
== ANTIBOM
) {
727 } else if (bom
== BOM
) {
743 if (encoding
== UNKNOWN
) {
753 if (encoding
!= UTF8
&& (cd
= g_iconv_open ("UTF-8", encoding_names
[encoding
])) == (GIConv
) -1) {
772 if (cd
!= (GIConv
) -1) {
785 return eof
&& buflen
== 0;
789 TextStream::Read (char *buf
, size_t n
)
791 size_t inleft
= buflen
;
792 char *inbuf
= bufptr
;
799 if (cd
!= (GIConv
) -1) {
800 if (g_iconv (cd
, &inbuf
, &inleft
, &outbuf
, &outleft
) == (size_t) -1) {
803 // not enough space available in the output buffer
806 // incomplete multibyte character sequence
809 // illegal multibyte sequence
812 // unknown error, fail
817 r
= MIN (inleft
, outleft
);
818 memcpy (outbuf
, inbuf
, r
);
825 if (outleft
== 0 || eof
)
830 memmove (buffer
, inbuf
, inleft
);
832 inbuf
= buffer
+ inleft
;
833 if ((nread
= ReadInternal (inbuf
, sizeof (buffer
) - inleft
)) <= 0) {
842 if (eof
&& cd
!= (GIConv
) -1)
843 g_iconv (cd
, NULL
, NULL
, &outbuf
, &outleft
);
850 return (outbuf
- buf
);
854 TextStream::ReadInternal (char *buffer
, ssize_t size
)
857 return read_internal (fd
, buffer
, size
);
859 ssize_t nread
= size
;
864 if (textbufptr
+ size
> textbuf
+ textbufsize
) {
866 nread
= textbuf
+ textbufsize
- textbufptr
;
868 memcpy (buffer
, textbufptr
, nread
);
877 GArray
*double_garray_from_str (const char *s
, gint max
)
879 char *next
= (char *)s
;
880 GArray
*values
= g_array_sized_new (false, true, sizeof (double), max
> 0 ? max
: 16);
882 guint end
= max
> 0 ? max
: G_MAXINT
;
884 while (next
&& values
->len
< end
) {
885 while (g_ascii_isspace (*next
) || *next
== ',')
886 next
= g_utf8_next_char (next
);
891 coord
= g_ascii_strtod (prev
, &next
);
892 if (errno
!= 0 || next
== prev
)
895 g_array_append_val (values
, coord
);
900 while (values
->len
< (guint
) max
) {
902 g_array_append_val (values
, coord
);
908 char * parse_rfc_1945_quoted_string (char *input
, char *c
, char **end
)
912 if (input
== NULL
|| input
[0] != '"')
922 * We're parsing a quoted-string according to RFC 1945:
924 * quoted-string = ( <"> *(qdtext) <"> )
926 * qdtext = <any CHAR except <"> and CTLs,
929 * LWS = [CRLF] 1*( SP | HT )
930 * CTL = <any US-ASCII control character
931 * (octets 0 - 31) and DEL (127)>
938 if (h
== 10 || h
== 13 || h
== ' ' || h
== 9)
945 if ((h
> 0 && h
<= 31) || h
== 127) {
958 } while (*(input
++));
963 char *parse_rfc_1945_token (char *input
, char *c
, char **end
)
968 if (input
== NULL
|| c
== NULL
|| end
== NULL
)
977 * We're parsing a token according to RFC 1945:
979 * token = 1*<any CHAR except CTLs or tspecials>
981 * tspecials = "(" | ")" | "<" | ">" | "@"
982 * | "," | ";" | ":" | "\" | <">
983 * | "/" | "[" | "]" | "?" | "="
984 * | "{" | "}" | SP | HT
986 * CTL = <any US-ASCII control character
987 * (octets 0 - 31) and DEL (127)>
988 * SP = <US-ASCII SP, space (32)>
989 * HT = <US-ASCII HT, horizontal-tab (9)>
999 if ((h
> 0 && h
<= 31) || h
== 127) {
1039 } while (*(input
++));
1042 // reached the end of the input, only one token
1046 Cancellable::Cancellable ()
1052 Cancellable::~Cancellable ()
1055 downloader
->unref ();
1059 Cancellable::Cancel ()
1062 cancel_cb (downloader
, context
);
1066 Cancellable::SetCancelFuncAndData (CancelCallback cb
, Downloader
*downloader
, void *_context
)
1070 if (this->downloader
)
1071 this->downloader
->unref ();
1072 this->downloader
= downloader
;
1073 if (this->downloader
)
1074 this->downloader
->ref ();