1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22#include "setup.h"
23
24#include <curl/curl.h>
25
26#define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
27#include <curl/mprintf.h>
28
29#include "tool_writeout.h"
30
31#include "memdebug.h" /* keep this as LAST include */
32
33typedef enum {
34  VAR_NONE,       /* must be the first */
35  VAR_TOTAL_TIME,
36  VAR_NAMELOOKUP_TIME,
37  VAR_CONNECT_TIME,
38  VAR_APPCONNECT_TIME,
39  VAR_PRETRANSFER_TIME,
40  VAR_STARTTRANSFER_TIME,
41  VAR_SIZE_DOWNLOAD,
42  VAR_SIZE_UPLOAD,
43  VAR_SPEED_DOWNLOAD,
44  VAR_SPEED_UPLOAD,
45  VAR_HTTP_CODE,
46  VAR_HTTP_CODE_PROXY,
47  VAR_HEADER_SIZE,
48  VAR_REQUEST_SIZE,
49  VAR_EFFECTIVE_URL,
50  VAR_CONTENT_TYPE,
51  VAR_NUM_CONNECTS,
52  VAR_REDIRECT_TIME,
53  VAR_REDIRECT_COUNT,
54  VAR_FTP_ENTRY_PATH,
55  VAR_REDIRECT_URL,
56  VAR_SSL_VERIFY_RESULT,
57  VAR_NUM_OF_VARS /* must be the last */
58} replaceid;
59
60struct variable {
61  const char *name;
62  replaceid id;
63};
64
65
66static const struct variable replacements[]={
67  {"url_effective", VAR_EFFECTIVE_URL},
68  {"http_code", VAR_HTTP_CODE},
69  {"response_code", VAR_HTTP_CODE},
70  {"http_connect", VAR_HTTP_CODE_PROXY},
71  {"time_total", VAR_TOTAL_TIME},
72  {"time_namelookup", VAR_NAMELOOKUP_TIME},
73  {"time_connect", VAR_CONNECT_TIME},
74  {"time_appconnect", VAR_APPCONNECT_TIME},
75  {"time_pretransfer", VAR_PRETRANSFER_TIME},
76  {"time_starttransfer", VAR_STARTTRANSFER_TIME},
77  {"size_header", VAR_HEADER_SIZE},
78  {"size_request", VAR_REQUEST_SIZE},
79  {"size_download", VAR_SIZE_DOWNLOAD},
80  {"size_upload", VAR_SIZE_UPLOAD},
81  {"speed_download", VAR_SPEED_DOWNLOAD},
82  {"speed_upload", VAR_SPEED_UPLOAD},
83  {"content_type", VAR_CONTENT_TYPE},
84  {"num_connects", VAR_NUM_CONNECTS},
85  {"time_redirect", VAR_REDIRECT_TIME},
86  {"num_redirects", VAR_REDIRECT_COUNT},
87  {"ftp_entry_path", VAR_FTP_ENTRY_PATH},
88  {"redirect_url", VAR_REDIRECT_URL},
89  {"ssl_verify_result", VAR_SSL_VERIFY_RESULT},
90  {NULL, VAR_NONE}
91};
92
93void ourWriteOut(CURL *curl, const char *writeinfo)
94{
95  FILE *stream = stdout;
96  const char *ptr = writeinfo;
97  char *stringp;
98  long longinfo;
99  double doubleinfo;
100
101  while(ptr && *ptr) {
102    if('%' == *ptr) {
103      if('%' == ptr[1]) {
104        /* an escaped %-letter */
105        fputc('%', stream);
106        ptr += 2;
107      }
108      else {
109        /* this is meant as a variable to output */
110        char *end;
111        char keepit;
112        int i;
113        if(('{' == ptr[1]) && ((end = strchr(ptr, '}')) != NULL)) {
114          bool match = FALSE;
115          ptr += 2; /* pass the % and the { */
116          keepit = *end;
117          *end = 0; /* zero terminate */
118          for(i = 0; replacements[i].name; i++) {
119            if(curl_strequal(ptr, replacements[i].name)) {
120              match = TRUE;
121              switch(replacements[i].id) {
122              case VAR_EFFECTIVE_URL:
123                if((CURLE_OK ==
124                    curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &stringp))
125                   && stringp)
126                  fputs(stringp, stream);
127                break;
128              case VAR_HTTP_CODE:
129                if(CURLE_OK ==
130                   curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &longinfo))
131                  fprintf(stream, "%03ld", longinfo);
132                break;
133              case VAR_HTTP_CODE_PROXY:
134                if(CURLE_OK ==
135                   curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE,
136                                     &longinfo))
137                  fprintf(stream, "%03ld", longinfo);
138                break;
139              case VAR_HEADER_SIZE:
140                if(CURLE_OK ==
141                   curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &longinfo))
142                  fprintf(stream, "%ld", longinfo);
143                break;
144              case VAR_REQUEST_SIZE:
145                if(CURLE_OK ==
146                   curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &longinfo))
147                  fprintf(stream, "%ld", longinfo);
148                break;
149              case VAR_NUM_CONNECTS:
150                if(CURLE_OK ==
151                   curl_easy_getinfo(curl, CURLINFO_NUM_CONNECTS, &longinfo))
152                  fprintf(stream, "%ld", longinfo);
153                break;
154              case VAR_REDIRECT_COUNT:
155                if(CURLE_OK ==
156                   curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &longinfo))
157                  fprintf(stream, "%ld", longinfo);
158                break;
159              case VAR_REDIRECT_TIME:
160                if(CURLE_OK ==
161                   curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME,
162                                     &doubleinfo))
163                  fprintf(stream, "%.3f", doubleinfo);
164                break;
165              case VAR_TOTAL_TIME:
166                if(CURLE_OK ==
167                   curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &doubleinfo))
168                  fprintf(stream, "%.3f", doubleinfo);
169                break;
170              case VAR_NAMELOOKUP_TIME:
171                if(CURLE_OK ==
172                   curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME,
173                                     &doubleinfo))
174                  fprintf(stream, "%.3f", doubleinfo);
175                break;
176              case VAR_CONNECT_TIME:
177                if(CURLE_OK ==
178                   curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &doubleinfo))
179                  fprintf(stream, "%.3f", doubleinfo);
180                break;
181              case VAR_APPCONNECT_TIME:
182                if(CURLE_OK ==
183                   curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME,
184                                     &doubleinfo))
185                  fprintf(stream, "%.3f", doubleinfo);
186                break;
187              case VAR_PRETRANSFER_TIME:
188                if(CURLE_OK ==
189                   curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME,
190                                     &doubleinfo))
191                  fprintf(stream, "%.3f", doubleinfo);
192                break;
193              case VAR_STARTTRANSFER_TIME:
194                if(CURLE_OK ==
195                   curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME,
196                                     &doubleinfo))
197                  fprintf(stream, "%.3f", doubleinfo);
198                break;
199              case VAR_SIZE_UPLOAD:
200                if(CURLE_OK ==
201                   curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, &doubleinfo))
202                  fprintf(stream, "%.0f", doubleinfo);
203                break;
204              case VAR_SIZE_DOWNLOAD:
205                if(CURLE_OK ==
206                   curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD,
207                                     &doubleinfo))
208                  fprintf(stream, "%.0f", doubleinfo);
209                break;
210              case VAR_SPEED_DOWNLOAD:
211                if(CURLE_OK ==
212                   curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD,
213                                     &doubleinfo))
214                  fprintf(stream, "%.3f", doubleinfo);
215                break;
216              case VAR_SPEED_UPLOAD:
217                if(CURLE_OK ==
218                   curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &doubleinfo))
219                  fprintf(stream, "%.3f", doubleinfo);
220                break;
221              case VAR_CONTENT_TYPE:
222                if((CURLE_OK ==
223                    curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &stringp))
224                   && stringp)
225                  fputs(stringp, stream);
226                break;
227              case VAR_FTP_ENTRY_PATH:
228                if((CURLE_OK ==
229                    curl_easy_getinfo(curl, CURLINFO_FTP_ENTRY_PATH, &stringp))
230                   && stringp)
231                  fputs(stringp, stream);
232                break;
233              case VAR_REDIRECT_URL:
234                if((CURLE_OK ==
235                    curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &stringp))
236                   && stringp)
237                  fputs(stringp, stream);
238                break;
239              case VAR_SSL_VERIFY_RESULT:
240                if(CURLE_OK ==
241                   curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT,
242                                     &longinfo))
243                  fprintf(stream, "%ld", longinfo);
244                break;
245              default:
246                break;
247              }
248              break;
249            }
250          }
251          if(!match) {
252            fprintf(stderr, "curl: unknown --write-out variable: '%s'\n", ptr);
253          }
254          ptr = end + 1; /* pass the end */
255          *end = keepit;
256        }
257        else {
258          /* illegal syntax, then just output the characters that are used */
259          fputc('%', stream);
260          fputc(ptr[1], stream);
261          ptr += 2;
262        }
263      }
264    }
265    else if('\\' == *ptr) {
266      switch(ptr[1]) {
267      case 'r':
268        fputc('\r', stream);
269        break;
270      case 'n':
271        fputc('\n', stream);
272        break;
273      case 't':
274        fputc('\t', stream);
275        break;
276      default:
277        /* unknown, just output this */
278        fputc(*ptr, stream);
279        fputc(ptr[1], stream);
280        break;
281      }
282      ptr += 2;
283    }
284    else {
285      fputc(*ptr, stream);
286      ptr++;
287    }
288  }
289
290}
291