2 Syren -- a lightweight downloader for Linux/BSD/Win/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 2 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 SyKVListClear (TSyKVList
*lst
) {
80 TSyKVListItem
*item
, *next
;
86 SyStrFree(item
->key
); SyStrFree(item
->value
); SyStrFree(item
->ustr
); free(item
);
89 lst
->first
= lst
->last
= NULL
;
94 static TSyKVListItem
*SyKVListFindInternal (TSyKVList
*lst
, const char *key
, TSyKVListItem
**prev
) {
97 if (prev
) *prev
= NULL
;
98 if (!lst
|| !key
) return NULL
;
101 if ((lst
->casesens
&& !strcmp(item
->key
, key
)) || !strcasecmp(item
->key
, key
)) break;
102 if (prev
) *prev
= item
;
109 TSyKVListItem
*SyKVListFind (TSyKVList
*lst
, const char *key
) {
110 return SyKVListFindInternal(lst
, key
, NULL
);
114 TSyResult
SyKVListDelete (TSyKVList
*lst
, const char *key
) {
115 TSyKVListItem
*item
, *prev
;
117 item
= SyKVListFindInternal(lst
, key
, &prev
);
118 if (!item
) return SY_ERROR
;
119 if (prev
) prev
->next
= item
->next
; else lst
->first
= item
->next
;
120 if (!item
->next
) lst
->last
= prev
;
122 SyStrFree(item
->key
); SyStrFree(item
->value
); SyStrFree(item
->ustr
); free(item
);
127 TSyKVListItem
*SyKVListSet (TSyKVList
*lst
, const char *key
, const char *value
, int *newKey
) {
131 if (newKey
) *newKey
= 0;
132 if (!lst
|| !key
) return NULL
;
133 sk
= SyStrDup(key
); if (!sk
) return NULL
;
134 sv
= SyStrDup(value
); if (!sv
) { SyStrFree(sk
); return NULL
; }
135 item
= SyKVListFind(lst
, key
);
136 if (item
) { free(sk
); SyStrFree(item
->value
); }
138 if (newKey
) *newKey
= 1;
139 item
= calloc(1, sizeof(TSyKVListItem
));
140 if (!item
) { free(sk
); free(sv
); return NULL
; }
142 if (lst
->count
) lst
->last
->next
= item
; else lst
->first
= item
;
143 lst
->last
= item
; lst
->count
++;
150 /* trim [dynamic] string "in-place" */
151 void SyStrTrim (char *s
) {
154 while (s
[f
] && (unsigned char)(s
[f
]) <= ' ') f
++;
155 if (f
) { while (s
[f
]) s
[p
++] = s
[f
++]; s
[p
] = '\0'; }
157 while (f
>= 0 && (unsigned char)(s
[f
]) <= ' ') f
--;
162 /* return NULL or new string */
163 char *SyURLDecode (const char *s
) {
167 if (!s
) return SyStrNewEmpty();
168 destSize
= strlen(s
)+1;
169 dest
= calloc(1, destSize
); if (!dest
) return NULL
;
173 if (ch
== '%' && *s
) {
174 char h0
= *s
, h1
= s
[1];
175 if (isxdigit(h0
) && isxdigit(h1
)) {
176 if (h0
>= 'a' && h0
<= 'f') h0
-= 32;
177 if (h1
>= 'a' && h1
<= 'f') h1
-= 32;
178 if (h0
> '9') h0
-= 7; if (h1
> '9') h1
-= 7;
179 h0
-= '0'; h1
-= '0';
180 ch
= ((int)h0
*16)+((int)h1
); s
+= 2;
189 /* return NULL or new string */
190 char *SyURLEncode (const char *s
) {
194 if (!s
|| !(*s
)) return SyStrNewEmpty();
196 dest
= malloc(len
*3+1); /* max size */
197 if (!dest
) { free(dest
); return SyStrNewEmpty(); }
198 memset(dest
, 0, len
*3+1); d
= dest
;
201 if (ch
<= ' ' || ch
>= '\x7F' || ch
== '%') { sprintf(d
, "%%%02X", (int)((unsigned char)ch
)); d
+= 3; }
208 /* return NULL or new string */
209 char *SyBuildAuthStr (const char *user
, const char *pass
) {
210 static const char b64Alph
[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
214 if ((!user
|| !(*user
)) && (!pass
|| !(*pass
))) return SyStrNewEmpty();
215 f
= (user
?strlen(user
):0)+(pass
?strlen(pass
):0)+16;
216 auth
= calloc(1, f
); if (!auth
) return NULL
;
217 snprintf(auth
, f
-1, "%s:%s", user
?user
:"", pass
?pass
:"");
218 f
= (strlen(auth
)+2)/3*4+1; /* length of encoded string+EOS */
219 dest
= malloc(f
); if (!dest
) { free(auth
); return NULL
; }
221 for (f
= 0; auth
[f
*3]; f
++) {
222 dest
[f
*4+0] = b64Alph
[(auth
[f
*3]>>2)];
223 dest
[f
*4+1] = b64Alph
[((auth
[f
*3]&3)<<4)|(auth
[f
*3+1]>>4)];
224 dest
[f
*4+2] = b64Alph
[((auth
[f
*3+1]&15)<<2)|(auth
[f
*3+2]>>6)];
225 dest
[f
*4+3] = b64Alph
[auth
[f
*3+2]&63];
226 if (!auth
[f
*3+2]) dest
[f
*4+3] = '=';
227 if (!auth
[f
*3+1]) dest
[f
*4+2] = '=';
234 TSyURL
*SyURLNew (void) {
235 TSyURL
*url
= calloc(1, sizeof(TSyURL
));
236 if (url
) url
->proto
= SY_PROTO_UNKNOWN
;
241 void SyURLFree (TSyURL
*url
) {
242 if (url
) { SyURLClear(url
); free(url
); }
246 void SyURLClear (TSyURL
*url
) {
248 SyStrFree(url
->protostr
);
249 SyStrFree(url
->user
);
250 SyStrFree(url
->pass
);
251 SyStrFree(url
->host
);
253 SyStrFree(url
->file
);
254 SyStrFree(url
->query
);
255 SyStrFree(url
->anchor
);
256 memset(url
, 0, sizeof(TSyURL
));
257 url
->proto
= SY_PROTO_UNKNOWN
;
261 TSyURL
*SyURLClone (const TSyURL
*src
) {
264 if (!src
) return NULL
;
266 if (!res
) return NULL
;
268 res
->proto
= src
->proto
;
269 res
->port
= src
->port
;
270 res
->defaultPort
= src
->defaultPort
;
272 res
->protostr
= SyStrDup(src
->protostr
); if (!res
->protostr
) break;
273 res
->user
= SyStrDup(src
->user
); if (!res
->user
) break;
274 res
->pass
= SyStrDup(src
->pass
); if (!res
->pass
) break;
275 res
->host
= SyStrDup(src
->host
); if (!res
->host
) break;
276 res
->dir
= SyStrDup(src
->dir
); if (!res
->dir
) break;
277 res
->file
= SyStrDup(src
->file
); if (!res
->file
) break;
278 res
->query
= SyStrDup(src
->query
); if (!res
->query
) break;
279 res
->anchor
= SyStrDup(src
->anchor
); if (!res
->anchor
) break;
288 TSyResult
SyURLParse (TSyURL
*dest
, const char *urlstr
) {
290 char *s
, *t
, *q
, *dn
, ch
;
294 if (!urlstr
|| !(*urlstr
)) return SY_ERROR
;
295 buf
= SyStrNew(urlstr
, -1); if (!buf
) return SY_ERROR
;
296 url
= buf
; SyStrTrim(url
);
297 if (!url
[0]) { free(buf
); return SY_ERROR
; }
300 s
= strstr(url
, "://");
302 /* no protocol specified */
303 dest
->proto
= SY_PROTO_DEFAULT
;
304 dest
->protostr
= SyStrNew(SY_PROTO_DEFAULT_STR
, -1);
306 *s
= '\0'; /* for easy comparisons, etc */
307 dest
->protostr
= SyStrNew(url
, -1); /* copy proto string */
308 if (!strcasecmp(url
, "ftp")) dest
->proto
= SY_PROTO_FTP
;
309 else if (!strcasecmp(url
, "http")) dest
->proto
= SY_PROTO_HTTP
;
310 else if (!strcasecmp(url
, "https")) dest
->proto
= SY_PROTO_HTTPS
;
311 else dest
->proto
= SY_PROTO_UNKNOWN
;
312 url
= s
+3; /* skip proto definition */
314 switch (dest
->proto
) {
315 case SY_PROTO_HTTP
: dest
->port
= 80; break;
316 case SY_PROTO_FTP
: dest
->port
= 21; break;
317 case SY_PROTO_HTTPS
: dest
->port
= 443; break;
318 default: dest
->port
= 0; break;
320 dest
->defaultPort
= SY_TRUE
; /* for now*/
322 /* split to host and dir/name */
323 dn
= strchr(url
, '/');
324 if (dn
) *dn
= '\0'; /* cut path for now (will be restored later) */
325 /* check for username in host field */
326 t
= strrchr(url
, '@');
328 s
= url
; *t
= '\0'; url
= t
+1; /* what's left is host[:port] */
331 if (t
) *(t
++) = '\0';
332 dest
->pass
= SyStrNew(t
, -1);
333 dest
->user
= SyStrNew(s
, -1);
335 /* no user/password */
336 dest
->user
= SyStrNew((dest
->proto
== SY_PROTO_FTP
)?"anonymous":NULL
, -1);
337 dest
->pass
= SyStrNew((dest
->proto
== SY_PROTO_FTP
)?"syren@nowhere.net":NULL
, -1);
340 s
= strchr(url
, ':');
345 if (ch
< '0' || ch
> '9') { free(buf
); SyURLClear(dest
); return SY_ERROR
; }
347 if (f
> 65535) { free(buf
); SyURLClear(dest
); return SY_ERROR
; }
349 if (f
< 1) { free(buf
); SyURLClear(dest
); return SY_ERROR
; }
350 dest
->port
= f
; dest
->defaultPort
= SY_FALSE
;
353 if (!url
[0]) dest
->host
= SyStrNewEmpty(); else dest
->host
= SyStrNew(url
, -1);
356 if (*dest
->protostr
== 'f') { free(buf
); SyURLClear(dest
); return SY_ERROR
; }
357 dest
->dir
= SyStrNew("/", -1);
358 dest
->file
= SyStrNewEmpty();
359 dest
->query
= SyStrNewEmpty();
360 dest
->anchor
= SyStrNewEmpty();
362 if (*dest
->protostr
== 'f') {
363 /* FTP, no queries/anchors */
364 /* split to dir and file */
365 *dn
= '/'; t
= strrchr(dn
, '/')+1;
366 dest
->dir
= SyStrNew(dn
, t
-dn
);
367 dest
->file
= SyStrNew(t
, -1);
368 dest
->query
= SyStrNewEmpty();
369 dest
->anchor
= SyStrNewEmpty();
371 /* HTTP, possible queries/anchors */
373 /* split to dir and file */
374 s
= dn
; t
= dn
+1; q
= NULL
;
377 if (ch
== '?' || ch
== '#') { q
= s
-1; break; }
378 if (ch
== '/') t
= s
;
380 dest
->dir
= SyStrNew(dn
, t
-dn
);
382 ch
= *q
; *q
= '\0'; dest
->file
= SyStrNew(t
, -1); *q
= ch
;
384 s
= strchr(q
, '#'); if (s
) *s
= '\0';
385 dest
->query
= SyStrNew(q
, -1);
386 if (s
) { *s
= '#'; q
= s
; } else q
= NULL
;
387 } else dest
->query
= SyStrNewEmpty();
388 dest
->anchor
= SyStrNew(q
, -1);
390 dest
->file
= SyStrNew(t
, -1);
391 dest
->query
= SyStrNewEmpty();
392 dest
->anchor
= SyStrNewEmpty();
401 #ifdef SY_STR_URL2STR
402 /* return NULL or new string */
403 char *SyURL2StrEx (TSyURL
*url
, TSyBool userpass
, TSyBool hidepass
) {
406 if (!url
) return NULL
;
407 if (userpass
== SY_TRUE
&& ((url
->user
&& *url
->user
) || (url
->pass
&& *url
->pass
))) {
408 if (hidepass
== SY_TRUE
) t
= SySPrintf("%s:********@", url
->user
?url
->user
:"");
409 else t
= SySPrintf("%s:%s@", url
->user
?url
->user
:"", url
->pass
?url
->pass
:"");
410 } else t
= SyStrNewEmpty();
412 if (url
->protostr
[0] == 'h' && url
->port
== 80)
413 s
= SySPrintf("%s://%s%s%s%s%s%s", url
->protostr
, t
, url
->host
, url
->dir
, url
->file
, url
->query
, url
->anchor
);
415 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
);
421 /* return NULL or new string */
422 char *SyURL2Str (TSyURL
*url
) {
423 return SyURL2StrEx(url
, SY_TRUE
, SY_TRUE
);
429 int64_t SyStr2Long (const char *s
) {
433 while (*s
&& (unsigned char)(*s
) <= ' ') s
++;
434 if (!isdigit(*s
)) return -1;
435 while (isdigit(*s
)) {
436 n
*= 10; if (n
< 0) return -1;
439 while (*s
&& (unsigned char)(*s
) <= ' ') s
++;
446 int SyStr2Int (const char *s
) {
447 int64_t res
= SyStr2Long(s
);
449 if (res
> 0x7fffffff) res
= -1;
454 TSyResult
SyLong2Str (char *dest
, int64_t num
) {
459 if (!dest
) return SY_ERROR
;
461 if (num
< 0) return SY_ERROR
;
462 pp
= sizeof(tmp
); tmp
[--pp
] = '\0';
464 if (!pp
) return SY_ERROR
;
465 tmp
[--pp
] = (num
%10)+'0'; num
/= 10;
467 strcpy(dest
, &(tmp
[pp
]));
473 /* dest: at least 26 chars */
475 int SyLong2StrComma (char *dest
, int64_t num
) {
477 int len
, ccnt
, t
, res
, pp
;
479 if (!dest
) return 0; *dest
= '\0';
481 /*ccnt = snprintf(tmp, 24, "%lli", num);
482 if (ccnt < 0 || ccnt > 23) return 0;*/ /* the thing that should not be */
483 if (num
< 0) { strcpy(tmp
, "---"); return strlen(tmp
); }
484 pp
= sizeof(tmp
); tmp
[--pp
] = '\0';
486 if (!pp
) { strcpy(tmp
, "BAD"); return strlen(tmp
); }
487 tmp
[--pp
] = (num
%10)+'0'; num
/= 10;
489 len
= strlen(&(tmp
[pp
])); ccnt
= (len
-1)/3;
491 dest
+= res
; *(dest
--) = '\0';
493 len
--; *(dest
--) = tmp
[pp
+len
];
494 if (!--t
) { t
= 3; *(dest
--) = ','; }
500 /* dest: at least 10 bytes */
501 void SySize2Str (char *dest
, int64_t size
) {
502 if (size
< (int64_t)0) { strcpy(dest
, "--------"); return; }
503 if (size
< (int64_t)1024L) { sprintf(dest
, "%7iB", (int)size
); return; }
504 if (size
< (int64_t)1024*1024L) { sprintf(dest
, "%6.1fKB", (double)size
/1024); return; }
505 if (size
< (int64_t)1024*1024*1024L) { sprintf(dest
, "%6.1fMB", (double)size
/(1024*1024)); return; }
506 if (size
< (int64_t)1024*1024*1024*1024L) { sprintf(dest
, "%6.1fGB", (double)size
/(double)(1024*1024*1024)); return; }
507 sprintf(dest
, "%6.1fTB", (double)size
/(double)(1024*1024)/(double)(1024*1024));
512 /* dest: at least 10 bytes */
513 void SyTime2Str (char *dest
, int value
) {
516 if (value
< 0) { strcpy(dest
, "--:--"); return; }
517 if (value
< 3600) { sprintf(dest
, "%02i:%02i", value
/60, value
%60); return; }
518 h
= value
/3600; if (h
> 999) h
= 999;
519 sprintf(dest
, "%i:%02i:%02i", value
/3600, (value
/60)%60, value
%60);