~dannf/ubuntu/saucy/djvulibre/lp1224195

« back to all changes in this revision

Viewing changes to .pc/debian-changes-3.5.24-7/libdjvu/GURL.cpp

  • Committer: Package Import Robot
  • Author(s): Barak A. Pearlmutter
  • Date: 2011-06-09 23:06:30 UTC
  • mfrom: (1.1.8) (12.2.19 sid)
  • Revision ID: package-import@ubuntu.com-20110609230630-iixyb3dzba1lio13
Tags: 3.5.24-7
upstream fix to page extraction bug (closes: #629941)

Show diffs side-by-side

added added

removed removed

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