forgot to include .jam rules in redist package %-)
[syren.git] / src / syren_str.c
blobbdfad6a017cbe5a3c16a9019bd71ab7f256f58dc
1 /*
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
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 SyKVListClear (TSyKVList *lst) {
80 TSyKVListItem *item, *next;
82 if (!lst) return;
83 item = lst->first;
84 while (item) {
85 next = item->next;
86 SyStrFree(item->key); SyStrFree(item->value); SyStrFree(item->ustr); free(item);
87 item = next;
89 lst->first = lst->last = NULL;
90 lst->count = 0;
94 static TSyKVListItem *SyKVListFindInternal (TSyKVList *lst, const char *key, TSyKVListItem **prev) {
95 TSyKVListItem *item;
97 if (prev) *prev = NULL;
98 if (!lst || !key) return NULL;
99 item = lst->first;
100 while (item) {
101 if ((lst->casesens && !strcmp(item->key, key)) || !strcasecmp(item->key, key)) break;
102 if (prev) *prev = item;
103 item = item->next;
105 return 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;
121 lst->count--;
122 SyStrFree(item->key); SyStrFree(item->value); SyStrFree(item->ustr); free(item);
123 return SY_TRUE;
127 TSyKVListItem *SyKVListSet (TSyKVList *lst, const char *key, const char *value, int *newKey) {
128 TSyKVListItem *item;
129 char *sk, *sv;
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); }
137 else {
138 if (newKey) *newKey = 1;
139 item = calloc(1, sizeof(TSyKVListItem));
140 if (!item) { free(sk); free(sv); return NULL; }
141 item->key = sk;
142 if (lst->count) lst->last->next = item; else lst->first = item;
143 lst->last = item; lst->count++;
145 item->value = sv;
146 return item;
150 /* trim [dynamic] string "in-place" */
151 void SyStrTrim (char *s) {
152 int f = 0, p = 0;
154 while (s[f] && (unsigned char)(s[f]) <= ' ') f++;
155 if (f) { while (s[f]) s[p++] = s[f++]; s[p] = '\0'; }
156 f = strlen(s);
157 while (f >= 0 && (unsigned char)(s[f]) <= ' ') f--;
158 s[f+1] = '\0';
162 /* return NULL or new string */
163 char *SyURLDecode (const char *s) {
164 char *d, *dest;
165 int destSize;
167 if (!s) return SyStrNewEmpty();
168 destSize = strlen(s)+1;
169 dest = calloc(1, destSize); if (!dest) return NULL;
170 d = dest;
171 while (*s) {
172 char ch = *(s++);
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;
183 *(d++) = ch;
185 return dest;
189 /* return NULL or new string */
190 char *SyURLEncode (const char *s) {
191 char *d, *dest;
192 int len;
194 if (!s || !(*s)) return SyStrNewEmpty();
195 len = strlen(s);
196 dest = malloc(len*3+1); /* max size */
197 if (!dest) { free(dest); return SyStrNewEmpty(); }
198 memset(dest, 0, len*3+1); d = dest;
199 while (*s) {
200 char ch = *(s++);
201 if (ch <= ' ' || ch >= '\x7F' || ch == '%') { sprintf(d, "%%%02X", (int)((unsigned char)ch)); d += 3; }
202 else *(d++) = ch;
204 return dest;
208 /* return NULL or new string */
209 char *SyBuildAuthStr (const char *user, const char *pass) {
210 static const char b64Alph[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
211 int f;
212 char *auth, *dest;
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; }
220 memset(dest, 0, f);
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] = '=';
229 free(auth);
230 return dest;
234 TSyURL *SyURLNew (void) {
235 TSyURL *url = calloc(1, sizeof(TSyURL));
236 if (url) url->proto = SY_PROTO_UNKNOWN;
237 return url;
241 void SyURLFree (TSyURL *url) {
242 if (url) { SyURLClear(url); free(url); }
246 void SyURLClear (TSyURL *url) {
247 if (!url) return;
248 SyStrFree(url->protostr);
249 SyStrFree(url->user);
250 SyStrFree(url->pass);
251 SyStrFree(url->host);
252 SyStrFree(url->dir);
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) {
262 TSyURL *res;
264 if (!src) return NULL;
265 res = SyURLNew();
266 if (!res) return NULL;
268 res->proto = src->proto;
269 res->port = src->port;
270 res->defaultPort = src->defaultPort;
271 do {
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;
280 return res;
281 } while (0);
283 SyURLFree(res);
284 return NULL;
288 TSyResult SyURLParse (TSyURL *dest, const char *urlstr) {
289 char *url, *buf;
290 char *s, *t, *q, *dn, ch;
291 int f;
293 SyURLClear(dest);
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; }
299 /* protocol:// */
300 s = strstr(url, "://");
301 if (!s) {
302 /* no protocol specified */
303 dest->proto = SY_PROTO_DEFAULT;
304 dest->protostr = SyStrNew(SY_PROTO_DEFAULT_STR, -1);
305 } else {
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, '@');
327 if (t) {
328 s = url; *t = '\0'; url = t+1; /* what's left is host[:port] */
329 /* user:password? */
330 t = strchr(s, ':');
331 if (t) *(t++) = '\0';
332 dest->pass = SyStrNew(t, -1);
333 dest->user = SyStrNew(s, -1);
334 } else {
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);
339 /* port number? */
340 s = strchr(url, ':');
341 if (s) {
342 *(s++) = '\0';
343 f = 0; while (*s) {
344 char ch = *(s++);
345 if (ch < '0' || ch > '9') { free(buf); SyURLClear(dest); return SY_ERROR; }
346 f = (f*10)+(ch-'0');
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;
352 /* save host */
353 if (!url[0]) dest->host = SyStrNewEmpty(); else dest->host = SyStrNew(url, -1);
355 if (!dn) {
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();
361 } else {
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();
370 } else {
371 /* HTTP, possible queries/anchors */
372 *dn = '/';
373 /* split to dir and file */
374 s = dn; t = dn+1; q = NULL;
375 while (*s) {
376 char ch = *(s++);
377 if (ch == '?' || ch == '#') { q = s-1; break; }
378 if (ch == '/') t = s;
380 dest->dir = SyStrNew(dn, t-dn);
381 if (q) {
382 ch = *q; *q = '\0'; dest->file = SyStrNew(t, -1); *q = ch;
383 if (*q == '?') {
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);
389 } else {
390 dest->file = SyStrNew(t, -1);
391 dest->query = SyStrNewEmpty();
392 dest->anchor = SyStrNewEmpty();
396 free(buf);
397 return SY_OK;
401 #ifdef SY_STR_URL2STR
402 /* return NULL or new string */
403 char *SyURL2StrEx (TSyURL *url, TSyBool userpass, TSyBool hidepass) {
404 char *s, *t;
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();
411 if (!t) return NULL;
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);
414 else
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);
416 free(t);
417 return s;
421 /* return NULL or new string */
422 char *SyURL2Str (TSyURL *url) {
423 return SyURL2StrEx(url, SY_TRUE, SY_TRUE);
425 #endif
428 /* -1: error */
429 int64_t SyStr2Long (const char *s) {
430 int64_t n = 0;
432 if (!s) return -1;
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;
437 n += (*(s++))-'0';
439 while (*s && (unsigned char)(*s) <= ' ') s++;
440 if (*s) return -1;
441 return n;
445 /* -1: error */
446 int SyStr2Int (const char *s) {
447 int64_t res = SyStr2Long(s);
449 if (res > 0x7fffffff) res = -1;
450 return res;
454 TSyResult SyLong2Str (char *dest, int64_t num) {
455 char tmp[24];
456 int pp;
458 /* fuck msvcrt! */
459 if (!dest) return SY_ERROR;
460 *dest = '\0';
461 if (num < 0) return SY_ERROR;
462 pp = sizeof(tmp); tmp[--pp] = '\0';
463 do {
464 if (!pp) return SY_ERROR;
465 tmp[--pp] = (num%10)+'0'; num /= 10;
466 } while (num);
467 strcpy(dest, &(tmp[pp]));
468 return SY_OK;
472 #ifdef SY_STR_ADDON
473 /* dest: at least 26 chars */
474 /* returns len */
475 int SyLong2StrComma (char *dest, int64_t num) {
476 char tmp[24];
477 int len, ccnt, t, res, pp;
479 if (!dest) return 0; *dest = '\0';
480 /* fuck msvcrt! */
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';
485 do {
486 if (!pp) { strcpy(tmp, "BAD"); return strlen(tmp); }
487 tmp[--pp] = (num%10)+'0'; num /= 10;
488 } while (num);
489 len = strlen(&(tmp[pp])); ccnt = (len-1)/3;
490 res = len+ccnt;
491 dest += res; *(dest--) = '\0';
492 t = 3; while (len) {
493 len--; *(dest--) = tmp[pp+len];
494 if (!--t) { t = 3; *(dest--) = ','; }
496 return res;
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));
508 return;
512 /* dest: at least 10 bytes */
513 void SyTime2Str (char *dest, int value) {
514 int h;
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);
520 return;
522 #endif
525 #endif