no preallocating if there's no state file
[syren.git] / src / syren_str.c
blob7e5eb3b9ca2717ace4cb7d8f6e646ae56b6d562d
1 /*
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
25 #ifndef _SYREN_STR_C
26 #define _SYREN_STR_C
28 #include "syren_str.h"
32 char *SySPrintfEx (int addonBytes, const char *fmt, ...) {
33 int n, size = 256;
34 va_list ap;
35 char *p, *np;
37 if (addonBytes < 0) addonBytes = 0; addonBytes +=8;
38 if ((p = malloc(size+addonBytes)) == NULL) return NULL;
39 while (1) {
40 memset(p, 0, size+addonBytes);
41 va_start(ap, fmt);
42 n = vsnprintf(p, size, fmt?fmt:"", ap);
43 va_end(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; }
47 p = np;
49 return p;
53 /* src can be NULL; maxlen < 0: maxlen = strlen(src?src:"") */
54 char *SyStrNew (const char *src, int maxlen) {
55 char *res;
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);
60 return res;
63 void SyStrFree (char *str) {
64 if (str) free(str);
68 /* key/value lists */
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) {
80 if (!item) return;
81 SyStrFree(item->key); SyStrFree(item->value); SyStrFree(item->ustr);
82 memset(item, 0, sizeof(TSyKVListItem));
86 void SyKVListFreeItem (TSyKVListItem *item) {
87 if (!item) return;
88 SyStrFree(item->key); SyStrFree(item->value); SyStrFree(item->ustr);
89 free(item);
93 void SyKVListClear (TSyKVList *lst) {
94 TSyKVListItem *item, *next;
96 if (!lst) return;
97 item = lst->first;
98 while (item) {
99 next = item->next;
100 SyKVListFreeItem(item);
101 item = next;
103 lst->first = lst->last = NULL;
104 lst->count = 0;
108 static TSyKVListItem *SyKVListFindInternal (const TSyKVList *lst, const char *key, TSyKVListItem **prev) {
109 TSyKVListItem *item;
111 if (prev) *prev = NULL;
112 if (!lst || !key) return NULL;
113 item = lst->first;
114 while (item) {
115 if ((lst->casesens && !strcmp(item->key, key)) || !strcasecmp(item->key, key)) break;
116 if (prev) *prev = item;
117 item = item->next;
119 return 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;
135 lst->count--;
136 SyKVListFreeItem(item);
138 return SY_TRUE;
142 TSyKVListItem *SyKVListSet (TSyKVList *lst, const char *key, const char *value, int *newKey) {
143 TSyKVListItem *item;
144 char *sk, *sv;
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); }
152 else {
153 if (newKey) *newKey = 1;
154 item = calloc(1, sizeof(TSyKVListItem));
155 if (!item) { free(sk); free(sv); return NULL; }
156 item->key = sk;
157 if (lst->count) lst->last->next = item; else lst->first = item;
158 lst->last = item; lst->count++;
160 item->value = sv;
161 return item;
165 /* trim [dynamic] string "in-place" */
166 void SyStrTrim (char *s) {
167 int f = 0, p = 0;
169 while (s[f] && (unsigned char)(s[f]) <= ' ') f++;
170 if (f) { while (s[f]) s[p++] = s[f++]; s[p] = '\0'; }
171 f = strlen(s);
172 while (f >= 0 && (unsigned char)(s[f]) <= ' ') f--;
173 s[f+1] = '\0';
177 /* return NULL or new string */
178 char *SyURLDecode (const char *s) {
179 char *d, *dest;
180 int destSize;
182 if (!s) return SyStrNewEmpty();
183 destSize = strlen(s)+1;
184 dest = calloc(1, destSize); if (!dest) return NULL;
185 d = dest;
186 while (*s) {
187 char ch = *(s++);
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;
198 *(d++) = ch;
200 return dest;
204 /* return NULL or new string */
205 char *SyURLEncode (const char *s) {
206 char *d, *dest;
207 int len;
209 if (!s || !(*s)) return SyStrNewEmpty();
210 len = strlen(s);
211 dest = malloc(len*3+1); /* max size */
212 if (!dest) { free(dest); return SyStrNewEmpty(); }
213 memset(dest, 0, len*3+1); d = dest;
214 while (*s) {
215 char ch = *(s++);
216 if (ch <= ' ' || ch >= '\x7F' || ch == '%') { sprintf(d, "%%%02X", (int)((unsigned char)ch)); d += 3; }
217 else *(d++) = ch;
219 return dest;
223 /* return NULL or new string */
224 char *SyBuildAuthStr (const char *user, const char *pass) {
225 static const char b64Alph[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
226 int f;
227 char *auth, *dest;
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; }
235 memset(dest, 0, f);
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] = '=';
244 free(auth);
245 return dest;
249 TSyURL *SyURLNew (void) {
250 TSyURL *url = calloc(1, sizeof(TSyURL));
251 if (url) url->proto = SY_PROTO_UNKNOWN;
252 return url;
256 void SyURLFree (TSyURL *url) {
257 if (url) { SyURLClear(url); free(url); }
261 void SyURLClear (TSyURL *url) {
262 if (!url) return;
263 SyStrFree(url->protostr);
264 SyStrFree(url->user);
265 SyStrFree(url->pass);
266 SyStrFree(url->host);
267 SyStrFree(url->dir);
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) {
277 TSyURL *res;
279 if (!src) return NULL;
280 res = SyURLNew();
281 if (!res) return NULL;
283 res->proto = src->proto;
284 res->port = src->port;
285 res->defaultPort = src->defaultPort;
286 do {
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;
295 return res;
296 } while (0);
298 SyURLFree(res);
299 return NULL;
303 TSyResult SyURLParse (TSyURL *dest, const char *urlstr) {
304 char *url, *buf;
305 char *s, *t, *q, *dn, ch;
306 int f;
308 SyURLClear(dest);
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; }
314 /* protocol:// */
315 s = strstr(url, "://");
316 if (!s) {
317 /* no protocol specified */
318 dest->proto = SY_PROTO_DEFAULT;
319 dest->protostr = SyStrNew(SY_PROTO_DEFAULT_STR, -1);
320 } else {
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, '@');
342 if (t) {
343 s = url; *t = '\0'; url = t+1; /* what's left is host[:port] */
344 /* user:password? */
345 t = strchr(s, ':');
346 if (t) *(t++) = '\0';
347 dest->pass = SyStrNew(t, -1);
348 dest->user = SyStrNew(s, -1);
349 } else {
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);
354 /* port number? */
355 s = strchr(url, ':');
356 if (s) {
357 *(s++) = '\0';
358 f = 0; while (*s) {
359 char ch = *(s++);
360 if (ch < '0' || ch > '9') { free(buf); SyURLClear(dest); return SY_ERROR; }
361 f = (f*10)+(ch-'0');
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;
367 /* save host */
368 if (!url[0]) dest->host = SyStrNewEmpty(); else dest->host = SyStrNew(url, -1);
370 if (!dn) {
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();
376 } else {
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();
385 } else {
386 /* HTTP, possible queries/anchors */
387 *dn = '/';
388 /* split to dir and file */
389 s = dn; t = dn+1; q = NULL;
390 while (*s) {
391 char ch = *(s++);
392 if (ch == '?' || ch == '#') { q = s-1; break; }
393 if (ch == '/') t = s;
395 dest->dir = SyStrNew(dn, t-dn);
396 if (q) {
397 ch = *q; *q = '\0'; dest->file = SyStrNew(t, -1); *q = ch;
398 if (*q == '?') {
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);
404 } else {
405 dest->file = SyStrNew(t, -1);
406 dest->query = SyStrNewEmpty();
407 dest->anchor = SyStrNewEmpty();
411 free(buf);
412 return SY_OK;
416 #ifdef SY_STR_URL2STR
417 /* return NULL or new string */
418 char *SyURL2StrEx (const TSyURL *url, TSyBool userpass, TSyBool hidepass) {
419 char *s, *t;
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();
426 if (!t) return NULL;
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);
429 else
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);
431 free(t);
432 return s;
436 /* return NULL or new string */
437 char *SyURL2Str (const TSyURL *url) {
438 return SyURL2StrEx(url, SY_TRUE, SY_TRUE);
440 #endif
443 /* -1: error */
444 int64_t SyStr2Long (const char *s) {
445 int64_t n = 0;
447 if (!s) return -1;
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;
452 n += (*(s++))-'0';
454 while (*s && (unsigned char)(*s) <= ' ') s++;
455 if (*s) return -1;
456 return n;
460 /* -1: error */
461 int SyStr2Int (const char *s) {
462 int64_t res = SyStr2Long(s);
464 if (res > 0x7fffffff) res = -1;
465 return res;
469 TSyResult SyLong2Str (char *dest, int64_t num) {
470 char tmp[24];
471 int pp;
473 /* fuck msvcrt! */
474 if (!dest) return SY_ERROR;
475 *dest = '\0';
476 if (num < 0) return SY_ERROR;
477 pp = sizeof(tmp); tmp[--pp] = '\0';
478 do {
479 if (!pp) return SY_ERROR;
480 tmp[--pp] = (num%10)+'0'; num /= 10;
481 } while (num);
482 strcpy(dest, &(tmp[pp]));
483 return SY_OK;
487 #ifdef SY_STR_ADDON
488 /* dest: at least 26 chars */
489 /* returns len */
490 int SyLong2StrComma (char *dest, int64_t num) {
491 char tmp[24];
492 int len, ccnt, t, res, pp;
494 if (!dest) return 0; *dest = '\0';
495 /* fuck msvcrt! */
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';
500 do {
501 if (!pp) { strcpy(tmp, "BAD"); return strlen(tmp); }
502 tmp[--pp] = (num%10)+'0'; num /= 10;
503 } while (num);
504 len = strlen(&(tmp[pp])); ccnt = (len-1)/3;
505 res = len+ccnt;
506 dest += res; *(dest--) = '\0';
507 t = 3; while (len) {
508 len--; *(dest--) = tmp[pp+len];
509 if (!--t) { t = 3; *(dest--) = ','; }
511 return res;
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));
523 return;
527 /* dest: at least 10 bytes */
528 void SyTime2Str (char *dest, int value) {
529 int h;
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);
535 return;
537 #endif
540 #endif