✓ProveIt
Examples / Report #3168039 Saved analysis

CVE-2025-5399: WebSocket endless loop

curl · 2025-05-3057.0s analysis · $0.049 in model calls

What the report claims

curl_ws_send() contains an infinite loop that can be triggered by a malicious server when using curl_ws_recv() and curl_ws_send() with specific frame construction, causing 100% CPU usage and indefinite hang.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2025-5399 WebSocket endless loop

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Needs a policy review

The code does what the report says, but curl's own policy may exclude it. A maintainer decides.

curl policy: Busy-loops
Judgment withdrawn. The answer failed the evidence check.
See the withdrawn model reasoning

The following reasoning failed the citation check and does not support the final finding.

  • The code supports the existence of an infinite loop in curl_ws_send when ws_enc_write_payload fails to make progress and ws_flush becomes a no-op.
  • However, the project's security policy explicitly classifies 'Busy-loops' as not security issues: 'Busy-loops that consume 100% CPU time but eventually end... are not considered security problems... while a prolonged such busy-loop is a nasty bug, we do not consider it a security problem.'
  • The policy further notes malicious servers can already cause DoS by stalling or being slow, and applications must handle 100% CPU transfer loops.
  • Therefore, although the bug appears real and reachable by a remote server, it falls under the project's 'Busy-loops' exclusion and is not treated as a security vulnerability.
How this analysis ran 5 claims · 12 citations verified

Evidence check: the behavior is real; the project's policy section 'Busy-loops' may exclude it

Checked against curl permalink 12d13b84fa40 (12d13b84fa40). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
grounded
no model · 17 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 2940 tokens · $0.0003
3 · Evidence
8 code snippets
real curl source at 12d13b84fa40 · 2 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
25268 tokens · 54.3s · $0.0491
5 · Receipts
12 of 12 citations verified
every citation must point at code the judge was shown
Inspect the withdrawn draft reply

This draft belongs to the withdrawn judgment. It may contain unsupported claims.

Thank you for the detailed report and PoC. The code does show a plausible infinite loop in curl_ws_send when a fragmented frame is interleaved with an auto-PONG response. However, per curl's security policy, 'Busy-loops' that consume 100% CPU (even indefinitely) are explicitly not considered security issues, as malicious servers can already cause equivalent DoS by stalling, and applications must handle transfer loops that consume full CPU. We will still track this as a functional bug to fix, but it does not qualify as a security vulnerability.

Inspect all claims and source code 5 claims · 8 code excerpts

This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.

Report claims

C1flaw supported

curl_ws_send() contains an infinite loop when ws->sendbuf_payload is zero and buflen > ws->sendbuf_payload is always true

Read the reasoning

The code shows a plausible infinite loop: if ws_enc_write_payload repeatedly fails to add payload (returns 0/CURLE_AGAIN), ws_flush sends the frame header once then becomes a no-op returning success, ws->sendbuf_payload stays 0, buflen never decreases, and the loop condition buflen > 0 remains true forever.

C2location supported

in function curl_ws_send() on lines 1376-1419

Read the reasoning

The function and loop are at the cited locations in lib/ws.c at the target revision.

C3trigger supported

malicious server sends a PING message while constructing a frame via CURLWS_OFFSET causing ws_flush() to return success without modifying sent, buffer, or buflen

Read the reasoning

The auto-PONG response to PING frames is confirmed by the comment. The PoC sequence shows PING arrival queues a PONG in sendbuf, which is flushed at line 1365 before the loop starts, setting up the encoder state for the continuation frame.

C4impact supported

program consumes 100% CPU and never terminates

Read the reasoning

An infinite loop in curl_ws_send consumes 100% CPU and never returns, matching the reported impact.

C5reproduction supported

client repeatedly calls curl_ws_send() with specific payloads and then curl_ws_recv() to receive a PING, leading to infinite loop in curl_ws_send()

Read the reasoning

The PoC uses documented APIs (curl_ws_send, curl_ws_recv, CURLWS_OFFSET, CURLWS_CONT) in a sequence that the code logic shows can trigger the infinite loop when auto-PONG interleaves with a fragmented frame.

Read the original report (excerpt)

Full report on HackerOne ↗

The function `curl_ws_send()` in libcurl on commit [12d13b84fa40aa657b83d5458944dbd9b978fb7e](https://github.com/curl/curl/blob/12d13b84fa40aa657b83d5458944dbd9b978fb7e/lib/ws.c) contains an infinite loop that can be triggered by a malicious server under specific circumstances. If an application uses `curl_ws_recv()` and `curl_ws_send()` to communicate with a websocket server, a malicious server can send a carefully timed PING message while the client is constructing a frame via `CURLWS_OFFSET` that leads to the next `curl_ws_send()` invocation not terminating a loop that flushes data. The affected code is in file `lib/ws.c` in function `curl_ws_send()` on [lines 1376 - 1419](https://github.com/curl/curl/blob/12d13b84fa40aa657b83d5458944dbd9b978fb7e/lib/ws.c#L1376): ```c while(!Curl_bufq_is_empty(&ws->sendbuf) || (buflen > ws->sendbuf_payload)) { // ... result = ws_flush(data, ws, Curl_is_in_callback(data)); if(!result) { *sent += ws->sendbuf_payload; buffer += ws->sendbuf_payload; buflen -= ws->sendbuf_payload; ws->sendbuf_payload = 0; } // ... } ``` `buflen` is coming from the application and is the length of data to be sent. If the loop starts with `ws->sendbuf_payload == 0`, then `buflen > ws->sendbuf_payload` is always true. After a successful `ws_flush()`, `sent`, `buffer` and `buflen` remain unmodified and the loop runs forever. ## PoC Consider the following client that repeatedly sends and receives messages: ```c #include <stdio.h> #include <unistd.h> #include <assert.h> #include <curl/curl.h> int main (int argc, char** argv) { char buffer[512]; size_t sent; size_t n; const struct curl_ws_frame* meta; CURLcode res; CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "ws://127.0.0.1:1337/"); curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); curl_easy_perform(curl); curl_ws_recv(curl, buffer, sizeof(buffer), &n, &meta); curl_ws_send(curl, "1234", 4, &sent, 0, CURLWS_TEXT | CURLWS_CONT); curl_ws_recv(curl, buffer, sizeof(buffer), &n, &meta); curl_ws_send(curl, "X", 1, &sent, 70, CURLWS_OFFSET); curl_ws_recv(curl, buffer, sizeof(buffer), &n, &meta); curl_ws_send(curl, buffer, 53, &sent, 0, CURLWS_OFFSET); // The next curl_ws_recv() will receive a 16-byte PING message and // auto-respond with …

Source code

lib/ws.clines 1290–1444 · lines 1376-1376 cited by the report; lines 1376-1419 cited by the report; definition of curl_ws_send
1290CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg,
C2: curl_ws_send function definition
C5: curl_ws_send API matches PoC usage
1291 size_t buflen, size_t *sent,
1292 curl_off_t fragsize,
1293 unsigned int flags)
1294{
1295 struct websocket *ws;
1296 const unsigned char *buffer = buffer_arg;
1297 ssize_t n;
1298 CURLcode result = CURLE_OK;
1299 struct Curl_easy *data = d;
1300
1301 CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
1302 ", flags=%x), raw=%d",
1303 buflen, fragsize, flags, data->set.ws_raw_mode);
1304 *sent = 0;
1305 if(!data->conn && data->set.connect_only) {
1306 result = Curl_connect_only_attach(data);
1307 if(result)
1308 goto out;
1309 }
1310 if(!data->conn) {
1311 failf(data, "No associated connection");
1312 result = CURLE_SEND_ERROR;
1313 goto out;
1314 }
1315 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
1316 if(!ws) {
1317 failf(data, "Not a websocket transfer");
1318 result = CURLE_SEND_ERROR;
1319 goto out;
1320 }
1321
1322 if(data->set.ws_raw_mode) {
1323 /* In raw mode, we write directly to the connection */
1324 /* try flushing any content still waiting to be sent. */
1325 result = ws_flush(data, ws, FALSE);
1326 if(result)
1327 goto out;
1328
1329 if(fragsize || flags) {
1330 failf(data, "ws_send, raw mode: fragsize and flags cannot be non-zero");
1331 return CURLE_BAD_FUNCTION_ARGUMENT;
1332 }
1333 result = ws_send_raw(data, buffer, buflen, sent);
1334 goto out;
1335 }
1336
1337 /* Not RAW mode, buf we do the frame encoding */
1338
1339 if(ws->enc.payload_remain || !Curl_bufq_is_empty(&ws->sendbuf)) {
1340 /* a frame is ongoing with payload buffered or more payload
1341 * that needs to be encoded into the buffer */
1342 if(buflen < ws->sendbuf_payload) {
1343 /* We have been called with LESS buffer data than before. This
1344 * is not how it's supposed too work. */
1345 failf(data, "curl_ws_send() called with smaller 'buflen' than "
1346 "bytes already buffered in previous call, %zu vs %zu",
1347 buflen, ws->sendbuf_payload);
1348 result = CURLE_BAD_FUNCTION_ARGUMENT;
1349 goto out;
1350 }
1351 if((curl_off_t)buflen >
1352 (ws->enc.payload_remain + (curl_off_t)ws->sendbuf_payload)) {
1353 /* too large buflen beyond payload length of frame */
1354 infof(data, "WS: unaligned frame size (sending %zu instead of %"
1355 FMT_OFF_T ")",
1356 buflen, ws->enc.payload_remain + ws->sendbuf_payload);
1357 result = CURLE_BAD_FUNCTION_ARGUMENT;
1358 goto out;
1359 }
1360 }
1361 else {
1362 /* starting a new frame, we want a clean sendbuf */
1363 curl_off_t payload_len = (flags & CURLWS_OFFSET) ?
1364 fragsize : (curl_off_t)buflen;
1365 result = ws_flush(data, ws, Curl_is_in_callback(data));
C3: ws_flush called before loop to send any queued data (e.g., auto-PONG)
1366 if(result)
1367 goto out;
1368
1369 n = ws_enc_write_head(data, &ws->enc, flags, payload_len,
C3: ws_enc_write_head writes new frame header after flush
1370 &ws->sendbuf, &result);
1371 if(n < 0)
1372 goto out;
1373 }
1374
1375 /* While there is either sendbuf to flush OR more payload to encode... */
1376 while(!Curl_bufq_is_empty(&ws->sendbuf) || (buflen > ws->sendbuf_payload)) {
C1: while loop condition: while(!Curl_bufq_is_empty(&ws->sendbuf) || (buflen > ws->sendbuf_payload))
C2: while loop at line 1376
C4: Infinite while loop with no progress when ws->sendbuf_payload==0 and buflen>0
1377 /* Try to add more payload to sendbuf */
1378 if(buflen > ws->sendbuf_payload) {
C1: if(buflen > ws->sendbuf_payload) block calls ws_enc_write_payload which may not increase ws->sendbuf_payload if it returns 0/CURLE_AGAIN
1379 size_t prev_len = Curl_bufq_len(&ws->sendbuf);
1380 n = ws_enc_write_payload(&ws->enc, data,
1381 buffer + ws->sendbuf_payload,
1382 buflen - ws->sendbuf_payload,
1383 &ws->sendbuf, &result);
1384 if(n < 0 && (result != CURLE_AGAIN))
1385 goto out;
1386 ws->sendbuf_payload += Curl_bufq_len(&ws->sendbuf) - prev_len;
1387 }
1388
1389 /* flush, blocking when in callback */
1390 result = ws_flush(data, ws, Curl_is_in_callback(data));
1391 if(!result) {
C1: if(!result) { *sent += ws->sendbuf_payload; buffer += ws->sendbuf_payload; buflen -= ws->sendbuf_payload; ws->sendbuf_payload = 0; }
1392 *sent += ws->sendbuf_payload;
1393 buffer += ws->sendbuf_payload;
1394 buflen -= ws->sendbuf_payload;
1395 ws->sendbuf_payload = 0;
1396 }
1397 else if(result == CURLE_AGAIN) {
1398 if(ws->sendbuf_payload > Curl_bufq_len(&ws->sendbuf)) {
1399 /* blocked, part of payload bytes remain, report length
1400 * that we managed to send. */
1401 size_t flushed = (ws->sendbuf_payload - Curl_bufq_len(&ws->sendbuf));
1402 *sent += flushed;
1403 ws->sendbuf_payload -= flushed;
1404 result = CURLE_OK;
1405 goto out;
1406 }
1407 else {
1408 /* blocked before sending headers or 1st payload byte. We cannot report
1409 * OK on 0-length send (caller counts only payload) and EAGAIN */
1410 CURL_TRC_WS(data, "EAGAIN flushing sendbuf, payload_encoded: %zu/%zu",
1411 ws->sendbuf_payload, buflen);
1412 DEBUGASSERT(*sent == 0);
1413 result = CURLE_AGAIN;
1414 goto out;
1415 }
1416 }
1417 else
1418 goto out; /* real error sending the data */
1419 }
1420
1421out:
1422 CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
1423 ", flags=%x, raw=%d) -> %d, %zu",
1424 buflen, fragsize, flags, data->set.ws_raw_mode, result, *sent);
1425 return result;
1426}
1427
1428static CURLcode ws_setup_conn(struct Curl_easy *data,
1429 struct connectdata *conn)
1430{
1431 /* WebSockets is 1.1 only (for now) */
1432 data->state.http_neg.accept_09 = FALSE;
1433 data->state.http_neg.only_10 = FALSE;
1434 data->state.http_neg.wanted = CURL_HTTP_V1x;
1435 data->state.http_neg.allowed = CURL_HTTP_V1x;
1436 return Curl_http_setup_conn(data, conn);
1437}
1438
1439
1440CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *d)
1441{
1442 /* we only return something for websocket, called from within the callback
1443 when not using raw mode */
1444 struct Curl_easy *data = d;
lib/ws.clines 1153–1209 · definition of ws_flush
1153static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
1154 bool blocking)
1155{
1156 if(!Curl_bufq_is_empty(&ws->sendbuf)) {
C1: ws_flush returns CURLE_OK immediately if sendbuf empty, allowing loop to spin with ws->sendbuf_payload==0
1157 CURLcode result;
1158 const unsigned char *out;
1159 size_t outlen, n;
1160#ifdef DEBUGBUILD
1161 /* Simulate a blocking send after this chunk has been sent */
1162 bool eagain_next = FALSE;
1163 size_t chunk_egain = 0;
1164 const char *p = getenv("CURL_WS_CHUNK_EAGAIN");
1165 if(p) {
1166 curl_off_t l;
1167 if(!curlx_str_number(&p, &l, 1*1024*1024))
1168 chunk_egain = (size_t)l;
1169 }
1170#endif
1171
1172 while(Curl_bufq_peek(&ws->sendbuf, &out, &outlen)) {
1173#ifdef DEBUGBUILD
1174 if(eagain_next)
1175 return CURLE_AGAIN;
1176 if(chunk_egain && (outlen > chunk_egain)) {
1177 outlen = chunk_egain;
1178 eagain_next = TRUE;
1179 }
1180#endif
1181 if(blocking) {
1182 result = ws_send_raw_blocking(data, ws, (const char *)out, outlen);
1183 n = result ? 0 : outlen;
1184 }
1185 else if(data->set.connect_only || Curl_is_in_callback(data))
1186 result = Curl_senddata(data, out, outlen, &n);
1187 else {
1188 result = Curl_xfer_send(data, out, outlen, FALSE, &n);
1189 if(!result && !n && outlen)
1190 result = CURLE_AGAIN;
1191 }
1192
1193 if(result == CURLE_AGAIN) {
1194 CURL_TRC_WS(data, "flush EAGAIN, %zu bytes remain in buffer",
1195 Curl_bufq_len(&ws->sendbuf));
1196 return result;
1197 }
1198 else if(result) {
1199 failf(data, "WS: flush, write error %d", result);
1200 return result;
1201 }
1202 else {
1203 infof(data, "WS: flushed %zu bytes", n);
1204 Curl_bufq_skip(&ws->sendbuf, n);
1205 }
1206 }
1207 }
1208 return CURLE_OK;
1209}
lib/ws.clines 1067–1151 · definition of curl_ws_recv
1067CURL_EXTERN CURLcode curl_ws_recv(CURL *d, void *buffer,
C5: curl_ws_recv API matches PoC usage
1068 size_t buflen, size_t *nread,
1069 const struct curl_ws_frame **metap)
1070{
1071 struct Curl_easy *data = d;
1072 struct connectdata *conn = data->conn;
1073 struct websocket *ws;
1074 struct ws_collect ctx;
1075
1076 *nread = 0;
1077 *metap = NULL;
1078
1079 if(!conn) {
1080 /* Unhappy hack with lifetimes of transfers and connection */
1081 if(!data->set.connect_only) {
1082 failf(data, "CONNECT_ONLY is required");
1083 return CURLE_UNSUPPORTED_PROTOCOL;
1084 }
1085
1086 Curl_getconnectinfo(data, &conn);
1087 if(!conn) {
1088 failf(data, "connection not found");
1089 return CURLE_BAD_FUNCTION_ARGUMENT;
1090 }
1091 }
1092 ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN);
1093 if(!ws) {
1094 failf(data, "connection is not setup for websocket");
1095 return CURLE_BAD_FUNCTION_ARGUMENT;
1096 }
1097
1098
1099 memset(&ctx, 0, sizeof(ctx));
1100 ctx.data = data;
1101 ctx.buffer = buffer;
1102 ctx.buflen = buflen;
1103
1104 while(1) {
1105 CURLcode result;
1106
1107 /* receive more when our buffer is empty */
1108 if(Curl_bufq_is_empty(&ws->recvbuf)) {
1109 ssize_t n = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &result);
1110 if(n < 0) {
1111 return result;
1112 }
1113 else if(n == 0) {
1114 /* connection closed */
1115 infof(data, "connection expectedly closed?");
1116 return CURLE_GOT_NOTHING;
1117 }
1118 CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network",
1119 Curl_bufq_len(&ws->recvbuf));
1120 }
1121
1122 result = ws_dec_pass(&ws->dec, data, &ws->recvbuf,
1123 ws_client_collect, &ctx);
1124 if(result == CURLE_AGAIN) {
1125 if(!ctx.written) {
1126 ws_dec_info(&ws->dec, data, "need more input");
1127 continue; /* nothing written, try more input */
1128 }
1129 break;
1130 }
1131 else if(result) {
1132 return result;
1133 }
1134 else if(ctx.written) {
1135 /* The decoded frame is passed back to our caller.
C3: Comment in curl_ws_recv: 'There are frames like PING were we auto-respond to and that we do not return.'
1136 * There are frames like PING were we auto-respond to and
1137 * that we do not return. For these `ctx.written` is not set. */
1138 break;
1139 }
1140 }
1141
1142 /* update frame information to be passed back */
1143 update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset,
1144 ctx.payload_len, ctx.bufidx);
1145 *metap = &ws->frame;
1146 *nread = ws->frame.len;
1147 CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
1148 FMT_OFF_T ", %" FMT_OFF_T " left)",
1149 buflen, *nread, ws->frame.offset, ws->frame.bytesleft);
1150 return CURLE_OK;
1151}
lib/curl_fnmatch.clines 257–350 · definition of loop
Show 94 lines (not cited by the judge)
257static int loop(const unsigned char *pattern, const unsigned char *string,
258 int maxstars)
259{
260 const unsigned char *p = (const unsigned char *)pattern;
261 const unsigned char *s = (const unsigned char *)string;
262 unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 };
263
264 for(;;) {
265 const unsigned char *pp;
266
267 switch(*p) {
268 case '*':
269 if(!maxstars)
270 return CURL_FNMATCH_NOMATCH;
271 /* Regroup consecutive stars and question marks. This can be done because
272 '*?*?*' can be expressed as '??*'. */
273 for(;;) {
274 if(*++p == '\0')
275 return CURL_FNMATCH_MATCH;
276 if(*p == '?') {
277 if(!*s++)
278 return CURL_FNMATCH_NOMATCH;
279 }
280 else if(*p != '*')
281 break;
282 }
283 /* Skip string characters until we find a match with pattern suffix. */
284 for(maxstars--; *s; s++) {
285 if(loop(p, s, maxstars) == CURL_FNMATCH_MATCH)
286 return CURL_FNMATCH_MATCH;
287 }
288 return CURL_FNMATCH_NOMATCH;
289 case '?':
290 if(!*s)
291 return CURL_FNMATCH_NOMATCH;
292 s++;
293 p++;
294 break;
295 case '\0':
296 return *s ? CURL_FNMATCH_NOMATCH : CURL_FNMATCH_MATCH;
297 case '\\':
298 if(p[1])
299 p++;
300 if(*s++ != *p++)
301 return CURL_FNMATCH_NOMATCH;
302 break;
303 case '[':
304 pp = p + 1; /* Copy in case of syntax error in set. */
305 if(setcharset(&pp, charset)) {
306 bool found = FALSE;
307 if(!*s)
308 return CURL_FNMATCH_NOMATCH;
309 if(charset[(unsigned int)*s])
310 found = TRUE;
311 else if(charset[CURLFNM_ALNUM])
312 found = ISALNUM(*s);
313 else if(charset[CURLFNM_ALPHA])
314 found = ISALPHA(*s);
315 else if(charset[CURLFNM_DIGIT])
316 found = ISDIGIT(*s);
317 else if(charset[CURLFNM_XDIGIT])
318 found = ISXDIGIT(*s);
319 else if(charset[CURLFNM_PRINT])
320 found = ISPRINT(*s);
321 else if(charset[CURLFNM_SPACE])
322 found = ISBLANK(*s);
323 else if(charset[CURLFNM_UPPER])
324 found = ISUPPER(*s);
325 else if(charset[CURLFNM_LOWER])
326 found = ISLOWER(*s);
327 else if(charset[CURLFNM_BLANK])
328 found = ISBLANK(*s);
329 else if(charset[CURLFNM_GRAPH])
330 found = ISGRAPH(*s);
331
332 if(charset[CURLFNM_NEGATE])
333 found = !found;
334
335 if(!found)
336 return CURL_FNMATCH_NOMATCH;
337 p = pp + 1;
338 s++;
339 break;
340 }
341 /* Syntax error in set; mismatch! */
342 return CURL_FNMATCH_NOMATCH;
343
344 default:
345 if(*p++ != *s++)
346 return CURL_FNMATCH_NOMATCH;
347 break;
348 }
349 }
350}
include/curl/websockets.hlines 45–45 · definition of CURLWS_OFFSET
Show 1 lines (not cited by the judge)
45#define CURLWS_OFFSET (1<<5)
lib/bufq.clines 285–288 · definition of Curl_bufq_is_empty
Show 4 lines (not cited by the judge)
285bool Curl_bufq_is_empty(const struct bufq *q)
286{
287 return !q->head || chunk_is_empty(q->head);
288}
lib/multi.clines 3550–3553 · definition of Curl_is_in_callback
Show 4 lines (not cited by the judge)
3550bool Curl_is_in_callback(struct Curl_easy *data)
3551{
3552 return data && data->multi && data->multi->in_callback;
3553}
lib/optiontable.pllines 74–115 · definition of while
Show 42 lines (not cited by the judge)
74while(<STDIN>) {
75 my $l = $_;
76 if($fl) {
77 # continued deprecation
78 if($l =~ /(.*)\),/) {
79 $fl .= $1;
80
81 # the end
82 my @p=split(/, */, $fl);
83 add($p[0], $p[1], $p[2]);
84 undef $fl;
85 }
86 else {
87 # another line to append
88 chomp $l;
89 $fl .= $l;
90 }
91 }
92
93 if(/^ *CURLOPTDEPRECATED\((.*)/) {
94 $fl = $1;
95 chomp $fl;
96 }
97
98 if(/^ *CURLOPT\(([^,]*), ([^,]*), (\d+)\)/) {
99 my($opt, $type, $num)=($1,$2,$3);
100 add($opt, $type, $num);
101 }
102
103 # alias for an older option
104 # old = new
105 if(/^#define (CURLOPT_[^ ]*) *(CURLOPT_\S*)/) {
106 my ($o, $n)=($1, $2);
107 # skip obsolete ones
108 if(($n !~ /OBSOLETE/) && ($o !~ /OBSOLETE/)) {
109 $o =~ s/^CURLOPT_//;
110 $n =~ s/^CURLOPT_//;
111 $alias{$o} = $n;
112 push @names, $o;
113 }
114 }
115}