]> sjero.net Git - wget/blob - src/cmpt.c
[svn] Comment fix.
[wget] / src / cmpt.c
1 /* Replacements for routines missing on some systems.
2    Copyright (C) 1995-2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Wget; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables.  You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL".  If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so.  If you do not wish to do
28 so, delete this exception statement from your version.  */
29
30 #include <config.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40
41 #include <errno.h>
42
43 #include "wget.h"
44
45 /* Some systems lack certain functions normally taken for granted.
46    For example, Windows doesn't have strptime, and some systems don't
47    have a usable fnmatch.  This file should contain fallback
48    implementations of such missing functions.  It should *not* define
49    new Wget-specific interfaces -- those should be placed in utils.c
50    or elsewhere.  */
51 \f
52 /* strcasecmp and strncasecmp apparently originated with BSD 4.4.
53    SUSv3 seems to be the only standard out there (that I can find)
54    that requires their existence, so in theory there might be systems
55    still in use that lack them.  Note that these don't get defined
56    under Windows because mswindows.h defines them to the equivalent
57    Windows functions stricmp and strnicmp.  */
58
59 #ifndef HAVE_STRCASECMP
60 /* From GNU libc.  */
61 /* Compare S1 and S2, ignoring case, returning less than, equal to or
62    greater than zero if S1 is lexiographically less than,
63    equal to or greater than S2.  */
64 int
65 strcasecmp (const char *s1, const char *s2)
66 {
67   register const unsigned char *p1 = (const unsigned char *) s1;
68   register const unsigned char *p2 = (const unsigned char *) s2;
69   unsigned char c1, c2;
70
71   if (p1 == p2)
72     return 0;
73
74   do
75     {
76       c1 = TOLOWER (*p1++);
77       c2 = TOLOWER (*p2++);
78       if (c1 == '\0')
79         break;
80     }
81   while (c1 == c2);
82
83   return c1 - c2;
84 }
85 #endif /* not HAVE_STRCASECMP */
86
87 #ifndef HAVE_STRNCASECMP
88 /* From GNU libc.  */
89 /* Compare no more than N characters of S1 and S2,
90    ignoring case, returning less than, equal to or
91    greater than zero if S1 is lexicographically less
92    than, equal to or greater than S2.  */
93 int
94 strncasecmp (const char *s1, const char *s2, size_t n)
95 {
96   register const unsigned char *p1 = (const unsigned char *) s1;
97   register const unsigned char *p2 = (const unsigned char *) s2;
98   unsigned char c1, c2;
99
100   if (p1 == p2 || n == 0)
101     return 0;
102
103   do
104     {
105       c1 = TOLOWER (*p1++);
106       c2 = TOLOWER (*p2++);
107       if (c1 == '\0' || c1 != c2)
108         return c1 - c2;
109     } while (--n > 0);
110
111   return c1 - c2;
112 }
113 #endif /* not HAVE_STRNCASECMP */
114 \f
115 /* strptime is required by POSIX, but it is missing from Windows,
116    which means we must keep a fallback implementation.  It is
117    reportedly missing or broken on many older systems as well.  */
118
119 #ifndef HAVE_STRPTIME
120 /* From GNU libc 2.1.3.  */
121 /* Ulrich, thanks for helping me out with this!  --hniksic  */
122
123 /* strptime - Convert a string representation of time to a time value.
124    Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
125    This file is part of the GNU C Library.
126    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.  */
127
128 /* XXX This version of the implementation is not really complete.
129    Some of the fields cannot add information alone.  But if seeing
130    some of them in the same format (such as year, week and weekday)
131    this is enough information for determining the date.  */
132
133 #ifndef __P
134 # define __P(args) args
135 #endif /* not __P */
136
137 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
138 # ifdef _LIBC
139 #  define localtime_r __localtime_r
140 # else
141 /* Approximate localtime_r as best we can in its absence.  */
142 #  define localtime_r my_localtime_r
143 static struct tm *localtime_r __P ((const time_t *, struct tm *));
144 static struct tm *
145 localtime_r (t, tp)
146      const time_t *t;
147      struct tm *tp;
148 {
149   struct tm *l = localtime (t);
150   if (! l)
151     return 0;
152   *tp = *l;
153   return tp;
154 }
155 # endif /* ! _LIBC */
156 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
157
158
159 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
160 #if defined __GNUC__ && __GNUC__ >= 2
161 # define match_string(cs1, s2) \
162   ({ size_t len = strlen (cs1);                                               \
163      int result = strncasecmp ((cs1), (s2), len) == 0;                        \
164      if (result) (s2) += len;                                                 \
165      result; })
166 #else
167 /* Oh come on.  Get a reasonable compiler.  */
168 # define match_string(cs1, s2) \
169   (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
170 #endif
171 /* We intentionally do not use isdigit() for testing because this will
172    lead to problems with the wide character version.  */
173 #define get_number(from, to, n) \
174   do {                                                                        \
175     int __n = n;                                                              \
176     val = 0;                                                                  \
177     while (*rp == ' ')                                                        \
178       ++rp;                                                                   \
179     if (*rp < '0' || *rp > '9')                                               \
180       return NULL;                                                            \
181     do {                                                                      \
182       val *= 10;                                                              \
183       val += *rp++ - '0';                                                     \
184     } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9');        \
185     if (val < from || val > to)                                               \
186       return NULL;                                                            \
187   } while (0)
188 #ifdef _NL_CURRENT
189 /* Added check for __GNUC__ extensions here for Wget. --abbotti */
190 # if defined __GNUC__ && __GNUC__ >= 2
191 #  define get_alt_number(from, to, n) \
192   ({                                                                          \
193     __label__ do_normal;                                                      \
194     if (*decided != raw)                                                      \
195       {                                                                       \
196         const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS);                 \
197         int __n = n;                                                          \
198         int any = 0;                                                          \
199         while (*rp == ' ')                                                    \
200           ++rp;                                                               \
201         val = 0;                                                              \
202         do {                                                                  \
203           val *= 10;                                                          \
204           while (*alts != '\0')                                               \
205             {                                                                 \
206               size_t len = strlen (alts);                                     \
207               if (strncasecmp (alts, rp, len) == 0)                           \
208                 break;                                                        \
209               alts += len + 1;                                                \
210               ++val;                                                          \
211             }                                                                 \
212           if (*alts == '\0')                                                  \
213             {                                                                 \
214               if (*decided == not && ! any)                                   \
215                 goto do_normal;                                               \
216               /* If we haven't read anything it's an error.  */               \
217               if (! any)                                                      \
218                 return NULL;                                                  \
219               /* Correct the premature multiplication.  */                    \
220               val /= 10;                                                      \
221               break;                                                          \
222             }                                                                 \
223           else                                                                \
224             *decided = loc;                                                   \
225         } while (--__n > 0 && val * 10 <= to);                                \
226         if (val < from || val > to)                                           \
227           return NULL;                                                        \
228       }                                                                       \
229     else                                                                      \
230       {                                                                       \
231        do_normal:                                                             \
232         get_number (from, to, n);                                             \
233       }                                                                       \
234     0;                                                                        \
235   })
236 # else
237 #  define get_alt_number(from, to, n) \
238   do {
239     if (*decided != raw)                                                      \
240       {                                                                       \
241         const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS);                 \
242         int __n = n;                                                          \
243         int any = 0;                                                          \
244         while (*rp == ' ')                                                    \
245           ++rp;                                                               \
246         val = 0;                                                              \
247         do {                                                                  \
248           val *= 10;                                                          \
249           while (*alts != '\0')                                               \
250             {                                                                 \
251               size_t len = strlen (alts);                                     \
252               if (strncasecmp (alts, rp, len) == 0)                           \
253                 break;                                                        \
254               alts += len + 1;                                                \
255               ++val;                                                          \
256             }                                                                 \
257           if (*alts == '\0')                                                  \
258             {                                                                 \
259               if (*decided == not && ! any)                                   \
260                 goto do_normal;                                               \
261               /* If we haven't read anything it's an error.  */               \
262               if (! any)                                                      \
263                 return NULL;                                                  \
264               /* Correct the premature multiplication.  */                    \
265               val /= 10;                                                      \
266               break;                                                          \
267             }                                                                 \
268           else                                                                \
269             *decided = loc;                                                   \
270         } while (--__n > 0 && val * 10 <= to);                                \
271         if (val < from || val > to)                                           \
272           return NULL;                                                        \
273       }                                                                       \
274     else                                                                      \
275       {                                                                       \
276        do_normal:                                                             \
277         get_number (from, to, n);                                             \
278       }                                                                       \
279   } while (0)
280 # endif /* defined __GNUC__ && __GNUC__ >= 2 */
281 #else
282 # define get_alt_number(from, to, n) \
283   /* We don't have the alternate representation.  */                          \
284   get_number(from, to, n)
285 #endif
286 #define recursive(new_fmt) \
287   (*(new_fmt) != '\0'                                                         \
288    && (rp = strptime_internal (rp, (new_fmt), tm, decided)) != NULL)
289
290
291 #ifdef _LIBC
292 /* This is defined in locale/C-time.c in the GNU libc.  */
293 extern const struct locale_data _nl_C_LC_TIME;
294 extern const unsigned short int __mon_yday[2][13];
295
296 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
297 # define ab_weekday_name \
298   (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
299 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
300 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
301 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
302 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
303 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
304 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
305 # define HERE_T_FMT_AMPM \
306   (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
307 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
308
309 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
310 #else
311 static char const weekday_name[][10] =
312   {
313     "Sunday", "Monday", "Tuesday", "Wednesday",
314     "Thursday", "Friday", "Saturday"
315   };
316 static char const ab_weekday_name[][4] =
317   {
318     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
319   };
320 static char const month_name[][10] =
321   {
322     "January", "February", "March", "April", "May", "June",
323     "July", "August", "September", "October", "November", "December"
324   };
325 static char const ab_month_name[][4] =
326   {
327     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
328     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
329   };
330 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
331 # define HERE_D_FMT "%m/%d/%y"
332 # define HERE_AM_STR "AM"
333 # define HERE_PM_STR "PM"
334 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
335 # define HERE_T_FMT "%H:%M:%S"
336
337 const unsigned short int __mon_yday[2][13];
338 # ifndef NEED_MON_YDAY
339 #  define NEED_MON_YDAY
340 # endif
341 #endif
342
343 /* Status of lookup: do we use the locale data or the raw data?  */
344 enum locale_status { not, loc, raw };
345
346
347 #ifndef __isleap
348 /* Nonzero if YEAR is a leap year (every 4 years,
349    except every 100th isn't, and every 400th is).  */
350 # define __isleap(year) \
351   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
352 #endif
353
354 /* Compute the day of the week.  */
355 static void
356 day_of_the_week (struct tm *tm)
357 {
358   /* We know that January 1st 1970 was a Thursday (= 4).  Compute the
359      the difference between this data in the one on TM and so determine
360      the weekday.  */
361   int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
362   int wday = (-473
363               + (365 * (tm->tm_year - 70))
364               + (corr_year / 4)
365               - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
366               + (((corr_year / 4) / 25) / 4)
367               + __mon_yday[0][tm->tm_mon]
368               + tm->tm_mday - 1);
369   tm->tm_wday = ((wday % 7) + 7) % 7;
370 }
371
372 /* Compute the day of the year.  */
373 static void
374 day_of_the_year (struct tm *tm)
375 {
376   tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
377                  + (tm->tm_mday - 1));
378 }
379
380 static char *
381 #ifdef _LIBC
382 internal_function
383 #endif
384 strptime_internal __P ((const char *buf, const char *format, struct tm *tm,
385                         enum locale_status *decided));
386
387 static char *
388 #ifdef _LIBC
389 internal_function
390 #endif
391 strptime_internal (rp, fmt, tm, decided)
392      const char *rp;
393      const char *fmt;
394      struct tm *tm;
395      enum locale_status *decided;
396 {
397 #ifdef _NL_CURRENT
398   const char *rp_backup;
399 #endif
400   int cnt;
401   size_t val;
402   int have_I, is_pm;
403   int century, want_century;
404   int have_wday, want_xday;
405   int have_yday;
406   int have_mon, have_mday;
407
408   have_I = is_pm = 0;
409   century = -1;
410   want_century = 0;
411   have_wday = want_xday = have_yday = have_mon = have_mday = 0;
412
413   while (*fmt != '\0')
414     {
415       /* A white space in the format string matches 0 more or white
416          space in the input string.  */
417       if (ISSPACE (*fmt))
418         {
419           while (ISSPACE (*rp))
420             ++rp;
421           ++fmt;
422           continue;
423         }
424
425       /* Any character but `%' must be matched by the same character
426          in the iput string.  */
427       if (*fmt != '%')
428         {
429           match_char (*fmt++, *rp++);
430           continue;
431         }
432
433       ++fmt;
434 #ifndef _NL_CURRENT
435       /* We need this for handling the `E' modifier.  */
436     start_over:
437 #endif
438
439 #ifdef _NL_CURRENT
440       /* Make back up of current processing pointer.  */
441       rp_backup = rp;
442 #endif
443
444       switch (*fmt++)
445         {
446         case '%':
447           /* Match the `%' character itself.  */
448           match_char ('%', *rp++);
449           break;
450         case 'a':
451         case 'A':
452           /* Match day of week.  */
453           for (cnt = 0; cnt < 7; ++cnt)
454             {
455 #ifdef _NL_CURRENT
456               if (*decided !=raw)
457                 {
458                   if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
459                     {
460                       if (*decided == not
461                           && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
462                                      weekday_name[cnt]))
463                         *decided = loc;
464                       break;
465                     }
466                   if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
467                     {
468                       if (*decided == not
469                           && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
470                                      ab_weekday_name[cnt]))
471                         *decided = loc;
472                       break;
473                     }
474                 }
475 #endif
476               if (*decided != loc
477                   && (match_string (weekday_name[cnt], rp)
478                       || match_string (ab_weekday_name[cnt], rp)))
479                 {
480                   *decided = raw;
481                   break;
482                 }
483             }
484           if (cnt == 7)
485             /* Does not match a weekday name.  */
486             return NULL;
487           tm->tm_wday = cnt;
488           have_wday = 1;
489           break;
490         case 'b':
491         case 'B':
492         case 'h':
493           /* Match month name.  */
494           for (cnt = 0; cnt < 12; ++cnt)
495             {
496 #ifdef _NL_CURRENT
497               if (*decided !=raw)
498                 {
499                   if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
500                     {
501                       if (*decided == not
502                           && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
503                                      month_name[cnt]))
504                         *decided = loc;
505                       break;
506                     }
507                   if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
508                     {
509                       if (*decided == not
510                           && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
511                                      ab_month_name[cnt]))
512                         *decided = loc;
513                       break;
514                     }
515                 }
516 #endif
517               if (match_string (month_name[cnt], rp)
518                   || match_string (ab_month_name[cnt], rp))
519                 {
520                   *decided = raw;
521                   break;
522                 }
523             }
524           if (cnt == 12)
525             /* Does not match a month name.  */
526             return NULL;
527           tm->tm_mon = cnt;
528           want_xday = 1;
529           break;
530         case 'c':
531           /* Match locale's date and time format.  */
532 #ifdef _NL_CURRENT
533           if (*decided != raw)
534             {
535               if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
536                 {
537                   if (*decided == loc)
538                     return NULL;
539                   else
540                     rp = rp_backup;
541                 }
542               else
543                 {
544                   if (*decided == not &&
545                       strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
546                     *decided = loc;
547                   want_xday = 1;
548                   break;
549                 }
550               *decided = raw;
551             }
552 #endif
553           if (!recursive (HERE_D_T_FMT))
554             return NULL;
555           want_xday = 1;
556           break;
557         case 'C':
558           /* Match century number.  */
559           get_number (0, 99, 2);
560           century = val;
561           want_xday = 1;
562           break;
563         case 'd':
564         case 'e':
565           /* Match day of month.  */
566           get_number (1, 31, 2);
567           tm->tm_mday = val;
568           have_mday = 1;
569           want_xday = 1;
570           break;
571         case 'F':
572           if (!recursive ("%Y-%m-%d"))
573             return NULL;
574           want_xday = 1;
575           break;
576         case 'x':
577 #ifdef _NL_CURRENT
578           if (*decided != raw)
579             {
580               if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
581                 {
582                   if (*decided == loc)
583                     return NULL;
584                   else
585                     rp = rp_backup;
586                 }
587               else
588                 {
589                   if (*decided == not
590                       && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
591                     *decided = loc;
592                   want_xday = 1;
593                   break;
594                 }
595               *decided = raw;
596             }
597 #endif
598           /* Fall through.  */
599         case 'D':
600           /* Match standard day format.  */
601           if (!recursive (HERE_D_FMT))
602             return NULL;
603           want_xday = 1;
604           break;
605         case 'k':
606         case 'H':
607           /* Match hour in 24-hour clock.  */
608           get_number (0, 23, 2);
609           tm->tm_hour = val;
610           have_I = 0;
611           break;
612         case 'I':
613           /* Match hour in 12-hour clock.  */
614           get_number (1, 12, 2);
615           tm->tm_hour = val % 12;
616           have_I = 1;
617           break;
618         case 'j':
619           /* Match day number of year.  */
620           get_number (1, 366, 3);
621           tm->tm_yday = val - 1;
622           have_yday = 1;
623           break;
624         case 'm':
625           /* Match number of month.  */
626           get_number (1, 12, 2);
627           tm->tm_mon = val - 1;
628           have_mon = 1;
629           want_xday = 1;
630           break;
631         case 'M':
632           /* Match minute.  */
633           get_number (0, 59, 2);
634           tm->tm_min = val;
635           break;
636         case 'n':
637         case 't':
638           /* Match any white space.  */
639           while (ISSPACE (*rp))
640             ++rp;
641           break;
642         case 'p':
643           /* Match locale's equivalent of AM/PM.  */
644 #ifdef _NL_CURRENT
645           if (*decided != raw)
646             {
647               if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
648                 {
649                   if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
650                     *decided = loc;
651                   break;
652                 }
653               if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
654                 {
655                   if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
656                     *decided = loc;
657                   is_pm = 1;
658                   break;
659                 }
660               *decided = raw;
661             }
662 #endif
663           if (!match_string (HERE_AM_STR, rp))
664             {
665               if (match_string (HERE_PM_STR, rp))
666                 is_pm = 1;
667               else
668                 return NULL;
669             }
670           break;
671         case 'r':
672 #ifdef _NL_CURRENT
673           if (*decided != raw)
674             {
675               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
676                 {
677                   if (*decided == loc)
678                     return NULL;
679                   else
680                     rp = rp_backup;
681                 }
682               else
683                 {
684                   if (*decided == not &&
685                       strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
686                               HERE_T_FMT_AMPM))
687                     *decided = loc;
688                   break;
689                 }
690               *decided = raw;
691             }
692 #endif
693           if (!recursive (HERE_T_FMT_AMPM))
694             return NULL;
695           break;
696         case 'R':
697           if (!recursive ("%H:%M"))
698             return NULL;
699           break;
700         case 's':
701           {
702             /* The number of seconds may be very high so we cannot use
703                the `get_number' macro.  Instead read the number
704                character for character and construct the result while
705                doing this.  */
706             time_t secs = 0;
707             if (*rp < '0' || *rp > '9')
708               /* We need at least one digit.  */
709               return NULL;
710
711             do
712               {
713                 secs *= 10;
714                 secs += *rp++ - '0';
715               }
716             while (*rp >= '0' && *rp <= '9');
717
718             if (localtime_r (&secs, tm) == NULL)
719               /* Error in function.  */
720               return NULL;
721           }
722           break;
723         case 'S':
724           get_number (0, 61, 2);
725           tm->tm_sec = val;
726           break;
727         case 'X':
728 #ifdef _NL_CURRENT
729           if (*decided != raw)
730             {
731               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
732                 {
733                   if (*decided == loc)
734                     return NULL;
735                   else
736                     rp = rp_backup;
737                 }
738               else
739                 {
740                   if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
741                     *decided = loc;
742                   break;
743                 }
744               *decided = raw;
745             }
746 #endif
747           /* Fall through.  */
748         case 'T':
749           if (!recursive (HERE_T_FMT))
750             return NULL;
751           break;
752         case 'u':
753           get_number (1, 7, 1);
754           tm->tm_wday = val % 7;
755           have_wday = 1;
756           break;
757         case 'g':
758           get_number (0, 99, 2);
759           /* XXX This cannot determine any field in TM.  */
760           break;
761         case 'G':
762           if (*rp < '0' || *rp > '9')
763             return NULL;
764           /* XXX Ignore the number since we would need some more
765              information to compute a real date.  */
766           do
767             ++rp;
768           while (*rp >= '0' && *rp <= '9');
769           break;
770         case 'U':
771         case 'V':
772         case 'W':
773           get_number (0, 53, 2);
774           /* XXX This cannot determine any field in TM without some
775              information.  */
776           break;
777         case 'w':
778           /* Match number of weekday.  */
779           get_number (0, 6, 1);
780           tm->tm_wday = val;
781           have_wday = 1;
782           break;
783         case 'y':
784           /* Match year within century.  */
785           get_number (0, 99, 2);
786           /* The "Year 2000: The Millennium Rollover" paper suggests that
787              values in the range 69-99 refer to the twentieth century.  */
788           tm->tm_year = val >= 69 ? val : val + 100;
789           /* Indicate that we want to use the century, if specified.  */
790           want_century = 1;
791           want_xday = 1;
792           break;
793         case 'Y':
794           /* Match year including century number.  */
795           get_number (0, 9999, 4);
796           tm->tm_year = val - 1900;
797           want_century = 0;
798           want_xday = 1;
799           break;
800         case 'Z':
801           /* XXX How to handle this?  */
802           break;
803         case 'E':
804 #ifdef _NL_CURRENT
805           switch (*fmt++)
806             {
807             case 'c':
808               /* Match locale's alternate date and time format.  */
809               if (*decided != raw)
810                 {
811                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
812
813                   if (*fmt == '\0')
814                     fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
815
816                   if (!recursive (fmt))
817                     {
818                       if (*decided == loc)
819                         return NULL;
820                       else
821                         rp = rp_backup;
822                     }
823                   else
824                     {
825                       if (strcmp (fmt, HERE_D_T_FMT))
826                         *decided = loc;
827                       want_xday = 1;
828                       break;
829                     }
830                   *decided = raw;
831                 }
832               /* The C locale has no era information, so use the
833                  normal representation.  */
834               if (!recursive (HERE_D_T_FMT))
835                 return NULL;
836               want_xday = 1;
837               break;
838             case 'C':
839             case 'y':
840             case 'Y':
841               /* Match name of base year in locale's alternate
842                  representation.  */
843               /* XXX This is currently not implemented.  It should
844                  use the value _NL_CURRENT (LC_TIME, ERA).  */
845               break;
846             case 'x':
847               if (*decided != raw)
848                 {
849                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
850
851                   if (*fmt == '\0')
852                     fmt = _NL_CURRENT (LC_TIME, D_FMT);
853
854                   if (!recursive (fmt))
855                     {
856                       if (*decided == loc)
857                         return NULL;
858                       else
859                         rp = rp_backup;
860                     }
861                   else
862                     {
863                       if (strcmp (fmt, HERE_D_FMT))
864                         *decided = loc;
865                       break;
866                     }
867                   *decided = raw;
868                 }
869               if (!recursive (HERE_D_FMT))
870                 return NULL;
871               break;
872             case 'X':
873               if (*decided != raw)
874                 {
875                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
876
877                   if (*fmt == '\0')
878                     fmt = _NL_CURRENT (LC_TIME, T_FMT);
879
880                   if (!recursive (fmt))
881                     {
882                       if (*decided == loc)
883                         return NULL;
884                       else
885                         rp = rp_backup;
886                     }
887                   else
888                     {
889                       if (strcmp (fmt, HERE_T_FMT))
890                         *decided = loc;
891                       break;
892                     }
893                   *decided = raw;
894                 }
895               if (!recursive (HERE_T_FMT))
896                 return NULL;
897               break;
898             default:
899               return NULL;
900             }
901           break;
902 #else
903           /* We have no information about the era format.  Just use
904              the normal format.  */
905           if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
906               && *fmt != 'x' && *fmt != 'X')
907             /* This is an illegal format.  */
908             return NULL;
909
910           goto start_over;
911 #endif
912         case 'O':
913           switch (*fmt++)
914             {
915             case 'd':
916             case 'e':
917               /* Match day of month using alternate numeric symbols.  */
918               get_alt_number (1, 31, 2);
919               tm->tm_mday = val;
920               have_mday = 1;
921               want_xday = 1;
922               break;
923             case 'H':
924               /* Match hour in 24-hour clock using alternate numeric
925                  symbols.  */
926               get_alt_number (0, 23, 2);
927               tm->tm_hour = val;
928               have_I = 0;
929               break;
930             case 'I':
931               /* Match hour in 12-hour clock using alternate numeric
932                  symbols.  */
933               get_alt_number (1, 12, 2);
934               tm->tm_hour = val - 1;
935               have_I = 1;
936               break;
937             case 'm':
938               /* Match month using alternate numeric symbols.  */
939               get_alt_number (1, 12, 2);
940               tm->tm_mon = val - 1;
941               have_mon = 1;
942               want_xday = 1;
943               break;
944             case 'M':
945               /* Match minutes using alternate numeric symbols.  */
946               get_alt_number (0, 59, 2);
947               tm->tm_min = val;
948               break;
949             case 'S':
950               /* Match seconds using alternate numeric symbols.  */
951               get_alt_number (0, 61, 2);
952               tm->tm_sec = val;
953               break;
954             case 'U':
955             case 'V':
956             case 'W':
957               get_alt_number (0, 53, 2);
958               /* XXX This cannot determine any field in TM without
959                  further information.  */
960               break;
961             case 'w':
962               /* Match number of weekday using alternate numeric symbols.  */
963               get_alt_number (0, 6, 1);
964               tm->tm_wday = val;
965               have_wday = 1;
966               break;
967             case 'y':
968               /* Match year within century using alternate numeric symbols.  */
969               get_alt_number (0, 99, 2);
970               tm->tm_year = val >= 69 ? val : val + 100;
971               want_xday = 1;
972               break;
973             default:
974               return NULL;
975             }
976           break;
977         default:
978           return NULL;
979         }
980     }
981
982   if (have_I && is_pm)
983     tm->tm_hour += 12;
984
985   if (century != -1)
986     {
987       if (want_century)
988         tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
989       else
990         /* Only the century, but not the year.  Strange, but so be it.  */
991         tm->tm_year = (century - 19) * 100;
992     }
993
994   if (want_xday && !have_wday) {
995       if ( !(have_mon && have_mday) && have_yday)  {
996           /* we don't have tm_mon and/or tm_mday, compute them */
997           int t_mon = 0;
998           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
999               t_mon++;
1000           if (!have_mon)
1001               tm->tm_mon = t_mon - 1;
1002           if (!have_mday)
1003               tm->tm_mday = tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1;
1004       }
1005       day_of_the_week (tm);
1006   }
1007   if (want_xday && !have_yday)
1008     day_of_the_year (tm);
1009
1010   return (char *) rp;
1011 }
1012
1013
1014 char *
1015 strptime (buf, format, tm)
1016      const char *buf;
1017      const char *format;
1018      struct tm *tm;
1019 {
1020   enum locale_status decided;
1021 #ifdef _NL_CURRENT
1022   decided = not;
1023 #else
1024   decided = raw;
1025 #endif
1026   return strptime_internal (buf, format, tm, &decided);
1027 }
1028 #endif /* not HAVE_STRPTIME */
1029
1030 #ifdef NEED_MON_YDAY
1031 const unsigned short int __mon_yday[2][13] =
1032   {
1033     /* Normal years.  */
1034     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
1035     /* Leap years.  */
1036     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
1037   };
1038 #endif
1039 \f
1040 /* fnmatch is required by POSIX, but we include an implementation for
1041    the sake of systems that don't have it, most notably Windows.  Some
1042    systems do have fnmatch, but Apache's installation process installs
1043    its own fnmatch.h (incompatible with the system one!) in a system
1044    include directory, effectively rendering fnmatch unusable.  This
1045    has been fixed with Apache 2, where fnmatch has been moved to apr
1046    and given a prefix, but many systems out there are still (as of
1047    this writing in 2005) broken and we must cater to them.
1048
1049    Additionally, according to anecdotal evidence and conventional
1050    wisdom I lack courage to challenge, many implementations of fnmatch
1051    are notoriously buggy and unreliable.  So we use our version by
1052    default, except when compiling under systems where fnmatch is known
1053    to work (currently on GNU libc-based systems and Solaris.)  */
1054
1055 #ifndef SYSTEM_FNMATCH
1056
1057 #define __FNM_FLAGS     (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD)
1058
1059 /* Match STRING against the filename pattern PATTERN, returning zero
1060    if it matches, FNM_NOMATCH if not.  This implementation comes from
1061    an earlier version of GNU Bash.  (It doesn't make sense to update
1062    it with a newer version because those versions add a lot of
1063    features Wget doesn't use or care about.)  */
1064
1065 int
1066 fnmatch (const char *pattern, const char *string, int flags)
1067 {
1068   register const char *p = pattern, *n = string;
1069   register char c;
1070
1071   if ((flags & ~__FNM_FLAGS) != 0)
1072     {
1073       errno = EINVAL;
1074       return (-1);
1075     }
1076
1077   while ((c = *p++) != '\0')
1078     {
1079       switch (c)
1080         {
1081         case '?':
1082           if (*n == '\0')
1083             return (FNM_NOMATCH);
1084           else if ((flags & FNM_PATHNAME) && *n == '/')
1085             return (FNM_NOMATCH);
1086           else if ((flags & FNM_PERIOD) && *n == '.' &&
1087                    (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
1088             return (FNM_NOMATCH);
1089           break;
1090
1091         case '\\':
1092           if (!(flags & FNM_NOESCAPE))
1093             c = *p++;
1094           if (*n != c)
1095             return (FNM_NOMATCH);
1096           break;
1097
1098         case '*':
1099           if ((flags & FNM_PERIOD) && *n == '.' &&
1100               (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
1101             return (FNM_NOMATCH);
1102
1103           for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
1104             if (((flags & FNM_PATHNAME) && *n == '/') ||
1105                 (c == '?' && *n == '\0'))
1106               return (FNM_NOMATCH);
1107
1108           if (c == '\0')
1109             return (0);
1110
1111           {
1112             char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
1113             for (--p; *n != '\0'; ++n)
1114               if ((c == '[' || *n == c1) &&
1115                   fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
1116                 return (0);
1117             return (FNM_NOMATCH);
1118           }
1119
1120         case '[':
1121           {
1122             /* Nonzero if the sense of the character class is
1123                inverted.  */
1124             register int not;
1125
1126             if (*n == '\0')
1127               return (FNM_NOMATCH);
1128
1129             if ((flags & FNM_PERIOD) && *n == '.' &&
1130                 (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
1131               return (FNM_NOMATCH);
1132
1133             /* Make sure there is a closing `]'.  If there isn't,
1134                the `[' is just a character to be matched.  */
1135             {
1136               register const char *np;
1137
1138               for (np = p; np && *np && *np != ']'; np++);
1139
1140               if (np && !*np)
1141                 {
1142                   if (*n != '[')
1143                     return (FNM_NOMATCH);
1144                   goto next_char;
1145                 }
1146             }
1147
1148             not = (*p == '!' || *p == '^');
1149             if (not)
1150               ++p;
1151
1152             c = *p++;
1153             while (1)
1154               {
1155                 register char cstart = c, cend = c;
1156
1157                 if (!(flags & FNM_NOESCAPE) && c == '\\')
1158                   cstart = cend = *p++;
1159
1160                 if (c == '\0')
1161                   /* [ (unterminated) loses.  */
1162                   return (FNM_NOMATCH);
1163
1164                 c = *p++;
1165
1166                 if ((flags & FNM_PATHNAME) && c == '/')
1167                   /* [/] can never match.  */
1168                   return (FNM_NOMATCH);
1169
1170                 if (c == '-' && *p != ']')
1171                   {
1172                     cend = *p++;
1173                     if (!(flags & FNM_NOESCAPE) && cend == '\\')
1174                       cend = *p++;
1175                     if (cend == '\0')
1176                       return (FNM_NOMATCH);
1177                     c = *p++;
1178                   }
1179
1180                 if (*n >= cstart && *n <= cend)
1181                   goto matched;
1182
1183                 if (c == ']')
1184                   break;
1185               }
1186             if (!not)
1187               return (FNM_NOMATCH);
1188
1189           next_char:
1190             break;
1191
1192           matched:
1193             /* Skip the rest of the [...] that already matched.  */
1194             while (c != ']')
1195               {
1196                 if (c == '\0')
1197                   /* [... (unterminated) loses.  */
1198                   return (FNM_NOMATCH);
1199
1200                 c = *p++;
1201                 if (!(flags & FNM_NOESCAPE) && c == '\\')
1202                   /* 1003.2d11 is unclear if this is right.  %%% */
1203                   ++p;
1204               }
1205             if (not)
1206               return (FNM_NOMATCH);
1207           }
1208           break;
1209
1210         default:
1211           if (c != *n)
1212             return (FNM_NOMATCH);
1213         }
1214
1215       ++n;
1216     }
1217
1218   if (*n == '\0')
1219     return (0);
1220
1221   return (FNM_NOMATCH);
1222 }
1223
1224 #endif /* not SYSTEM_FNMATCH */