]> sjero.net Git - wget/blob - src/headers.c
[svn] Applied Philipp Thomas's safe-ctype patch. Published in
[wget] / src / headers.c
1 /* Generic support for headers.
2    Copyright (C) 1997, 1998 Free Software Foundation, Inc.
3
4 This file is part of Wget.
5
6 This program 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 This program 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 this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #ifdef HAVE_STRING_H
25 # include <string.h>
26 #else
27 # include <strings.h>
28 #endif
29
30 #include "wget.h"
31 #include "connect.h"
32 #include "rbuf.h"
33 #include "headers.h"
34
35 /* This file contains the generic routines for work with headers.
36    Currently they are used only by HTTP in http.c, but they can be
37    used by anything that cares about RFC822-style headers.
38
39    Header is defined in RFC2068, as quoted below.  Note that this
40    definition is not HTTP-specific -- it is virtually
41    indistinguishable from the one given in RFC822 or RFC1036.
42
43         message-header = field-name ":" [ field-value ] CRLF
44
45         field-name     = token
46         field-value    = *( field-content | LWS )
47
48         field-content  = <the OCTETs making up the field-value
49                          and consisting of either *TEXT or combinations
50                          of token, tspecials, and quoted-string>
51
52    The public functions are header_get() and header_process(), which
53    see.  */
54
55 \f
56 /* Get a header from read-buffer RBUF and return it in *HDR.
57
58    As defined in RFC2068 and elsewhere, a header can be folded into
59    multiple lines if the continuation line begins with a space or
60    horizontal TAB.  Also, this function will accept a header ending
61    with just LF instead of CRLF.
62
63    The header may be of arbitrary length; the function will allocate
64    as much memory as necessary for it to fit.  It need not contain a
65    `:', thus you can use it to retrieve, say, HTTP status line.
66
67    The trailing CRLF or LF are stripped from the header, and it is
68    zero-terminated.   #### Is this well-behaved?  */
69 int
70 header_get (struct rbuf *rbuf, char **hdr, enum header_get_flags flags)
71 {
72   int i;
73   int bufsize = 80;
74
75   *hdr = (char *)xmalloc (bufsize);
76   for (i = 0; 1; i++)
77     {
78       int res;
79       /* #### Use DO_REALLOC?  */
80       if (i > bufsize - 1)
81         *hdr = (char *)xrealloc (*hdr, (bufsize <<= 1));
82       res = RBUF_READCHAR (rbuf, *hdr + i);
83       if (res == 1)
84         {
85           if ((*hdr)[i] == '\n')
86             {
87               if (!((flags & HG_NO_CONTINUATIONS)
88                     || i == 0
89                     || (i == 1 && (*hdr)[0] == '\r')))
90                 {
91                   char next;
92                   /* If the header is non-empty, we need to check if
93                      it continues on to the other line.  We do that by
94                      peeking at the next character.  */
95                   res = rbuf_peek (rbuf, &next);
96                   if (res == 0)
97                     return HG_EOF;
98                   else if (res == -1)
99                     return HG_ERROR;
100                   /*  If the next character is HT or SP, just continue.  */
101                   if (next == '\t' || next == ' ')
102                     continue;
103                 }
104               /* The header ends.  */
105               (*hdr)[i] = '\0';
106               /* Get rid of '\r'.  */
107               if (i > 0 && (*hdr)[i - 1] == '\r')
108                 (*hdr)[i - 1] = '\0';
109               break;
110             }
111         }
112       else if (res == 0)
113         return HG_EOF;
114       else
115         return HG_ERROR;
116     }
117   DEBUGP (("%s\n", *hdr));
118   return HG_OK;
119 }
120 \f
121 /* Check whether HEADER begins with NAME and, if yes, skip the `:' and
122    the whitespace, and call PROCFUN with the arguments of HEADER's
123    contents (after the `:' and space) and ARG.  Otherwise, return 0.  */
124 int
125 header_process (const char *header, const char *name,
126                 int (*procfun) (const char *, void *),
127                 void *arg)
128 {
129   /* Check whether HEADER matches NAME.  */
130   while (*name && (TOLOWER (*name) == TOLOWER (*header)))
131     ++name, ++header;
132   if (*name || *header++ != ':')
133     return 0;
134
135   header += skip_lws (header);
136
137   return ((*procfun) (header, arg));
138 }
139 \f
140 /* Helper functions for use with header_process().  */
141
142 /* Extract a long integer from HEADER and store it to CLOSURE.  If an
143    error is encountered, return 0, else 1.  */
144 int
145 header_extract_number (const char *header, void *closure)
146 {
147   const char *p = header;
148   long result;
149
150   for (result = 0; ISDIGIT (*p); p++)
151     result = 10 * result + (*p - '0');
152   if (*p)
153     return 0;
154
155   *(long *)closure = result;
156   return 1;
157 }
158
159 /* Strdup HEADER, and place the pointer to CLOSURE.  */
160 int
161 header_strdup (const char *header, void *closure)
162 {
163   *(char **)closure = xstrdup (header);
164   return 1;
165 }
166
167 /* Write the value 1 into the integer pointed to by CLOSURE.  */
168 int
169 header_exists (const char *header, void *closure)
170 {
171   *(int *)closure = 1;
172   return 1;
173 }
174
175 /* Skip LWS (linear white space), if present.  Returns number of
176    characters to skip.  */
177 int
178 skip_lws (const char *string)
179 {
180   const char *p = string;
181
182   while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
183     ++p;
184   return p - string;
185 }