source: trunk/libdjvu/GURL.cpp @ 269

Last change on this file since 269 was 206, checked in by Eugene Romanenko, 14 years ago

DJVU plugin: djvulibre updated to version 3.5.19

File size: 47.2 KB
Line 
1//C-  -*- C++ -*-
2//C- -------------------------------------------------------------------
3//C- DjVuLibre-3.5
4//C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
5//C- Copyright (c) 2001  AT&T
6//C-
7//C- This software is subject to, and may be distributed under, the
8//C- GNU General Public License, either Version 2 of the license,
9//C- or (at your option) any later version. The license should have
10//C- accompanied the software or you may obtain a copy of the license
11//C- from the Free Software Foundation at http://www.fsf.org .
12//C-
13//C- This program is distributed in the hope that it will be useful,
14//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
15//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16//C- GNU General Public License for more details.
17//C-
18//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
19//C- Lizardtech Software.  Lizardtech Software has authorized us to
20//C- replace the original DjVu(r) Reference Library notice by the following
21//C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
22//C-
23//C-  ------------------------------------------------------------------
24//C- | DjVu (r) Reference Library (v. 3.5)
25//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
26//C- | The DjVu Reference Library is protected by U.S. Pat. No.
27//C- | 6,058,214 and patents pending.
28//C- |
29//C- | This software is subject to, and may be distributed under, the
30//C- | GNU General Public License, either Version 2 of the license,
31//C- | or (at your option) any later version. The license should have
32//C- | accompanied the software or you may obtain a copy of the license
33//C- | from the Free Software Foundation at http://www.fsf.org .
34//C- |
35//C- | The computer code originally released by LizardTech under this
36//C- | license and unmodified by other parties is deemed "the LIZARDTECH
37//C- | ORIGINAL CODE."  Subject to any third party intellectual property
38//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
39//C- | non-exclusive license to make, use, sell, or otherwise dispose of
40//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
41//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
42//C- | General Public License.   This grant only confers the right to
43//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
44//C- | the extent such infringement is reasonably necessary to enable
45//C- | recipient to make, have made, practice, sell, or otherwise dispose
46//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
47//C- | any greater extent that may be necessary to utilize further
48//C- | modifications or combinations.
49//C- |
50//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
51//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
52//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
53//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
54//C- +------------------------------------------------------------------
55//
56// $Id: GURL.cpp,v 1.24 2007/03/25 20:48:32 leonb Exp $
57// $Name: release_3_5_19 $
58
59#ifdef HAVE_CONFIG_H
60# include "config.h"
61#endif
62#if NEED_GNUG_PRAGMAS
63# pragma implementation
64#endif
65
66// From: Leon Bottou, 1/31/2002
67// This has been heavily changed by Lizardtech.
68// They decided to use URLs for everyting, including
69// the most basic file access.  The URL class now is a unholy
70// mixture of code for syntactically parsing the urls (which it was)
71// and file status code (only for local file: urls).
72
73#include "GException.h"
74#include "GOS.h"
75#include "GURL.h"
76#include "debug.h"
77
78#include <stdlib.h>
79#include <stdio.h>
80#include <ctype.h>
81#include <math.h>
82#include <string.h>
83
84#ifdef WIN32
85# include <atlbase.h>
86# include <windows.h>
87# include <direct.h>
88#endif /* WIN32 */
89
90// -- MAXPATHLEN
91#ifndef MAXPATHLEN
92# ifdef _MAX_PATH
93#  define MAXPATHLEN _MAX_PATH
94# else
95#  define MAXPATHLEN 1024
96# endif
97#else
98# if ( MAXPATHLEN < 1024 )
99#  undef MAXPATHLEN
100#  define MAXPATHLEN 1024
101# endif
102#endif
103
104#if defined(UNIX) || defined(OS2)
105# include <unistd.h>
106# include <sys/types.h>
107# include <sys/stat.h>
108# include <errno.h>
109# include <fcntl.h>
110# include <pwd.h>
111# include <stdio.h>
112# ifdef AUTOCONF
113#  ifdef TIME_WITH_SYS_TIME
114#   include <sys/time.h>
115#   include <time.h>
116#  else
117#   ifdef HAVE_SYS_TIME_H
118#    include <sys/time.h>
119#   else
120#    include <time.h>
121#   endif
122#  endif
123#  ifdef HAVE_DIRENT_H
124#   include <dirent.h>
125#   define NAMLEN(dirent) strlen((dirent)->d_name)
126#  else
127#   define dirent direct
128#   define NAMLEN(dirent) (dirent)->d_namlen
129#   ifdef HAVE_SYS_NDIR_H
130#    include <sys/ndir.h>
131#   endif
132#   ifdef HAVE_SYS_DIR_H
133#    include <sys/dir.h>
134#   endif
135#   ifdef HAVE_NDIR_H
136#    include <ndir.h>
137#   endif
138#  endif
139# else /* !AUTOCONF */ 
140#  include <sys/time.h>
141#  if defined(XENIX)
142#   define USE_DIRECT
143#   include <sys/ndir.h>
144#  elif defined(OLDBSD)
145#   define USE_DIRECT
146#   include <sys/dir.h>
147#  endif
148#  ifdef USE_DIRECT
149#   define dirent direct
150#   define NAMLEN(dirent) (dirent)->d_namlen
151#  else
152#   include <dirent.h>
153#   define NAMLEN(dirent) strlen((dirent)->d_name)
154#  endif
155# endif /* !AUTOCONF */
156#endif /* UNIX */
157
158#ifdef macintosh
159#include <unix.h>
160#include <errno.h>
161#include <unistd.h>
162#endif
163
164#ifdef OS2
165#define _tcsrchr strrchr
166#endif
167
168#ifdef HAVE_NAMESPACES
169namespace DJVU {
170# ifdef NOT_DEFINED // Just to fool emacs c++ mode
171}
172#endif
173#endif
174
175
176static const char djvuopts[]="DJVUOPTS";
177static const char localhost[]="file://localhost/";
178static const char backslash='\\'; 
179static const char colon=':';
180static const char dot='.';
181static const char filespecslashes[] = "file://";
182static const char filespec[] = "file:";
183static const char slash='/';
184static const char percent='%';
185static const char localhostspec1[] = "//localhost/";
186static const char localhostspec2[] = "///";
187static const char nillchar=0;
188#if defined(UNIX)
189  static const char tilde='~';
190  static const char root[] = "/";
191#elif defined(WIN32) || defined(OS2)
192  static const char root[] = "\\";
193#elif defined(macintosh)
194  static char const * const root = &nillchar; 
195#else
196#error "Define something here for your operating system"
197#endif
198
199
200static const int
201pathname_start(const GUTF8String &url, const int protolength);
202
203// hexval --
204// -- Returns the hexvalue of a character.
205//    Returns -1 if illegal;
206
207static int 
208hexval(char c)
209{
210  return ((c>='0' && c<='9')
211    ?(c-'0')
212    :((c>='A' && c<='F')
213      ?(c-'A'+10)
214      :((c>='a' && c<='f')
215        ?(c-'a'+10):(-1))));
216}
217
218
219static bool
220is_argument(const char * start)
221      // Returns TRUE if 'start' points to the beginning of an argument
222      // (either hash or CGI)
223{
224   // return (*start=='#' || *start=='?' || *start=='&' || *start==';');
225   return (*start=='#' || *start=='?' );
226}
227
228static bool
229is_argument_sep(const char * start)
230      // Returns TRUE if 'start' points to the beginning of an argument
231      // (either hash or CGI)
232{
233   return (*start=='&')||(*start == ';');
234}
235
236void
237GURL::convert_slashes(void)
238{
239   GUTF8String xurl(get_string());
240#if defined(WIN32)
241   const int protocol_length=protocol(xurl).length();
242   for(char *ptr=(xurl.getbuf()+protocol_length);*ptr;ptr++)
243     if(*ptr == backslash)
244       *ptr=slash;
245   url=xurl;
246#endif
247}
248
249static void
250collapse(char * ptr, const int chars)
251      // Will remove the first 'chars' chars from the string and
252      // move the rest toward the beginning. Will take into account
253      // string length
254{
255   const int length=strlen(ptr);
256   const char *srcptr=ptr+((chars>length)?length:chars);
257   while((*(ptr++) = *(srcptr++)))
258     EMPTY_LOOP;
259}
260
261GUTF8String
262GURL::beautify_path(GUTF8String xurl)
263{
264
265  const int protocol_length=GURL::protocol(xurl).length();
266   
267  // Eats parts like ./ or ../ or ///
268  char * buffer;
269  GPBuffer<char> gbuffer(buffer,xurl.length()+1);
270  strcpy(buffer, (const char *)xurl);
271   
272  // Find start point
273  char * start=buffer+pathname_start(xurl,protocol_length);
274
275  // Find end of the url (don't touch arguments)
276  char * ptr;
277  GUTF8String args;
278  for(ptr=start;*ptr;ptr++)
279  {
280    if (is_argument(ptr))
281    {
282      args=ptr;
283      *ptr=0;
284      break;
285    }
286  }
287
288  // Eat multiple slashes
289  for(;(ptr=strstr(start, "////"));collapse(ptr, 3))
290    EMPTY_LOOP;
291  for(;(ptr=strstr(start, "//"));collapse(ptr, 1))
292    EMPTY_LOOP;
293  // Convert /./ stuff into plain /
294  for(;(ptr=strstr(start, "/./"));collapse(ptr, 2))
295    EMPTY_LOOP;
296#if defined(WIN32) || defined(OS2)
297  if(!xurl.cmp(filespec,sizeof(filespec)-1))
298  {
299        int offset=1;
300        if(start&&(start[0] == '/')&& 
301           !xurl.cmp("file:////",sizeof("file:////")-1))
302        {
303          collapse(start, 1);
304          offset=0;
305        }
306    for(ptr=start+offset;(ptr=strchr(ptr, '/'));)
307        {
308          if(isalpha((++ptr)[0]))
309          {
310            if((ptr[1] == ':')&&(ptr[2]=='/'))
311                {
312                  char *buffer2;
313                  GPBuffer<char> gbuffer2(buffer2,strlen(ptr)+1);
314                  strcpy(buffer2,ptr);
315                  gbuffer.resize(strlen(ptr)+sizeof(localhost));
316                  strcpy(buffer,localhost);
317                  strcat(buffer,buffer2);
318                  ptr=(start=buffer+sizeof(localhost))+1;
319                }
320          }
321        }
322  }
323#endif
324  // Process /../
325  while((ptr=strstr(start, "/../")))
326  {
327    for(char * ptr1=ptr-1;(ptr1>=start);ptr1--)
328    {
329      if (*ptr1==slash)
330      {
331        collapse(ptr1, ptr-ptr1+3);
332        break;
333      }
334    }
335  }
336
337  // Remove trailing /.
338  ptr=start+strlen(start)-2;
339  if((ptr>=start)&& (ptr == GUTF8String("/.")))
340  {
341    ptr[1]=0;
342  }
343  // Eat trailing /..
344  ptr=start+strlen(start)-3;
345  if((ptr >= start) && (ptr == GUTF8String("/..")))
346  {
347    for(char * ptr1=ptr-1;(ptr1>=start);ptr1--)
348    {
349      if (*ptr1==slash)
350      {
351        ptr1[1]=0;
352        break;
353      }
354    }
355  }
356
357  // Done. Copy the buffer back into the URL and add arguments.
358  xurl=buffer;
359  return (xurl+args);
360}
361
362
363void
364GURL::beautify_path(void)
365{
366  url=beautify_path(get_string());
367}
368
369void
370GURL::init(const bool nothrow)
371{
372   GCriticalSectionLock lock(&class_lock);
373   validurl=true;
374   
375   if (url.length())
376   {
377      GUTF8String proto=protocol();
378      if (proto.length()<2)
379      {
380        validurl=false;
381        if(!nothrow)
382          G_THROW( ERR_MSG("GURL.no_protocol") "\t"+url);
383        return;
384      }
385
386         // Below we have to make this complex test to detect URLs really
387         // referring to *local* files. Surprisingly, file://hostname/dir/file
388         // is also valid, but shouldn't be treated thru local FS.
389      if (proto=="file" && url[5]==slash &&
390          (url[6]!=slash || !url.cmp(localhost, sizeof(localhost))))
391      {
392            // Separate the arguments
393         GUTF8String arg;
394         {
395           const char * const url_ptr=url;
396           const char * ptr;
397           for(ptr=url_ptr;*ptr&&!is_argument(ptr);ptr++)
398                        EMPTY_LOOP;
399           arg=ptr;
400           url=url.substr(0,(size_t)(ptr-url_ptr));
401         }
402
403            // Do double conversion
404         GUTF8String tmp=UTF8Filename();
405         if (!tmp.length())
406         {
407           validurl=false;
408           if(!nothrow)
409             G_THROW( ERR_MSG("GURL.fail_to_file") );
410           return;
411         }
412         url=GURL::Filename::UTF8(tmp).get_string();
413         if (!url.length())
414         {
415           validurl=false;
416           if(!nothrow)
417             G_THROW( ERR_MSG("GURL.fail_to_URL") );
418           return;
419         }
420            // Return the argument back
421         url+=arg;
422      }
423      convert_slashes();
424      beautify_path();
425      parse_cgi_args();
426   }
427}
428
429GURL::GURL(void) 
430  : validurl(false) 
431{
432}
433
434GURL::GURL(const char * url_in) 
435  : url(url_in ? url_in : ""), validurl(false)
436{
437}
438
439GURL::GURL(const GUTF8String & url_in)
440  : url(url_in), validurl(false)
441{
442}
443
444GURL::GURL(const GNativeString & url_in)
445  : url(url_in.getNative2UTF8()), validurl(false)
446{
447#if defined(WIN32) || defined(OS2)
448  if(is_valid() && is_local_file_url())
449  {
450    GURL::Filename::UTF8 xurl(UTF8Filename());
451    url=xurl.get_string(true);
452    validurl=false;
453  }
454#endif
455}
456
457GURL::GURL(const GURL & url_in)
458  : validurl(false)
459{
460  if(url_in.is_valid())
461  {
462    url=url_in.get_string();
463    init();
464  }else
465  {
466    url=url_in.url;
467  }
468}
469
470GURL &
471GURL::operator=(const GURL & url_in)
472{
473   GCriticalSectionLock lock(&class_lock);
474   if(url_in.is_valid())
475   {
476     url=url_in.get_string();
477     init(true);
478   }else
479   {
480     url=url_in.url;
481     validurl=false;
482   }
483   return *this;
484}
485
486GUTF8String
487GURL::protocol(const GUTF8String& url)
488{
489   const char * const url_ptr=url;
490   const char * ptr=url_ptr;
491   for(char c=*ptr;
492     c && (isalnum(c) || c == '+' || c == '-' || c == '.');
493     c=*(++ptr)) EMPTY_LOOP;
494   return(*ptr==colon)?GUTF8String(url_ptr, ptr-url_ptr):GUTF8String();
495}
496
497GUTF8String
498GURL::hash_argument(void) const
499      // Returns the HASH argument (anything after '#' and before '?')
500{
501   const GUTF8String xurl(get_string());
502
503   bool found=false;
504   GUTF8String arg;
505
506         // Break if CGI argument is found
507   for(const char * start=xurl;*start&&(*start!='?');start++)
508   {
509      if (found)
510      {
511         arg+=*start;
512      }else
513      {
514         found=(*start=='#');
515      }
516   }
517   return decode_reserved(arg);
518}
519
520void
521GURL::set_hash_argument(const GUTF8String &arg)
522{
523   const GUTF8String xurl(get_string());
524
525   GUTF8String new_url;
526   bool found=false;
527   const char * ptr;
528   for(ptr=xurl;*ptr;ptr++)
529   {
530      if (is_argument(ptr))
531      {
532         if (*ptr!='#')
533         {
534           break;
535         }
536         found=true;
537      } else if (!found)
538      {
539         new_url+=*ptr;
540      }
541   }
542
543   url=new_url+"#"+GURL::encode_reserved(arg)+ptr;
544}
545
546void
547GURL::parse_cgi_args(void)
548      // Will read CGI arguments from the URL into
549      // cgi_name_arr and cgi_value_arr
550{
551   if(!validurl)
552     init();
553   GCriticalSectionLock lock1(&class_lock);
554   cgi_name_arr.empty();
555   cgi_value_arr.empty();
556
557      // Search for the beginning of CGI arguments
558   const char * start=url;
559   while(*start)
560   {
561     if(*(start++)=='?')
562     {
563       break;
564     }
565   }
566
567      // Now loop until we see all of them
568   while(*start)
569   {
570      GUTF8String arg;        // Storage for another argument
571      while(*start)        // Seek for the end of it
572      {
573         if (is_argument_sep(start))
574         {
575            start++;
576            break;
577         } else
578         {
579           arg+=*start++;
580         }
581      }
582      if (arg.length())
583      {
584            // Got argument in 'arg'. Split it into 'name' and 'value'
585         const char * ptr;
586         const char * const arg_ptr=arg;
587         for(ptr=arg_ptr;*ptr&&(*ptr != '=');ptr++)
588           EMPTY_LOOP;
589
590         GUTF8String name, value;
591         if (*ptr)
592         {
593            name=GUTF8String(arg_ptr, (int)((ptr++)-arg_ptr));
594            value=GUTF8String(ptr, arg.length()-name.length()-1);
595         } else
596         {
597           name=arg;
598         }
599           
600         int args=cgi_name_arr.size();
601         cgi_name_arr.resize(args);
602         cgi_value_arr.resize(args);
603         cgi_name_arr[args]=decode_reserved(name);
604         cgi_value_arr[args]=decode_reserved(value);
605      }
606   }
607}
608
609void
610GURL::store_cgi_args(void)
611      // Will store CGI arguments from the cgi_name_arr and cgi_value_arr
612      // back into the URL
613{
614   if(!validurl)
615     init();
616   GCriticalSectionLock lock1(&class_lock);
617
618   const char * const url_ptr=url;
619   const char * ptr;
620   for(ptr=url_ptr;*ptr&&(*ptr!='?');ptr++)
621                EMPTY_LOOP;
622   
623   GUTF8String new_url(url_ptr, ptr-url_ptr);
624   
625   for(int i=0;i<cgi_name_arr.size();i++)
626   {
627      GUTF8String name=GURL::encode_reserved(cgi_name_arr[i]);
628      GUTF8String value=GURL::encode_reserved(cgi_value_arr[i]);
629      new_url+=(i?"&":"?")+name;
630      if (value.length())
631         new_url+="="+value;
632   }
633
634   url=new_url;
635}
636
637int
638GURL::cgi_arguments(void) const
639{
640   if(!validurl)
641      const_cast<GURL *>(this)->init();
642   return cgi_name_arr.size();
643}
644
645int
646GURL::djvu_cgi_arguments(void) const
647{
648   if(!validurl)
649     const_cast<GURL *>(this)->init();
650   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
651
652   int args=0;
653   for(int i=0;i<cgi_name_arr.size();i++)
654   {
655      if (cgi_name_arr[i].upcase()==djvuopts)
656      {
657         args=cgi_name_arr.size()-(i+1);
658         break;
659      }
660   } 
661   return args;
662}
663
664GUTF8String
665GURL::cgi_name(int num) const
666{
667   if(!validurl) const_cast<GURL *>(this)->init();
668   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
669   return (num<cgi_name_arr.size())?cgi_name_arr[num]:GUTF8String();
670}
671
672GUTF8String
673GURL::djvu_cgi_name(int num) const
674{
675   if(!validurl) const_cast<GURL *>(this)->init();
676   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
677
678   GUTF8String arg;
679   for(int i=0;i<cgi_name_arr.size();i++)
680      if (cgi_name_arr[i].upcase()==djvuopts)
681      {
682         for(i++;i<cgi_name_arr.size();i++)
683            if (! num--)
684            {
685               arg=cgi_name_arr[i];
686               break;
687            }
688         break;
689      }
690   return arg;
691}
692
693GUTF8String
694GURL::cgi_value(int num) const
695{
696   if(!validurl) const_cast<GURL *>(this)->init();
697   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
698   return (num<cgi_value_arr.size())?cgi_value_arr[num]:GUTF8String();
699}
700
701GUTF8String
702GURL::djvu_cgi_value(int num) const
703{
704   if(!validurl) const_cast<GURL *>(this)->init();
705   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
706
707   GUTF8String arg;
708   for(int i=0;i<cgi_name_arr.size();i++)
709   {
710      if (cgi_name_arr[i].upcase()==djvuopts)
711      {
712         for(i++;i<cgi_name_arr.size();i++)
713         {
714            if (! num--)
715            {
716               arg=cgi_value_arr[i];
717               break;
718            }
719         }
720         break;
721      }
722   }
723   return arg;
724}
725
726DArray<GUTF8String>
727GURL::cgi_names(void) const
728{
729   if(!validurl) const_cast<GURL *>(this)->init();
730   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
731   return cgi_name_arr;
732}
733
734DArray<GUTF8String>
735GURL::cgi_values(void) const
736{
737   if(!validurl) const_cast<GURL *>(this)->init();
738   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
739   return cgi_value_arr;
740}
741
742DArray<GUTF8String>
743GURL::djvu_cgi_names(void) const
744{
745   if(!validurl) const_cast<GURL *>(this)->init();
746   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
747
748   int i;
749   DArray<GUTF8String> arr;
750   for(i=0;(i<cgi_name_arr.size())&&
751     (cgi_name_arr[i].upcase()!=djvuopts)
752     ;i++)
753        EMPTY_LOOP;
754
755   int size=cgi_name_arr.size()-(i+1);
756   if (size>0)
757   {
758      arr.resize(size-1);
759      for(i=0;i<arr.size();i++)
760         arr[i]=cgi_name_arr[cgi_name_arr.size()-arr.size()+i];
761   }
762
763   return arr;
764}
765
766DArray<GUTF8String>
767GURL::djvu_cgi_values(void) const
768{
769   if(!validurl) const_cast<GURL *>(this)->init();
770   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
771
772   int i;
773   DArray<GUTF8String> arr;
774   for(i=0;i<cgi_name_arr.size()&&(cgi_name_arr[i].upcase()!=djvuopts);i++)
775                EMPTY_LOOP;
776
777   int size=cgi_name_arr.size()-(i+1);
778   if (size>0)
779   {
780      arr.resize(size-1);
781      for(i=0;i<arr.size();i++)
782         arr[i]=cgi_value_arr[cgi_value_arr.size()-arr.size()+i];
783   }
784
785   return arr;
786}
787
788void
789GURL::clear_all_arguments(void)
790{
791   clear_hash_argument();
792   clear_cgi_arguments();
793}
794
795void
796GURL::clear_hash_argument(void)
797      // Clear anything after first '#' and before the following '?'
798{
799   if(!validurl) init();
800   GCriticalSectionLock lock(&class_lock);
801   bool found=false;
802   GUTF8String new_url;
803   for(const char * start=url;*start;start++)
804   {
805         // Break on first CGI arg.
806      if (*start=='?')
807      {
808         new_url+=start;
809         break;
810      }
811
812      if (!found)
813      { 
814        if (*start=='#')
815          found=true;
816        else
817          new_url+=*start;
818      }
819   }
820   url=new_url;
821}
822
823void
824GURL::clear_cgi_arguments(void)
825{
826   if(!validurl)
827     init();
828   GCriticalSectionLock lock1(&class_lock);
829
830      // Clear the arrays
831   cgi_name_arr.empty();
832   cgi_value_arr.empty();
833
834      // And clear everything past the '?' sign in the URL
835   const char * ptrurl = url;
836   for(const char *ptr = ptrurl; *ptr; ptr++)
837     if (*ptr=='?')
838       {
839         url.setat(ptr-ptrurl, 0);
840         break;
841       }
842}
843
844void
845GURL::clear_djvu_cgi_arguments(void)
846{
847   if(!validurl) init();
848      // First - modify the arrays
849   GCriticalSectionLock lock(&class_lock);
850   for(int i=0;i<cgi_name_arr.size();i++)
851   {
852      if (cgi_name_arr[i].upcase()==djvuopts)
853      {
854         cgi_name_arr.resize(i-1);
855         cgi_value_arr.resize(i-1);
856         break;
857      }
858   }
859
860      // And store them back into the URL
861   store_cgi_args();
862}
863
864void
865GURL::add_djvu_cgi_argument(const GUTF8String &name, const char * value)
866{
867   if(!validurl)
868     init();
869   GCriticalSectionLock lock1(&class_lock);
870
871      // Check if we already have the "DJVUOPTS" argument
872   bool have_djvuopts=false;
873   for(int i=0;i<cgi_name_arr.size();i++)
874   {
875      if (cgi_name_arr[i].upcase()==djvuopts)
876      {
877         have_djvuopts=true;
878         break;
879      }
880   }
881
882      // If there is no DJVUOPTS, insert it
883   if (!have_djvuopts)
884   {
885      int pos=cgi_name_arr.size();
886      cgi_name_arr.resize(pos);
887      cgi_value_arr.resize(pos);
888      cgi_name_arr[pos]=djvuopts;
889   }
890
891      // Add new argument to the array
892   int pos=cgi_name_arr.size();
893   cgi_name_arr.resize(pos);
894   cgi_value_arr.resize(pos);
895   cgi_name_arr[pos]=name;
896   cgi_value_arr[pos]=value;
897
898      // And update the URL
899   store_cgi_args();
900}
901
902bool
903GURL::is_local_file_url(void) const
904{
905   if(!validurl) const_cast<GURL *>(this)->init();
906   GCriticalSectionLock lock((GCriticalSection *) &class_lock);
907   return (protocol()=="file" && url[5]==slash);
908}
909
910static const int
911pathname_start(const GUTF8String &url, const int protolength)
912{
913  const int length=url.length();
914  int retval=0;
915  if(protolength+1<length)
916  {
917    retval=url.search(slash,((url[protolength+1] == '/')
918      ?((url[protolength+2] == '/')?(protolength+3):(protolength+2))
919      :(protolength+1)));
920  }
921  return (retval>0)?retval:length;
922}
923
924GUTF8String
925GURL::pathname(void) const
926{
927  return (is_local_file_url())
928    ?GURL::encode_reserved(UTF8Filename()) 
929    :url.substr(pathname_start(url,protocol().length()),(unsigned int)(-1));
930}
931
932GURL
933GURL::base(void) const
934{
935   const GUTF8String xurl(get_string());
936   const int protocol_length=protocol(xurl).length();
937   const char * const url_ptr=xurl;
938   const char * ptr, * xslash;
939   ptr=xslash=url_ptr+protocol_length+1;
940   if(xslash[0] == '/')
941   {
942     xslash++;
943     if(xslash[0] == '/')
944       xslash++;
945     for(ptr=xslash;ptr[0] && !is_argument(ptr);ptr++)
946     {
947       if ((ptr[0]==slash)&&ptr[1]&&!is_argument(ptr+1))
948        xslash=ptr;
949     }
950     if(xslash[0] != '/')
951     {
952       xslash=ptr;
953     }
954   }
955   return GURL::UTF8(GUTF8String(xurl,(int)(xslash-url_ptr))+"/"+ptr);
956}
957
958bool
959GURL::operator==(const GURL & gurl2) const
960{
961  const GUTF8String g1(get_string());
962  const GUTF8String g2(gurl2.get_string());
963  const char *s1 = (const char*)g1;
964  const char *s2 = (const char*)g2;
965  int n1=0;
966  int n2=0;
967  while (s1[n1] && !is_argument(s1+n1))
968    n1 += 1;
969  while (s2[n2] && !is_argument(s2+n2))
970    n2 += 1;
971  if (n1 == n2)
972    return !strcmp(s1+n1,s2+n2) && !strncmp(s1,s2,n1);
973  if (n1 == n2+1 && s1[n2]=='/')
974    return !strcmp(s1+n1,s2+n2) && !strncmp(s1,s2,n2);
975  if (n2 == n1+1 && s2[n1]=='/')
976    return !strcmp(s1+n1,s2+n2) && !strncmp(s1,s2,n1);   
977  return false;
978}
979
980GUTF8String
981GURL::name(void) const
982{
983   if(!validurl)
984     const_cast<GURL *>(this)->init();
985   GUTF8String retval;
986   if(!is_empty())
987   {
988     const GUTF8String xurl(url);
989     const int protocol_length=protocol(xurl).length();
990     const char * ptr, * xslash=(const char *)xurl+protocol_length-1;
991     for(ptr=(const char *)xurl+protocol_length;
992       *ptr && !is_argument(ptr);ptr++)
993         {
994       if (*ptr==slash)
995          xslash=ptr;
996         }
997     retval=GUTF8String(xslash+1, ptr-xslash-1);
998   }
999   return retval;
1000}
1001
1002GUTF8String
1003GURL::fname(void) const
1004{
1005   if(!validurl)
1006     const_cast<GURL *>(this)->init();
1007   return decode_reserved(name());
1008}
1009
1010GUTF8String
1011GURL::extension(void) const
1012{
1013   if(!validurl)
1014     const_cast<GURL *>(this)->init();
1015   GUTF8String xfilename=name();
1016   GUTF8String retval;
1017
1018   for(int i=xfilename.length()-1;i>=0;i--)
1019   {
1020      if (xfilename[i]=='.')
1021      {
1022         retval=(const char*)xfilename+i+1;
1023         break;
1024      }
1025   } 
1026   return retval;
1027}
1028
1029GUTF8String
1030GURL::decode_reserved(const GUTF8String &gurl)
1031{
1032  const char *url=gurl;
1033  char *res;
1034  GPBuffer<char> gres(res,gurl.length()+1);
1035  char *r=res;
1036  for(const char * ptr=url;*ptr;++ptr,++r)
1037  {
1038    if (*ptr!=percent)
1039    {
1040      r[0]=*ptr;
1041    }else
1042    {
1043      int c1,c2;
1044      if ( ((c1=hexval(ptr[1]))>=0)
1045        && ((c2=hexval(ptr[2]))>=0) )
1046      {
1047        r[0]=(c1<<4)|c2;
1048        ptr+=2;
1049      } else
1050      {
1051        r[0]=*ptr;
1052      }
1053    }
1054  }
1055  r[0]=0;
1056  GUTF8String retval(res);
1057  if(!retval.is_valid())
1058  {
1059    retval=GNativeString(res);
1060  }
1061  return retval;
1062}
1063
1064GUTF8String
1065GURL::encode_reserved(const GUTF8String &gs)
1066{
1067  const char *s=(const char *)gs;
1068  // Potentially unsafe characters (cf. RFC1738 and RFC1808)
1069  static const char hex[] = "0123456789ABCDEF";
1070 
1071  unsigned char *retval;
1072  GPBuffer<unsigned char> gd(retval,strlen(s)*3+1);
1073  unsigned char *d=retval;
1074  for (; *s; s++,d++)
1075  {
1076    // Convert directory separator to slashes
1077#if defined(WIN32) || defined(OS2)
1078    if (*s == backslash || *s== slash)
1079#else
1080#ifdef macintosh
1081    if (*s == colon )
1082#else
1083#ifdef UNIX
1084    if (*s == slash )
1085#else
1086#error "Define something here for your operating system"
1087#endif 
1088#endif
1089#endif
1090    {
1091      *d = slash; 
1092      continue;
1093    }
1094    unsigned char const ss=(unsigned char const)(*s);
1095    // WARNING: Whenever you modify this conversion code,
1096    // make sure, that the following functions are in sync:
1097    //   encode_reserved()
1098    //   decode_reserved()
1099    //   url_to_filename()
1100    //   filename_to_url()
1101    // unreserved characters
1102    if ( (ss>='a' && ss<='z') ||
1103         (ss>='A' && ss<='Z') ||
1104         (ss>='0' && ss<='9') ||
1105         (strchr("$-_.+!*'(),:~=", ss)) ) 
1106    {
1107      *d = ss;
1108      continue;
1109    }
1110    // escape sequence
1111    d[0] = percent;
1112    d[1] = hex[ (ss >> 4) & 0xf ];
1113    d[2] = hex[ (ss) & 0xf ];
1114    d+=2;
1115  }
1116  *d = 0;
1117  return retval;
1118}
1119
1120// -------------------------------------------
1121// Functions for converting filenames and urls
1122// -------------------------------------------
1123
1124static GUTF8String
1125url_from_UTF8filename(const GUTF8String &gfilename)
1126{
1127  if(GURL::UTF8(gfilename).is_valid())
1128  {
1129    DEBUG_MSG("Debug: URL as Filename: " << gfilename << "\n");
1130  } 
1131  const char *filename=gfilename;
1132  if(filename && (unsigned char)filename[0] == (unsigned char)0xEF
1133     && (unsigned char)filename[1] == (unsigned char)0xBB 
1134     && (unsigned char)filename[2] == (unsigned char)0xBF)
1135  {
1136    filename+=3;
1137  }
1138
1139  // Special case for blank pages
1140  if(!filename || !filename[0])
1141  {
1142    return GUTF8String();
1143  } 
1144
1145  // Normalize file name to url slash-and-escape syntax
1146  GUTF8String oname=GURL::expand_name(filename);
1147  GUTF8String nname=GURL::encode_reserved(oname);
1148
1149  // Preprend "file://" to file name. If file is on the local
1150  // machine, include "localhost".
1151  GUTF8String url=filespecslashes;
1152  const char *cnname=nname;
1153  if (cnname[0] == slash)
1154  {
1155    if (cnname[1] == slash)
1156    {
1157      url += cnname+2;
1158    }else
1159    {
1160      url = localhost + nname;
1161    }
1162  }else
1163  {
1164    url += (localhostspec1+2) + nname;
1165  }
1166  return url;
1167}
1168
1169GUTF8String
1170GURL::get_string(const bool nothrow) const
1171{
1172  if(!validurl)
1173    const_cast<GURL *>(this)->init(nothrow);
1174  return url;
1175}
1176
1177// -- Returns a url for accessing a given file.
1178//    If useragent is not provided, standard url will be created,
1179//    but will not be understood by some versions if IE.
1180GUTF8String
1181GURL::get_string(const GUTF8String &useragent) const
1182{
1183  if(!validurl)
1184    const_cast<GURL *>(this)->init();
1185  GUTF8String retval(url);
1186  if(is_local_file_url()&&useragent.length())
1187  {
1188    if(useragent.search("MSIE") >= 0 || useragent.search("Microsoft")>=0)
1189    {
1190      retval=filespecslashes + expand_name(UTF8Filename());
1191    }
1192  }
1193  return retval;
1194}
1195
1196GURL::UTF8::UTF8(const GUTF8String &xurl)
1197: GURL(xurl) {}
1198
1199GURL::UTF8::UTF8(const GUTF8String &xurl,const GURL &codebase)
1200: GURL(xurl,codebase) {}
1201
1202GURL::GURL(const GUTF8String &xurl,const GURL &codebase)
1203  : validurl(false)
1204{
1205  if(GURL::UTF8(xurl).is_valid())
1206    {
1207      url=xurl;
1208    }
1209  else
1210    {
1211      // split codebase
1212      const char *buffer = codebase;
1213      GUTF8String all(buffer);
1214      GUTF8String suffix;
1215      GUTF8String path;
1216      GUTF8String prefix;
1217      const int protocol_length=GURL::protocol(all).length();
1218      const char *start = buffer + pathname_start(all,protocol_length);
1219      if (start > buffer)
1220        prefix = GUTF8String(buffer, start-buffer);
1221      const char *ptr = start;
1222      while (*ptr && !is_argument(ptr))
1223        ptr++;
1224      if (*ptr)
1225        suffix = GUTF8String(ptr);
1226      if (ptr > start)
1227        path = GUTF8String(start, ptr-start);
1228      // append xurl to path
1229      const char *c = xurl;
1230      if(c[0] == slash)
1231        path = GURL::encode_reserved(xurl);
1232      else
1233        path = path + GUTF8String(slash)+GURL::encode_reserved(xurl);
1234      // construct url
1235      url = beautify_path(prefix + path + suffix);
1236    }
1237}
1238
1239GURL::Native::Native(const GNativeString &xurl)
1240: GURL(xurl) {}
1241
1242GURL::Native::Native(const GNativeString &xurl,const GURL &codebase)
1243: GURL(xurl,codebase) {}
1244
1245GURL::GURL(const GNativeString &xurl,const GURL &codebase)
1246  : validurl(false)
1247{
1248  GURL retval(xurl.getNative2UTF8(),codebase);
1249  if(retval.is_valid())
1250  {
1251#if defined(WIN32)
1252    // Hack for IE to change \\ to /
1253    if(retval.is_local_file_url())
1254    {
1255      GURL::Filename::UTF8 retval2(retval.UTF8Filename());
1256      url=retval2.get_string(true);
1257      validurl=false;
1258    }else
1259#endif // WIN32
1260    {
1261      url=retval.get_string(true);
1262      validurl=false;
1263    }
1264  }
1265}
1266
1267GURL::Filename::Filename(const GNativeString &gfilename)
1268{
1269  url=url_from_UTF8filename(gfilename.getNative2UTF8());
1270}
1271
1272GURL::Filename::Native::Native(const GNativeString &gfilename)
1273: GURL::Filename(gfilename) {}
1274
1275GURL::Filename::Filename(const GUTF8String &gfilename)
1276{
1277  url=url_from_UTF8filename(gfilename);
1278}
1279
1280GURL::Filename::UTF8::UTF8(const GUTF8String &gfilename)
1281: GURL::Filename(gfilename) {}
1282
1283// filename --
1284// -- Applies heuristic rules to convert a url into a valid file name. 
1285//    Returns a simple basename in case of failure.
1286GUTF8String
1287GURL::UTF8Filename(void) const
1288{
1289  GUTF8String retval;
1290  if(! is_empty())
1291  {
1292    const char *url_ptr=url;
1293 
1294    // WARNING: Whenever you modify this conversion code,
1295    // make sure, that the following functions are in sync:
1296    //   encode_reserved()
1297    //   decode_reserved()
1298    //   url_to_filename()
1299    //   filename_to_url()
1300
1301    GUTF8String urlcopy=decode_reserved(url);
1302    url_ptr = urlcopy;
1303
1304    // All file urls are expected to start with filespec which is "file:"
1305    if (GStringRep::cmp(filespec, url_ptr, sizeof(filespec)-1))  //if not
1306      return GOS::basename(url_ptr);
1307    url_ptr += sizeof(filespec)-1;
1308 
1309#if defined(macintosh)
1310    //remove all leading slashes
1311    for(;*url_ptr==slash;url_ptr++)
1312      EMPTY_LOOP;
1313    // Remove possible localhost spec
1314    if ( !GStringRep::cmp(localhost, url_ptr, sizeof(localhost)-1) )
1315      url_ptr += sizeof(localhost)-1;
1316    //remove all leading slashes
1317    while(*url_ptr==slash)
1318      url_ptr++;
1319#else
1320    // Remove possible localhost spec
1321    if ( !GStringRep::cmp(localhostspec1, url_ptr, sizeof(localhostspec1)-1) )
1322      // RFC 1738 local host form
1323      url_ptr += sizeof(localhostspec1)-1;
1324    else if ( !GStringRep::cmp(localhostspec2, url_ptr, sizeof(localhostspec2)-1 ) )
1325      // RFC 1738 local host form
1326      url_ptr += sizeof(localhostspec2)-1;
1327    else if ( (strlen(url_ptr) > 4)   // "file://<letter>:/<path>"
1328        && (url_ptr[0] == slash)      // "file://<letter>|/<path>"
1329        && (url_ptr[1] == slash)
1330        && isalpha(url_ptr[2])
1331        && ( url_ptr[3] == colon || url_ptr[3] == '|' )
1332        && (url_ptr[4] == slash) )
1333      url_ptr += 2;
1334    else if ( (strlen(url_ptr)) > 2 // "file:/<path>"
1335        && (url_ptr[0] == slash)
1336        && (url_ptr[1] != slash) )
1337      url_ptr++;
1338#endif
1339
1340    // Check if we are finished
1341#if defined(macintosh)
1342    {
1343      char *l_url;
1344      GPBuffer<char> gl_url(l_url,strlen(url_ptr)+1);
1345      const char *s;
1346      char *r;
1347      for ( s=url_ptr,r=l_url; *s; s++,r++)
1348      {
1349        *r=(*s == slash)?colon:*s;
1350      }
1351      *r=0;
1352      retval = expand_name(l_url,root);
1353    }
1354#else 
1355    retval = expand_name(url_ptr,root);
1356#endif
1357   
1358#if defined(WIN32) || defined(OS2)
1359    if (url_ptr[0] && url_ptr[1]=='|' && url_ptr[2]== slash)
1360    {
1361      if ((url_ptr[0]>='a' && url_ptr[0]<='z') 
1362          || (url_ptr[0]>='A' && url_ptr[0]<='Z'))
1363      {
1364        GUTF8String drive;
1365        drive.format("%c%c%c", url_ptr[0],colon,backslash);
1366        retval = expand_name(url_ptr+3, drive);
1367      }
1368    }
1369#endif
1370  }
1371  // Return what we have
1372  return retval;
1373}
1374
1375GNativeString
1376GURL::NativeFilename(void) const
1377{
1378  return UTF8Filename().getUTF82Native();
1379}
1380
1381#if defined(UNIX) || defined(macintosh) || defined(OS2)
1382static int
1383urlstat(const GURL &url,struct stat &buf)
1384{
1385  return ::stat(url.NativeFilename(),&buf);
1386}
1387#endif
1388
1389// is_file(url) --
1390// -- returns true if filename denotes a regular file.
1391bool
1392GURL::is_file(void) const
1393{
1394  bool retval=false;
1395  if(is_local_file_url())
1396  {
1397#if defined(UNIX) || defined(macintosh) || defined(OS2)
1398    struct stat buf;
1399    if (!urlstat(*this,buf))
1400    {
1401      retval=!(buf.st_mode & S_IFDIR);
1402    }
1403#elif defined(WIN32)
1404    GUTF8String filename(UTF8Filename());
1405    if(filename.length() >= MAX_PATH)
1406      {
1407        if(!filename.cmp("\\\\",2))
1408          filename="\\\\?\\UNC"+filename.substr(1,-1);
1409        else
1410          filename="\\\\?\\"+filename;
1411      }
1412    wchar_t *wfilename;
1413    const size_t wfilename_size=filename.length()+1;
1414    GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size);
1415    filename.ncopy(wfilename,wfilename_size);
1416    DWORD dwAttrib;
1417    dwAttrib = GetFileAttributesW(wfilename);
1418    if((dwAttrib|1) == 0xFFFFFFFF)
1419      {
1420        USES_CONVERSION ;
1421        dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt
1422      }
1423    retval=!( dwAttrib & FILE_ATTRIBUTE_DIRECTORY );
1424#else
1425# error "Define something here for your operating system"
1426#endif
1427  }
1428  return retval;
1429}
1430
1431bool
1432GURL::is_local_path(void) const
1433{
1434  bool retval=false;
1435  if(is_local_file_url())
1436  {
1437#if defined(UNIX) || defined(macintosh) || defined(OS2)
1438    struct stat buf;
1439    retval=!urlstat(*this,buf);
1440#else
1441    GUTF8String filename(UTF8Filename());
1442    if(filename.length() >= MAX_PATH)
1443      {
1444        if(!filename.cmp("\\\\",2))
1445          filename="\\\\?\\UNC"+filename.substr(1,-1);
1446        else
1447          filename="\\\\?\\"+filename;
1448      }
1449    wchar_t *wfilename;
1450    const size_t wfilename_size=filename.length()+1;
1451    GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size);
1452    filename.ncopy(wfilename,wfilename_size);
1453    DWORD dwAttrib;
1454    dwAttrib = GetFileAttributesW(wfilename);
1455    if((dwAttrib|1) == 0xFFFFFFFF)
1456      {
1457        USES_CONVERSION ;
1458        dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt
1459      }
1460    retval=( (dwAttrib|1) != 0xFFFFFFFF);
1461#endif
1462  }
1463  return retval;
1464}
1465
1466// is_dir(url) --
1467// -- returns true if url denotes a directory.
1468bool 
1469GURL::is_dir(void) const
1470{
1471  bool retval=false;
1472  if(is_local_file_url())
1473  {
1474    // UNIX implementation
1475#if defined(UNIX) || defined(macintosh) || defined(OS2)
1476    struct stat buf;
1477    if (!urlstat(*this,buf))
1478    {
1479      retval=(buf.st_mode & S_IFDIR);
1480    }
1481#elif defined(WIN32)   // (either Windows or WCE)
1482    GUTF8String filename(UTF8Filename());
1483    if(filename.length() >= MAX_PATH)
1484      {
1485        if(!filename.cmp("\\\\",2))
1486          filename="\\\\?\\UNC"+filename.substr(1,-1);
1487        else
1488          filename="\\\\?\\"+filename;
1489      }
1490    wchar_t *wfilename;
1491    const size_t wfilename_size=filename.length()+1;
1492    GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size);
1493    filename.ncopy(wfilename,wfilename_size);
1494    DWORD dwAttrib;
1495    dwAttrib = GetFileAttributesW(wfilename);
1496    if((dwAttrib|1) == 0xFFFFFFFF)
1497      {
1498        USES_CONVERSION ;
1499        dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt
1500      }
1501    retval=((dwAttrib != 0xFFFFFFFF)&&( dwAttrib & FILE_ATTRIBUTE_DIRECTORY ));
1502#else
1503# error "Define something here for your operating system"
1504#endif
1505  }
1506  return retval;
1507}
1508
1509// Follows symbolic links.
1510GURL
1511GURL::follow_symlinks(void) const
1512{
1513  GURL ret = *this;
1514#if defined(S_IFLNK)
1515#if defined(UNIX) || defined(macintosh)
1516  int lnklen;
1517  char lnkbuf[MAXPATHLEN+1];
1518  struct stat buf;
1519  while ( (urlstat(ret, buf) >= 0) &&
1520          (buf.st_mode & S_IFLNK) &&
1521          ((lnklen = readlink(ret.NativeFilename(),lnkbuf,sizeof(lnkbuf))) > 0) )
1522    {
1523      lnkbuf[lnklen] = 0;
1524      GNativeString lnk(lnkbuf);
1525      ret = GURL(lnk, ret.base());
1526    }
1527#endif
1528#endif
1529  return ret;
1530}
1531
1532int
1533GURL::mkdir() const
1534{
1535  if(! is_local_file_url())
1536    return -1;
1537  int retval=0;
1538  const GURL baseURL=base();
1539  if (baseURL.get_string() != url && !baseURL.is_dir())
1540    retval = baseURL.mkdir();
1541  if(!retval)
1542    {
1543#if defined(UNIX) || defined(OS2)
1544      if (is_dir())
1545        retval = 0;
1546      else 
1547        retval = ::mkdir(NativeFilename(), 0755);
1548#elif defined(WIN32)
1549      USES_CONVERSION;
1550      if (is_dir())
1551        retval = 0;
1552      else 
1553        retval = CreateDirectory(A2CT(NativeFilename()), NULL);
1554#else
1555# error "Define something here for your operating system"
1556#endif
1557    }
1558  return retval;
1559}
1560
1561// deletefile
1562// -- deletes a file or directory
1563 
1564int
1565GURL::deletefile(void) const
1566{
1567  int retval = -1;
1568  if(is_local_file_url())
1569    {
1570#if defined(UNIX) || defined(OS2)
1571      if (is_dir())
1572        retval = ::rmdir(NativeFilename());
1573      else
1574        retval = ::unlink(NativeFilename());
1575#elif defined(WIN32)
1576      USES_CONVERSION;
1577      if (is_dir())
1578        retval = ::RemoveDirectory(A2CT(NativeFilename()));
1579      else
1580        retval = ::DeleteFile(A2CT(NativeFilename()));
1581#else
1582# error "Define something here for your operating system"
1583#endif
1584  }
1585  return retval;
1586}
1587
1588GList<GURL>
1589GURL::listdir(void) const
1590{
1591  GList<GURL> retval;
1592  if(is_dir())
1593  {
1594#if defined(UNIX) || defined(OS2)
1595    DIR * dir=opendir(NativeFilename());//MBCS cvt
1596    for(dirent *de=readdir(dir);de;de=readdir(dir))
1597    {
1598      const int len = NAMLEN(de);
1599      if (de->d_name[0]== dot  && len==1)
1600        continue;
1601      if (de->d_name[0]== dot  && de->d_name[1]== dot  && len==2)
1602        continue;
1603      retval.append(GURL::Native(de->d_name,*this));
1604    }
1605    closedir(dir);
1606#elif defined (WIN32)
1607    GURL::UTF8 wildcard("*.*",*this);
1608    WIN32_FIND_DATA finddata;
1609    HANDLE handle = FindFirstFile(wildcard.NativeFilename(), &finddata);//MBCS cvt
1610    const GUTF8String gpathname=pathname();
1611    const GUTF8String gbase=base().pathname();
1612    if( handle != INVALID_HANDLE_VALUE)
1613    {
1614      do
1615      {
1616        GURL::UTF8 Entry(finddata.cFileName,*this);
1617        const GUTF8String gentry=Entry.pathname();
1618        if((gentry != gpathname) && (gentry != gbase))
1619          retval.append(Entry);
1620      } while( FindNextFile(handle, &finddata) );
1621
1622      FindClose(handle);
1623    }
1624#else
1625# error "Define something here for your operating system"
1626#endif
1627  }
1628  return retval;
1629}
1630
1631int
1632GURL::cleardir(const int timeout) const
1633{
1634  int retval=(-1);
1635  if(is_dir())
1636  {
1637    GList<GURL> dirlist=listdir();
1638    retval=0;
1639    for(GPosition pos=dirlist;pos&&!retval;++pos)
1640    {
1641      const GURL &Entry=dirlist[pos];
1642      if(Entry.is_dir())
1643      {
1644        if((retval=Entry.cleardir(timeout)) < 0)
1645        {
1646          break;
1647        }
1648      }
1649      if(((retval=Entry.deletefile())<0) && (timeout>0))
1650      {
1651        GOS::sleep(timeout);
1652        retval=Entry.deletefile();
1653      }
1654    }
1655  }
1656  return retval;
1657}
1658
1659int
1660GURL::renameto(const GURL &newurl) const
1661{
1662  if (is_local_file_url() && newurl.is_local_file_url())
1663    return rename(NativeFilename(),newurl.NativeFilename());
1664  return -1;
1665}
1666
1667// expand_name(filename[, fromdirname])
1668// -- returns the full path name of filename interpreted
1669//    relative to fromdirname.  Use current working dir when
1670//    fromdirname is null.
1671GUTF8String
1672GURL::expand_name(const GUTF8String &xfname, const char *from)
1673{
1674  const char *fname=xfname;
1675  GUTF8String retval;
1676  const size_t maxlen=xfname.length()*9+MAXPATHLEN+10;
1677  char * const string_buffer = retval.getbuf(maxlen);
1678  // UNIX implementation
1679#if defined(UNIX)
1680  // Perform tilde expansion
1681  GUTF8String senv;
1682  if (fname && fname[0]==tilde)
1683  {
1684    int n;
1685    for(n=1;fname[n] && fname[n]!= slash;n++) 
1686      EMPTY_LOOP;
1687    struct passwd *pw=0;
1688    if (n!=1)
1689    {
1690      GUTF8String user(fname+1, n-1);
1691      pw=getpwnam(user);
1692    }else if ((senv=GOS::getenv("HOME")).length())
1693    {
1694      from=(const char *)senv;
1695      fname = fname + n;
1696    }else if ((senv=GOS::getenv("LOGNAME")).length())
1697    {
1698      pw = getpwnam((const char *)senv.getUTF82Native());
1699    }else
1700    {
1701      pw=getpwuid(getuid());
1702    }
1703    if (pw)
1704    {
1705      senv=GNativeString(pw->pw_dir).getNative2UTF8();
1706      from = (const char *)senv;
1707      fname = fname + n;
1708    }
1709    for(;fname[0] == slash; fname++)
1710      EMPTY_LOOP;
1711  }
1712  // Process absolute vs. relative path
1713  if (fname && fname[0]== slash)
1714  {
1715    string_buffer[0]=slash;
1716    string_buffer[1]=0;
1717  }else if (from)
1718  {
1719    strcpy(string_buffer, expand_name(from));
1720  }else
1721  {
1722    strcpy(string_buffer, GOS::cwd());
1723  }
1724  char *s = string_buffer + strlen(string_buffer);
1725  if(fname)
1726  {
1727    for(;fname[0]== slash;fname++)
1728      EMPTY_LOOP;
1729    // Process path components
1730    while(fname[0])
1731    {
1732      if (fname[0] == dot )
1733      {
1734        if (!fname[1] || fname[1]== slash)
1735        {
1736          fname++;
1737          continue;
1738        }else if (fname[1]== dot && (fname[2]== slash || !fname[2]))
1739        {
1740          fname +=2;
1741          for(;s>string_buffer+1 && *(s-1)== slash; s--)
1742            EMPTY_LOOP;
1743          for(;s>string_buffer+1 && *(s-1)!= slash; s--)
1744            EMPTY_LOOP;
1745          continue;
1746        }
1747      }
1748      if ((s==string_buffer)||(*(s-1)!= slash))
1749      {
1750        *s = slash;
1751        s++;
1752      }
1753      while (*fname &&(*fname!= slash))
1754      {
1755        *s = *fname++;
1756        if ((size_t)((++s)-string_buffer) > maxlen)
1757        {
1758          G_THROW( ERR_MSG("GURL.big_name") );
1759        }
1760      }
1761      *s = 0;
1762      for(;fname[0]== slash;fname++)
1763        EMPTY_LOOP;
1764    }
1765  }
1766  if (!fname || !fname[0])
1767  {
1768    for(;s>string_buffer+1 && *(s-1) == slash; s--)
1769      EMPTY_LOOP;
1770    *s = 0;
1771  }
1772#elif defined(WIN32) || defined(OS2) // WIN32 implementation (ER: and OS/2)
1773  // Handle base
1774  strcpy(string_buffer, (char const *)(from ? expand_name(from) : GOS::cwd()));
1775  //  GNativeString native;
1776  if (fname)
1777  {
1778    char *s = string_buffer;
1779    char  drv[4];
1780    // Handle absolute part of fname
1781    //      Put absolute part of the file name in string_buffer, and
1782    //      the relative part pointed to by fname.
1783    if (fname[0]== slash || fname[0]== backslash)
1784    {
1785      if (fname[1]== slash || fname[1]== backslash)
1786      {       // Case "//abcd"
1787        s[0]=s[1]= backslash; s[2]=0;
1788      }
1789      else
1790      {       // Case "/abcd" or "/"
1791              //    File is at the root of the current drive. Delete the
1792              //    slash at the beginning of the filename and leave
1793              //    an explicit identification of the root of the drive in
1794              //    string_buffer.
1795        fname++;
1796        s[3] = '\0';
1797      }
1798    }
1799    else if (fname[0] && fname[1]==colon)
1800    {
1801      if (fname[2]!= slash && fname[2]!= backslash)
1802      {       // Case "x:abcd"
1803        if ( toupper((unsigned char)s[0]) != toupper((unsigned char)fname[0])
1804          || s[1]!=colon)
1805        {
1806          drv[0]=fname[0];
1807          drv[1]=colon;
1808          drv[2]= dot ;
1809          drv[3]=0;
1810          // !!! ER todo !!! GetFullPathName(drv, maxlen, string_buffer, &s);
1811          strcpy(string_buffer,(const char *)GUTF8String(string_buffer).getNative2UTF8());
1812          s = string_buffer;
1813        }
1814        fname += 2;
1815      }
1816      else if (fname[3]!= slash && fname[3]!= backslash)
1817      {       // Case "x:/abcd"
1818        s[0]=toupper((unsigned char)fname[0]);
1819        s[1]=colon;
1820        s[2]=backslash;
1821        s[3]=0;
1822        fname += 3;
1823      }
1824      else
1825      {       // Case "x://abcd"
1826        s[0]=s[1]=backslash;
1827        s[2]=0;
1828        fname += 4;
1829      }
1830    }
1831    // Process path components
1832    for(;*fname== slash || *fname==backslash;fname++)
1833      EMPTY_LOOP;
1834    while(*fname)
1835    {
1836      if (fname[0]== dot )
1837      {
1838        if (fname[1]== slash || fname[1]==backslash || !fname[1])
1839        {
1840          fname++;
1841          continue;
1842        }else if ((fname[1] == dot)
1843          && (fname[2]== slash || fname[2]==backslash || !fname[2]))
1844        {
1845          fname += 2;
1846          char *back=_tcsrchr(string_buffer,backslash);
1847          char *forward=_tcsrchr(string_buffer,slash);
1848          if(back>forward)
1849          {
1850            *back=0;
1851          }else if(forward)
1852          {
1853            *forward=0;
1854          }
1855          s = string_buffer;
1856          continue;
1857        }
1858        char* s2=s;//MBCS DBCS
1859        for(;*s;s++) 
1860          EMPTY_LOOP;
1861        char* back = _tcsrchr(s2,backslash);//MBCS DBCS
1862        if ((s>string_buffer)&&(*(s-1)!= slash)&&
1863            (back == NULL || (back!=NULL && s-1 != back) ))//MBCS DBCS
1864        {
1865          *s = backslash;
1866          s++;
1867        }
1868        while (*fname && *fname!= slash && *fname!=backslash)
1869        {
1870          *s = *fname++;
1871          if ((size_t)((++s)-string_buffer) > maxlen)
1872            G_THROW( ERR_MSG("GURL.big_name") );
1873        }
1874        *s = 0;
1875      }
1876      char* s2=s;//MBCS DBCS
1877      for(;*s;s++) 
1878        EMPTY_LOOP;
1879      char* back = _tcsrchr(s2,backslash);//MBCS DBCS
1880      if ((s>string_buffer)&&(*(s-1)!= slash)
1881          &&(back == NULL || (back!=NULL && s-1 != back) ))//MBCS DBCS
1882      {
1883        *s = backslash;
1884        s++;
1885      }
1886      while (*fname && (*fname!= slash) && (*fname!=backslash))
1887      {
1888        *s = *fname++;
1889        if ((size_t)((++s)-string_buffer) > maxlen)
1890          G_THROW( ERR_MSG("GURL.big_name") );
1891      }
1892      *s = 0;
1893      for(;(*fname== slash)||(*fname==backslash);fname++)
1894        EMPTY_LOOP;
1895    }
1896  }
1897#elif defined(macintosh) // MACINTOSH implementation
1898  strcpy(string_buffer, (const char *)(from?from:GOS::cwd()));
1899 
1900  if (!GStringRep::cmp(fname, string_buffer,strlen(string_buffer)) || is_file(fname))
1901  {
1902    strcpy(string_buffer, "");//please don't expand, the logic of filename is chaos.
1903  }
1904 
1905  // Process path components
1906  char *s = string_buffer + strlen(string_buffer);
1907  if(fname)
1908  {
1909    for(;fname[0]==colon;fname++)
1910      EMPTY_LOOP;
1911    while(fname[0])
1912    {
1913      if (fname[0]== dot )
1914      {
1915        if (fname[1]==colon || !fname[1])
1916        {
1917          fname++;
1918          continue;
1919        }
1920        if ((fname[1]== dot )
1921          &&(fname[2]==colon || fname[2]==0))
1922        {
1923          fname +=2;
1924          for(;(s>string_buffer+1)&&(*(s-1)==colon);s--)
1925            EMPTY_LOOP;
1926          for(;(s>string_buffer+1)&&(*(s-1)!=colon);s--)
1927            EMPTY_LOOP;
1928          continue;
1929        }
1930      }
1931      if ((s==string_buffer)||(*(s-1)!=colon))
1932      {
1933        *s = colon;
1934        s++;
1935      }
1936      while (*fname!=0 && *fname!=colon)
1937      {
1938        *s = *fname++;
1939        if ((++s)-string_buffer > maxlen)
1940          G_THROW( ERR_MSG("GURL.big_name") );
1941      }
1942      *s = 0;
1943      for(;fname[0]==colon;fname++)
1944        EMPTY_LOOP;
1945    }
1946  }
1947  for(;(s>string_buffer+1) && (*(s-1)==colon);s--)
1948    EMPTY_LOOP;
1949  *s = 0;
1950  return ((string_buffer[0]==colon)?(string_buffer+1):string_buffer);
1951#else
1952# error "Define something here for your operating system"
1953#endif 
1954  return retval;
1955}
1956
1957unsigned int
1958hash(const GURL & gurl)
1959{
1960  unsigned int retval;
1961  const GUTF8String s(gurl.get_string());
1962  const int len=s.length();
1963  if(len && (s[len-1] == '/')) // Don't include the trailing slash as part of the hash.
1964  {
1965        retval=hash(s.substr(0,len-1));
1966  }else
1967  {
1968    retval=hash(s);
1969  }
1970  return retval;
1971}
1972
1973
1974#ifdef HAVE_NAMESPACES
1975}
1976# ifndef NOT_USING_DJVU_NAMESPACE
1977using namespace DJVU;
1978# endif
1979#endif
Note: See TracBrowser for help on using the repository browser.