Intial commit
[profile/ivi/w3m.git] / indep.c
1 /* $Id: indep.c,v 1.38 2007/05/23 15:06:05 inu Exp $ */
2 #include "fm.h"
3 #include <stdio.h>
4 #ifndef __MINGW32_VERSION
5 #include <pwd.h>
6 #endif /* __MINGW32_VERSION */
7 #include <sys/param.h>
8 #include <sys/types.h>
9 #include <stdlib.h>
10 #include "indep.h"
11 #include "Str.h"
12 #include <gc.h>
13 #include "myctype.h"
14 #include "entity.h"
15
16 unsigned char QUOTE_MAP[0x100] = {
17     /* NUL SOH STX ETX EOT ENQ ACK BEL  BS  HT  LF  VT  FF  CR  SO  SI */
18     24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
19     /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN  EM SUB ESC  FS  GS  RS  US */
20     24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
21     /* SPC   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   / */
22     24, 72, 76, 40, 8, 40, 41, 72, 72, 72, 72, 40, 72, 8, 0, 64,
23     /*   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ? */
24     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 72, 74, 72, 75, 40,
25     /*   @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O */
26     72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
27     /*   P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _ */
28     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 72, 72, 72, 0,
29     /*   `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o */
30     72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
31     /*   p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~ DEL */
32     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 72, 72, 72, 24,
33
34     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
35     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
36     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
37     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
38     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
39     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
40     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
41     16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
42 };
43
44 char *HTML_QUOTE_MAP[] = {
45     NULL,
46     "&amp;",
47     "&lt;",
48     "&gt;",
49     "&quot;",
50     NULL,
51     NULL,
52     NULL,
53 };
54
55 clen_t
56 strtoclen(const char *s)
57 {
58 #ifdef HAVE_STRTOLL
59     return strtoll(s, NULL, 10);
60 #elif defined(HAVE_STRTOQ)
61     return strtoq(s, NULL, 10);
62 #elif defined(HAVE_ATOLL)
63     return atoll(s);
64 #elif defined(HAVE_ATOQ)
65     return atoq(s);
66 #else
67     return atoi(s);
68 #endif
69 }
70
71 #ifndef HAVE_BCOPY
72 void
73 bcopy(const void *src, void *dest, int len)
74 {
75     int i;
76     if (src == dest)
77         return;
78     if (src < dest) {
79         for (i = len - 1; i >= 0; i--)
80             ((char *)dest)[i] = ((const char *)src)[i];
81     }
82     else {                      /* src > dest */
83         for (i = 0; i < len; i++)
84             ((char *)dest)[i] = ((const char *)src)[i];
85     }
86 }
87
88 void
89 bzero(void *ptr, int len)
90 {
91     int i;
92     char *p = ptr;
93     for (i = 0; i < len; i++)
94         *(p++) = 0;
95 }
96 #endif                          /* not HAVE_BCOPY */
97
98 char *
99 allocStr(const char *s, int len)
100 {
101     char *ptr;
102
103     if (s == NULL)
104         return NULL;
105     if (len < 0)
106         len = strlen(s);
107     ptr = NewAtom_N(char, len + 1);
108     if (ptr == NULL) {
109         fprintf(stderr, "fm: Can't allocate string. Give me more memory!\n");
110         exit(-1);
111     }
112     bcopy(s, ptr, len);
113     ptr[len] = '\0';
114     return ptr;
115 }
116
117 int
118 strCmp(const void *s1, const void *s2)
119 {
120     return strcmp(*(const char **)s1, *(const char **)s2);
121 }
122
123 char *
124 currentdir()
125 {
126     char *path;
127 #ifdef HAVE_GETCWD
128 #ifdef MAXPATHLEN
129     path = NewAtom_N(char, MAXPATHLEN);
130     getcwd(path, MAXPATHLEN);
131 #else
132     path = getcwd(NULL, 0);
133 #endif
134 #else                           /* not HAVE_GETCWD */
135 #ifdef HAVE_GETWD
136     path = NewAtom_N(char, 1024);
137     getwd(path);
138 #else                           /* not HAVE_GETWD */
139     FILE *f;
140     char *p;
141     path = NewAtom_N(char, 1024);
142     f = popen("pwd", "r");
143     fgets(path, 1024, f);
144     pclose(f);
145     for (p = path; *p; p++)
146         if (*p == '\n') {
147             *p = '\0';
148             break;
149         }
150 #endif                          /* not HAVE_GETWD */
151 #endif                          /* not HAVE_GETCWD */
152     return path;
153 }
154
155 char *
156 cleanupName(char *name)
157 {
158     char *buf, *p, *q;
159
160     buf = allocStr(name, -1);
161     p = buf;
162     q = name;
163     while (*q != '\0') {
164         if (strncmp(p, "/../", 4) == 0) {       /* foo/bar/../FOO */
165             if (p - 2 == buf && strncmp(p - 2, "..", 2) == 0) {
166                 /* ../../       */
167                 p += 3;
168                 q += 3;
169             }
170             else if (p - 3 >= buf && strncmp(p - 3, "/..", 3) == 0) {
171                 /* ../../../    */
172                 p += 3;
173                 q += 3;
174             }
175             else {
176                 while (p != buf && *--p != '/') ;       /* ->foo/FOO */
177                 *p = '\0';
178                 q += 3;
179                 strcat(buf, q);
180             }
181         }
182         else if (strcmp(p, "/..") == 0) {       /* foo/bar/..   */
183             if (p - 2 == buf && strncmp(p - 2, "..", 2) == 0) {
184                 /* ../..        */
185             }
186             else if (p - 3 >= buf && strncmp(p - 3, "/..", 3) == 0) {
187                 /* ../../..     */
188             }
189             else {
190                 while (p != buf && *--p != '/') ;       /* ->foo/ */
191                 *++p = '\0';
192             }
193             break;
194         }
195         else if (strncmp(p, "/./", 3) == 0) {   /* foo/./bar */
196             *p = '\0';          /* -> foo/bar           */
197             q += 2;
198             strcat(buf, q);
199         }
200         else if (strcmp(p, "/.") == 0) {        /* foo/. */
201             *++p = '\0';        /* -> foo/              */
202             break;
203         }
204         else if (strncmp(p, "//", 2) == 0) {    /* foo//bar */
205             /* -> foo/bar           */
206             *p = '\0';
207             q++;
208             strcat(buf, q);
209         }
210         else {
211             p++;
212             q++;
213         }
214     }
215     return buf;
216 }
217
218 char *
219 expandPath(char *name)
220 {
221     char *p;
222     struct passwd *passent, *getpwnam(const char *);
223     Str extpath = NULL;
224
225     if (name == NULL)
226         return NULL;
227     p = name;
228     if (*p == '~') {
229         p++;
230 #ifndef __MINGW32_VERSION
231         if (IS_ALPHA(*p)) {
232             char *q = strchr(p, '/');
233             if (q) {            /* ~user/dir... */
234                 passent = getpwnam(allocStr(p, q - p));
235                 p = q;
236             }
237             else {              /* ~user */
238                 passent = getpwnam(p);
239                 p = "";
240             }
241             if (!passent)
242                 goto rest;
243             extpath = Strnew_charp(passent->pw_dir);
244         } else
245 #endif /* __MINGW32_VERSION */
246           if (*p == '/' || *p == '\0') {        /* ~/dir... or ~ */
247             extpath = Strnew_charp(getenv("HOME"));
248         }
249         else
250             goto rest;
251         if (Strcmp_charp(extpath, "/") == 0 && *p == '/')
252             p++;
253         Strcat_charp(extpath, p);
254         return extpath->ptr;
255     }
256   rest:
257     return name;
258 }
259
260 #ifndef HAVE_STRCHR
261 char *
262 strchr(const char *s, int c)
263 {
264     while (*s) {
265         if ((unsigned char)*s == c)
266             return (char *)s;
267         s++;
268     }
269     return NULL;
270 }
271 #endif                          /* not HAVE_STRCHR */
272
273 #ifndef HAVE_STRCASECMP
274 int
275 strcasecmp(const char *s1, const char *s2)
276 {
277     int x;
278     while (*s1) {
279         x = TOLOWER(*s1) - TOLOWER(*s2);
280         if (x != 0)
281             return x;
282         s1++;
283         s2++;
284     }
285     return -TOLOWER(*s2);
286 }
287
288 int
289 strncasecmp(const char *s1, const char *s2, size_t n)
290 {
291     int x;
292     while (*s1 && n) {
293         x = TOLOWER(*s1) - TOLOWER(*s2);
294         if (x != 0)
295             return x;
296         s1++;
297         s2++;
298         n--;
299     }
300     return n ? -TOLOWER(*s2) : 0;
301 }
302 #endif                          /* not HAVE_STRCASECMP */
303
304 #ifndef HAVE_STRCASESTR
305 /* string search using the simplest algorithm */
306 char *
307 strcasestr(const char *s1, const char *s2)
308 {
309     int len1, len2;
310     if (s2 == NULL)
311         return (char *)s1;
312     if (*s2 == '\0')
313         return (char *)s1;
314     len1 = strlen(s1);
315     len2 = strlen(s2);
316     while (*s1 && len1 >= len2) {
317         if (strncasecmp(s1, s2, len2) == 0)
318             return (char *)s1;
319         s1++;
320         len1--;
321     }
322     return 0;
323 }
324 #endif
325
326 static int
327 strcasematch(char *s1, char *s2)
328 {
329     int x;
330     while (*s1) {
331         if (*s2 == '\0')
332             return 1;
333         x = TOLOWER(*s1) - TOLOWER(*s2);
334         if (x != 0)
335             break;
336         s1++;
337         s2++;
338     }
339     return (*s2 == '\0');
340 }
341
342 /* search multiple strings */
343 int
344 strcasemstr(char *str, char *srch[], char **ret_ptr)
345 {
346     int i;
347     while (*str) {
348         for (i = 0; srch[i]; i++) {
349             if (strcasematch(str, srch[i])) {
350                 if (ret_ptr)
351                     *ret_ptr = str;
352                 return i;
353             }
354         }
355         str++;
356     }
357     return -1;
358 }
359
360 char *
361 remove_space(char *str)
362 {
363     char *p, *q;
364
365     for (p = str; *p && IS_SPACE(*p); p++) ;
366     for (q = p; *q; q++) ;
367     for (; q > p && IS_SPACE(*(q - 1)); q--) ;
368     if (*q != '\0')
369         return Strnew_charp_n(p, q - p)->ptr;
370     return p;
371 }
372
373 int
374 non_null(char *s)
375 {
376     if (s == NULL)
377         return FALSE;
378     while (*s) {
379         if (!IS_SPACE(*s))
380             return TRUE;
381         s++;
382     }
383     return FALSE;
384 }
385
386 void
387 cleanup_line(Str s, int mode)
388 {
389     if (s->length >= 2 &&
390         s->ptr[s->length - 2] == '\r' && s->ptr[s->length - 1] == '\n') {
391         Strshrink(s, 2);
392         Strcat_char(s, '\n');
393     }
394     else if (Strlastchar(s) == '\r')
395         s->ptr[s->length - 1] = '\n';
396     else if (Strlastchar(s) != '\n')
397         Strcat_char(s, '\n');
398     if (mode != PAGER_MODE) {
399         int i;
400         for (i = 0; i < s->length; i++) {
401             if (s->ptr[i] == '\0')
402                 s->ptr[i] = ' ';
403         }
404     }
405 }
406
407 int
408 getescapechar(char **str)
409 {
410     int dummy = -1;
411     char *p = *str, *q;
412     int strict_entity = TRUE;
413
414     if (*p == '&')
415         p++;
416     if (*p == '#') {
417         p++;
418         if (*p == 'x' || *p == 'X') {
419             p++;
420             if (!IS_XDIGIT(*p)) {
421                 *str = p;
422                 return -1;
423             }
424             for (dummy = GET_MYCDIGIT(*p), p++; IS_XDIGIT(*p); p++)
425                 dummy = dummy * 0x10 + GET_MYCDIGIT(*p);
426             if (*p == ';')
427                 p++;
428             *str = p;
429             return dummy;
430         }
431         else {
432             if (!IS_DIGIT(*p)) {
433                 *str = p;
434                 return -1;
435             }
436             for (dummy = GET_MYCDIGIT(*p), p++; IS_DIGIT(*p); p++)
437                 dummy = dummy * 10 + GET_MYCDIGIT(*p);
438             if (*p == ';')
439                 p++;
440             *str = p;
441             return dummy;
442         }
443     }
444     if (!IS_ALPHA(*p)) {
445         *str = p;
446         return -1;
447     }
448     q = p;
449     for (p++; IS_ALNUM(*p); p++) ;
450     q = allocStr(q, p - q);
451     if (strcasestr("lt gt amp quot nbsp", q) && *p != '=') {
452         /* a character entity MUST be terminated with ";". However,
453          * there's MANY web pages which uses &lt , &gt or something
454          * like them as &lt;, &gt;, etc. Therefore, we treat the most
455          * popular character entities (including &#xxxx;) without
456          * the last ";" as character entities. If the trailing character
457          * is "=", it must be a part of query in an URL. So &lt=, &gt=, etc.
458          * are not regarded as character entities.
459          */
460         strict_entity = FALSE;
461     }
462     if (*p == ';')
463         p++;
464     else if (strict_entity) {
465         *str = p;
466         return -1;
467     }
468     *str = p;
469     return getHash_si(&entity, q, -1);
470 }
471
472 char *
473 getescapecmd(char **s)
474 {
475     char *save = *s;
476     Str tmp;
477     int ch = getescapechar(s);
478
479     if (ch >= 0)
480         return conv_entity(ch);
481
482     if (*save != '&')
483         tmp = Strnew_charp("&");
484     else
485         tmp = Strnew();
486     Strcat_charp_n(tmp, save, *s - save);
487     return tmp->ptr;
488 }
489
490 char *
491 html_quote(char *str)
492 {
493     Str tmp = NULL;
494     char *p, *q;
495
496     for (p = str; *p; p++) {
497         q = html_quote_char(*p);
498         if (q) {
499             if (tmp == NULL)
500                 tmp = Strnew_charp_n(str, (int)(p - str));
501             Strcat_charp(tmp, q);
502         }
503         else {
504             if (tmp)
505                 Strcat_char(tmp, *p);
506         }
507     }
508     if (tmp)
509         return tmp->ptr;
510     return str;
511 }
512
513 char *
514 html_unquote(char *str)
515 {
516     Str tmp = NULL;
517     char *p, *q;
518
519     for (p = str; *p;) {
520         if (*p == '&') {
521             if (tmp == NULL)
522                 tmp = Strnew_charp_n(str, (int)(p - str));
523             q = getescapecmd(&p);
524             Strcat_charp(tmp, q);
525         }
526         else {
527             if (tmp)
528                 Strcat_char(tmp, *p);
529             p++;
530         }
531     }
532
533     if (tmp)
534         return tmp->ptr;
535     return str;
536 }
537
538 static char xdigit[0x10] = "0123456789ABCDEF";
539
540 #define url_unquote_char(pstr) \
541   ((IS_XDIGIT((*(pstr))[1]) && IS_XDIGIT((*(pstr))[2])) ? \
542     (*(pstr) += 3, (GET_MYCDIGIT((*(pstr))[-2]) << 4) | GET_MYCDIGIT((*(pstr))[-1])) : \
543    -1)
544
545 char *
546 url_quote(char *str)
547 {
548     Str tmp = NULL;
549     char *p;
550
551     for (p = str; *p; p++) {
552         if (is_url_quote(*p)) {
553             if (tmp == NULL)
554                 tmp = Strnew_charp_n(str, (int)(p - str));
555             Strcat_char(tmp, '%');
556             Strcat_char(tmp, xdigit[((unsigned char)*p >> 4) & 0xF]);
557             Strcat_char(tmp, xdigit[(unsigned char)*p & 0xF]);
558         }
559         else {
560             if (tmp)
561                 Strcat_char(tmp, *p);
562         }
563     }
564     if (tmp)
565         return tmp->ptr;
566     return str;
567 }
568
569 char *
570 file_quote(char *str)
571 {
572     Str tmp = NULL;
573     char *p;
574     char buf[4];
575
576     for (p = str; *p; p++) {
577         if (is_file_quote(*p)) {
578             if (tmp == NULL)
579                 tmp = Strnew_charp_n(str, (int)(p - str));
580             sprintf(buf, "%%%02X", (unsigned char)*p);
581             Strcat_charp(tmp, buf);
582         }
583         else {
584             if (tmp)
585                 Strcat_char(tmp, *p);
586         }
587     }
588     if (tmp)
589         return tmp->ptr;
590     return str;
591 }
592
593 char *
594 file_unquote(char *str)
595 {
596     Str tmp = NULL;
597     char *p, *q;
598     int c;
599
600     for (p = str; *p;) {
601         if (*p == '%') {
602             q = p;
603             c = url_unquote_char(&q);
604             if (c >= 0) {
605                 if (tmp == NULL)
606                     tmp = Strnew_charp_n(str, (int)(p - str));
607                 if (c != '\0' && c != '\n' && c != '\r')
608                     Strcat_char(tmp, (char)c);
609                 p = q;
610                 continue;
611             }
612         }
613         if (tmp)
614             Strcat_char(tmp, *p);
615         p++;
616     }
617     if (tmp)
618         return tmp->ptr;
619     return str;
620 }
621
622 Str
623 Str_form_quote(Str x)
624 {
625     Str tmp = NULL;
626     char *p = x->ptr, *ep = x->ptr + x->length;
627     char buf[4];
628
629     for (; p < ep; p++) {
630         if (*p == ' ') {
631             if (tmp == NULL)
632                 tmp = Strnew_charp_n(x->ptr, (int)(p - x->ptr));
633             Strcat_char(tmp, '+');
634         }
635         else if (is_url_unsafe(*p)) {
636             if (tmp == NULL)
637                 tmp = Strnew_charp_n(x->ptr, (int)(p - x->ptr));
638             sprintf(buf, "%%%02X", (unsigned char)*p);
639             Strcat_charp(tmp, buf);
640         }
641         else {
642             if (tmp)
643                 Strcat_char(tmp, *p);
644         }
645     }
646     if (tmp)
647         return tmp;
648     return x;
649 }
650
651
652 Str
653 Str_url_unquote(Str x, int is_form, int safe)
654 {
655     Str tmp = NULL;
656     char *p = x->ptr, *ep = x->ptr + x->length, *q;
657     int c;
658
659     for (; p < ep;) {
660         if (is_form && *p == '+') {
661             if (tmp == NULL)
662                 tmp = Strnew_charp_n(x->ptr, (int)(p - x->ptr));
663             Strcat_char(tmp, ' ');
664             p++;
665             continue;
666         }
667         else if (*p == '%') {
668             q = p;
669             c = url_unquote_char(&q);
670             if (c >= 0 && (!safe || !IS_ASCII(c) || !is_file_quote(c))) {
671                 if (tmp == NULL)
672                     tmp = Strnew_charp_n(x->ptr, (int)(p - x->ptr));
673                 Strcat_char(tmp, (char)c);
674                 p = q;
675                 continue;
676             }
677         }
678         if (tmp)
679             Strcat_char(tmp, *p);
680         p++;
681     }
682     if (tmp)
683         return tmp;
684     return x;
685 }
686
687 char *
688 shell_quote(char *str)
689 {
690     Str tmp = NULL;
691     char *p;
692
693     for (p = str; *p; p++) {
694         if (is_shell_unsafe(*p)) {
695             if (tmp == NULL)
696                 tmp = Strnew_charp_n(str, (int)(p - str));
697             Strcat_char(tmp, '\\');
698             Strcat_char(tmp, *p);
699         }
700         else {
701             if (tmp)
702                 Strcat_char(tmp, *p);
703         }
704     }
705     if (tmp)
706         return tmp->ptr;
707     return str;
708 }
709
710 static char *
711 w3m_dir(const char *name, char *dft)
712 {
713 #ifdef USE_PATH_ENVVAR
714     char *value = getenv(name);
715     return value ? value : dft;
716 #else
717     return dft;
718 #endif
719 }
720
721 char *
722 w3m_auxbin_dir()
723 {
724     return w3m_dir("W3M_AUXBIN_DIR", AUXBIN_DIR);
725 }
726
727 char *
728 w3m_lib_dir()
729 {
730     /* FIXME: use W3M_CGIBIN_DIR? */
731     return w3m_dir("W3M_LIB_DIR", CGIBIN_DIR);
732 }
733
734 char *
735 w3m_etc_dir()
736 {
737     return w3m_dir("W3M_ETC_DIR", ETC_DIR);
738 }
739
740 char *
741 w3m_conf_dir()
742 {
743     return w3m_dir("W3M_CONF_DIR", CONF_DIR);
744 }
745
746 char *
747 w3m_help_dir()
748 {
749     return w3m_dir("W3M_HELP_DIR", HELP_DIR);
750 }
751 /* Local Variables:    */
752 /* c-basic-offset: 4   */
753 /* tab-width: 8        */
754 /* End:                */