Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/src/tool_cb_wrt.c
2065 views
1
/***************************************************************************
2
* _ _ ____ _
3
* Project ___| | | | _ \| |
4
* / __| | | | |_) | |
5
* | (__| |_| | _ <| |___
6
* \___|\___/|_| \_\_____|
7
*
8
* Copyright (C) Daniel Stenberg, <[email protected]>, 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 https://curl.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
* SPDX-License-Identifier: curl
22
*
23
***************************************************************************/
24
#include "tool_setup.h"
25
26
#ifdef HAVE_FCNTL_H
27
/* for open() */
28
#include <fcntl.h>
29
#endif
30
31
#include <sys/stat.h>
32
33
#include <curlx.h>
34
35
#include "tool_cfgable.h"
36
#include "tool_msgs.h"
37
#include "tool_cb_wrt.h"
38
#include "tool_operate.h"
39
40
#include <memdebug.h> /* keep this as LAST include */
41
42
#ifdef _WIN32
43
#define OPENMODE S_IREAD | S_IWRITE
44
#else
45
#define OPENMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
46
#endif
47
48
/* create/open a local file for writing, return TRUE on success */
49
bool tool_create_output_file(struct OutStruct *outs,
50
struct OperationConfig *config)
51
{
52
struct GlobalConfig *global;
53
FILE *file = NULL;
54
const char *fname = outs->filename;
55
DEBUGASSERT(outs);
56
DEBUGASSERT(config);
57
global = config->global;
58
DEBUGASSERT(fname && *fname);
59
60
if(config->file_clobber_mode == CLOBBER_ALWAYS ||
61
(config->file_clobber_mode == CLOBBER_DEFAULT &&
62
!outs->is_cd_filename)) {
63
/* open file for writing */
64
file = fopen(fname, "wb");
65
}
66
else {
67
int fd;
68
do {
69
fd = open(fname, O_CREAT | O_WRONLY | O_EXCL | CURL_O_BINARY, OPENMODE);
70
/* Keep retrying in the hope that it is not interrupted sometime */
71
/* !checksrc! disable ERRNOVAR 1 */
72
} while(fd == -1 && errno == EINTR);
73
if(config->file_clobber_mode == CLOBBER_NEVER && fd == -1) {
74
int next_num = 1;
75
size_t len = strlen(fname);
76
size_t newlen = len + 13; /* nul + 1-11 digits + dot */
77
char *newname;
78
/* Guard against wraparound in new filename */
79
if(newlen < len) {
80
errorf(global, "overflow in filename generation");
81
return FALSE;
82
}
83
newname = malloc(newlen);
84
if(!newname) {
85
errorf(global, "out of memory");
86
return FALSE;
87
}
88
memcpy(newname, fname, len);
89
newname[len] = '.';
90
/* !checksrc! disable ERRNOVAR 1 */
91
while(fd == -1 && /* have not successfully opened a file */
92
(errno == EEXIST || errno == EISDIR) &&
93
/* because we keep having files that already exist */
94
next_num < 100 /* and we have not reached the retry limit */ ) {
95
msnprintf(newname + len + 1, 12, "%d", next_num);
96
next_num++;
97
do {
98
fd = open(newname, O_CREAT | O_WRONLY | O_EXCL | CURL_O_BINARY,
99
OPENMODE);
100
/* Keep retrying in the hope that it is not interrupted sometime */
101
} while(fd == -1 && errno == EINTR);
102
}
103
outs->filename = newname; /* remember the new one */
104
outs->alloc_filename = TRUE;
105
}
106
/* An else statement to not overwrite existing files and not retry with
107
new numbered names (which would cover
108
config->file_clobber_mode == CLOBBER_DEFAULT && outs->is_cd_filename)
109
is not needed because we would have failed earlier, in the while loop
110
and `fd` would now be -1 */
111
if(fd != -1) {
112
file = fdopen(fd, "wb");
113
if(!file)
114
close(fd);
115
}
116
}
117
118
if(!file) {
119
warnf(global, "Failed to open the file %s: %s", fname,
120
strerror(errno));
121
return FALSE;
122
}
123
outs->s_isreg = TRUE;
124
outs->fopened = TRUE;
125
outs->stream = file;
126
outs->bytes = 0;
127
outs->init = 0;
128
return TRUE;
129
}
130
131
/*
132
** callback for CURLOPT_WRITEFUNCTION
133
*/
134
135
size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
136
{
137
size_t rc;
138
struct per_transfer *per = userdata;
139
struct OutStruct *outs = &per->outs;
140
struct OperationConfig *config = per->config;
141
size_t bytes = sz * nmemb;
142
bool is_tty = config->global->isatty;
143
#if defined(_WIN32) && !defined(UNDER_CE)
144
CONSOLE_SCREEN_BUFFER_INFO console_info;
145
intptr_t fhnd;
146
#endif
147
148
#ifdef DEBUGBUILD
149
{
150
char *tty = curl_getenv("CURL_ISATTY");
151
if(tty) {
152
is_tty = TRUE;
153
curl_free(tty);
154
}
155
}
156
157
if(config->show_headers) {
158
if(bytes > (size_t)CURL_MAX_HTTP_HEADER) {
159
warnf(config->global, "Header data size exceeds single call write "
160
"limit");
161
return CURL_WRITEFUNC_ERROR;
162
}
163
}
164
else {
165
if(bytes > (size_t)CURL_MAX_WRITE_SIZE) {
166
warnf(config->global, "Data size exceeds single call write limit");
167
return CURL_WRITEFUNC_ERROR;
168
}
169
}
170
171
{
172
/* Some internal congruency checks on received OutStruct */
173
bool check_fails = FALSE;
174
if(outs->filename) {
175
/* regular file */
176
if(!*outs->filename)
177
check_fails = TRUE;
178
if(!outs->s_isreg)
179
check_fails = TRUE;
180
if(outs->fopened && !outs->stream)
181
check_fails = TRUE;
182
if(!outs->fopened && outs->stream)
183
check_fails = TRUE;
184
if(!outs->fopened && outs->bytes)
185
check_fails = TRUE;
186
}
187
else {
188
/* standard stream */
189
if(!outs->stream || outs->s_isreg || outs->fopened)
190
check_fails = TRUE;
191
if(outs->alloc_filename || outs->is_cd_filename || outs->init)
192
check_fails = TRUE;
193
}
194
if(check_fails) {
195
warnf(config->global, "Invalid output struct data for write callback");
196
return CURL_WRITEFUNC_ERROR;
197
}
198
}
199
#endif
200
201
if(!outs->stream && !tool_create_output_file(outs, per->config))
202
return CURL_WRITEFUNC_ERROR;
203
204
if(is_tty && (outs->bytes < 2000) && !config->terminal_binary_ok) {
205
/* binary output to terminal? */
206
if(memchr(buffer, 0, bytes)) {
207
warnf(config->global, "Binary output can mess up your terminal. "
208
"Use \"--output -\" to tell curl to output it to your terminal "
209
"anyway, or consider \"--output <FILE>\" to save to a file.");
210
config->synthetic_error = TRUE;
211
return CURL_WRITEFUNC_ERROR;
212
}
213
}
214
215
#if defined(_WIN32) && !defined(UNDER_CE)
216
fhnd = _get_osfhandle(fileno(outs->stream));
217
/* if Windows console then UTF-8 must be converted to UTF-16 */
218
if(isatty(fileno(outs->stream)) &&
219
GetConsoleScreenBufferInfo((HANDLE)fhnd, &console_info)) {
220
wchar_t *wc_buf;
221
DWORD wc_len, chars_written;
222
unsigned char *rbuf = (unsigned char *)buffer;
223
DWORD rlen = (DWORD)bytes;
224
225
#define IS_TRAILING_BYTE(x) (0x80 <= (x) && (x) < 0xC0)
226
227
/* attempt to complete an incomplete UTF-8 sequence from previous call.
228
the sequence does not have to be well-formed. */
229
if(outs->utf8seq[0] && rlen) {
230
bool complete = false;
231
/* two byte sequence (lead byte 110yyyyy) */
232
if(0xC0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xE0) {
233
outs->utf8seq[1] = *rbuf++;
234
--rlen;
235
complete = true;
236
}
237
/* three byte sequence (lead byte 1110zzzz) */
238
else if(0xE0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xF0) {
239
if(!outs->utf8seq[1]) {
240
outs->utf8seq[1] = *rbuf++;
241
--rlen;
242
}
243
if(rlen && !outs->utf8seq[2]) {
244
outs->utf8seq[2] = *rbuf++;
245
--rlen;
246
complete = true;
247
}
248
}
249
/* four byte sequence (lead byte 11110uuu) */
250
else if(0xF0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xF8) {
251
if(!outs->utf8seq[1]) {
252
outs->utf8seq[1] = *rbuf++;
253
--rlen;
254
}
255
if(rlen && !outs->utf8seq[2]) {
256
outs->utf8seq[2] = *rbuf++;
257
--rlen;
258
}
259
if(rlen && !outs->utf8seq[3]) {
260
outs->utf8seq[3] = *rbuf++;
261
--rlen;
262
complete = true;
263
}
264
}
265
266
if(complete) {
267
WCHAR prefix[3] = {0}; /* UTF-16 (1-2 WCHARs) + NUL */
268
269
if(MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)outs->utf8seq, -1,
270
prefix, CURL_ARRAYSIZE(prefix))) {
271
DEBUGASSERT(prefix[2] == L'\0');
272
if(!WriteConsoleW(
273
(HANDLE) fhnd,
274
prefix,
275
prefix[1] ? 2 : 1,
276
&chars_written,
277
NULL)) {
278
return CURL_WRITEFUNC_ERROR;
279
}
280
}
281
/* else: UTF-8 input was not well formed and OS is pre-Vista which
282
drops invalid characters instead of writing U+FFFD to output. */
283
284
memset(outs->utf8seq, 0, sizeof(outs->utf8seq));
285
}
286
}
287
288
/* suppress an incomplete utf-8 sequence at end of rbuf */
289
if(!outs->utf8seq[0] && rlen && (rbuf[rlen - 1] & 0x80)) {
290
/* check for lead byte from a two, three or four byte sequence */
291
if(0xC0 <= rbuf[rlen - 1] && rbuf[rlen - 1] < 0xF8) {
292
outs->utf8seq[0] = rbuf[rlen - 1];
293
rlen -= 1;
294
}
295
else if(rlen >= 2 && IS_TRAILING_BYTE(rbuf[rlen - 1])) {
296
/* check for lead byte from a three or four byte sequence */
297
if(0xE0 <= rbuf[rlen - 2] && rbuf[rlen - 2] < 0xF8) {
298
outs->utf8seq[0] = rbuf[rlen - 2];
299
outs->utf8seq[1] = rbuf[rlen - 1];
300
rlen -= 2;
301
}
302
else if(rlen >= 3 && IS_TRAILING_BYTE(rbuf[rlen - 2])) {
303
/* check for lead byte from a four byte sequence */
304
if(0xF0 <= rbuf[rlen - 3] && rbuf[rlen - 3] < 0xF8) {
305
outs->utf8seq[0] = rbuf[rlen - 3];
306
outs->utf8seq[1] = rbuf[rlen - 2];
307
outs->utf8seq[2] = rbuf[rlen - 1];
308
rlen -= 3;
309
}
310
}
311
}
312
}
313
314
if(rlen) {
315
/* calculate buffer size for wide characters */
316
wc_len = (DWORD)MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)rbuf, (int)rlen,
317
NULL, 0);
318
if(!wc_len)
319
return CURL_WRITEFUNC_ERROR;
320
321
wc_buf = (wchar_t*) malloc(wc_len * sizeof(wchar_t));
322
if(!wc_buf)
323
return CURL_WRITEFUNC_ERROR;
324
325
wc_len = (DWORD)MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)rbuf, (int)rlen,
326
wc_buf, (int)wc_len);
327
if(!wc_len) {
328
free(wc_buf);
329
return CURL_WRITEFUNC_ERROR;
330
}
331
332
if(!WriteConsoleW(
333
(HANDLE) fhnd,
334
wc_buf,
335
wc_len,
336
&chars_written,
337
NULL)) {
338
free(wc_buf);
339
return CURL_WRITEFUNC_ERROR;
340
}
341
free(wc_buf);
342
}
343
344
rc = bytes;
345
}
346
else
347
#endif
348
{
349
if(per->hdrcbdata.headlist) {
350
if(tool_write_headers(&per->hdrcbdata, outs->stream))
351
return CURL_WRITEFUNC_ERROR;
352
}
353
rc = fwrite(buffer, sz, nmemb, outs->stream);
354
}
355
356
if(bytes == rc)
357
/* we added this amount of data to the output */
358
outs->bytes += bytes;
359
360
if(config->readbusy) {
361
config->readbusy = FALSE;
362
curl_easy_pause(per->curl, CURLPAUSE_CONT);
363
}
364
365
if(config->nobuffer) {
366
/* output buffering disabled */
367
int res;
368
do {
369
res = fflush(outs->stream);
370
/* Keep retrying in the hope that it is not interrupted sometime */
371
/* !checksrc! disable ERRNOVAR 1 */
372
} while(res && errno == EINTR);
373
if(res)
374
return CURL_WRITEFUNC_ERROR;
375
}
376
377
return rc;
378
}
379
380