2 Syren -- a lightweight downloader for Linux/BSD/MacOSX
3 inspired by Axel Copyright 2001-2002 Wilmer van der Gaast
4 version 0.0.6 (atomic alien)
5 coded by Ketmar // Avalon Group
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License with
18 the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL;
19 if not, write to the Free Software Foundation, Inc., 59 Temple Place,
20 Suite 330, Boston, MA 02111-1307 USA
23 Syren string manipulation package
28 #include "syren_str.h"
32 char *SySPrintfEx (int addonBytes
, const char *fmt
, ...) {
37 if (addonBytes
< 0) addonBytes
= 0; addonBytes
+=8;
38 if ((p
= malloc(size
+addonBytes
)) == NULL
) return NULL
;
40 memset(p
, 0, size
+addonBytes
);
42 n
= vsnprintf(p
, size
, fmt
?fmt
:"", ap
);
44 if (n
> -1 && n
< size
) break;
45 if (n
> -1) size
= n
+1; else size
*= 2;
46 if ((np
= realloc(p
, size
+addonBytes
)) == NULL
) { free(p
); return NULL
; }
53 /* src can be NULL; maxlen < 0: maxlen = strlen(src?src:"") */
54 char *SyStrNew (const char *src
, int maxlen
) {
56 if (maxlen
< 0) { if (src
) maxlen
= strlen(src
); else maxlen
= 0; }
57 res
= malloc(maxlen
+1); if (!res
) return NULL
;
58 memset(res
, 0, maxlen
+1);
59 if (src
&& maxlen
) strncpy(res
, src
, maxlen
);
63 void SyStrFree (char *str
) {
69 TSyKVList
*SyKVListNew () {
70 return calloc(1, sizeof(TSyKVList
));
74 void SyKVListFree (TSyKVList
*lst
) {
75 if (lst
) { SyKVListClear(lst
); free(lst
); }
79 void SyKVListClearItem (TSyKVListItem
*item
) {
81 SyStrFree(item
->key
); SyStrFree(item
->value
); SyStrFree(item
->ustr
);
82 memset(item
, 0, sizeof(TSyKVListItem
));
86 void SyKVListFreeItem (TSyKVListItem
*item
) {
88 SyStrFree(item
->key
); SyStrFree(item
->value
); SyStrFree(item
->ustr
);
93 void SyKVListClear (TSyKVList
*lst
) {
94 TSyKVListItem
*item
, *next
;
100 SyKVListFreeItem(item
);
103 lst
->first
= lst
->last
= NULL
;
108 static TSyKVListItem
*SyKVListFindInternal (const TSyKVList
*lst
, const char *key
, TSyKVListItem
**prev
) {
111 if (prev
) *prev
= NULL
;
112 if (!lst
|| !key
) return NULL
;
115 if ((lst
->casesens
&& !strcmp(item
->key
, key
)) || !strcasecmp(item
->key
, key
)) break;
116 if (prev
) *prev
= item
;
123 TSyKVListItem
*SyKVListFind (const TSyKVList
*lst
, const char *key
) {
124 return SyKVListFindInternal(lst
, key
, NULL
);
128 TSyResult
SyKVListDelete (TSyKVList
*lst
, const char *key
) {
129 TSyKVListItem
*item
, *prev
;
131 item
= SyKVListFindInternal(lst
, key
, &prev
);
132 if (!item
) return SY_ERROR
;
133 if (prev
) prev
->next
= item
->next
; else lst
->first
= item
->next
;
134 if (!item
->next
) lst
->last
= prev
;
136 SyKVListFreeItem(item
);
142 TSyKVListItem
*SyKVListSet (TSyKVList
*lst
, const char *key
, const char *value
, int *newKey
) {
146 if (newKey
) *newKey
= 0;
147 if (!lst
|| !key
) return NULL
;
148 sk
= SyStrDup(key
); if (!sk
) return NULL
;
149 sv
= SyStrDup(value
); if (!sv
) { SyStrFree(sk
); return NULL
; }
150 item
= SyKVListFind(lst
, key
);
151 if (item
) { free(sk
); SyStrFree(item
->value
); }
153 if (newKey
) *newKey
= 1;
154 item
= calloc(1, sizeof(TSyKVListItem
));
155 if (!item
) { free(sk
); free(sv
); return NULL
; }
157 if (lst
->count
) lst
->last
->next
= item
; else lst
->first
= item
;
158 lst
->last
= item
; lst
->count
++;
165 /* trim [dynamic] string "in-place" */
166 void SyStrTrim (char *s
) {
169 while (s
[f
] && (unsigned char)(s
[f
]) <= ' ') f
++;
170 if (f
) { while (s
[f
]) s
[p
++] = s
[f
++]; s
[p
] = '\0'; }
172 while (f
>= 0 && (unsigned char)(s
[f
]) <= ' ') f
--;
177 /* return NULL or new string */
178 char *SyURLDecode (const char *s
) {
182 if (!s
) return SyStrNewEmpty();
183 destSize
= strlen(s
)+1;
184 dest
= calloc(1, destSize
); if (!dest
) return NULL
;
188 if (ch
== '%' && *s
) {
189 char h0
= *s
, h1
= s
[1];
190 if (isxdigit(h0
) && isxdigit(h1
)) {
191 if (h0
>= 'a' && h0
<= 'f') h0
-= 32;
192 if (h1
>= 'a' && h1
<= 'f') h1
-= 32;
193 if (h0
> '9') h0
-= 7; if (h1
> '9') h1
-= 7;
194 h0
-= '0'; h1
-= '0';
195 ch
= ((int)h0
*16)+((int)h1
); s
+= 2;
204 /* return NULL or new string */
205 char *SyURLEncode (const char *s
) {
209 if (!s
|| !(*s
)) return SyStrNewEmpty();
211 dest
= malloc(len
*3+1); /* max size */
212 if (!dest
) { free(dest
); return SyStrNewEmpty(); }
213 memset(dest
, 0, len
*3+1); d
= dest
;
216 if (ch
<= ' ' || ch
>= '\x7F' || ch
== '%') { sprintf(d
, "%%%02X", (int)((unsigned char)ch
)); d
+= 3; }
223 /* return NULL or new string */
224 char *SyBuildAuthStr (const char *user
, const char *pass
) {
225 static const char b64Alph
[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
229 if ((!user
|| !(*user
)) && (!pass
|| !(*pass
))) return SyStrNewEmpty();
230 f
= (user
?strlen(user
):0)+(pass
?strlen(pass
):0)+16;
231 auth
= calloc(1, f
); if (!auth
) return NULL
;
232 snprintf(auth
, f
-1, "%s:%s", user
?user
:"", pass
?pass
:"");
233 f
= (strlen(auth
)+2)/3*4+1; /* length of encoded string+EOS */
234 dest
= malloc(f
); if (!dest
) { free(auth
); return NULL
; }
236 for (f
= 0; auth
[f
*3]; f
++) {
237 dest
[f
*4+0] = b64Alph
[(auth
[f
*3]>>2)];
238 dest
[f
*4+1] = b64Alph
[((auth
[f
*3]&3)<<4)|(auth
[f
*3+1]>>4)];
239 dest
[f
*4+2] = b64Alph
[((auth
[f
*3+1]&15)<<2)|(auth
[f
*3+2]>>6)];
240 dest
[f
*4+3] = b64Alph
[auth
[f
*3+2]&63];
241 if (!auth
[f
*3+2]) dest
[f
*4+3] = '=';
242 if (!auth
[f
*3+1]) dest
[f
*4+2] = '=';
249 TSyURL
*SyURLNew (void) {
250 TSyURL
*url
= calloc(1, sizeof(TSyURL
));
251 if (url
) url
->proto
= SY_PROTO_UNKNOWN
;
256 void SyURLFree (TSyURL
*url
) {
257 if (url
) { SyURLClear(url
); free(url
); }
261 void SyURLClear (TSyURL
*url
) {
263 SyStrFree(url
->protostr
);
264 SyStrFree(url
->user
);
265 SyStrFree(url
->pass
);
266 SyStrFree(url
->host
);
268 SyStrFree(url
->file
);
269 SyStrFree(url
->query
);
270 SyStrFree(url
->anchor
);
271 memset(url
, 0, sizeof(TSyURL
));
272 url
->proto
= SY_PROTO_UNKNOWN
;
276 TSyURL
*SyURLClone (const TSyURL
*src
) {
279 if (!src
) return NULL
;
281 if (!res
) return NULL
;
283 res
->proto
= src
->proto
;
284 res
->port
= src
->port
;
285 res
->defaultPort
= src
->defaultPort
;
287 res
->protostr
= SyStrDup(src
->protostr
); if (!res
->protostr
) break;
288 res
->user
= SyStrDup(src
->user
); if (!res
->user
) break;
289 res
->pass
= SyStrDup(src
->pass
); if (!res
->pass
) break;
290 res
->host
= SyStrDup(src
->host
); if (!res
->host
) break;
291 res
->dir
= SyStrDup(src
->dir
); if (!res
->dir
) break;
292 res
->file
= SyStrDup(src
->file
); if (!res
->file
) break;
293 res
->query
= SyStrDup(src
->query
); if (!res
->query
) break;
294 res
->anchor
= SyStrDup(src
->anchor
); if (!res
->anchor
) break;
303 TSyResult
SyURLParse (TSyURL
*dest
, const char *urlstr
) {
305 char *s
, *t
, *q
, *dn
, ch
;
309 if (!urlstr
|| !(*urlstr
)) return SY_ERROR
;
310 buf
= SyStrNew(urlstr
, -1); if (!buf
) return SY_ERROR
;
311 url
= buf
; SyStrTrim(url
);
312 if (!url
[0]) { free(buf
); return SY_ERROR
; }
315 s
= strstr(url
, "://");
317 /* no protocol specified */
318 dest
->proto
= SY_PROTO_DEFAULT
;
319 dest
->protostr
= SyStrNew(SY_PROTO_DEFAULT_STR
, -1);
321 *s
= '\0'; /* for easy comparisons, etc */
322 dest
->protostr
= SyStrNew(url
, -1); /* copy proto string */
323 if (!strcasecmp(url
, "ftp")) dest
->proto
= SY_PROTO_FTP
;
324 else if (!strcasecmp(url
, "http")) dest
->proto
= SY_PROTO_HTTP
;
325 else if (!strcasecmp(url
, "https")) dest
->proto
= SY_PROTO_HTTPS
;
326 else dest
->proto
= SY_PROTO_UNKNOWN
;
327 url
= s
+3; /* skip proto definition */
329 switch (dest
->proto
) {
330 case SY_PROTO_HTTP
: dest
->port
= 80; break;
331 case SY_PROTO_FTP
: dest
->port
= 21; break;
332 case SY_PROTO_HTTPS
: dest
->port
= 443; break;
333 default: dest
->port
= 0; break;
335 dest
->defaultPort
= SY_TRUE
; /* for now*/
337 /* split to host and dir/name */
338 dn
= strchr(url
, '/');
339 if (dn
) *dn
= '\0'; /* cut path for now (will be restored later) */
340 /* check for username in host field */
341 t
= strrchr(url
, '@');
343 s
= url
; *t
= '\0'; url
= t
+1; /* what's left is host[:port] */
346 if (t
) *(t
++) = '\0';
347 dest
->pass
= SyStrNew(t
, -1);
348 dest
->user
= SyStrNew(s
, -1);
350 /* no user/password */
351 dest
->user
= SyStrNew((dest
->proto
== SY_PROTO_FTP
)?"anonymous":NULL
, -1);
352 dest
->pass
= SyStrNew((dest
->proto
== SY_PROTO_FTP
)?"syren@nowhere.net":NULL
, -1);
355 s
= strchr(url
, ':');
360 if (ch
< '0' || ch
> '9') { free(buf
); SyURLClear(dest
); return SY_ERROR
; }
362 if (f
> 65535) { free(buf
); SyURLClear(dest
); return SY_ERROR
; }
364 if (f
< 1) { free(buf
); SyURLClear(dest
); return SY_ERROR
; }
365 dest
->port
= f
; dest
->defaultPort
= SY_FALSE
;
368 if (!url
[0]) dest
->host
= SyStrNewEmpty(); else dest
->host
= SyStrNew(url
, -1);
371 if (*dest
->protostr
== 'f') { free(buf
); SyURLClear(dest
); return SY_ERROR
; }
372 dest
->dir
= SyStrNew("/", -1);
373 dest
->file
= SyStrNewEmpty();
374 dest
->query
= SyStrNewEmpty();
375 dest
->anchor
= SyStrNewEmpty();
377 if (*dest
->protostr
== 'f') {
378 /* FTP, no queries/anchors */
379 /* split to dir and file */
380 *dn
= '/'; t
= strrchr(dn
, '/')+1;
381 dest
->dir
= SyStrNew(dn
, t
-dn
);
382 dest
->file
= SyStrNew(t
, -1);
383 dest
->query
= SyStrNewEmpty();
384 dest
->anchor
= SyStrNewEmpty();
386 /* HTTP, possible queries/anchors */
388 /* split to dir and file */
389 s
= dn
; t
= dn
+1; q
= NULL
;
392 if (ch
== '?' || ch
== '#') { q
= s
-1; break; }
393 if (ch
== '/') t
= s
;
395 dest
->dir
= SyStrNew(dn
, t
-dn
);
397 ch
= *q
; *q
= '\0'; dest
->file
= SyStrNew(t
, -1); *q
= ch
;
399 s
= strchr(q
, '#'); if (s
) *s
= '\0';
400 dest
->query
= SyStrNew(q
, -1);
401 if (s
) { *s
= '#'; q
= s
; } else q
= NULL
;
402 } else dest
->query
= SyStrNewEmpty();
403 dest
->anchor
= SyStrNew(q
, -1);
405 dest
->file
= SyStrNew(t
, -1);
406 dest
->query
= SyStrNewEmpty();
407 dest
->anchor
= SyStrNewEmpty();
416 #ifdef SY_STR_URL2STR
417 /* return NULL or new string */
418 char *SyURL2StrEx (const TSyURL
*url
, TSyBool userpass
, TSyBool hidepass
) {
421 if (!url
) return NULL
;
422 if (userpass
== SY_TRUE
&& ((url
->user
&& *url
->user
) || (url
->pass
&& *url
->pass
))) {
423 if (hidepass
== SY_TRUE
) t
= SySPrintf("%s:********@", url
->user
?url
->user
:"");
424 else t
= SySPrintf("%s:%s@", url
->user
?url
->user
:"", url
->pass
?url
->pass
:"");
425 } else t
= SyStrNewEmpty();
427 if (url
->protostr
[0] == 'h' && url
->port
== 80)
428 s
= SySPrintf("%s://%s%s%s%s%s%s", url
->protostr
, t
, url
->host
, url
->dir
, url
->file
, url
->query
, url
->anchor
);
430 s
= SySPrintf("%s://%s%s:%i%s%s%s%s", url
->protostr
, t
, url
->host
, url
->port
, url
->dir
, url
->file
, url
->query
, url
->anchor
);
436 /* return NULL or new string */
437 char *SyURL2Str (const TSyURL
*url
) {
438 return SyURL2StrEx(url
, SY_TRUE
, SY_TRUE
);
444 int64_t SyStr2Long (const char *s
) {
448 while (*s
&& (unsigned char)(*s
) <= ' ') s
++;
449 if (!isdigit(*s
)) return -1;
450 while (isdigit(*s
)) {
451 n
*= 10; if (n
< 0) return -1;
454 while (*s
&& (unsigned char)(*s
) <= ' ') s
++;
461 int SyStr2Int (const char *s
) {
462 int64_t res
= SyStr2Long(s
);
464 if (res
> 0x7fffffff) res
= -1;
469 TSyResult
SyLong2Str (char *dest
, int64_t num
) {
474 if (!dest
) return SY_ERROR
;
476 if (num
< 0) return SY_ERROR
;
477 pp
= sizeof(tmp
); tmp
[--pp
] = '\0';
479 if (!pp
) return SY_ERROR
;
480 tmp
[--pp
] = (num
%10)+'0'; num
/= 10;
482 strcpy(dest
, &(tmp
[pp
]));
488 /* dest: at least 26 chars */
490 int SyLong2StrComma (char *dest
, int64_t num
) {
492 int len
, ccnt
, t
, res
, pp
;
494 if (!dest
) return 0; *dest
= '\0';
496 /*ccnt = snprintf(tmp, 24, "%lli", num);
497 if (ccnt < 0 || ccnt > 23) return 0;*/ /* the thing that should not be */
498 if (num
< 0) { strcpy(tmp
, "---"); return strlen(tmp
); }
499 pp
= sizeof(tmp
); tmp
[--pp
] = '\0';
501 if (!pp
) { strcpy(tmp
, "BAD"); return strlen(tmp
); }
502 tmp
[--pp
] = (num
%10)+'0'; num
/= 10;
504 len
= strlen(&(tmp
[pp
])); ccnt
= (len
-1)/3;
506 dest
+= res
; *(dest
--) = '\0';
508 len
--; *(dest
--) = tmp
[pp
+len
];
509 if (!--t
) { t
= 3; *(dest
--) = ','; }
515 /* dest: at least 10 bytes */
516 void SySize2Str (char *dest
, int64_t size
) {
517 if (size
< (int64_t)0) { strcpy(dest
, "--------"); return; }
518 if (size
< (int64_t)1024L) { sprintf(dest
, "%7iB", (int)size
); return; }
519 if (size
< (int64_t)1024*1024L) { sprintf(dest
, "%6.1fKB", (double)size
/1024); return; }
520 if (size
< (int64_t)1024*1024*1024L) { sprintf(dest
, "%6.1fMB", (double)size
/(1024*1024)); return; }
521 if (size
< (int64_t)1024*1024*1024*1024L) { sprintf(dest
, "%6.1fGB", (double)size
/(double)(1024*1024*1024)); return; }
522 sprintf(dest
, "%6.1fTB", (double)size
/(double)(1024*1024)/(double)(1024*1024));
527 /* dest: at least 10 bytes */
528 void SyTime2Str (char *dest
, int value
) {
531 if (value
< 0) { strcpy(dest
, "--:--"); return; }
532 if (value
< 3600) { sprintf(dest
, "%02i:%02i", value
/60, value
%60); return; }
533 h
= value
/3600; if (h
> 999) h
= 999;
534 sprintf(dest
, "%i:%02i:%02i", value
/3600, (value
/60)%60, value
%60);