w3m

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/w3m.git/
Log | Files | Refs | README

etc.c (38903B)


      1 /* $Id$ */
      2 #include "fm.h"
      3 #ifndef __MINGW32_VERSION
      4 #include <pwd.h>
      5 #endif
      6 #include "myctype.h"
      7 #include "html.h"
      8 #include "local.h"
      9 #include "hash.h"
     10 
     11 #include <fcntl.h>
     12 #include <sys/types.h>
     13 #include <time.h>
     14 #if defined(HAVE_WAITPID) || defined(HAVE_WAIT3)
     15 #include <sys/wait.h>
     16 #endif
     17 #include <signal.h>
     18 
     19 #ifdef	__WATT32__
     20 #define	read(a,b,c)	read_s(a,b,c)
     21 #define	close(x)	close_s(x)
     22 #endif				/* __WATT32__ */
     23 
     24 struct auth_pass {
     25     int bad;
     26     int is_proxy;
     27     Str host;
     28     int port;
     29 /*    Str file; */
     30     Str realm;
     31     Str uname;
     32     Str pwd;
     33     struct auth_pass *next;
     34 };
     35 
     36 struct auth_pass *passwords = NULL;
     37 
     38 int
     39 columnSkip(Buffer *buf, int offset)
     40 {
     41     int i, maxColumn;
     42     int column = buf->currentColumn + offset;
     43     int nlines = buf->LINES + 1;
     44     Line *l;
     45 
     46     maxColumn = 0;
     47     for (i = 0, l = buf->topLine; i < nlines && l != NULL; i++, l = l->next) {
     48 	if (l->width < 0)
     49 	    l->width = COLPOS(l, l->len);
     50 	if (l->width - 1 > maxColumn)
     51 	    maxColumn = l->width - 1;
     52     }
     53     maxColumn -= buf->COLS - 1;
     54     if (column < maxColumn)
     55 	maxColumn = column;
     56     if (maxColumn < 0)
     57 	maxColumn = 0;
     58 
     59     if (buf->currentColumn == maxColumn)
     60 	return 0;
     61     buf->currentColumn = maxColumn;
     62     return 1;
     63 }
     64 
     65 int
     66 columnPos(Line *line, int column)
     67 {
     68     int i;
     69 
     70     for (i = 1; i < line->len; i++) {
     71 	if (COLPOS(line, i) > column)
     72 	    break;
     73     }
     74 #ifdef USE_M17N
     75     for (i--; i > 0 && line->propBuf[i] & PC_WCHAR2; i--) ;
     76     return i;
     77 #else
     78     return i - 1;
     79 #endif
     80 }
     81 
     82 Line *
     83 lineSkip(Buffer *buf, Line *line, int offset, int last)
     84 {
     85     int i;
     86     Line *l;
     87 
     88     l = currentLineSkip(buf, line, offset, last);
     89     if (!nextpage_topline)
     90 	for (i = buf->LINES - 1 - (buf->lastLine->linenumber - l->linenumber);
     91 	     i > 0 && l->prev != NULL; i--, l = l->prev) ;
     92     return l;
     93 }
     94 
     95 Line *
     96 currentLineSkip(Buffer *buf, Line *line, int offset, int last)
     97 {
     98     int i, n;
     99     Line *l = line;
    100 
    101     if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) {
    102 	n = line->linenumber + offset + buf->LINES;
    103 	if (buf->lastLine->linenumber < n)
    104 	    getNextPage(buf, n - buf->lastLine->linenumber);
    105 	while ((last || (buf->lastLine->linenumber < n)) &&
    106 	       (getNextPage(buf, 1) != NULL)) ;
    107 	if (last)
    108 	    l = buf->lastLine;
    109     }
    110 
    111     if (offset == 0)
    112 	return l;
    113     if (offset > 0)
    114 	for (i = 0; i < offset && l->next != NULL; i++, l = l->next) ;
    115     else
    116 	for (i = 0; i < -offset && l->prev != NULL; i++, l = l->prev) ;
    117     return l;
    118 }
    119 
    120 #define MAX_CMD_LEN 128
    121 
    122 int
    123 gethtmlcmd(char **s)
    124 {
    125     extern Hash_si tagtable;
    126     char cmdstr[MAX_CMD_LEN];
    127     char *p = cmdstr;
    128     char *save = *s;
    129     int cmd;
    130 
    131     (*s)++;
    132     /* first character */
    133     if (IS_ALNUM(**s) || **s == '_' || **s == '/') {
    134 	*(p++) = TOLOWER(**s);
    135 	(*s)++;
    136     }
    137     else
    138 	return HTML_UNKNOWN;
    139     if (p[-1] == '/')
    140 	SKIP_BLANKS(*s);
    141     while ((IS_ALNUM(**s) || **s == '_') && p - cmdstr < MAX_CMD_LEN) {
    142 	*(p++) = TOLOWER(**s);
    143 	(*s)++;
    144     }
    145     if (p - cmdstr == MAX_CMD_LEN) {
    146 	/* buffer overflow: perhaps caused by bad HTML source */
    147 	*s = save + 1;
    148 	return HTML_UNKNOWN;
    149     }
    150     *p = '\0';
    151 
    152     /* hash search */
    153     cmd = getHash_si(&tagtable, cmdstr, HTML_UNKNOWN);
    154     while (**s && **s != '>')
    155 	(*s)++;
    156     if (**s == '>')
    157 	(*s)++;
    158     return cmd;
    159 }
    160 
    161 #ifdef USE_ANSI_COLOR
    162 static int
    163 parse_ansi_color(char **str, Lineprop *effect, Linecolor *color)
    164 {
    165     char *p = *str, *q;
    166     Lineprop e = *effect;
    167     Linecolor c = *color;
    168     int i;
    169 
    170     if (*p != ESC_CODE || *(p + 1) != '[')
    171 	return 0;
    172     p += 2;
    173     for (q = p; IS_DIGIT(*q) || *q == ';'; q++) ;
    174     if (*q != 'm')
    175 	return 0;
    176     *str = q + 1;
    177     while (1) {
    178 	if (*p == 'm') {
    179 	    e = PE_NORMAL;
    180 	    c = 0;
    181 	    break;
    182 	}
    183 	if (IS_DIGIT(*p)) {
    184 	    q = p;
    185 	    for (p++; IS_DIGIT(*p); p++) ;
    186 	    i = atoi(allocStr(q, p - q));
    187 	    switch (i) {
    188 	    case 0:
    189 		e = PE_NORMAL;
    190 		c = 0;
    191 		break;
    192 	    case 1:
    193 	    case 5:
    194 		e = PE_BOLD;
    195 		break;
    196 	    case 4:
    197 		e = PE_UNDER;
    198 		break;
    199 	    case 7:
    200 		e = PE_STAND;
    201 		break;
    202 	    case 100:		/* for EWS4800 kterm */
    203 		c = 0;
    204 		break;
    205 	    case 39:
    206 		c &= 0xf0;
    207 		break;
    208 	    case 49:
    209 		c &= 0x0f;
    210 		break;
    211 	    default:
    212 		if (i >= 30 && i <= 37)
    213 		    c = (c & 0xf0) | (i - 30) | 0x08;
    214 		else if (i >= 40 && i <= 47)
    215 		    c = (c & 0x0f) | ((i - 40) << 4) | 0x80;
    216 		break;
    217 	    }
    218 	    if (*p == 'm')
    219 		break;
    220 	}
    221 	else {
    222 	    e = PE_NORMAL;
    223 	    c = 0;
    224 	    break;
    225 	}
    226 	p++;			/* *p == ';' */
    227     }
    228     *effect = e;
    229     *color = c;
    230     return 1;
    231 }
    232 #endif
    233 /* 
    234  * Check character type
    235  */
    236 
    237 Str
    238 checkType(Str s, Lineprop **oprop, Linecolor **ocolor)
    239 {
    240     Lineprop mode;
    241     Lineprop effect = PE_NORMAL;
    242     Lineprop *prop;
    243     static Lineprop *prop_buffer = NULL;
    244     static int prop_size = 0;
    245     char *str = s->ptr, *endp = &s->ptr[s->length], *bs = NULL;
    246 #ifdef USE_ANSI_COLOR
    247     Lineprop ceffect = PE_NORMAL;
    248     Linecolor cmode = 0;
    249     int check_color = FALSE;
    250     Linecolor *color = NULL;
    251     static Linecolor *color_buffer = NULL;
    252     static int color_size = 0;
    253     char *es = NULL;
    254 #endif
    255     int do_copy = FALSE;
    256     int i;
    257     int plen = 0, clen;
    258 
    259     if (prop_size < s->length) {
    260 	prop_size = (s->length > LINELEN) ? s->length : LINELEN;
    261 	prop_buffer = New_Reuse(Lineprop, prop_buffer, prop_size);
    262     }
    263     prop = prop_buffer;
    264 
    265     if (ShowEffect) {
    266 	bs = memchr(str, '\b', s->length);
    267 #ifdef USE_ANSI_COLOR
    268 	if (ocolor) {
    269 	    es = memchr(str, ESC_CODE, s->length);
    270 	    if (es) {
    271 		if (color_size < s->length) {
    272 		    color_size = (s->length > LINELEN) ? s->length : LINELEN;
    273 		    color_buffer = New_Reuse(Linecolor, color_buffer,
    274 					     color_size);
    275 		}
    276 		color = color_buffer;
    277 	    }
    278 	}
    279 #endif
    280 	if ((bs != NULL)
    281 #ifdef USE_ANSI_COLOR
    282 	    || (es != NULL)
    283 #endif
    284 	    ) {
    285 	    char *sp = str, *ep;
    286 	    s = Strnew_size(s->length);
    287 	    do_copy = TRUE;
    288 	    ep = bs ? (bs - 2) : endp;
    289 #ifdef USE_ANSI_COLOR
    290 	    if (es && ep > es - 2)
    291 		ep = es - 2;
    292 #endif
    293 	    for (; str < ep && IS_ASCII(*str); str++) {
    294 		*(prop++) = PE_NORMAL | (IS_CNTRL(*str) ? PC_CTRL : PC_ASCII);
    295 #ifdef USE_ANSI_COLOR
    296 		if (color)
    297 		    *(color++) = 0;
    298 #endif
    299 	    }
    300 	    Strcat_charp_n(s, sp, (int)(str - sp));
    301 	}
    302     }
    303     if (!do_copy) {
    304 	for (; str < endp && IS_ASCII(*str); str++)
    305 	    *(prop++) = PE_NORMAL | (IS_CNTRL(*str) ? PC_CTRL : PC_ASCII);
    306     }
    307 
    308     while (str < endp) {
    309 	if (prop - prop_buffer >= prop_size)
    310 	    break;
    311 	if (bs != NULL) {
    312 #ifdef USE_M17N
    313 	    if (str == bs - 2 && !strncmp(str, "__\b\b", 4)) {
    314 		str += 4;
    315 		effect = PE_UNDER;
    316 		if (str < endp)
    317 		    bs = memchr(str, '\b', endp - str);
    318 		continue;
    319 	    }
    320 	    else
    321 #endif
    322 	    if (str == bs - 1 && *str == '_') {
    323 		str += 2;
    324 		effect = PE_UNDER;
    325 		if (str < endp)
    326 		    bs = memchr(str, '\b', endp - str);
    327 		continue;
    328 	    }
    329 	    else if (str == bs) {
    330 		if (*(str + 1) == '_') {
    331 		    if (s->length) {
    332 			str += 2;
    333 #ifdef USE_M17N
    334 			for (i = 1; i <= plen; i++)
    335 			    *(prop - i) |= PE_UNDER;
    336 #else
    337 			*(prop - 1) |= PE_UNDER;
    338 #endif
    339 		    }
    340 		    else {
    341 			str++;
    342 		    }
    343 		}
    344 #ifdef USE_M17N
    345 		else if (!strncmp(str + 1, "\b__", 3)) {
    346 		    if (s->length) {
    347 			str += (plen == 1) ? 3 : 4;
    348 			for (i = 1; i <= plen; i++)
    349 			    *(prop - i) |= PE_UNDER;
    350 		    }
    351 		    else {
    352 			str += 2;
    353 		    }
    354 		}
    355 		else if (*(str + 1) == '\b') {
    356 		    if (s->length) {
    357 			clen = get_mclen(str + 2);
    358 			if (plen == clen &&
    359 			    !strncmp(str - plen, str + 2, plen)) {
    360 			    for (i = 1; i <= plen; i++)
    361 				*(prop - i) |= PE_BOLD;
    362 			    str += 2 + clen;
    363 			}
    364 			else {
    365 			    Strshrink(s, plen);
    366 			    prop -= plen;
    367 			    str += 2;
    368 			}
    369 		    }
    370 		    else {
    371 			str += 2;
    372 		    }
    373 		}
    374 #endif
    375 		else {
    376 		    if (s->length) {
    377 #ifdef USE_M17N
    378 			clen = get_mclen(str + 1);
    379 			if (plen == clen &&
    380 			    !strncmp(str - plen, str + 1, plen)) {
    381 			    for (i = 1; i <= plen; i++)
    382 				*(prop - i) |= PE_BOLD;
    383 			    str += 1 + clen;
    384 			}
    385 			else {
    386 			    Strshrink(s, plen);
    387 			    prop -= plen;
    388 			    str++;
    389 			}
    390 #else
    391 			if (*(str - 1) == *(str + 1)) {
    392 			    *(prop - 1) |= PE_BOLD;
    393 			    str += 2;
    394 			}
    395 			else {
    396 			    Strshrink(s, 1);
    397 			    prop--;
    398 			    str++;
    399 			}
    400 #endif
    401 		    }
    402 		    else {
    403 			str++;
    404 		    }
    405 		}
    406 		if (str < endp)
    407 		    bs = memchr(str, '\b', endp - str);
    408 		continue;
    409 	    }
    410 #ifdef USE_ANSI_COLOR
    411 	    else if (str > bs)
    412 		bs = memchr(str, '\b', endp - str);
    413 #endif
    414 	}
    415 #ifdef USE_ANSI_COLOR
    416 	if (es != NULL) {
    417 	    if (str == es) {
    418 		int ok = parse_ansi_color(&str, &ceffect, &cmode);
    419 		if (str < endp)
    420 		    es = memchr(str, ESC_CODE, endp - str);
    421 		if (ok) {
    422 		    if (cmode)
    423 			check_color = TRUE;
    424 		    continue;
    425 		}
    426 	    }
    427 	    else if (str > es)
    428 		es = memchr(str, ESC_CODE, endp - str);
    429 	}
    430 #endif
    431 
    432 	plen = get_mclen(str);
    433 	mode = get_mctype(str) | effect;
    434 #ifdef USE_ANSI_COLOR
    435 	if (color) {
    436 	    *(color++) = cmode;
    437 	    mode |= ceffect;
    438 	}
    439 #endif
    440 	*(prop++) = mode;
    441 #ifdef USE_M17N
    442 	if (plen > 1) {
    443 	    mode = (mode & ~PC_WCHAR1) | PC_WCHAR2;
    444 	    for (i = 1; i < plen; i++) {
    445 		*(prop++) = mode;
    446 #ifdef USE_ANSI_COLOR
    447 		if (color)
    448 		    *(color++) = cmode;
    449 #endif
    450 	    }
    451 	    if (do_copy)
    452 		Strcat_charp_n(s, (char *)str, plen);
    453 	    str += plen;
    454 	}
    455 	else
    456 #endif
    457 	{
    458 	    if (do_copy)
    459 		Strcat_char(s, (char)*str);
    460 	    str++;
    461 	}
    462 	effect = PE_NORMAL;
    463     }
    464     *oprop = prop_buffer;
    465 #ifdef USE_ANSI_COLOR
    466     if (ocolor)
    467 	*ocolor = check_color ? color_buffer : NULL;
    468 #endif
    469     return s;
    470 }
    471 
    472 static int
    473 nextColumn(int n, char *p, Lineprop *pr)
    474 {
    475     if (*pr & PC_CTRL) {
    476 	if (*p == '\t')
    477 	    return (n + Tabstop) / Tabstop * Tabstop;
    478 	else if (*p == '\n')
    479 	    return n + 1;
    480 	else if (*p != '\r')
    481 	    return n + 2;
    482 	return n;
    483     }
    484 #ifdef USE_M17N
    485     if (*pr & PC_UNKNOWN)
    486 	return n + 4;
    487     return n + wtf_width((wc_uchar *) p);
    488 #else
    489     return n + 1;
    490 #endif
    491 }
    492 
    493 int
    494 calcPosition(char *l, Lineprop *pr, int len, int pos, int bpos, int mode)
    495 {
    496     static int *realColumn = NULL;
    497     static int size = 0;
    498     static char *prevl = NULL;
    499     int i, j;
    500 
    501     if (l == NULL || len == 0)
    502 	return bpos;
    503     if (l == prevl && mode == CP_AUTO) {
    504 	if (pos <= len)
    505 	    return realColumn[pos];
    506     }
    507     if (size < len + 1) {
    508 	size = (len + 1 > LINELEN) ? (len + 1) : LINELEN;
    509 	realColumn = New_N(int, size);
    510     }
    511     prevl = l;
    512     i = 0;
    513     j = bpos;
    514 #ifdef USE_M17N
    515     if (pr[i] & PC_WCHAR2) {
    516 	for (; i < len && pr[i] & PC_WCHAR2; i++)
    517 	    realColumn[i] = j;
    518 	if (i > 0 && pr[i - 1] & PC_KANJI && WcOption.use_wide)
    519 	    j++;
    520     }
    521 #endif
    522     while (1) {
    523 	realColumn[i] = j;
    524 	if (i == len)
    525 	    break;
    526 	j = nextColumn(j, &l[i], &pr[i]);
    527 	i++;
    528 #ifdef USE_M17N
    529 	for (; i < len && pr[i] & PC_WCHAR2; i++)
    530 	    realColumn[i] = realColumn[i - 1];
    531 #endif
    532     }
    533     if (pos >= i)
    534 	return j;
    535     return realColumn[pos];
    536 }
    537 
    538 int
    539 columnLen(Line *line, int column)
    540 {
    541     int i, j;
    542 
    543     for (i = 0, j = 0; i < line->len;) {
    544 	j = nextColumn(j, &line->lineBuf[i], &line->propBuf[i]);
    545 	if (j > column)
    546 	    return i;
    547 	i++;
    548 #ifdef USE_M17N
    549 	while (i < line->len && line->propBuf[i] & PC_WCHAR2)
    550 	    i++;
    551 #endif
    552     }
    553     return line->len;
    554 }
    555 
    556 char *
    557 lastFileName(char *path)
    558 {
    559     char *p, *q;
    560 
    561     p = q = path;
    562     while (*p != '\0') {
    563 	if (*p == '/')
    564 	    q = p + 1;
    565 	p++;
    566     }
    567 
    568     return allocStr(q, -1);
    569 }
    570 
    571 #ifdef USE_INCLUDED_SRAND48
    572 static unsigned long R1 = 0x1234abcd;
    573 static unsigned long R2 = 0x330e;
    574 #define A1 0x5deec
    575 #define A2 0xe66d
    576 #define C 0xb
    577 
    578 void
    579 srand48(long seed)
    580 {
    581     R1 = (unsigned long)seed;
    582     R2 = 0x330e;
    583 }
    584 
    585 long
    586 lrand48(void)
    587 {
    588     R1 = (A1 * R1 << 16) + A1 * R2 + A2 * R1 + ((A2 * R2 + C) >> 16);
    589     R2 = (A2 * R2 + C) & 0xffff;
    590     return (long)(R1 >> 1);
    591 }
    592 #endif
    593 
    594 char *
    595 mybasename(char *s)
    596 {
    597     char *p = s;
    598     while (*p)
    599 	p++;
    600     while (s <= p && *p != '/')
    601 	p--;
    602     if (*p == '/')
    603 	p++;
    604     else
    605 	p = s;
    606     return allocStr(p, -1);
    607 }
    608 
    609 char *
    610 mydirname(char *s)
    611 {
    612     char *p = s;
    613     while (*p)
    614 	p++;
    615     if (s != p)
    616 	p--;
    617     while (s != p && *p == '/')
    618 	p--;
    619     while (s != p && *p != '/')
    620 	p--;
    621     if (*p != '/')
    622 	return ".";
    623     while (s != p && *p == '/')
    624 	p--;
    625     return allocStr(s, strlen(s) - strlen(p) + 1);
    626 }
    627 
    628 #ifndef HAVE_STRERROR
    629 char *
    630 strerror(int errno)
    631 {
    632     extern char *sys_errlist[];
    633     return sys_errlist[errno];
    634 }
    635 #endif				/* not HAVE_STRERROR */
    636 
    637 #ifndef HAVE_SYS_ERRLIST
    638 char **sys_errlist;
    639 
    640 prepare_sys_errlist()
    641 {
    642     int i, n;
    643 
    644     i = 1;
    645     while (strerror(i) != NULL)
    646 	i++;
    647     n = i;
    648     sys_errlist = New_N(char *, n);
    649     sys_errlist[0] = "";
    650     for (i = 1; i < n; i++)
    651 	sys_errlist[i] = strerror(i);
    652 }
    653 #endif				/* not HAVE_SYS_ERRLIST */
    654 
    655 int
    656 next_status(char c, int *status)
    657 {
    658     switch (*status) {
    659     case R_ST_NORMAL:
    660 	if (c == '<') {
    661 	    *status = R_ST_TAG0;
    662 	    return 0;
    663 	}
    664 	else if (c == '&') {
    665 	    *status = R_ST_AMP;
    666 	    return 1;
    667 	}
    668 	else
    669 	    return 1;
    670 	break;
    671     case R_ST_TAG0:
    672 	if (c == '!') {
    673 	    *status = R_ST_CMNT1;
    674 	    return 0;
    675 	}
    676 	*status = R_ST_TAG;
    677 	/* continues to next case */
    678     case R_ST_TAG:
    679 	if (c == '>')
    680 	    *status = R_ST_NORMAL;
    681 	else if (c == '=')
    682 	    *status = R_ST_EQL;
    683 	return 0;
    684     case R_ST_EQL:
    685 	if (c == '"')
    686 	    *status = R_ST_DQUOTE;
    687 	else if (c == '\'')
    688 	    *status = R_ST_QUOTE;
    689 	else if (IS_SPACE(c))
    690 	    *status = R_ST_EQL;
    691 	else if (c == '>')
    692 	    *status = R_ST_NORMAL;
    693 	else
    694 	    *status = R_ST_VALUE;
    695 	return 0;
    696     case R_ST_QUOTE:
    697 	if (c == '\'')
    698 	    *status = R_ST_TAG;
    699 	return 0;
    700     case R_ST_DQUOTE:
    701 	if (c == '"')
    702 	    *status = R_ST_TAG;
    703 	return 0;
    704     case R_ST_VALUE:
    705 	if (c == '>')
    706 	    *status = R_ST_NORMAL;
    707 	else if (IS_SPACE(c))
    708 	    *status = R_ST_TAG;
    709 	return 0;
    710     case R_ST_AMP:
    711 	if (c == ';') {
    712 	    *status = R_ST_NORMAL;
    713 	    return 0;
    714 	}
    715 	else if (c != '#' && !IS_ALNUM(c) && c != '_') {
    716 	    /* something's wrong! */
    717 	    *status = R_ST_NORMAL;
    718 	    return 0;
    719 	}
    720 	else
    721 	    return 0;
    722     case R_ST_CMNT1:
    723 	switch (c) {
    724 	case '-':
    725 	    *status = R_ST_CMNT2;
    726 	    break;
    727 	case '>':
    728 	    *status = R_ST_NORMAL;
    729 	    break;
    730 	default:
    731 	    *status = R_ST_IRRTAG;
    732 	}
    733 	return 0;
    734     case R_ST_CMNT2:
    735 	switch (c) {
    736 	case '-':
    737 	    *status = R_ST_CMNT;
    738 	    break;
    739 	case '>':
    740 	    *status = R_ST_NORMAL;
    741 	    break;
    742 	default:
    743 	    *status = R_ST_IRRTAG;
    744 	}
    745 	return 0;
    746     case R_ST_CMNT:
    747 	if (c == '-')
    748 	    *status = R_ST_NCMNT1;
    749 	return 0;
    750     case R_ST_NCMNT1:
    751 	if (c == '-')
    752 	    *status = R_ST_NCMNT2;
    753 	else
    754 	    *status = R_ST_CMNT;
    755 	return 0;
    756     case R_ST_NCMNT2:
    757 	switch (c) {
    758 	case '>':
    759 	    *status = R_ST_NORMAL;
    760 	    break;
    761 	case '-':
    762 	    *status = R_ST_NCMNT2;
    763 	    break;
    764 	default:
    765 	    if (IS_SPACE(c))
    766 		*status = R_ST_NCMNT3;
    767 	    else
    768 		*status = R_ST_CMNT;
    769 	    break;
    770 	}
    771 	break;
    772     case R_ST_NCMNT3:
    773 	switch (c) {
    774 	case '>':
    775 	    *status = R_ST_NORMAL;
    776 	    break;
    777 	case '-':
    778 	    *status = R_ST_NCMNT1;
    779 	    break;
    780 	default:
    781 	    if (IS_SPACE(c))
    782 		*status = R_ST_NCMNT3;
    783 	    else
    784 		*status = R_ST_CMNT;
    785 	    break;
    786 	}
    787 	return 0;
    788     case R_ST_IRRTAG:
    789 	if (c == '>')
    790 	    *status = R_ST_NORMAL;
    791 	return 0;
    792     }
    793     /* notreached */
    794     return 0;
    795 }
    796 
    797 int
    798 read_token(Str buf, char **instr, int *status, int pre, int append)
    799 {
    800     char *p;
    801     int prev_status;
    802 
    803     if (!append)
    804 	Strclear(buf);
    805     if (**instr == '\0')
    806 	return 0;
    807     for (p = *instr; *p; p++) {
    808 	prev_status = *status;
    809 	next_status(*p, status);
    810 	switch (*status) {
    811 	case R_ST_NORMAL:
    812 	    if (prev_status == R_ST_AMP && *p != ';') {
    813 		p--;
    814 		break;
    815 	    }
    816 	    if (prev_status == R_ST_NCMNT2 || prev_status == R_ST_NCMNT3 ||
    817 		prev_status == R_ST_IRRTAG || prev_status == R_ST_CMNT1) {
    818 		if (prev_status == R_ST_CMNT1 && !append && !pre)
    819 		    Strclear(buf);
    820 		if (pre)
    821 		    Strcat_char(buf, *p);
    822 		p++;
    823 		goto proc_end;
    824 	    }
    825 	    Strcat_char(buf, (!pre && IS_SPACE(*p)) ? ' ' : *p);
    826 	    if (ST_IS_REAL_TAG(prev_status)) {
    827 		*instr = p + 1;
    828 		if (buf->length < 2 ||
    829 		    buf->ptr[buf->length - 2] != '<' ||
    830 		    buf->ptr[buf->length - 1] != '>')
    831 		    return 1;
    832 		Strshrink(buf, 2);
    833 	    }
    834 	    break;
    835 	case R_ST_TAG0:
    836 	case R_ST_TAG:
    837 	    if (prev_status == R_ST_NORMAL && p != *instr) {
    838 		*instr = p;
    839 		*status = prev_status;
    840 		return 1;
    841 	    }
    842 	    if (*status == R_ST_TAG0 && !REALLY_THE_BEGINNING_OF_A_TAG(p)) {
    843 		/* it seems that this '<' is not a beginning of a tag */
    844 		/*
    845 		 * Strcat_charp(buf, "&lt;");
    846 		 */
    847 		Strcat_char(buf, '<');
    848 		*status = R_ST_NORMAL;
    849 	    }
    850 	    else
    851 		Strcat_char(buf, *p);
    852 	    break;
    853 	case R_ST_EQL:
    854 	case R_ST_QUOTE:
    855 	case R_ST_DQUOTE:
    856 	case R_ST_VALUE:
    857 	case R_ST_AMP:
    858 	    Strcat_char(buf, *p);
    859 	    break;
    860 	case R_ST_CMNT:
    861 	case R_ST_IRRTAG:
    862 	    if (pre)
    863 		Strcat_char(buf, *p);
    864 	    else if (!append)
    865 		Strclear(buf);
    866 	    break;
    867 	case R_ST_CMNT1:
    868 	case R_ST_CMNT2:
    869 	case R_ST_NCMNT1:
    870 	case R_ST_NCMNT2:
    871 	case R_ST_NCMNT3:
    872 	    /* do nothing */
    873 	    if (pre)
    874 		Strcat_char(buf, *p);
    875 	    break;
    876 	}
    877     }
    878   proc_end:
    879     *instr = p;
    880     return 1;
    881 }
    882 
    883 Str
    884 correct_irrtag(int status)
    885 {
    886     char c;
    887     Str tmp = Strnew();
    888 
    889     while (status != R_ST_NORMAL) {
    890 	switch (status) {
    891 	case R_ST_CMNT:	/* required "-->" */
    892 	case R_ST_NCMNT1:	/* required "->" */
    893 	    c = '-';
    894 	    break;
    895 	case R_ST_NCMNT2:
    896 	case R_ST_NCMNT3:
    897 	case R_ST_IRRTAG:
    898 	case R_ST_CMNT1:
    899 	case R_ST_CMNT2:
    900 	case R_ST_TAG:
    901 	case R_ST_TAG0:
    902 	case R_ST_EQL:		/* required ">" */
    903 	case R_ST_VALUE:
    904 	    c = '>';
    905 	    break;
    906 	case R_ST_QUOTE:
    907 	    c = '\'';
    908 	    break;
    909 	case R_ST_DQUOTE:
    910 	    c = '"';
    911 	    break;
    912 	case R_ST_AMP:
    913 	    c = ';';
    914 	    break;
    915 	default:
    916 	    return tmp;
    917 	}
    918 	next_status(c, &status);
    919 	Strcat_char(tmp, c);
    920     }
    921     return tmp;
    922 }
    923 
    924 /*
    925  * RFC2617: 1.2 Access Authentication Framework
    926  *
    927  * The realm value (case-sensitive), in combination with the canonical root
    928  * URL (the absoluteURI for the server whose abs_path is empty; see section
    929  * 5.1.2 of RFC2616 ) of the server being accessed, defines the protection
    930  * space. These realms allow the protected resources on a server to be
    931  * partitioned into a set of protection spaces, each with its own
    932  * authentication scheme and/or authorization database.
    933  *
    934  */
    935 static void
    936 add_auth_pass_entry(const struct auth_pass *ent, int netrc, int override)
    937 {
    938     if ((ent->host || netrc)	/* netrc accept default (host == NULL) */
    939 	&&(ent->is_proxy || ent->realm || netrc)
    940 	&& ent->uname && ent->pwd) {
    941 	struct auth_pass *newent = New(struct auth_pass);
    942 	memcpy(newent, ent, sizeof(struct auth_pass));
    943 	if (override) {
    944 	    newent->next = passwords;
    945 	    passwords = newent;
    946 	} 
    947 	else {
    948 	    if (passwords == NULL)
    949 		passwords = newent;
    950 	    else if (passwords->next == NULL)
    951 		passwords->next = newent;
    952 	    else {
    953 		struct auth_pass *ep = passwords;
    954 		for (; ep->next; ep = ep->next) ;
    955 		ep->next = newent;
    956 	    }
    957 	}
    958     }
    959     /* ignore invalid entries */
    960 }
    961 
    962 static struct auth_pass *
    963 find_auth_pass_entry(char *host, int port, char *realm, char *uname, 
    964 		     int is_proxy)
    965 {
    966     struct auth_pass *ent;
    967     for (ent = passwords; ent != NULL; ent = ent->next) {
    968 	if (ent->is_proxy == is_proxy
    969 	    && (ent->bad != TRUE)
    970 	    && (!ent->host || !Strcasecmp_charp(ent->host, host))
    971 	    && (!ent->port || ent->port == port)
    972 	    && (!ent->uname || !uname || !Strcmp_charp(ent->uname, uname))
    973 	    && (!ent->realm || !realm || !Strcmp_charp(ent->realm, realm))
    974 	    )
    975 	    return ent;
    976     }
    977     return NULL;
    978 }
    979 
    980 int
    981 find_auth_user_passwd(ParsedURL *pu, char *realm,
    982 		      Str *uname, Str *pwd, int is_proxy)
    983 {
    984     struct auth_pass *ent;
    985 
    986     if (pu->user && pu->pass) {
    987 	*uname = Strnew_charp(pu->user);
    988 	*pwd = Strnew_charp(pu->pass);
    989 	return 1;
    990     }
    991     ent = find_auth_pass_entry(pu->host, pu->port, realm, pu->user, is_proxy);
    992     if (ent) {
    993 	*uname = ent->uname;
    994 	*pwd = ent->pwd;
    995 	return 1;
    996     }
    997     return 0;
    998 }
    999 
   1000 void
   1001 add_auth_user_passwd(ParsedURL *pu, char *realm, Str uname, Str pwd, 
   1002 		     int is_proxy)
   1003 {
   1004     struct auth_pass ent;
   1005     memset(&ent, 0, sizeof(ent));
   1006 
   1007     ent.is_proxy = is_proxy;
   1008     ent.host = Strnew_charp(pu->host);
   1009     ent.port = pu->port;
   1010     ent.realm = Strnew_charp(realm);
   1011     ent.uname = uname;
   1012     ent.pwd = pwd;
   1013     add_auth_pass_entry(&ent, 0, 1);
   1014 }
   1015 
   1016 void
   1017 invalidate_auth_user_passwd(ParsedURL *pu, char *realm, Str uname, Str pwd, 
   1018 			    int is_proxy)
   1019 {
   1020     struct auth_pass *ent;
   1021     ent = find_auth_pass_entry(pu->host, pu->port, realm, NULL, is_proxy);
   1022     if (ent) {
   1023 	ent->bad = TRUE;
   1024     }
   1025     return;
   1026 }
   1027 
   1028 /* passwd */
   1029 /*
   1030  * machine <host>
   1031  * host <host>
   1032  * port <port>
   1033  * proxy
   1034  * path <file>	; not used
   1035  * realm <realm>
   1036  * login <login>
   1037  * passwd <passwd>
   1038  * password <passwd>
   1039  */
   1040 
   1041 static Str
   1042 next_token(Str arg)
   1043 {
   1044     Str narg = NULL;
   1045     char *p, *q;
   1046     if (arg == NULL || arg->length == 0)
   1047 	return NULL;
   1048     p = arg->ptr;
   1049     q = p;
   1050     SKIP_NON_BLANKS(q);
   1051     if (*q != '\0') {
   1052 	*q++ = '\0';
   1053 	SKIP_BLANKS(q);
   1054 	if (*q != '\0')
   1055 	    narg = Strnew_charp(q);
   1056     }
   1057     return narg;
   1058 }
   1059 
   1060 static void
   1061 parsePasswd(FILE * fp, int netrc)
   1062 {
   1063     struct auth_pass ent;
   1064     Str line = NULL;
   1065 
   1066     bzero(&ent, sizeof(struct auth_pass));
   1067     while (1) {
   1068 	Str arg = NULL;
   1069 	char *p;
   1070 
   1071 	if (line == NULL || line->length == 0)
   1072 	    line = Strfgets(fp);
   1073 	if (line->length == 0)
   1074 	    break;
   1075 	Strchop(line);
   1076 	Strremovefirstspaces(line);
   1077 	p = line->ptr;
   1078 	if (*p == '#' || *p == '\0') {
   1079 	    line = NULL;
   1080 	    continue;		/* comment or empty line */
   1081 	}
   1082 	arg = next_token(line);
   1083 
   1084 	if (!strcmp(p, "machine") || !strcmp(p, "host")
   1085 	    || (netrc && !strcmp(p, "default"))) {
   1086 	    add_auth_pass_entry(&ent, netrc, 0);
   1087 	    bzero(&ent, sizeof(struct auth_pass));
   1088 	    if (netrc)
   1089 		ent.port = 21;	/* XXX: getservbyname("ftp"); ? */
   1090 	    if (strcmp(p, "default") != 0) {
   1091 		line = next_token(arg);
   1092 		ent.host = arg;
   1093 	    }
   1094 	    else {
   1095 		line = arg;
   1096 	    }
   1097 	}
   1098 	else if (!netrc && !strcmp(p, "port") && arg) {
   1099 	    line = next_token(arg);
   1100 	    ent.port = atoi(arg->ptr);
   1101 	}
   1102 	else if (!netrc && !strcmp(p, "proxy")) {
   1103 	    ent.is_proxy = 1;
   1104 	    line = arg;
   1105 	}
   1106 	else if (!netrc && !strcmp(p, "path")) {
   1107 	    line = next_token(arg);
   1108 	    /* ent.file = arg; */
   1109 	}
   1110 	else if (!netrc && !strcmp(p, "realm")) {
   1111 	    /* XXX: rest of line becomes arg for realm */
   1112 	    line = NULL;
   1113 	    ent.realm = arg;
   1114 	}
   1115 	else if (!strcmp(p, "login")) {
   1116 	    line = next_token(arg);
   1117 	    ent.uname = arg;
   1118 	}
   1119 	else if (!strcmp(p, "password") || !strcmp(p, "passwd")) {
   1120 	    line = next_token(arg);
   1121 	    ent.pwd = arg;
   1122 	}
   1123 	else if (netrc && !strcmp(p, "machdef")) {
   1124 	    while ((line = Strfgets(fp))->length != 0) {
   1125 		if (*line->ptr == '\n')
   1126 		    break;
   1127 	    }
   1128 	    line = NULL;
   1129 	}
   1130 	else if (netrc && !strcmp(p, "account")) {
   1131 	    /* ignore */
   1132 	    line = next_token(arg);
   1133 	}
   1134 	else {
   1135 	    /* ignore rest of line */
   1136 	    line = NULL;
   1137 	}
   1138     }
   1139     add_auth_pass_entry(&ent, netrc, 0);
   1140 }
   1141 
   1142 /* FIXME: gettextize? */
   1143 #define FILE_IS_READABLE_MSG "SECURITY NOTE: file %s must not be accessible by others"
   1144 
   1145 FILE *
   1146 openSecretFile(char *fname)
   1147 {
   1148     char *efname;
   1149     struct stat st;
   1150 
   1151     if (fname == NULL)
   1152 	return NULL;
   1153     efname = expandPath(fname);
   1154     if (stat(efname, &st) < 0)
   1155 	return NULL;
   1156 
   1157     /* check permissions, if group or others readable or writable,
   1158      * refuse it, because it's insecure.
   1159      *
   1160      * XXX: disable_secret_security_check will introduce some
   1161      *    security issues, but on some platform such as Windows
   1162      *    it's not possible (or feasible) to disable group|other
   1163      *    readable and writable.
   1164      *   [w3m-dev 03368][w3m-dev 03369][w3m-dev 03370]
   1165      */
   1166     if (disable_secret_security_check)
   1167 	/* do nothing */ ;
   1168     else if ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
   1169 	if (fmInitialized) {
   1170 	    message(Sprintf(FILE_IS_READABLE_MSG, fname)->ptr, 0, 0);
   1171 	    refresh();
   1172 	}
   1173 	else {
   1174 	    fputs(Sprintf(FILE_IS_READABLE_MSG, fname)->ptr, stderr);
   1175 	    fputc('\n', stderr);
   1176 	}
   1177 	sleep(2);
   1178 	return NULL;
   1179     }
   1180 
   1181     return fopen(efname, "r");
   1182 }
   1183 
   1184 void
   1185 loadPasswd(void)
   1186 {
   1187     FILE *fp;
   1188 
   1189     passwords = NULL;
   1190     fp = openSecretFile(passwd_file);
   1191     if (fp != NULL) {
   1192 	parsePasswd(fp, 0);
   1193 	fclose(fp);
   1194     }
   1195 
   1196     /* for FTP */
   1197     fp = openSecretFile("~/.netrc");
   1198     if (fp != NULL) {
   1199 	parsePasswd(fp, 1);
   1200 	fclose(fp);
   1201     }
   1202     return;
   1203 }
   1204 
   1205 /* get last modified time */
   1206 char *
   1207 last_modified(Buffer *buf)
   1208 {
   1209     TextListItem *ti;
   1210     struct stat st;
   1211 
   1212     if (buf->document_header) {
   1213 	for (ti = buf->document_header->first; ti; ti = ti->next) {
   1214 	    if (strncasecmp(ti->ptr, "Last-modified: ", 15) == 0) {
   1215 		return ti->ptr + 15;
   1216 	    }
   1217 	}
   1218 	return "unknown";
   1219     }
   1220     else if (buf->currentURL.scheme == SCM_LOCAL) {
   1221 	if (stat(buf->currentURL.file, &st) < 0)
   1222 	    return "unknown";
   1223 	return ctime(&st.st_mtime);
   1224     }
   1225     return "unknown";
   1226 }
   1227 
   1228 static char roman_num1[] = {
   1229     'i', 'x', 'c', 'm', '*',
   1230 };
   1231 static char roman_num5[] = {
   1232     'v', 'l', 'd', '*',
   1233 };
   1234 
   1235 static Str
   1236 romanNum2(int l, int n)
   1237 {
   1238     Str s = Strnew();
   1239 
   1240     switch (n) {
   1241     case 1:
   1242     case 2:
   1243     case 3:
   1244 	for (; n > 0; n--)
   1245 	    Strcat_char(s, roman_num1[l]);
   1246 	break;
   1247     case 4:
   1248 	Strcat_char(s, roman_num1[l]);
   1249 	Strcat_char(s, roman_num5[l]);
   1250 	break;
   1251     case 5:
   1252     case 6:
   1253     case 7:
   1254     case 8:
   1255 	Strcat_char(s, roman_num5[l]);
   1256 	for (n -= 5; n > 0; n--)
   1257 	    Strcat_char(s, roman_num1[l]);
   1258 	break;
   1259     case 9:
   1260 	Strcat_char(s, roman_num1[l]);
   1261 	Strcat_char(s, roman_num1[l + 1]);
   1262 	break;
   1263     }
   1264     return s;
   1265 }
   1266 
   1267 Str
   1268 romanNumeral(int n)
   1269 {
   1270     Str r = Strnew();
   1271 
   1272     if (n <= 0)
   1273 	return r;
   1274     if (n >= 4000) {
   1275 	Strcat_charp(r, "**");
   1276 	return r;
   1277     }
   1278     Strcat(r, romanNum2(3, n / 1000));
   1279     Strcat(r, romanNum2(2, (n % 1000) / 100));
   1280     Strcat(r, romanNum2(1, (n % 100) / 10));
   1281     Strcat(r, romanNum2(0, n % 10));
   1282 
   1283     return r;
   1284 }
   1285 
   1286 Str
   1287 romanAlphabet(int n)
   1288 {
   1289     Str r = Strnew();
   1290     int l;
   1291     char buf[14];
   1292 
   1293     if (n <= 0)
   1294 	return r;
   1295 
   1296     l = 0;
   1297     while (n) {
   1298 	buf[l++] = 'a' + (n - 1) % 26;
   1299 	n = (n - 1) / 26;
   1300     }
   1301     l--;
   1302     for (; l >= 0; l--)
   1303 	Strcat_char(r, buf[l]);
   1304 
   1305     return r;
   1306 }
   1307 
   1308 #ifndef SIGIOT
   1309 #define SIGIOT SIGABRT
   1310 #endif				/* not SIGIOT */
   1311 
   1312 static void
   1313 reset_signals(void)
   1314 {
   1315 #ifdef SIGHUP
   1316     mySignal(SIGHUP, SIG_DFL);	/* terminate process */
   1317 #endif
   1318     mySignal(SIGINT, SIG_DFL);	/* terminate process */
   1319 #ifdef SIGQUIT
   1320     mySignal(SIGQUIT, SIG_DFL);	/* terminate process */
   1321 #endif
   1322     mySignal(SIGTERM, SIG_DFL);	/* terminate process */
   1323     mySignal(SIGILL, SIG_DFL);	/* create core image */
   1324     mySignal(SIGIOT, SIG_DFL);	/* create core image */
   1325     mySignal(SIGFPE, SIG_DFL);	/* create core image */
   1326 #ifdef SIGBUS
   1327     mySignal(SIGBUS, SIG_DFL);	/* create core image */
   1328 #endif				/* SIGBUS */
   1329 #ifdef SIGCHLD
   1330     mySignal(SIGCHLD, SIG_IGN);
   1331 #endif
   1332 #ifdef SIGPIPE
   1333     mySignal(SIGPIPE, SIG_IGN);
   1334 #endif
   1335 }
   1336 
   1337 #ifndef FOPEN_MAX
   1338 #define FOPEN_MAX 1024		/* XXX */
   1339 #endif
   1340 
   1341 static void
   1342 close_all_fds_except(int i, int f)
   1343 {
   1344     switch (i) {		/* fall through */
   1345     case 0:
   1346 	dup2(open(DEV_NULL_PATH, O_RDONLY), 0);
   1347     case 1:
   1348 	dup2(open(DEV_NULL_PATH, O_WRONLY), 1);
   1349     case 2:
   1350 	dup2(open(DEV_NULL_PATH, O_WRONLY), 2);
   1351     }
   1352     /* close all other file descriptors (socket, ...) */
   1353     for (i = 3; i < FOPEN_MAX; i++) {
   1354 	if (i != f)
   1355 	    close(i);
   1356     }
   1357 }
   1358 
   1359 void
   1360 setup_child(int child, int i, int f)
   1361 {
   1362     reset_signals();
   1363     mySignal(SIGINT, SIG_IGN);
   1364 #ifndef __MINGW32_VERSION
   1365     if (!child)
   1366 	SETPGRP();
   1367 #endif /* __MINGW32_VERSION */
   1368     close_tty();
   1369     close_all_fds_except(i, f);
   1370     QuietMessage = TRUE;
   1371     fmInitialized = FALSE;
   1372     TrapSignal = FALSE;
   1373 }
   1374 
   1375 #ifndef __MINGW32_VERSION
   1376 pid_t
   1377 open_pipe_rw(FILE ** fr, FILE ** fw)
   1378 {
   1379     int fdr[2];
   1380     int fdw[2];
   1381     pid_t pid;
   1382 
   1383     if (fr && pipe(fdr) < 0)
   1384 	goto err0;
   1385     if (fw && pipe(fdw) < 0)
   1386 	goto err1;
   1387 
   1388     flush_tty();
   1389     pid = fork();
   1390     if (pid < 0)
   1391 	goto err2;
   1392     if (pid == 0) {
   1393 	/* child */
   1394 	if (fr) {
   1395 	    close(fdr[0]);
   1396 	    dup2(fdr[1], 1);
   1397 	}
   1398 	if (fw) {
   1399 	    close(fdw[1]);
   1400 	    dup2(fdw[0], 0);
   1401 	}
   1402     }
   1403     else {
   1404 	if (fr) {
   1405 	    close(fdr[1]);
   1406 	    if (*fr == stdin)
   1407 		dup2(fdr[0], 0);
   1408 	    else
   1409 		*fr = fdopen(fdr[0], "r");
   1410 	}
   1411 	if (fw) {
   1412 	    close(fdw[0]);
   1413 	    if (*fw == stdout)
   1414 		dup2(fdw[1], 1);
   1415 	    else
   1416 		*fw = fdopen(fdw[1], "w");
   1417 	}
   1418     }
   1419     return pid;
   1420   err2:
   1421     if (fw) {
   1422 	close(fdw[0]);
   1423 	close(fdw[1]);
   1424     }
   1425   err1:
   1426     if (fr) {
   1427 	close(fdr[0]);
   1428 	close(fdr[1]);
   1429     }
   1430   err0:
   1431     return (pid_t) - 1;
   1432 }
   1433 #endif /* __MINGW32_VERSION */
   1434 
   1435 void
   1436 myExec(char *command)
   1437 {
   1438     mySignal(SIGINT, SIG_DFL);
   1439     execl("/bin/sh", "sh", "-c", command, NULL);
   1440     exit(127);
   1441 }
   1442 
   1443 void
   1444 mySystem(char *command, int background)
   1445 {
   1446 #ifndef __MINGW32_VERSION
   1447     if (background) {
   1448 #ifndef __EMX__
   1449 	flush_tty();
   1450 	if (!fork()) {
   1451 	    setup_child(FALSE, 0, -1);
   1452 	    myExec(command);
   1453 	}
   1454 #else
   1455 	Str cmd = Strnew_charp("start /f ");
   1456 	Strcat_charp(cmd, command);
   1457 	system(cmd->ptr);
   1458 #endif
   1459     }
   1460     else
   1461 #endif /* __MINGW32_VERSION */
   1462 	system(command);
   1463 }
   1464 
   1465 Str
   1466 myExtCommand(char *cmd, char *arg, int redirect)
   1467 {
   1468     Str tmp = NULL;
   1469     char *p;
   1470     int set_arg = FALSE;
   1471 
   1472     for (p = cmd; *p; p++) {
   1473 	if (*p == '%' && *(p + 1) == 's' && !set_arg) {
   1474 	    if (tmp == NULL)
   1475 		tmp = Strnew_charp_n(cmd, (int)(p - cmd));
   1476 	    Strcat_charp(tmp, arg);
   1477 	    set_arg = TRUE;
   1478 	    p++;
   1479 	}
   1480 	else {
   1481 	    if (tmp)
   1482 		Strcat_char(tmp, *p);
   1483 	}
   1484     }
   1485     if (!set_arg) {
   1486 	if (redirect)
   1487 	    tmp = Strnew_m_charp("(", cmd, ") < ", arg, NULL);
   1488 	else
   1489 	    tmp = Strnew_m_charp(cmd, " ", arg, NULL);
   1490     }
   1491     return tmp;
   1492 }
   1493 
   1494 Str
   1495 myEditor(char *cmd, char *file, int line)
   1496 {
   1497     Str tmp = NULL;
   1498     char *p;
   1499     int set_file = FALSE, set_line = FALSE;
   1500 
   1501     for (p = cmd; *p; p++) {
   1502 	if (*p == '%' && *(p + 1) == 's' && !set_file) {
   1503 	    if (tmp == NULL)
   1504 		tmp = Strnew_charp_n(cmd, (int)(p - cmd));
   1505 	    Strcat_charp(tmp, file);
   1506 	    set_file = TRUE;
   1507 	    p++;
   1508 	}
   1509 	else if (*p == '%' && *(p + 1) == 'd' && !set_line && line > 0) {
   1510 	    if (tmp == NULL)
   1511 		tmp = Strnew_charp_n(cmd, (int)(p - cmd));
   1512 	    Strcat(tmp, Sprintf("%d", line));
   1513 	    set_line = TRUE;
   1514 	    p++;
   1515 	}
   1516 	else {
   1517 	    if (tmp)
   1518 		Strcat_char(tmp, *p);
   1519 	}
   1520     }
   1521     if (!set_file) {
   1522 	if (tmp == NULL)
   1523 	    tmp = Strnew_charp(cmd);
   1524 	if (!set_line && line > 1 && strcasestr(cmd, "vi"))
   1525 	    Strcat(tmp, Sprintf(" +%d", line));
   1526 	Strcat_m_charp(tmp, " ", file, NULL);
   1527     }
   1528     return tmp;
   1529 }
   1530 
   1531 #ifdef __MINGW32_VERSION
   1532 char *
   1533 expandName(char *name)
   1534 {
   1535     return getenv("HOME");
   1536 }
   1537 #else
   1538 char *
   1539 expandName(char *name)
   1540 {
   1541     char *p;
   1542     struct passwd *passent, *getpwnam(const char *);
   1543     Str extpath = NULL;
   1544 
   1545     if (name == NULL)
   1546 	return NULL;
   1547     p = name;
   1548     if (*p == '/') {
   1549 	if ((*(p + 1) == '~' && IS_ALPHA(*(p + 2)))
   1550 	    && personal_document_root) {
   1551 	    char *q;
   1552 	    p += 2;
   1553 	    q = strchr(p, '/');
   1554 	    if (q) {		/* /~user/dir... */
   1555 		passent = getpwnam(allocStr(p, q - p));
   1556 		p = q;
   1557 	    }
   1558 	    else {		/* /~user */
   1559 		passent = getpwnam(p);
   1560 		p = "";
   1561 	    }
   1562 	    if (!passent)
   1563 		goto rest;
   1564 	    extpath = Strnew_m_charp(passent->pw_dir, "/",
   1565 				     personal_document_root, NULL);
   1566 	    if (*personal_document_root == '\0' && *p == '/')
   1567 		p++;
   1568 	}
   1569 	else
   1570 	    goto rest;
   1571 	if (Strcmp_charp(extpath, "/") == 0 && *p == '/')
   1572 	    p++;
   1573 	Strcat_charp(extpath, p);
   1574 	return extpath->ptr;
   1575     }
   1576     else
   1577 	return expandPath(p);
   1578   rest:
   1579     return name;
   1580 }
   1581 #endif
   1582 
   1583 char *
   1584 file_to_url(char *file)
   1585 {
   1586     Str tmp;
   1587 #ifdef SUPPORT_DOS_DRIVE_PREFIX
   1588     char *drive = NULL;
   1589 #endif
   1590 #ifdef SUPPORT_NETBIOS_SHARE
   1591     char *host = NULL;
   1592 #endif
   1593 
   1594     file = expandPath(file);
   1595 #ifdef SUPPORT_NETBIOS_SHARE
   1596     if (file[0] == '/' && file[1] == '/') {
   1597 	char *p;
   1598 	file += 2;
   1599 	if (*file) {
   1600 	    p = strchr(file, '/');
   1601 	    if (p != NULL && p != file) {
   1602 		host = allocStr(file, (p - file));
   1603 		file = p;
   1604 	    }
   1605 	}
   1606     }
   1607 #endif
   1608 #ifdef SUPPORT_DOS_DRIVE_PREFIX
   1609     if (IS_ALPHA(file[0]) && file[1] == ':') {
   1610 	drive = allocStr(file, 2);
   1611 	file += 2;
   1612     }
   1613     else
   1614 #endif
   1615     if (file[0] != '/') {
   1616 	tmp = Strnew_charp(CurrentDir);
   1617 	if (Strlastchar(tmp) != '/')
   1618 	    Strcat_char(tmp, '/');
   1619 	Strcat_charp(tmp, file);
   1620 	file = tmp->ptr;
   1621     }
   1622     tmp = Strnew_charp("file://");
   1623 #ifdef SUPPORT_NETBIOS_SHARE
   1624     if (host)
   1625 	Strcat_charp(tmp, host);
   1626 #endif
   1627 #ifdef SUPPORT_DOS_DRIVE_PREFIX
   1628     if (drive)
   1629 	Strcat_charp(tmp, drive);
   1630 #endif
   1631     Strcat_charp(tmp, file_quote(cleanupName(file)));
   1632     return tmp->ptr;
   1633 }
   1634 
   1635 #ifdef USE_M17N
   1636 char *
   1637 url_unquote_conv(char *url, wc_ces charset)
   1638 #else
   1639 char *
   1640 url_unquote_conv0(char *url)
   1641 #endif
   1642 {
   1643 #ifdef USE_M17N
   1644     wc_uint8 old_auto_detect = WcOption.auto_detect;
   1645 #endif
   1646     Str tmp;
   1647     tmp = Str_url_unquote(Strnew_charp(url), FALSE, TRUE);
   1648 #ifdef USE_M17N
   1649     if (!charset || charset == WC_CES_US_ASCII)
   1650 	charset = SystemCharset;
   1651     WcOption.auto_detect = WC_OPT_DETECT_ON;
   1652     tmp = convertLine(NULL, tmp, RAW_MODE, &charset, charset);
   1653     WcOption.auto_detect = old_auto_detect;
   1654 #endif
   1655     return tmp->ptr;
   1656 }
   1657 
   1658 static char *tmpf_base[MAX_TMPF_TYPE] = {
   1659     "tmp", "src", "frame", "cache", "cookie",
   1660 };
   1661 static unsigned int tmpf_seq[MAX_TMPF_TYPE];
   1662 
   1663 Str
   1664 tmpfname(int type, char *ext)
   1665 {
   1666     Str tmpf;
   1667     tmpf = Sprintf("%s/w3m%s%d-%d%s",
   1668 		   tmp_dir,
   1669 		   tmpf_base[type],
   1670 		   CurrentPid, tmpf_seq[type]++, (ext) ? ext : "");
   1671     pushText(fileToDelete, tmpf->ptr);
   1672     return tmpf;
   1673 }
   1674 
   1675 static char *monthtbl[] = {
   1676     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
   1677     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
   1678 };
   1679 
   1680 static int
   1681 get_day(char **s)
   1682 {
   1683     Str tmp = Strnew();
   1684     int day;
   1685     char *ss = *s;
   1686 
   1687     if (!**s)
   1688 	return -1;
   1689 
   1690     while (**s && IS_DIGIT(**s))
   1691 	Strcat_char(tmp, *((*s)++));
   1692 
   1693     day = atoi(tmp->ptr);
   1694 
   1695     if (day < 1 || day > 31) {
   1696 	*s = ss;
   1697 	return -1;
   1698     }
   1699     return day;
   1700 }
   1701 
   1702 static int
   1703 get_month(char **s)
   1704 {
   1705     Str tmp = Strnew();
   1706     int mon;
   1707     char *ss = *s;
   1708 
   1709     if (!**s)
   1710 	return -1;
   1711 
   1712     while (**s && IS_DIGIT(**s))
   1713 	Strcat_char(tmp, *((*s)++));
   1714     if (tmp->length > 0) {
   1715 	mon = atoi(tmp->ptr);
   1716     }
   1717     else {
   1718 	while (**s && IS_ALPHA(**s))
   1719 	    Strcat_char(tmp, *((*s)++));
   1720 	for (mon = 1; mon <= 12; mon++) {
   1721 	    if (strncmp(tmp->ptr, monthtbl[mon - 1], 3) == 0)
   1722 		break;
   1723 	}
   1724     }
   1725     if (mon < 1 || mon > 12) {
   1726 	*s = ss;
   1727 	return -1;
   1728     }
   1729     return mon;
   1730 }
   1731 
   1732 static int
   1733 get_year(char **s)
   1734 {
   1735     Str tmp = Strnew();
   1736     int year;
   1737     char *ss = *s;
   1738 
   1739     if (!**s)
   1740 	return -1;
   1741 
   1742     while (**s && IS_DIGIT(**s))
   1743 	Strcat_char(tmp, *((*s)++));
   1744     if (tmp->length != 2 && tmp->length != 4) {
   1745 	*s = ss;
   1746 	return -1;
   1747     }
   1748 
   1749     year = atoi(tmp->ptr);
   1750     if (tmp->length == 2) {
   1751 	if (year >= 70)
   1752 	    year += 1900;
   1753 	else
   1754 	    year += 2000;
   1755     }
   1756     return year;
   1757 }
   1758 
   1759 static int
   1760 get_time(char **s, int *hour, int *min, int *sec)
   1761 {
   1762     Str tmp = Strnew();
   1763     char *ss = *s;
   1764 
   1765     if (!**s)
   1766 	return -1;
   1767 
   1768     while (**s && IS_DIGIT(**s))
   1769 	Strcat_char(tmp, *((*s)++));
   1770     if (**s != ':') {
   1771 	*s = ss;
   1772 	return -1;
   1773     }
   1774     *hour = atoi(tmp->ptr);
   1775 
   1776     (*s)++;
   1777     Strclear(tmp);
   1778     while (**s && IS_DIGIT(**s))
   1779 	Strcat_char(tmp, *((*s)++));
   1780     if (**s != ':') {
   1781 	*s = ss;
   1782 	return -1;
   1783     }
   1784     *min = atoi(tmp->ptr);
   1785 
   1786     (*s)++;
   1787     Strclear(tmp);
   1788     while (**s && IS_DIGIT(**s))
   1789 	Strcat_char(tmp, *((*s)++));
   1790     *sec = atoi(tmp->ptr);
   1791 
   1792     if (*hour < 0 || *hour >= 24 ||
   1793 	*min < 0 || *min >= 60 || *sec < 0 || *sec >= 60) {
   1794 	*s = ss;
   1795 	return -1;
   1796     }
   1797     return 0;
   1798 }
   1799 
   1800 static int
   1801 get_zone(char **s, int *z_hour, int *z_min)
   1802 {
   1803     Str tmp = Strnew();
   1804     int zone;
   1805     char *ss = *s;
   1806 
   1807     if (!**s)
   1808 	return -1;
   1809 
   1810     if (**s == '+' || **s == '-')
   1811 	Strcat_char(tmp, *((*s)++));
   1812     while (**s && IS_DIGIT(**s))
   1813 	Strcat_char(tmp, *((*s)++));
   1814     if (!(tmp->length == 4 && IS_DIGIT(*ss)) &&
   1815 	!(tmp->length == 5 && (*ss == '+' || *ss == '-'))) {
   1816 	*s = ss;
   1817 	return -1;
   1818     }
   1819 
   1820     zone = atoi(tmp->ptr);
   1821     *z_hour = zone / 100;
   1822     *z_min = zone - (zone / 100) * 100;
   1823     return 0;
   1824 }
   1825 
   1826 /* RFC 1123 or RFC 850 or ANSI C asctime() format string -> time_t */
   1827 time_t
   1828 mymktime(char *timestr)
   1829 {
   1830     char *s;
   1831     int day, mon, year, hour, min, sec, z_hour = 0, z_min = 0;
   1832 
   1833     if (!(timestr && *timestr))
   1834 	return -1;
   1835     s = timestr;
   1836 
   1837 #ifdef DEBUG
   1838     fprintf(stderr, "mktime: %s\n", timestr);
   1839 #endif				/* DEBUG */
   1840 
   1841     while (*s && IS_ALPHA(*s))
   1842 	s++;
   1843     while (*s && !IS_ALNUM(*s))
   1844 	s++;
   1845 
   1846     if (IS_DIGIT(*s)) {
   1847 	/* RFC 1123 or RFC 850 format */
   1848 	if ((day = get_day(&s)) == -1)
   1849 	    return -1;
   1850 
   1851 	while (*s && !IS_ALNUM(*s))
   1852 	    s++;
   1853 	if ((mon = get_month(&s)) == -1)
   1854 	    return -1;
   1855 
   1856 	while (*s && !IS_DIGIT(*s))
   1857 	    s++;
   1858 	if ((year = get_year(&s)) == -1)
   1859 	    return -1;
   1860 
   1861 	while (*s && !IS_DIGIT(*s))
   1862 	    s++;
   1863 	if (!*s) {
   1864 	    hour = 0;
   1865 	    min = 0;
   1866 	    sec = 0;
   1867 	}
   1868 	else {
   1869 	    if (get_time(&s, &hour, &min, &sec) == -1)
   1870 		return -1;
   1871 	    while (*s && !IS_DIGIT(*s) && *s != '+' && *s != '-')
   1872 		s++;
   1873 	    get_zone(&s, &z_hour, &z_min);
   1874 	}
   1875     }
   1876     else {
   1877 	/* ANSI C asctime() format. */
   1878 	while (*s && !IS_ALNUM(*s))
   1879 	    s++;
   1880 	if ((mon = get_month(&s)) == -1)
   1881 	    return -1;
   1882 
   1883 	while (*s && !IS_DIGIT(*s))
   1884 	    s++;
   1885 	if ((day = get_day(&s)) == -1)
   1886 	    return -1;
   1887 
   1888 	while (*s && !IS_DIGIT(*s))
   1889 	    s++;
   1890 	if (get_time(&s, &hour, &min, &sec) == -1)
   1891 	    return -1;
   1892 
   1893 	while (*s && !IS_DIGIT(*s))
   1894 	    s++;
   1895 	if ((year = get_year(&s)) == -1)
   1896 	    return -1;
   1897     }
   1898 #ifdef DEBUG
   1899     fprintf(stderr,
   1900 	    "year=%d month=%d day=%d hour:min:sec=%d:%d:%d zone=%d:%d\n", year,
   1901 	    mon, day, hour, min, sec, z_hour, z_min);
   1902 #endif				/* DEBUG */
   1903 
   1904     mon -= 3;
   1905     if (mon < 0) {
   1906 	mon += 12;
   1907 	year--;
   1908     }
   1909     day += (year - 1968) * 1461 / 4;
   1910     day += ((((mon * 153) + 2) / 5) - 672);
   1911     hour -= z_hour;
   1912     min -= z_min;
   1913     return (time_t) ((day * 60 * 60 * 24) +
   1914 		     (hour * 60 * 60) + (min * 60) + sec);
   1915 }
   1916 
   1917 #ifdef USE_COOKIE
   1918 #ifdef INET6
   1919 #include <sys/socket.h>
   1920 #endif				/* INET6 */
   1921 #ifndef __MINGW32_VERSION
   1922 #include <netdb.h>
   1923 #else
   1924 #include <winsock.h>
   1925 #endif
   1926 char *
   1927 FQDN(char *host)
   1928 {
   1929     char *p;
   1930 #ifndef INET6
   1931     struct hostent *entry;
   1932 #else				/* INET6 */
   1933     int *af;
   1934 #endif				/* INET6 */
   1935 
   1936     if (host == NULL)
   1937 	return NULL;
   1938 
   1939     if (strcasecmp(host, "localhost") == 0)
   1940 	return host;
   1941 
   1942     for (p = host; *p && *p != '.'; p++) ;
   1943 
   1944     if (*p == '.')
   1945 	return host;
   1946 
   1947 #ifndef INET6
   1948     if (!(entry = gethostbyname(host)))
   1949 	return NULL;
   1950 
   1951     return allocStr(entry->h_name, -1);
   1952 #else				/* INET6 */
   1953     for (af = ai_family_order_table[DNS_order];; af++) {
   1954 	int error;
   1955 	struct addrinfo hints;
   1956 	struct addrinfo *res, *res0;
   1957 	char *namebuf;
   1958 
   1959 	memset(&hints, 0, sizeof(hints));
   1960 	hints.ai_flags = AI_CANONNAME;
   1961 	hints.ai_family = *af;
   1962 	hints.ai_socktype = SOCK_STREAM;
   1963 	error = getaddrinfo(host, NULL, &hints, &res0);
   1964 	if (error) {
   1965 	    if (*af == PF_UNSPEC) {
   1966 		/* all done */
   1967 		break;
   1968 	    }
   1969 	    /* try next address family */
   1970 	    continue;
   1971 	}
   1972 	for (res = res0; res != NULL; res = res->ai_next) {
   1973 	    if (res->ai_canonname) {
   1974 		/* found */
   1975 		namebuf = strdup(res->ai_canonname);
   1976 		freeaddrinfo(res0);
   1977 		return namebuf;
   1978 	    }
   1979 	}
   1980 	freeaddrinfo(res0);
   1981 	if (*af == PF_UNSPEC) {
   1982 	    break;
   1983 	}
   1984     }
   1985     /* all failed */
   1986     return NULL;
   1987 #endif				/* INET6 */
   1988 }
   1989 
   1990 #endif				/* USE_COOKIE */
   1991 
   1992 void (*mySignal(int signal_number, void (*action) (int))) (int) {
   1993 #ifdef	SA_RESTART
   1994     struct sigaction new_action, old_action;
   1995 
   1996     sigemptyset(&new_action.sa_mask);
   1997     new_action.sa_handler = action;
   1998     if (signal_number == SIGALRM) {
   1999 #ifdef	SA_INTERRUPT
   2000 	new_action.sa_flags = SA_INTERRUPT;
   2001 #else
   2002 	new_action.sa_flags = 0;
   2003 #endif
   2004     }
   2005     else {
   2006 	new_action.sa_flags = SA_RESTART;
   2007     }
   2008     sigaction(signal_number, &new_action, &old_action);
   2009     return (old_action.sa_handler);
   2010 #else
   2011     return (signal(signal_number, action));
   2012 #endif
   2013 }