source: trunk/libdjvu/GURL.cpp @ 280

Last change on this file since 280 was 280, checked in by rbri, 12 years ago

DJVU plugin: djvulibre updated to version 3.5.22

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