
/* 
 * Initialization file etc.
 */
#include "fm.h"
#include "myctype.h"
#include <stdio.h>
#include <errno.h>
#include "parsetag.h"
#include "local.h"
#include "terms.h"
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

struct param_ptr {
    char *name;
    int type;
    int inputtype;
    void *varptr;
    char *comment;
    struct sel_c *select;
    int flag;
};

#define RCSAVE_NAME "rcsave_or_not"
#define PARAM_FLAG_RCSAVE (1 << 0)
#define PARAM_FLAG_RCSAVE_BY_PANEL (1 << 1)
#define PARAM_FLAG_CMT_SET (1 << 2)
static int rcsave_temp_mask = ~PARAM_FLAG_RCSAVE;
static int rcsave_temp = 0;
static int save_to_config_file = 0;
char *message_about_config_save = "Save options to file: ";

struct param_section {
    char *name;
    size_t beg, end;
    int name_set;
};

static char *config_file = NULL;
static Str to_str(struct param_ptr *p);

enum {
  P_INT,
  P_SHORT,
  P_CHARINT,
  P_CHAR,
  P_STRING,
#if defined(USE_SSL) && defined(USE_SSL_VERIFY)
  P_SSLPATH,
#endif
#ifdef USE_COLOR
  P_COLOR,
#endif
#ifdef JP_CHARSET
  P_CODE,
#endif
  P_PIXELS,
  P_NZINT,
  P_SCALE,
  P_SINT,
  P_STRINGV,
  P_SEL,
#ifdef MANY_CHARSET
  P_MBSETUP,
#endif
};

TextList AcceptEnc = {NULL, NULL, 0};
static TextList InitHTTPRequestHeaderList = {NULL, NULL, 0};
TextList mailcap_entries = {NULL, NULL, 0};
TextList browsecap_entries = {NULL, NULL, 0};

#ifdef MANY_CHARSET

TextList charset_cnames = {NULL, NULL, 0};
TextList tty_char_conv_list = {NULL, NULL, 0};

#ifdef KANJI_SYMBOLS

static int
fixed_mb_strlength(const char *s)
{
  int len = ttyfix_width(s);

  while ((s = strstr(s, "&nbsp;"))) {
    len -= sizeof("&nbsp;") - 2;
    s += sizeof("&nbsp;") - 1;
  }

  return len;
}

char **ullevel;
int *ullevel_width;
int MAX_UL_LEVEL;
int UL_TYPE_DISC_WIDTH;
int UL_TYPE_CIRCLE_WIDTH;
int UL_TYPE_SQUARE_WIDTH;
int HR_RULE_WIDTH;
int HR_RULE_LENGTH;

static void
make_mb_rule(const char *s, char **dfltv, int dfltn, int dfltw,
	     char ***p_sv, int *p_n, int *p_size, int *p_wmin, int *p_wmax)
{
  char **sv = *p_sv;
  int n = *p_n;
  int size = *p_size;
  int wmin = *p_wmin;
  int wmax = *p_wmax;
  int i, w;

  if (s)
    for (;; s += i + 1) {
      i = strcspn(s, ",");
      NEW_OBJV1(&size, n, (void **)&sv, sizeof(char *), 0);
      sv[n] = (i > 0 || n >= dfltn) ? Strnew_charp_n((char *)s, i)->ptr : dfltv[n];
      w = fixed_mb_strlength(sv[n]);

      if (wmax < w)
	wmax = w;

      if (!wmin || wmin > w)
	wmin = w;

      ++n;

      if (!s[i])
	break;
    }

  for (; n < dfltn ; ++n) {
    NEW_OBJV1(&size, n, (void **)&sv, sizeof(char *), 0);
    sv[n] = dfltv[n];

    if (!wmin || wmin > dfltw)
      wmin = dfltw;
      
    if (wmax < dfltw)
      wmax = dfltw;
  }

  *p_sv = sv;
  *p_n = n;
  *p_size = size;
  *p_wmin = wmin;
  *p_wmax = wmax;
}

static void
fix_mb_rule(char **sv, int n, int wmin, int wmax)
{
  if (wmin < wmax) {
    int j, w, lw, rw;

    for (j = 0 ; j < n ; ++j) {
      w = fixed_mb_strlength(sv[j]);
      rw = (wmax - w) / 2;
      lw = wmax - w - rw;

      if (lw > 0) {
	Str str = Strnew_charp(" ");

	while (--lw > 0)
	  Strcat_charp(str, " ");

	Strcat_charp(str, sv[j]);

	for (; rw > 0 ; --rw)
	  Strcat_charp(str, " ");

	sv[j] = str->ptr;
      }
    }
  }
}

#ifdef USE_MENU

static char *default_FRAME[] = {
  "+", "-", "+",
  "|", " ", "|",
  "+", "-", "+",
  ":", ":",
};

#define DEFAULT_FRAME_WIDTH (1)

char **FRAME;
int FRAME_WIDTH;
#endif /* USE_MENU */

static char *default_rule[] = {
  "+", /* center */
  "+", /* left separator */
  "+", /* top separator */
  "+", /* left top corner */
  "+", /* right separator */
  "|", /* vertical bar */
  "+", /* right top corner */
  "+", /* bottom separator */
  "+", /* left bottom corner */
  "-", /* horizontal bar */
  "+", /* right bottom corner */
  " ", /* none */
};

#define DEFAULT_RULE_WIDTH (1)

static void
translate_rule_tab(char **sv, char **dv, int wmax)
{
  int i, j, k;

  for (i = j = 0 ; i < RULEC_CODE_END - RULEC_CODE_BEG + 1 ;) {
    for (k = rule_translate[i] ; j < k ; ++j)
      dv[j] = Sprintf("%0*X", wmax, j)->ptr;

    dv[j++] = sv[i++];
  }
}

char *rule[16];
char *ruleB[16];
int RULE_WIDTH;

#endif /* KANJI_SYMBOL */

#else /* MANY_CHARSET */
#ifndef _
#define _(String) (String)
#endif
#ifndef N_
#define N_(String) (String)
#endif
#endif /* MANY_CHARSET */

#if LANG == JA
#define CMT_HELPER	 "外部ビューアの編集"
#define CMT_TABSTOP      "タブ幅"
#define CMT_PIXEL_PER_CHAR      "文字幅 (4.0...32.0)"
#define CMT_PIXEL_PER_LINE      "一行の高さ (4.0...64.0)"
#ifdef USE_IMAGE
#define CMT_AUTO_PIXEL_PER_CHAR "文字幅を自動設定"
#define CMT_AUTO_PIXEL_PER_LINE "一行の高さを自動設定"
#endif
#define CMT_PAGERLINE    "ページャとして利用した時に保存される行数"
#define CMT_HISTSIZE     "保持するURL履歴の数"
#define CMT_SAVEHIST     "URL履歴の保存"
#define CMT_KANJICODE    "表示用漢字コード"
#define CMT_FRAME        "フレームの自動表示"
#define CMT_ARGV_IS_URL  "scheme のない引数も URL とみなす"
#define CMT_TSELF        "targetが未指定の場合に_selfを使用する"
#define CMT_DISPLINK     "リンク先の自動表示"
#define CMT_DISPLINEINFO "現在の文書に関する情報を自動表示"
#define CMT_DISPLINEINFO_ALIAS ""
#define CMT_DISP_IMAGE   "インライン画像を表示"
#ifdef USE_IMAGE
#define CMT_PRELOAD_IMAGE   "インライン画像を表示前に読み込む"
#define CMT_IMAGE_SCALE  "画像のスケール(%)"
#define CMT_IMGDISPLAY   "画像を表示するためのコマンド"
#define CMT_IMGSIZE      "imgdisplayに統合されました、設定しても無効です"
#define CMT_IMG_VALIGN   "画像の縦方向の位置のデフォルト(top)"
#define CMT_TABLE_VALIGN "表のカラムの縦方向の位置のデフォルト(top)"
#define CMT_IMAGE_MAP_LIST "イメージマップのリンク先一覧を表示"
#endif
#define CMT_MULTICOL     "ファイル名のマルチカラム表示"
#define CMT_ALT_ENTITY   "エンティティを ASCII の代替表現で表す"
#define CMT_FOLD_TEXTAREA "TEXTAREA の行を折り返して表示"
#define CMT_COLOR        "カラー表示"
#define CMT_B_COLOR      "文字の色"
#define CMT_A_COLOR      "アンカーの色"
#define CMT_I_COLOR      "画像リンクの色"
#define CMT_F_COLOR      "フォームの色"
#define CMT_BG_COLOR     "背景の色"
#define CMT_MARK_COLOR   "マークの色"
#define CMT_ACTIVE_STYLE "現在選択されているリンクの色を指定する"
#define CMT_C_COLOR	 "現在選択されているリンクの色"
#define CMT_VISITED_ANCHOR	"訪れたことがあるリンクは色を変える"
#define CMT_V_COLOR	 "訪れたことがあるリンクの色"
#define CMT_FRAME_COLOR	 "フレームの境界の色"
#define CMT_HTTP_PROXY   "HTTPプロキシ(URLで入力)"
#ifdef USE_SSL
#define CMT_HTTPS_PROXY  "HTTPSプロキシ(URLで入力)"
#endif				/* USE_SSL */
#ifdef USE_GOPHER
#define CMT_GOPHER_PROXY "GOPHERプロキシ(URLで入力)"
#endif				/* USE_GOPHER */
#define CMT_FTP_PROXY    "FTPプロキシ(URLで入力)"
#define CMT_NO_PROXY     "プロキシから除外するドメイン"
#define CMT_NOPROXY_NETADDR	"ネットワークアドレスでプロキシ除外のチェック"
#define CMT_DNS_ORDER	"名前解決の順序"
#define CMT_NO_CACHE    "HTTPリクエストにcacheを使わない指示を加える"
#define CMT_CONCURRENT	"全体の並列度"
#define CMT_CONCURRENT_PER_SERVER	"サーバ毎の並列度"
#define CMT_FOLLOW_REDIRECTION "従うリダイレクトの回数"
#define CMT_WHEN_REDIRECTED "POSTリクエストがリダイレクトされた時の振舞い"
#define CMT_DROOT        "/ で表されるディレクトリ(document root)"
#define CMT_PDROOT       "/~user で表されるディレクトリ"
#define CMT_CGIBIN       "/cgi-bin で表されるディレクトリ"
#define CMT_CONFIRM_QQ   "q での終了時に確認する"
#ifdef USE_MARK
#define CMT_USE_MARK	"マーク機能を有効にする"
#endif
#ifdef EMACS_LIKE_LINEEDIT
#define CMT_EMACS_LIKE_LINEEDIT	"Emacs風の行編集にする"
#endif
#ifdef VI_PREC_NUM
#define CMT_VI_PREC_NUM "vi風の数値プレフィクス"
#endif
#define CMT_SHOW_NUM     "行番号を表示する"
#define CMT_SHOW_SRCH_STR "検索文字列を表示する"
#define CMT_MIMETYPES    "利用するmime.types"
#define CMT_MAILCAP      "利用するmailcap"
#define CMT_MAILCAP_ENTRY "一時使用のmailcapエントリ"
#define CMT_BROWSECAP_ENTRY "一時使用のbrowsecapエントリ"
#define CMT_EXTBRZ       "外部ブラウザ"
#define CMT_EXTBRZ2      "外部ブラウザその2"
#define CMT_EXTBRZ3      "外部ブラウザその3"
#define CMT_DISABLE_SECRET_SECURITY_CHECK	"パスワードファイルのパーミッションをチェックしない"
#define CMT_PASSWDFILE	 "パスワードファイル"
#define CMT_FTPPASS      "FTPのパスワード(普通は自分のmail addressを使う)"
#ifdef FTPPASS_HOSTNAMEGEN
#define CMT_FTPPASS_HOSTNAMEGEN	"FTPのパスワードのドメイン名を自動生成する"
#endif
#define CMT_REQUESTHEADER "HTTPリクエスト中のヘッダ"
#define CMT_HTTPVERSION "HTTPリクエストのバージョン"
#define CMT_ACCEPTENC    "受けつけるencoding(Accept-Encoding:)"
#define CMT_LANGEXT      "言語を表わす拡張子"
#define CMT_DOCUMENTCODE "文書の文字コード"
#define CMT_SYSTEMCODE   "システムの文字コード"
#define CMT_MARK_ALL_PAGES "全てのページのURL風の文字列をリンクにする"
#define CMT_WRAP         "折り返し検索"
#define CMT_VIEW_UNSEENOBJECTS "背景画像等へのリンクを作る"
#ifdef __EMX__
#define CMT_BGEXTVIEW	 "外部ビューアを別セッションで動かす"
#else
#define CMT_BGEXTVIEW    "外部ビューアをバックグラウンドで動かす"
#endif
#define CMT_EXT_DIRLIST  "ディレクトリリストに外部コマンドを使う"
#define CMT_DIRLIST_CMD  "ディレクトリリスト用コマンド"
#ifdef USE_DICT
#define CMT_USE_DICTCOMMAND  "辞書引きをCGI経由でおこなう"
#define CMT_DICTCOMMAND  "辞書引きコマンドのURL"
#endif				/* USE_DICT */
#define CMT_IGNORE_NULL_IMG_ALT "空のIMG ALT属性の時にリンク名を表示する"
#define CMT_ANCHOR_NUM_STYLE "アンカー番号のスタイル(例: [%d])"
#define CMT_IMG_NUM_STYLE "イメージ番号のスタイル(例: *%d)"
#define CMT_LABEL_WITHINPAGE_STYLE N_("同一文書内のラベルへのリンクのスタイル(例: (li#%d, co#%d))")
#define CMT_LINK_NUM_URL "リンク一覧生成時に使われるベースURL"
#define CMT_SCROLL_AMOUNT "画面外へのカーソル移動時のスクロール量"
#define CMT_WRAP_LINE "長い行を折り返して表示する"
#define CMT_LINE_TRUNCATED "切り詰められた行を示す文字列"
#define CMT_LINE_CONTINUED "折り返された行を示す文字列"
#define CMT_IFILE        "各ディレクトリのインデックスファイル"
#define CMT_RETRY_HTTP   "URLに自動的に http:// を補う"
#define CMT_DEFAULT_URL	 "URLを開く時のデフォルト文字列"
#define CMT_DECODE_CTE   "保存時に Content-Transfer-Encoding をデコードする"
#ifdef USE_MOUSE
#define CMT_MOUSE         "マウスを使う"
#define CMT_REVERSE_MOUSE "マウスのドラッグ動作を逆にする"
#endif				/* USE_MOUSE */
#define CMT_CLEAR_BUF     "表示されていないバッファのメモリを開放する"
#define CMT_NOSENDREFERER "Referer: を送らないようにする"
#define CMT_IGNORE_CASE "サーチ時に大文字小文字の区別をしない"
#define CMT_SEARCH_ACROSS_LINES "行を跨ぐ検索"
#define CMT_USE_LESSOPEN "LESSOPENを使用"
#ifdef USE_SSL
#ifdef USE_SSL_VERIFY
#define CMT_SSL_VERIFY_SERVER "SSLのサーバ認証を行う"
#define CMT_SSL_CERT_FILE "SSLのクライアント用PEM形式証明書ファイル"
#define CMT_SSL_KEY_FILE "SSLのクライアント用PEM形式秘密鍵ファイル"
#define CMT_SSL_CA_PATH "SSLの認証局のPEM形式証明書群のあるディレクトリへのパス"
#define CMT_SSL_CA_FILE "SSLの認証局のPEM形式証明書群のファイル"
#endif				/* USE_SSL_VERIFY */
#define CMT_SSL_FORBID_METHOD "使わないSSLメソッドのリスト(2: SSLv2, 3: SSLv3, t:TLSv1)"
#endif				/* USE_SSL */
#ifdef USE_COOKIE
#define CMT_USECOOKIE "クッキーを使用する"
#define CMT_ACCEPTCOOKIE "クッキーを受け付ける"
#define CMT_ACCEPTBADCOOKIE "問題のあるクッキーでも受け付ける"
#define CMT_COOKIE_REJECT_DOMAINS "クッキーを受け付けないドメイン"
#define CMT_COOKIE_ACCEPT_DOMAINS "クッキーを受け付けるドメイン"
#endif
#ifdef USE_ROMAJI
#define CMT_USE_ROMAJI_SEARCH "ローマ字検索を使用する"
#define CMT_ROMAJI_FILTER "ローマ字検索の文字列変換コマンド"
#endif				/* USE_ROMAJI */
#define CMT_MESSAGE_ABOUT_CONFIG_SAVE "設定ファイルに保存するかどうかの設定の説明"
#define CMT_KEYMAP_FILE "keymapファイル"
#define CMT_TRY_EXTENSIONS "ローカルファイルを開く時に追加する拡張子"
#define CMT_EDIT_REMOTE_SOURCE "リモートページのソースも編集可能にする"
#define CMT_REMOVE_TRAILING_SPACES "行末の空白を取り除く"

#define SECT_display_CMT "表示関係"
#ifdef USE_COLOR
#define SECT_color_CMT "表示色"
#endif				/* USE_COLOR */
#define SECT_misc_CMT "雑多な設定"
#define SECT_proxy_CMT "プロキシの設定"
#define SECT_directory_CMT "ディレクトリ設定"
#define SECT_external_CMT "外部プログラム"
#if defined(USE_SSL) && defined(USE_SSL_VERIFY)
#define SECT_ssl_CMT "SSLの設定"
#endif
#ifdef USE_COOKIE
#define SECT_cookie_CMT "クッキーの設定"
#endif
#define SECT_search_CMT "検索の設定"
#define SECT_network_CMT "ネットワークの設定"

#else				/* LANG != JA */


#define CMT_HELPER	 N_("External Viewer Setup")
#define CMT_TABSTOP      N_("Tab width in characters")
#define CMT_PIXEL_PER_CHAR      N_("Number of pixels per character (4.0...32.0)")
#define CMT_PIXEL_PER_LINE      N_("Number of pixels per line (4.0...64.0)")
#ifdef USE_IMAGE
#define CMT_AUTO_PIXEL_PER_CHAR N_("Auto detect number of pixels per character")
#define CMT_AUTO_PIXEL_PER_LINE N_("Auto detect number of pixels per line")
#endif
#define CMT_PAGERLINE    N_("Number of remembered lines when used as a pager")
#define CMT_HISTSIZE     N_("Number of remembered URL")
#define CMT_SAVEHIST     N_("Save URL history")
/* #define CMT_KANJICODE    N_("Display Kanji Code") */
#define CMT_FRAME        N_("Render frames automatically")
#define CMT_ARGV_IS_URL  N_("Treat argument without scheme as URL")
#define CMT_TSELF        N_("Use _self as default target")
#define CMT_DISPLINK     N_("Display link URL automatically")
#define CMT_DISPLINEINFO N_("Automatic display of current line number")
#define CMT_DISPLINEINFO_ALIAS ""
#define CMT_DISP_IMAGE   N_("Display inline images")
#ifdef USE_IMAGE
#define CMT_PRELOAD_IMAGE   N_("Pre-load inline images")
#define CMT_IMAGE_SCALE  N_("Scale of image (%)")
#define CMT_IMGDISPLAY   N_("External command to display image")
#define CMT_IMGSIZE      N_("Integrated into imgdisplay. No effect")
#define CMT_IMG_VALIGN   N_("Default vertical alignment of image (top)")
#define CMT_TABLE_VALIGN N_("Default vertical alignment of each column in table (top)")
#define CMT_IMAGE_MAP_LIST N_("Use link list of image map")
#endif
#define CMT_MULTICOL     N_("Display file names in multi-column format")
#define CMT_ALT_ENTITY   N_("Use ASCII equivalents to display entities")
#define CMT_FOLD_TEXTAREA N_("Fold lines in TEXTAREA")
#define CMT_COLOR        N_("Display with color")
#define CMT_B_COLOR      N_("Color of normal character")
#define CMT_A_COLOR      N_("Color of anchor")
#define CMT_I_COLOR      N_("Color of image link")
#define CMT_F_COLOR      N_("Color of form")
#define CMT_ACTIVE_STYLE N_("Enable coloring of active link")
#define CMT_C_COLOR	 N_("Color of currently active link")
#define CMT_VISITED_ANCHOR N_("Use visited link color")
#define CMT_V_COLOR	 N_("Color of visited link")
#define CMT_FRAME_COLOR	 N_("Color of frame border")
#define CMT_BG_COLOR     N_("Color of background")
#define CMT_MARK_COLOR   N_("Color of mark")
#define CMT_HTTP_PROXY   N_("URL of HTTP proxy host")
#ifdef USE_SSL
#define CMT_HTTPS_PROXY  N_("URL of HTTPS proxy host")
#endif				/* USE_SSL */
#ifdef USE_GOPHER
#define CMT_GOPHER_PROXY N_("URL of GOPHER proxy host")
#endif				/* USE_GOPHER */
#define CMT_FTP_PROXY    N_("URL of FTP proxy host")
#define CMT_NO_PROXY     N_("Domains to be accessed directly (no proxy)")
#define CMT_NOPROXY_NETADDR	N_("Check noproxy by network address")
#define CMT_DNS_ORDER	N_("Order of name resolution")
#define CMT_NO_CACHE    N_("Add an HTTP request header not to use cache")
#define CMT_CONCURRENT	N_("Concurrency")
#define CMT_CONCURRENT_PER_SERVER	N_("Concurrency per server")
#define CMT_FOLLOW_REDIRECTION N_("Number of redirections to follow")
#define CMT_WHEN_REDIRECTED N_("Behaviour when POST request is redirected")
#define CMT_DROOT        N_("Directory corresponding to / (document root)")
#define CMT_PDROOT       N_("Directory corresponding to /~user")
#define CMT_CGIBIN       N_("Directory corresponding to /cgi-bin")
#define CMT_CONFIRM_QQ   N_("Confirm when quitting with q")
#ifdef USE_MARK
#define CMT_USE_MARK	N_("Enable mark operations")
#endif
#ifdef EMACS_LIKE_LINEEDIT
#define CMT_EMACS_LIKE_LINEEDIT	N_("Enable Emacs-style line editing")
#endif
#ifdef VI_PREC_NUM
#define CMT_VI_PREC_NUM	 N_("Enable vi-like numeric prefix")
#endif
#define CMT_SHOW_NUM     N_("Show line numbers")
#define CMT_SHOW_SRCH_STR N_("Show search string")
#define CMT_MIMETYPES    N_("List of mime.types files")
#define CMT_MAILCAP      N_("List of mailcap files")
#define CMT_MAILCAP_ENTRY N_("Mailcap entry for temporary use")
#define CMT_BROWSECAP_ENTRY N_("Browsecap entry for temporary use")
#define CMT_EXTBRZ       N_("External Browser")
#define CMT_EXTBRZ2      N_("Second External Browser")
#define CMT_EXTBRZ3      N_("Third External Browser")
#define CMT_DISABLE_SECRET_SECURITY_CHECK	N_("Disable secret file security check")
#define CMT_PASSWDFILE	 N_("Password file")
#define CMT_FTPPASS      N_("Password for anonymous FTP (your mail address)")
#ifdef FTPPASS_HOSTNAMEGEN
#define CMT_FTPPASS_HOSTNAMEGEN N_("Generate domain part of password for FTP")
#endif
#define CMT_REQUESTHEADER N_("Headers in HTTP request")
#define CMT_HTTPVERSION N_("Version of HTTP request")
#define CMT_ACCEPTENC    N_("Accept encodings")
#define CMT_LANGEXT      N_("File extensions representing languages")
/* #define CMT_DOCUMENTCODE N_("Document Charset") */
#define CMT_MARK_ALL_PAGES N_("Mark URL-like strings as links in all pages")
#define CMT_WRAP         N_("Wrap search")
#define CMT_LINE_TRUNCATED N_("Indicator of truncated line")
#define CMT_LINE_CONTINUED N_("Indicator of continued line")
#define CMT_VIEW_UNSEENOBJECTS N_("Display unseen objects (e.g. background image tag)")
#ifdef __EMX__
#define CMT_BGEXTVIEW	 N_("Run external viewer in a separate session")
#else
#define CMT_BGEXTVIEW    N_("Run external viewer in the background")
#endif
#define CMT_EXT_DIRLIST  N_("Use external program for directory listing")
#define CMT_DIRLIST_CMD  N_("URL of directory listing command")
#ifdef USE_DICT
#define CMT_USE_DICTCOMMAND  N_("Enable dictionary lookup through CGI")
#define CMT_DICTCOMMAND  N_("URL of dictionary lookup command")
#endif				/* USE_DICT */
#define CMT_IGNORE_NULL_IMG_ALT	N_("Display link name for images lacking ALT")
#define CMT_ANCHOR_NUM_STYLE N_("Style of anchor number(e.g., [%d])")
#define CMT_IMG_NUM_STYLE N_("Style of image number(e.g., *%d)")
#define CMT_LABEL_WITHINPAGE_STYLE N_("Style of link to labels in the same document(e.g., (li#%d, co#%d))")
#define CMT_LINK_NUM_URL N_("Base URL to generate list of all links")
#define CMT_SCROLL_AMOUNT N_("Amount of scroll for cursor motion going outside current view")
#define CMT_WRAP_LINE N_("Wrap long line")
#define CMT_IFILE        N_("Index file for directories")
#define CMT_RETRY_HTTP   N_("Prepend http:// to URL automatically")
#define CMT_DEFAULT_URL  N_("Default value for open-URL command")
#define CMT_DECODE_CTE   N_("Decode Content-Transfer-Encoding when saving")
#ifdef USE_MOUSE
#define CMT_MOUSE         N_("Enable mouse")
#define CMT_REVERSE_MOUSE N_("Scroll in reverse direction of mouse drag")
#endif				/* USE_MOUSE */
#define CMT_CLEAR_BUF     N_("Free memory of undisplayed buffers")
#define CMT_NOSENDREFERER N_("Suppress `Referer:' header")
#define CMT_IGNORE_CASE N_("Search case-insensitively")
#define CMT_SEARCH_ACROSS_LINES N_("Search across lines")
#define CMT_USE_LESSOPEN N_("Use LESSOPEN")
#ifdef USE_SSL
#ifdef USE_SSL_VERIFY
#define CMT_SSL_VERIFY_SERVER N_("Perform SSL server verification")
#define CMT_SSL_CERT_FILE N_("PEM encoded certificate file of client")
#define CMT_SSL_KEY_FILE N_("PEM encoded private key file of client")
#define CMT_SSL_CA_PATH N_("Path to directory for PEM encoded certificates of CAs")
#define CMT_SSL_CA_FILE N_("File consisting of PEM encoded certificates of CAs")
#endif				/* USE_SSL_VERIFY */
#define CMT_SSL_FORBID_METHOD N_("List of forbidden SSL methods (2: SSLv2, 3: SSLv3, t:TLSv1)")
#endif				/* USE_SSL */
#ifdef USE_COOKIE
#define CMT_USECOOKIE   N_("Enable cookie processing")
#define CMT_ACCEPTCOOKIE N_("Accept cookies")
#define CMT_ACCEPTBADCOOKIE N_("Action to be taken on invalid cookie")
#define CMT_COOKIE_REJECT_DOMAINS N_("Domains to reject cookies from")
#define CMT_COOKIE_ACCEPT_DOMAINS N_("Domains to accept cookies from")
#endif
#ifdef USE_ROMAJI
#define CMT_USE_ROMAJI_SEARCH N_("Enable Roma-ji search")
#define CMT_ROMAJI_FILTER N_("Roma-ji filter command")
#endif				/* USE_ROMAJI */
#define CMT_MESSAGE_ABOUT_CONFIG_SAVE N_("Message about saving options to file")
#define CMT_KEYMAP_FILE N_("keymap file")
#define CMT_TRY_EXTENSIONS N_("Extra extensions when opening local files")
#define CMT_EDIT_REMOTE_SOURCE N_("Enable to edit sources of remote pages")
#define CMT_REMOVE_TRAILING_SPACES N_("Remove trailing spaces of each formatted line")

#define SECT_display_CMT N_("Display Settings")
#ifdef USE_COLOR
#define SECT_color_CMT N_("Color Settings")
#endif				/* USE_COLOR */
#define SECT_misc_CMT N_("Miscellaneous Settings")
#define SECT_proxy_CMT N_("Proxy Settings")
#define SECT_directory_CMT N_("Directory Settings")
#define SECT_external_CMT N_("External Program Settings")
#ifdef USE_SSL
#define SECT_ssl_CMT N_("SSL Settings")
#endif
#ifdef USE_COOKIE
#define SECT_cookie_CMT N_("Cookie Settings")
#endif
#define SECT_search_CMT N_("Search Settings")
#define SECT_network_CMT N_("Network Settings")
#if LANG == MANY
#define SECT_rc_CMT N_("Reconfigurable String Settings")
#define SECT_mb_CMT N_("Character Encoding Settings")
#endif

#endif				/* LANG != JA */

#define PI_TEXT    0
#define PI_ONOFF   1
#define PI_SEL_C   2
#define PI_TEXTA   3
#define PI_TEXTA_ROWS_MAX   10

struct sel_c {
    int value;
    char *cvalue;
    char *text;
};

#ifdef JP_CHARSET
static struct sel_c kcodestr[] =
{
    {CODE_EUC,   "E", STR_EUC},
    {CODE_SJIS,  "S", STR_SJIS},
    {CODE_JIS_j, "j", STR_JIS_j},
    {CODE_JIS_N, "N", STR_JIS_N},
    {CODE_JIS_m, "m", STR_JIS_m},
    {CODE_JIS_n, "n", STR_JIS_n},
    {0, NULL, NULL}
};

static struct sel_c dcodestr[] =
{
    {'\0',           "0", "auto detect"},
    {CODE_EUC,       "E", STR_EUC},
    {CODE_SJIS,      "S", STR_SJIS},
    {CODE_INNER_EUC, "I", STR_INNER_EUC},
    {0, NULL, NULL}
};

static struct sel_c scodestr[] =
{
    {CODE_EUC,       "E", STR_EUC},
    {CODE_SJIS,      "S", STR_SJIS},
    {0, NULL, NULL}
};
#endif				/* JP_CHARSET */

#ifdef USE_IMAGE
static struct sel_c img_valign_sel[] = {
  {-1, "D", N_("default")},
  {ALIGN_TOP, "T", N_("top")},
  {ALIGN_MIDDLE, "M", N_("middle")},
  {ALIGN_BOTTOM, "B", N_("bottom")},
  {0, NULL, NULL},
};

static struct sel_c table_valign_sel[] = {
  {VALIGN_TOP, "T", N_("top")},
  {VALIGN_MIDDLE, "M", N_("middle")},
  {VALIGN_BOTTOM, "B", N_("bottom")},
  {0, NULL, NULL},
};
#endif

static struct sel_c when_redirected_sel[] = {
  {FollowWithOriginalMethodWhenRedirected, "0", N_("Always follow with original method")},
  {FollowWithGETWhenRedirected, "1", N_("Always follow with \"GET\" method")},
  {IgnoreRedirection, "2", N_("Always ignore redirection")},
  {QueryWhenReidirected, "3", N_("Query at run time")},
  {0, NULL, NULL},
};

#ifdef USE_COLOR
static struct sel_c colorstr[] =
{
#if LANG == JA
    {0, "black", "黒"},
    {1, "red", "赤"},
    {2, "green", "緑"},
    {3, "yellow", "黄"},
    {4, "blue", "青"},
    {5, "magenta", "紫"},
    {6, "cyan", "空色"},
    {7, "white", "白"},
    {8, "terminal", "端末"},
    {0, NULL, NULL}
#else				/* LANG != JA */
    {0, "black", N_("black")},
    {1, "red", N_("red")},
    {2, "green", N_("green")},
    {3, "yellow", N_("yellow")},
    {4, "blue", N_("blue")},
    {5, "magenta", N_("magenta")},
    {6, "cyan", N_("cyan")},
    {7, "white", N_("white")},
    {8, "terminal", N_("terminal")},
    {0, NULL, NULL}
#endif				/* LANG != JA */
};
#endif				/* USE_COLOR */

#define N_S(x)	(x), '0' + (x)

static struct sel_c defaulturls[] = {
#if LANG == JA
    {DEFAULT_URL_EMPTY, "0", "無し"},
    {DEFAULT_URL_CURRENT, "1", "現在のURL"},
    {DEFAULT_URL_LINK, "2", "リンク先のURL"},
#else
    {DEFAULT_URL_EMPTY, "0", N_("none")},
    {DEFAULT_URL_CURRENT, "1", N_("current URL")},
    {DEFAULT_URL_LINK, "2", N_("link URL")},
#endif
    {0, NULL, NULL}
};

#ifdef INET6
static struct sel_c dnsorders[] =
{
    {DNS_ORDER_UNSPEC, "0", N_("unspecified")},
    {DNS_ORDER_INET_INET6, "1", N_("inet inet6")},
    {DNS_ORDER_INET6_INET, "2", N_("inet6 inet")},
    {0, NULL, NULL}
};
#endif				/* INET6 */

#ifdef USE_COOKIE
static struct sel_c badcookiestr[] = {
    {ACCEPT_BAD_COOKIE_DISCARD, "0", N_("discard")},
#if 0
    {ACCEPT_BAD_COOKIE_ACCEPT, "1", N_("accept")},
#endif
    {ACCEPT_BAD_COOKIE_ASK, "2", N_("ask")},
    {0, NULL, NULL}
};
#endif				/* USE_COOKIE */

#undef def_rcsect_begin
#define def_rcsect_begin(n)

#undef def_rcsect_end
#define def_rcsect_end(n)

#if LANG == MANY

#undef def_rcitem
#define def_rcitem(name, type, inputtype, varptr, comment, select)

#undef def_mbsetup
#define def_mbsetup(key, mac, inputtype, eng) def_rcitem(key, P_MBSETUP, inputtype, NULL, eng, NULL)

#undef def_rcstr
#define def_rcstr(key, mac, ini, eng) char *RCSTR_ ## mac = NULL;

#include "rc.h"

#undef def_rcstr
#define def_rcstr(key, mac, ini, eng) def_rcitem(key, P_STRING, PI_TEXT, &RCSTR_ ## mac, eng, NULL)

#endif

struct param_ptr paramv[] = {
#undef def_rcitem
#define def_rcitem(name, type, inputtype, varptr, comment, select) \
{# name, type, inputtype, varptr, comment, select, 0},
#include "rc.h"
};

enum {
#undef def_rcitem
#define def_rcitem(name, type, inputtype, varptr, comment, select) rcitem_ ## name,
#include "rc.h"
};

#undef def_rcsect_begin
#define def_rcsect_begin(n) sect_ ## n ## _beinning

#undef def_rcitem
#define def_rcitem(name, type, inputtype, varptr, comment, select) = rcitem_ ## name, rcitem_next_to_ ## name

#undef def_rcsect_end
#define def_rcsect_end(n) , sect_ ## n ## _end_plus_one,

enum {
#include "rc.h"
};

#undef def_rcsect_begin
#define def_rcsect_begin(n) {SECT_ ## n ## _CMT, sect_ ## n ## _beinning, sect_ ## n ## _end_plus_one - 1},

#undef def_rcitem
#define def_rcitem(name, type, inputtype, varptr, comment, select)

#undef def_rcsect_end
#define def_rcsect_end(n)

struct param_section sections[] = {
#include "rc.h"
  {NULL, 0, 0},
};

enum {
#undef def_rcsect_begin
#define def_rcsect_begin(n) secti_ ## n,
#include "rc.h"
};

btri_string_tab_t rc_paramtab[] = {
#include "rc_name.h"
};

struct param_ptr *
search_param(char *name)
{
  void *p;

  return (btri_fast_ci_search_str(name, rc_paramtab, &p) != bt_failure ?
	  p : NULL);
}

#ifdef MANY_CHARSET
static char *xv[3];
static size_t xe;
static int cmts_loaded;

static void
try_cfg_file(void (*func)(FILE *), Str cfgstr, char **xv, size_t xe, size_t xi, size_t depth, size_t depth_max)
{
  if (depth < depth_max) {
    size_t xsave, i;

    xsave = cfgstr->length;

    for (i = xi ; xe - i >= depth_max - depth ;) {
      Strcat_charp(cfgstr, xv[i++]);
      try_cfg_file(func, cfgstr, xv, xe, i, depth + 1, depth_max);
      cfgstr->ptr[cfgstr->length = xsave] = '\0';
    }
  }
  else {
    FILE *f;

    if ((f = fopen(cfgstr->ptr, "rt"))) {
      func(f);
      fclose(f);
    }
  }
}

void
try_cfg_files(const char *fn, void (*func)(FILE *), char *(*find)(char *))
{
  Str cfgstr;
  size_t i;

  cfgstr = Strnew_charp(find((char *)fn));

  for (i = 0 ; i <= xe ; ++i)
    try_cfg_file(func, cfgstr, xv, xe, 0, 0, i);
}

static void
set_rc_comments(FILE * f)
{
  Str line;
  int bok;
  const char *cs;

  for (cs = lookup_process_charset(myname) ;;) {
    line = conv_Str2mbStr(Strfgets(f), NULL, "@", cs);

    if (!line->length)
      break;

    if (line->ptr[bok = strcspn(line->ptr, "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_")]) {
      int eok;

      if (line->ptr[eok = strcspn(&line->ptr[bok], " \t=:")]) {
	char *key, *cmt;
	int boc, eoc;
	void *q;

	key = &line->ptr[bok];

	for (boc = bok + eok ; line->ptr[boc] && IS_SPACE(line->ptr[boc]) ; ++boc);

	if (line->ptr[boc] == '=') {
	  ++boc;
	  for (; line->ptr[boc] && IS_SPACE(line->ptr[boc]) ; ++boc);
	}

	key[eok] = '\0';
	for (eoc = strlen(&line->ptr[boc]) + boc ; eoc > boc && IS_SPACE(line->ptr[eoc - 1]) ; --eoc);
	cmt = &line->ptr[boc];
	line->ptr[eoc] = '\0';

	if (btri_fast_ci_search_mem(key, eok, rc_paramtab, &q) != bt_failure) {
	  if (key[0] == '*') {
	    struct param_section *p;

	    p = q;
	    p->name = cmt;
	    p->name_set = 1;
	  }
	  else {
	    struct param_ptr *p;

	    p = q;
	    p->comment = cmt;
	    p->flag |= PARAM_FLAG_CMT_SET;
	  }
	}
      }
    }
  }
}

static void
fetch_param_cmts(void)
{
  if (!cmts_loaded) {
    char *msgfile;
    FILE *f;

    try_cfg_files(W3MMESSAGES, set_rc_comments, helpFile);
    msgfile = rcFile(MESSAGES_FILE);

    if ((f = fopen(msgfile, "rt"))) {
      set_rc_comments(f);
      fclose(f);
    }

    cmts_loaded = 1;
  }
}
#endif

#define CMTSTART_COL (40)

void
show_params(FILE * fp)
{
    int i, j, k, l;
    char *t = NULL;
    char *cmt;
    Str tmp;
    struct sel_c *s;

    fputs("\nconfiguration parameters\n", fp);
#ifdef MANY_CHARSET
    fetch_param_cmts();
#endif
    for (j = 0; sections[j].name != NULL; j++) {
#ifdef MANY_CHARSET
	cmt = conv_str2isoStr(sections[j].name_set ? sections[j].name : _(sections[j].name),
			      "!", &tty_mb_w_setup)->ptr;
#else
#ifdef JP_CHARSET
	if (InnerCode != DisplayCode)
	    cmt = conv(sections[j].name, InnerCode, DisplayCode)->ptr;
	else
#endif				/* JP_CHARSET */
	    cmt = sections[j].name;
#endif
	fprintf(fp, "\n  section[%d]: %s\n", j, cmt);
	for (i = sections[j].beg ; i < sections[j].end ; ++i) {
	    switch (paramv[i].type) {
	    case P_INT:
	    case P_SHORT:
	    case P_CHARINT:
	    case P_NZINT:
	    case P_SINT:
		t = (paramv[i].inputtype == PI_ONOFF) ? "bool" : "number";
		break;
	    case P_CHAR:
		t = "char";
		break;
#ifdef MANY_CHARSET
	    case P_MBSETUP:
#endif
	    case P_STRINGV:
	    case P_STRING:
		t = "string";
		break;
#if defined(USE_SSL) && defined(USE_SSL_VERIFY)
	    case P_SSLPATH:
		t = "path";
		break;
#endif
#ifdef USE_COLOR
	    case P_COLOR:
		t = "color";
		break;
#endif
#ifdef JP_CHARSET
	    case P_CODE:
		t = "E|S|j|N|m|n";
		break;
#endif
	    case P_PIXELS:
		t = "number";
		break;
	    case P_SCALE:
		t = "percent";
		break;
	    case P_SEL:
		for (tmp = Strnew(), s = paramv[i].select ; s->text ; ++s) {
		  if (tmp->length)
		    Strcat_char(tmp, '|');
		  Strcat_charp(tmp, s->cvalue);
		}
		t = tmp->ptr;
		break;
	    }
#ifdef MANY_CHARSET
	    cmt = conv_str2isoStr(paramv[i].flag & PARAM_FLAG_CMT_SET ? paramv[i].comment : _(paramv[i].comment),
				  "!", &tty_mb_w_setup)->ptr;
#else
#ifdef JP_CHARSET
	    if (InnerCode != DisplayCode)
		cmt = conv(paramv[i].comment,
			   InnerCode,
			   DisplayCode)->ptr;
	    else
#endif				/* JP_CHARSET */
		cmt = paramv[i].comment;
#endif
	    fprintf(fp, "    -o %s=<%s>", paramv[i].name, t);
	    l = CMTSTART_COL - (sizeof("    -o %s=<%s>") - 1 - (sizeof("%s") - 1) * 2
				+ strlen(paramv[i].name) + strlen(t));
	    if (l <= 0) {
	      putc('\n', fp);
	      l = CMTSTART_COL;
	    }
	    for (;;) {
	      k = strcspn(cmt, "\n");
	      fprintf(fp, "%*s%.*s\n", l, " ", k, cmt);
	      if (!cmt[k]) break;
	      cmt += k + 1;
	      l = CMTSTART_COL;
	    }
	}
    }
}

#ifdef USE_COLOR
static int
str_to_color(char *value)
{
    if (value == NULL)
	return 8;		/* terminal */
    switch (tolower(*value)) {
    case '0':
	return 0;		/* black */
    case '1':
    case 'r':
	return 1;		/* red */
    case '2':
    case 'g':
	return 2;		/* green */
    case '3':
    case 'y':
	return 3;		/* yellow */
    case '4':
	return 4;		/* blue */
    case '5':
    case 'm':
	return 5;		/* magenta */
    case '6':
    case 'c':
	return 6;		/* cyan */
    case '7':
    case 'w':
	return 7;		/* white */
    case '8':
    case 't':
	return 8;		/* terminal */
    case 'b':
	if (!strncasecmp(value, "blu", 3))
	    return 4;		/* blue */
	else
	    return 0;		/* black */
    }
    return 8;			/* terminal */
}
#endif

#ifdef MANY_CHARSET

static process_charsets_t *process_charsets_by_locale;
static process_charsets_t *process_charsets;

static void
setup_process_charsets(const char *process_charset, process_charsets_t *from)
{
  process_charsets = from;

  if (process_charset && *process_charset) {
    Str temp;
    process_charsets_t *new, *t, *to;
    const char *p, *q;
    char *emesg, sep[2];
    size_t n, skip;

    temp = Strnew();
    sep[1] = '\0';
    p = process_charset;

    for (to = NULL ;;) {
      Strclear(temp);
      skip = 1;

      switch (*p) {
      case '(':
	sep[0] = ')';
	break;
      case '{':
	sep[0] = '}';
	break;
      case '[':
	sep[0] = ']';
	break;
      case '<':
	sep[0] = '>';
	break;
      case '^':
	sep[0] = '$';
	skip = 0;
	break;
      default:
	if (IS_ALNUM(*p)) {
	  Strcat_charp_n(temp, "^.*$", sizeof("^.*$") - 1);
	  q = p;
	  n = strlen(q);
	  goto make_re;
	}

	sep[0] = *p;
	break;
      }

      p += skip;
      n = strcspn(p, sep);
      Strcat_charp_n(temp, (char *)p, n + 1 - skip);
      q = p + n + 1;
      n = strcspn(q, " \t\r\n");
    make_re:
      new = New(process_charsets_t);
      new->prev = to;
      new->cs = allocStr(q, n);
      emesg = NULL;

      if ((new->re = newRegex(temp->ptr, 0, NULL, &emesg))) {
	if (emesg)
	  disp_err_message(emesg, FALSE);

	to = new;
      }

      p = q + n;
      SKIP_BLANKS(p);

      if (!*p)
	break;
    }

    while (to) {
      t = to->prev;
      to->prev = process_charsets;
      process_charsets = to;
      to = t;
    }
  }
}

const char *
lookup_process_charset(const char *process)
{
  process_charsets_t *l;
  size_t len;

  for (len = strlen(process), l = process_charsets ; l ; l = l->prev)
    if (RegexMatch(l->re, (char *)process, len))
      return l->cs;

  return "US-ASCII";
}

void
str_to_mbsetup(struct param_ptr *p, char *value)
{
  mb_ws_conv_t **p_convv;

  p->varptr = (value && *value) ? value : NULL;

  switch (p - paramv) {
  case rcitem_tty_charset:
    setup_tty_mb_r("@", p->varptr);
    setup_tty_mb_w("@", p->varptr);
    break;
  case rcitem_input_charset:
    conv_setup_r("@", p->varptr);
    break;
  case rcitem_output_charset:
    conv_setup_w("@", p->varptr);
    break;
  case rcitem_process_charset:
    setup_process_charsets((const char *)p->varptr, process_charsets_by_locale);
    break;
  case rcitem_unicode_width:
    mb_set_widthtable(p->varptr);
    break;
  case rcitem_tty_initial_charset: /* will be removed sometime. */
    tty_initial_input_charset = tty_initial_output_charset = (const char *)p->varptr;
    break;
  case rcitem_tty_initial_input_charset:
    tty_initial_input_charset = (const char *)p->varptr;
    break;
  case rcitem_tty_initial_output_charset:
    tty_initial_output_charset = (const char *)p->varptr;
    break;
  case rcitem_tty_input_converters:
    p_convv = &tty_input_converters;
    goto setup_converters;
  case rcitem_tty_output_converters:
    p_convv = &tty_output_converters;
    goto setup_converters;
  case rcitem_tty_fallback_converters:
    p_convv = &tty_fallback_converters;
    goto setup_converters;
  case rcitem_input_converters:
    p_convv = &input_converters;
    goto setup_converters;
  case rcitem_output_converters:
    p_convv = &output_converters;
  setup_converters:
    if (p->varptr) {
      char *s;
      int nconv;

      for (s = p->varptr, nconv = 0 ; *s && (s = strchr(s, ',')) ; ++s)
	++nconv;

      nconv += 2;
      *p_convv = New_N(mb_ws_conv_t, nconv);
      mb_namev_to_converterv(p->varptr, *p_convv, nconv, NULL);
      break;
    }

    *p_convv = NULL;
  default:
    break;
  }
}
#endif

void
str_to_stringv(void *varptr, char *value)
{
  TextList *p = varptr;
  int eol;

  if (*value) {
    char *l;

    for (;;) {
      eol = strcspn(value, "\n");
      l = allocStr(value, (eol && value[eol - 1] == '\r') ? eol - 1 : eol);
      pushValue((GeneralList *)p, l);

      if (!value[eol])
	break;

      value += eol + 1;
    }
  }
}

Str
stringv_to_str(void *varptr)
{
  TextList *p = varptr;

  if (p && p->first) {
    TextListItem *ti;
    Str d;

    for (d = Strnew_charp(p->first->ptr), ti = p->first->next ; ti ; ti = ti->next) {
      Strcat_char(d, '\n');
      Strcat_charp(d, ti->ptr);
    }

    return d;
  }
  else
    return Strnew();
}

#ifdef JP_CHARSET
char
str_to_code(char *str)
{
    if (str == NULL)
	return CODE_ASCII;
    switch (*str) {
    case CODE_ASCII:
	return CODE_ASCII;
    case CODE_EUC:
    case 'e':
	return CODE_EUC;
    case CODE_SJIS:
    case 's':
	return CODE_SJIS;
    case CODE_JIS_n:
	return CODE_JIS_n;
    case CODE_JIS_m:
	return CODE_JIS_m;
    case CODE_JIS_N:
	return CODE_JIS_N;
    case CODE_JIS_j:
	return CODE_JIS_j;
    case CODE_JIS_J:
	return CODE_JIS_J;
    case CODE_INNER_EUC:
	return CODE_INNER_EUC;
    }
    return CODE_ASCII;
}

char *
code_to_str(char code)
{
    switch (code) {
    case CODE_ASCII:
	return STR_ASCII;
    case CODE_EUC:
	return STR_EUC;
    case CODE_SJIS:
	return STR_SJIS;
    case CODE_JIS_n:
	return STR_JIS_n;
    case CODE_JIS_m:
	return STR_JIS_m;
    case CODE_JIS_N:
	return STR_JIS_N;
    case CODE_JIS_j:
	return STR_JIS_j;
    case CODE_JIS_J:
	return STR_JIS_J;
    case CODE_INNER_EUC:
	return STR_INNER_EUC;
    }
    return "unknown";
}
#endif

static void
str_to_sel(struct param_ptr *p, char *value)
{
  struct sel_c *s;

  for (s = p->select ; s->cvalue ; ++s)
    if (!strcasecmp(value, s->cvalue)) {
      *(int *)p->varptr = s->value;
      return;
    }
}

static Str
sel_to_str(struct param_ptr *p)
{
  struct sel_c *s;

  for (s = p->select ; s->cvalue ; ++s)
    if (s->value == *(int *)p->varptr)
      return Strnew_charp(s->cvalue);

  return Strnew_size(0);
}

static int
set_param(char *name, char *value)
{
    struct param_ptr *p;
    int i;
    double ppc;

    if (value == NULL)
	return 0;
    p = search_param(name);
    if (p == NULL)
	return 0;
    p->flag &= rcsave_temp_mask;
    p->flag |= rcsave_temp & PARAM_FLAG_RCSAVE;
    switch (p->type) {
    case P_INT:
	if ((i = atoi(value)) >= 0)
	  *(int *) p->varptr = p->inputtype == PI_ONOFF ? str_to_bool(value, *(int *) p->varptr) : i;
	break;
    case P_NZINT:
	if ((i = atoi(value)) > 0)
	  *(int *) p->varptr = i;
	break;
    case P_SHORT:
	*(short *) p->varptr = (p->inputtype == PI_ONOFF) ? str_to_bool(value, *(short *) p->varptr) : atoi(value);
	break;
    case P_SINT:
	*(int *) p->varptr = (p->inputtype == PI_ONOFF) ? str_to_bool(value, *(short *) p->varptr) : atoi(value);
	break;
    case P_CHARINT:
	*(char *) p->varptr = (p->inputtype == PI_ONOFF) ? str_to_bool(value, *(char *) p->varptr) : atoi(value);
	break;
    case P_CHAR:
	*(char *) p->varptr = value[0];
	break;
    case P_STRING:
	*(char **) p->varptr = value;
	break;
#if defined(USE_SSL) && defined(USE_SSL_VERIFY)
    case P_SSLPATH:
	if (value != NULL && value[0] != '\0')
	    *(char **) p->varptr = rcFile(value);
	else
	    *(char **) p->varptr = NULL;
	ssl_path_modified = 1;
	break;
#endif
#ifdef USE_COLOR
    case P_COLOR:
	*(int *) p->varptr = str_to_color(value);
	break;
#endif
#ifdef JP_CHARSET
    case P_CODE:
	*(char *) p->varptr = str_to_code(value);
	break;
#endif
    case P_PIXELS:
	ppc = atof(value);
	if (ppc >= MINIMUM_PIXEL_PER_CHAR && ppc <= MAXIMUM_PIXEL_PER_CHAR * 2)
	    *(double *)p->varptr = ppc;
	break;
    case P_SCALE:
	ppc = atof(value);
	if (ppc >= 10 && ppc <= 1000)
	    *(double *) p->varptr = ppc;
	break;
    case P_STRINGV:
	str_to_stringv(p->varptr, value);
	break;
    case P_SEL:
	str_to_sel(p, value);
	break;
#ifdef MANY_CHARSET
    case P_MBSETUP:
	str_to_mbsetup(p, value);
	break;
#endif
    }
    return 1;
}

int
set_param_option(char *option)
{
    Str tmp = Strnew();
    char *p = option, *q;

    while (*p && !IS_SPACE(*p) && *p != '=')
	Strcat_char(tmp, *p++);
    while (*p && IS_SPACE(*p))
	p++;
    if (*p == '=') {
	p++;
	while (*p && IS_SPACE(*p))
	    p++;
    }
    Strlower(tmp);
    if (set_param(tmp->ptr, p))
	goto option_assigned;
    q = tmp->ptr;
    if (!strncmp(q, "no", 2)) {	/* -o noxxx, -o no-xxx, -o no_xxx */
	q += 2;
	if (*q == '-' || *q == '_')
	    q++;
    }
    else if (tmp->ptr[0] == '-')	/* -o -xxx */
	q++;
    else
	return 0;
    if (set_param(q, "0"))
	goto option_assigned;
    return 0;
 option_assigned:
    return 1;
}

char *
get_param_option(char *name)
{
    struct param_ptr *p;

    p = search_param(name);
    return p ? to_str(p)->ptr : NULL;
}

struct rc_file_list {
  struct rc_file_list *prev;
  FILE *f;
};

static void
interpret_rc(FILE * f)
{
  Str line;
  char *key, *p, *fn;
  struct rc_file_list l0, *l, *ll;
  int fd_old;
  struct stat st_new, st_old;
#ifdef MANY_CHARSET
  const char *cs;

  cs = lookup_process_charset(myname);
#endif
  l0.prev = NULL;
  l0.f = f;
  l = &l0;

  for (;;) {
    for (f = l->f;;) {
#ifdef MANY_CHARSET
      line = conv_Str2mbStr(Strfgets(f), NULL, "@", cs);
#else
      line = Strfgets(f);
#endif
      if (line->length == 0)
	break;
      Strchop(line);
      if (line->length == 0)
	continue;
      Strremovefirstspaces(line);
      if (line->ptr[0] == '#')	/* comment */
	continue;
      for (key = p = line->ptr ; *p && !IS_SPACE(*p) ; ++p)
	*p = tolower(*p);
      if (*p)
	for (*p++ = '\0' ; *p && IS_SPACE(*p) ;)
	  p++;
      if (key[0] == '.' && !key[1]) {
	fn = rcFile(p);
	if (!stat(fn, &st_new)) {
	  for (ll = l ; ll ; ll = ll->prev)
	    if ((fd_old = fileno(ll->f)) >= 0 && !fstat(fd_old, &st_old) &&
		st_old.st_dev == st_new.st_dev && st_old.st_ino == st_new.st_ino)
	      break;
	  if (!ll) {
	    ll = New(struct rc_file_list);
	    if ((ll->f = fopen(fn, "r"))) {
	      ll->prev = l;
	      l = ll;
	      f = ll->f;
	      continue;
	    }
	  }
	}
      }
      set_param(key, p);
    }
    if (l->prev) {
      fclose(f);
      l = l->prev;
    }
    else
      break;
  }
}

#ifdef MANY_CHARSET
static mb_wchar_t
parse_tty_acl_uchar(char *s, char **p_e, int beg_p)
{
  char *e;
  mb_wchar_t wc = strtoul(s, &e, 16);

  if (s == e && !beg_p)
    wc = MB_NON_UCS_LOWER;

  *p_e = e;
  return wc;
}

static struct {
  mb_wchar_t set, fc_mask, c_limit;
} ichar_specv[] = {
  {mb_94, MB_SBC_ESC_MASK, MB_94_UNIT},
  {mb_96, MB_SBC_ESC_MASK, MB_96_UNIT},
  {mb_SBC, MB_SBC_ESC_MASK, MB_SBC_UNIT},
  {mb_94x94, MB_DBC_ESC_MASK, MB_94x94_UNIT},
  {mb_DBC, MB_DBC_ESC_MASK, MB_DBC_UNIT},
};

static mb_wchar_t
parse_tty_acl_ichar(char *s, char **p_e, int beg_p)
{
  char *e;
  long set, fc, c;

  set = strtoul(s, &e, 16);

  if (s < e && set >= 0 && set < sizeof(ichar_specv) / sizeof(ichar_specv[0])) {
    if (*e == '+') {
      s = e + 1;
      fc = strtoul(s, &e, 16);

      if (s < e) {
	if (fc < MB_ESC_FC_BASE || fc > MB_ESC_FC_BASE + ichar_specv[set].fc_mask) {
	  *p_e = NULL;
	  return 0;
	}

	fc = (fc - MB_ESC_FC_BASE) & ichar_specv[set].fc_mask;

	if (*e == '+') {
	  s = e + 1;
	  c = strtoul(s, &e, 16);

	  if (s < e) {
	    if (c < 0 || c >= ichar_specv[set].c_limit) {
	      *p_e = NULL;
	      return 0;
	    }
	  }
	  else
	    c = beg_p ? 0 : ichar_specv[set].c_limit - 1;
	}
	else
	  c = beg_p ? 0 : ichar_specv[set].c_limit - 1;
      }
      else if (beg_p)
	fc = c = 0;
      else {
	fc = ichar_specv[set].fc_mask;
	c = ichar_specv[set].c_limit - 1;
      }
    }
    else if (beg_p)
      fc = c = 0;
    else {
      fc = ichar_specv[set].fc_mask;
      c = ichar_specv[set].c_limit - 1;
    }
  }
  else {
    *p_e = NULL;
    return 0;
  }

  *p_e = e;
  return MB_WORD_ENC(ichar_specv[set].set, fc, c);
}

static char *
parse_tty_acl(char *s, mb_wchar_t *p_beg, mb_wchar_t *p_end)
{
  mb_wchar_t beg, end;
  char *e;
  mb_wchar_t (*func)(char *s, char **p_e, int beg_p);

  while (*s && IS_SPACE(*s)) ++s;

  if (!strncasecmp(s, "U+", sizeof("U+") - 1)) {
    s += sizeof("U+") - 1;
    func = parse_tty_acl_uchar;
  }
  else if (!strncasecmp(s, "I+", sizeof("I+") - 1)) {
    s += sizeof("I+") - 1;
    func = parse_tty_acl_ichar;
  }
  else
    return NULL;

  beg = func(s, &e, 1);

  if (e) {
    if (*e == '-') {
      s = e + 1;
      end = func(s, &e, 0);

      if (!e)
	return NULL;
    }
    else
      end = beg;

    if (beg <= end) {
      *p_beg = beg;
      *p_end = end;
      return e;
    }
  }

  return NULL;
}

#ifdef KANJI_SYMBOLS
void
rc_finish_ulmarks(void)
{
  char *s, **sv;
  int i, *wv, n, size;

  for (sv = NULL, wv = NULL, n = size = 0, s = RCSTR_UL_MARKS ; *s ; s += i + 1) {
    i = strcspn(s, ",");
    new_objv(&size, n, (void **)&sv, sizeof(char *), 0, (void **)&wv, sizeof(int), 1, NULL);
    sv[n] = Strnew_charp_n(s, i)->ptr;
    wv[n] = fixed_mb_strlength(sv[n]);
    ++n;
  }

  ullevel = sv;
  ullevel_width = wv;
  MAX_UL_LEVEL = n;
}

#ifdef USE_MENU
void
rc_finish_menu(void)
{
  /* if (RCSTR_MENU_FRAME) */
  char **sv = NULL;
  int n, size, wmin, wmax;

  n = size = wmin = wmax = 0;

  make_mb_rule(RCSTR_MENU_FRAME,
	       default_FRAME, sizeof(default_FRAME) / sizeof(default_FRAME[0]), DEFAULT_FRAME_WIDTH,
	       &sv, &n, &size, &wmin, &wmax);

  fix_mb_rule(sv, n, wmin, wmax);
  FRAME = sv;
  FRAME_WIDTH = wmax;
}
#endif

void
rc_finish_rule(void)
{
  char **rv, **rbv;
  int rn, rsize, rbn, rbsize, wmin, wmax;

  rv = rbv = NULL;
  rn = rsize = rbn = rbsize = wmin = wmax = 0;

  make_mb_rule(RCSTR_RULE,
	       default_rule, sizeof(default_rule) / sizeof(default_rule[0]), DEFAULT_RULE_WIDTH,
	       &rv, &rn, &rsize, &wmin, &wmax);

  make_mb_rule(RCSTR_RULE_BOLD,
	       default_rule, sizeof(default_rule) / sizeof(default_rule[0]), DEFAULT_RULE_WIDTH,
	       &rbv, &rbn, &rbsize, &wmin, &wmax);

  fix_mb_rule(rv, rn, wmin, wmax);
  translate_rule_tab(rv, rule, wmax);
  fix_mb_rule(rbv, rbn, wmin, wmax);
  translate_rule_tab(rbv, ruleB, wmax);
  RULE_WIDTH = wmax;
}
#endif

void
rc_finish_cscname(void)
{
  if (charset_cnames.first) {
    btri_string_tab_t *tab = btri_copy(&btri_string_ci_tab_desc, mb_set_ces_tab(NULL));
    char *orig_b, *orig_e;
    TextListItem *ti;

    for (ti = charset_cnames.first ; ti ; ti = ti->next) {
      orig_b = ti->ptr;
      SKIP_BLANKS(orig_b);
      orig_e = strchr(orig_b, '=');

      if (orig_e) {
	char *new_b = orig_e + 1;
	void *p;

	while (orig_b < orig_e && IS_SPACE(orig_e[-1]))
	  --orig_e;

	if (btri_fast_ci_search_mem(orig_b, orig_e - orig_b, tab, &p) != bt_failure) {
	  size_t new_n;

	  for (;;) {
	    SKIP_BLANKS(new_b);
	    new_n = strcspn(new_b, ",");

	    if (new_n) {
	      Str new;

	      new = Strnew_charp_n(new_b, new_n);
	      btri_search_mem(&btri_string_ci_tab_desc, BTRI_OP_ADD | BTRI_OP_WR, new->ptr, new->length, tab, &p);
	    }

	    if (!new_b[new_n])
	      break;

	    new_b += new_n + 1;
	  }
	}
      }
    }

    mb_set_ces_tab(tab);
  }
}

void
rc_finish_termchar(void)
{
  if (tty_char_conv_list.first) {
    int n, isquote, off;
    mb_wchar_t beg, end, wbuf[TTY_CHAR_CONV_LEN_MAX], *wp, *ewp, *pool;
    int pool_end, pool_max;
    char *s;
    btri_uint_opt_tab_t *tab;
    TextListItem *ti;

    ttyfix_wcwidth_init();
    tab = btri_new_node(&btri_uint_opt_tab_desc);
    pool = NewAtom_N(mb_wchar_t, BUFSIZ);
    pool_end = 0;
    pool_max = BUFSIZ;

    for (ti = tty_char_conv_list.first ; ti ; ti = ti->next)
      if ((s = parse_tty_acl(ti->ptr, &beg, &end))) {
	while (*s && IS_SPACE(*s))
	  ++s;

	if (*s == '"' || *s == '\'') {
	  int c = *s++;
	  char *e = strchr(s, c);

	  n = e ? e - s : strlen(s);
	  isquote = 1;
	}
	else {
	  for (n = strlen(s) ; n > 0 && IS_SPACE(s[n - 1]) ; --n)
	    ;

	  isquote = 0;
	}

	if (isquote || (n && strncmp(s, "NULL", n))) {
	  ewp = wbuf;

	  if (!isquote && !strncmp(s, "REJECT", n))
	    *ewp++ = mb_notchar_enc_invalid;
	  else
	    ewp = tty_char_conv_mbs2wcs(s, n, wbuf);

	  NEW_OBJV1(&pool_max, pool_end + ewp - wbuf + 1, &pool, sizeof(mb_wchar_t), 1);
	  pool[pool_end] = ewp - wbuf;
	  off = ++pool_end;

	  for (wp = wbuf ; wp < ewp ;)
	    pool[pool_end++] = *wp++;
	}
	else
	  off = 0;

	btri_add_uint_n_to_1(&btri_uint_opt_tab_desc, beg, end, tab, (void *)off);
      }

    if (tab->x.type[0] != bt_failure) {
      bt_result_t t;
      char e;
      unsigned int count, max;

      btri_uint_optimize(&btri_uint_opt_tab_desc, tab, &t, &e, &count, &max, 2);
      tty_char_conv_tab.tab = NewAtom_N(unsigned int, count);
      btri_pack_uint_tab(&btri_uint_opt_tab_desc, tab, tty_char_conv_tab.tab);
      tty_char_conv_tab.pool = pool;
    }
    else {
      tty_char_conv_tab.tab = NULL;
      tty_char_conv_tab.pool = NULL;
    }
  }
}

void
rc_finish(void)
{
  conv_init_r(paramv[rcitem_mylang].varptr, paramv[rcitem_mylang_charset].varptr, "");

#ifdef KANJI_SYMBOLS
  rc_finish_ulmarks();
  UL_TYPE_DISC_WIDTH = fixed_mb_strlength(RCSTR_UL_TYPE_DISC);
  UL_TYPE_CIRCLE_WIDTH = fixed_mb_strlength(RCSTR_UL_TYPE_CIRCLE);
  UL_TYPE_SQUARE_WIDTH = fixed_mb_strlength(RCSTR_UL_TYPE_SQUARE);
  HR_RULE_WIDTH = fixed_mb_strlength(RCSTR_HR_RULE);
  HR_RULE_LENGTH = strlen(RCSTR_HR_RULE);
#ifdef USE_MENU
  rc_finish_menu();
#endif
  rc_finish_rule();
#endif
  rc_finish_cscname();
  rc_finish_termchar();
}
#endif

void
parse_proxy()
{
    if (non_null(HTTP_proxy))
	parseURL(HTTP_proxy, &HTTP_proxy_parsed, NULL);
#ifdef USE_SSL
    if (non_null(HTTPS_proxy))
	parseURL(HTTPS_proxy, &HTTPS_proxy_parsed, NULL);
#endif				/* USE_SSL */
#ifdef USE_GOPHER
    if (non_null(GOPHER_proxy))
	parseURL(GOPHER_proxy, &GOPHER_proxy_parsed, NULL);
#endif				/* USE_GOPHER */
    if (non_null(FTP_proxy))
	parseURL(FTP_proxy, &FTP_proxy_parsed, NULL);
    if (non_null(NO_proxy))
	set_no_proxy(NO_proxy);
}

#ifdef USE_COOKIE
void
parse_cookie()
{
    if (non_null(cookie_reject_domains))
	Cookie_reject_domains = make_domain_list(cookie_reject_domains);
    if (non_null(cookie_accept_domains))
	Cookie_accept_domains = make_domain_list(cookie_accept_domains);
}
#endif

#ifdef __EMX__
static int
do_mkdir(const char *dir, long mode)
{
    char *r, abs[_MAX_PATH];
    size_t n;

    _abspath(abs, rc_dir, _MAX_PATH);	/* Translate '\\' to '/' */

    if(!(n=strlen(abs)))
	return -1;

    if(*(r=abs+n-1)=='/')	/* Ignore tailing slash if it is */
	*r = 0;

    return mkdir(abs, mode);
}
#else				/* not __EMX__ */
#define do_mkdir(dir,mode) mkdir(dir,mode)
#endif				/* not __EMX__ */

btri_string_tab_t *
loadMimeTypes(char *filename)
{
    FILE *f;
    char *d, *type, *val;
    Str tmp, tmp1;
    btri_string_tab_t *mtypes;

    f = fopen(expandName(filename), "r");
    if (f == NULL)
	return NULL;
    mtypes = btri_new_node(&btri_string_tab_desc);
    while (tmp = Strfgets(f), tmp->length > 0) {
	d = tmp->ptr;
	if (d[0] == '#')
	    continue;
	type = strtok(d, " \t\n\r");
	if (type == NULL)
	    continue;
	while (1) {
	    d = strtok(NULL, " \t\n\r");
	    if (d == NULL)
		break;
	    tmp1 = Strnew_charp(d);
	    val = Strnew_charp(type)->ptr;
	    btri_search_mem(&btri_string_tab_desc, BTRI_OP_ADD | BTRI_OP_WR, tmp1->ptr, tmp1->length, mtypes, (void **)&val);
	}
    }
    fclose(f);
    return mtypes;
}

void
initMimeTypes()
{
    int i;
    TextListItem *tl;

    if (non_null(mimetypes_files))
	mimetypes_list = make_domain_list(mimetypes_files);
    else
	mimetypes_list = NULL;
    if (mimetypes_list == NULL)
	return;
    UserMimeTypes = New_N(btri_string_tab_t *, mimetypes_list->nitem);
    for (i = 0, tl = mimetypes_list->first; tl; i++, tl = tl->next)
	UserMimeTypes[i] = loadMimeTypes(tl->ptr);

    resetEncodingMedia();

    if (AcceptEnc.first) {
      char *enc;
      TextListItem *ti;

      for (ti = AcceptEnc.first ; ti ; ti = ti->next) {
	size_t enc_n;

	enc = ti->ptr;
	SKIP_BLANKS(enc);

	if ((enc_n = strcspn(enc, " \t")) && enc[enc_n]) {
	  char *media = &enc[enc_n + 1];
	  size_t media_n;

	  SKIP_BLANKS(media);

	  if ((media_n = strcspn(media, " \t")) && media[media_n]) {
	    char *dec_name = &media[media_n + 1];
	    size_t dec_name_n;

	    SKIP_BLANKS(dec_name);

	    if ((dec_name_n = strcspn(dec_name, " \t")) && dec_name[dec_name_n]){
	      char *dec_cmd = &dec_name[dec_name_n + 1];

	      SKIP_BLANKS(dec_cmd);

	      if (*dec_cmd) {
		ContentEncoding *ce = New(ContentEncoding);

		ce->encoding = allocStr(enc, enc_n);
		ce->media = allocStr(media, media_n);
		ce->decoder_name = allocStr(dec_name, dec_name_n);
		ce->decoder = allocStr(dec_cmd, -1);
		searchEncodingMedia(BTRI_OP_ADD, enc, enc_n, &ce);
	      }
	    }
	  }
	}
      }
    }
}

#ifdef MANY_CHARSET
static btri_string_tab_t stdenc_tab[] = {
#include "rc_enc_name.h"
};

char *
parse_lang(const char *name, char **p_lang, char **p_country, char **p_encoding)
{
  char *val;

  if ((val = getenv(name)) && *val) {
    int i;
    Str lang, country, encoding;
    char *enc;

    switch (val[i = strcspn(val, "_.")]) {
    case '_':
      lang = Strnew_charp_n(val, i);
      val += i;

      if ((enc = strchr(val, '.'))) {
	country = Strnew_charp_n(val, enc - val);
	encoding = Strnew_charp(enc);
      }
      else {
	country = Strnew_charp(val);
	encoding = Strnew();
      }

      break;
    case '.':
      lang = Strnew_charp_n(val, i);
      country = Strnew();
      encoding = Strnew_charp(&val[i]);
      break;
    default:
      lang = Strnew_charp_n(val, i);
      country = encoding = Strnew();
      break;
    }

    Strlower(lang);
    *p_lang = lang->ptr;
    Strlower(country);
    *p_country = country->ptr;
    Strlower(encoding);

    if (encoding->length &&
	btri_fast_search_mem(encoding->ptr + 1, encoding->length - 1, stdenc_tab, (void **)&val) != bt_failure)
      *p_encoding = Strnew_m_charp(".", val, NULL)->ptr;
    else
      *p_encoding = encoding->ptr;
  }
  else
    val = *p_lang = *p_country = *p_encoding = NULL;

  return val;
}

static btri_string_tab_t default_locale2mime_tab[] = {
#include "locale2mime.h"
};

static btri_string_tab_t *locale2mime_tab = default_locale2mime_tab;

#ifdef LOCALE_DIR

static char *locale_charset;

char *
message_noconv(const char *message)
{
  return message ? allocStr(message, -1) : NULL;
}

static char *
do_message_conv(const char *message)
{
  return conv_str2mbStr(gettext(message), NULL, "@|", locale_charset,
			MB_FLAG_ASCIIATCTL | MB_FLAG_NOSSL | MB_FLAG_DISCARD_NOTPREFERED_CHAR)->ptr;
}

#endif

static char *
find_locale_charset(const char *envname)
{
  char *lang, *country, *encoding;
  static const char *fnv[3] = {SYS_LOCALE2MIME, USER_LOCALE2MIME, NULL}, **fnp;

  if (!fnp) {
    FILE *fp;

    for (fnp = fnv ; *fnp ; ++fnp)
      if ((fp = fopen(expandName((char *)*fnp), "r"))) {
	Str l;

	if (locale2mime_tab == default_locale2mime_tab)
	  locale2mime_tab = btri_copy(&btri_string_ci_tab_desc, default_locale2mime_tab);

	while ((l = Strfgets(fp))->length) {
	  char *cs, *eo_cs;

	  Strremovetrailingspaces(l);
	  cs = l->ptr;
	  SKIP_BLANKS(cs);

	  if (*cs != '#' && (eo_cs = strchr(cs, '='))) {
	    char *lang;
	    int eo_lang;

	    lang = eo_cs + 1;
	    
	    while (eo_cs > cs && IS_SPACE(*(eo_cs - 1)))
	      --eo_cs;

	    if (eo_cs > cs) {
	      cs = allocStr(cs, eo_cs - cs);

	      while (*lang) {
		SKIP_BLANKS(lang);

		if ((eo_lang = strcspn(lang, ","))) {
		  Str langstr;

		  langstr = Strnew_charp_n(lang, eo_lang);
		  Strremovetrailingspaces(langstr);

		  if (langstr->length)
		    btri_search_mem(&btri_string_ci_tab_desc, BTRI_OP_ADD | BTRI_OP_WR,
				    langstr->ptr, langstr->length, locale2mime_tab, (void **)&cs);
		}

		if (!lang[eo_lang])
		  break;

		lang += eo_lang + 1;
	      }
	    }
	  }
	}

	fclose(fp);
      }
  }

  if (parse_lang(envname, &lang, &country, &encoding)) {
    Str langstr;
    char *charset;

    langstr = Strnew_m_charp(lang, country, encoding, NULL);
    if (btri_fast_ci_search_mem(langstr->ptr, langstr->length, locale2mime_tab, (void **)&charset) != bt_failure) goto found;
    langstr = Strnew_m_charp(lang, encoding, NULL);
    if (btri_fast_ci_search_mem(langstr->ptr, langstr->length, locale2mime_tab, (void **)&charset) != bt_failure) goto found;
    langstr = Strnew_m_charp(country, encoding, NULL);
    if (btri_fast_ci_search_mem(langstr->ptr, langstr->length, locale2mime_tab, (void **)&charset) != bt_failure) goto found;
    if (btri_fast_ci_search_str(encoding, locale2mime_tab, (void **)&charset) != bt_failure) goto found;
    langstr = Strnew_m_charp(lang, country, NULL);
    if (btri_fast_ci_search_mem(langstr->ptr, langstr->length, locale2mime_tab, (void **)&charset) != bt_failure) goto found;
    if (btri_fast_ci_search_str(lang, locale2mime_tab, (void **)&charset) != bt_failure) goto found;
    if (btri_fast_ci_search_str(country, locale2mime_tab, (void **)&charset) != bt_failure) goto found;
    if (!*encoding) goto failure;
    charset = allocStr(&encoding[1], -1);
  found:
    return charset;
  }
failure:
  return NULL;
}

void
setup_locale_charset(void)
{
  char *lc;

#ifdef LOCALE_DIR
  if ((lc = find_locale_charset("LC_MESSAGES"))) {
    locale_charset = lc;
    message_conv = do_message_conv;
  }
  else if ((lc = find_locale_charset("LANG"))) {
    char *lc1;

    locale_charset = lc;
    message_conv = do_message_conv;
    setup_process_charsets((lc1 = find_locale_charset("LC_CTYPE")) ? lc1 : lc, NULL);
    process_charsets_by_locale = process_charsets;
    return;
  }
  else
    message_conv = message_noconv;
#endif

  if ((lc = find_locale_charset("LC_CTYPE")) ||
      (lc = find_locale_charset("LANG"))) {
    setup_process_charsets(lc, NULL);
    process_charsets_by_locale = process_charsets;
  }
}

#endif

#ifdef USE_COOKIE
static char *UserSpecifiedCookie;
static char *UserSpecifiedCookie2;
#endif

static btri_string_tab_t HTTPAutoHeader[] = {
#include "http_auto_header.h"
};

static btri_string_tab_t HTTPVersions[] = {
#include "http_version.h"
};

void
sync_with_header(void)
{
    TextListItem *ti;
    char *e, *ct;
    void *value;
    btri_string_tab_t *tab;
#ifdef USE_COOKIE
    char *cookie_in_init = NULL, *cookie2_in_init = NULL;
#endif

    UserAgent = AcceptMedia = AcceptEncoding = AcceptLang = UserSpecifiedReferer = NULL;
    HTTPRequestHeaderList.first = HTTPRequestHeaderList.last = NULL;
    HTTPRequestHeaderList.nitem = 0;
    tab = btri_new_node(&btri_string_ci_tab_desc);

    for (ti = InitHTTPRequestHeaderList.first ; ti ; ti = ti->next) {
      if ((e = strchr(ti->ptr, ':'))) {
	if (btri_fast_ci_search_mem(ti->ptr, e - ti->ptr, HTTPAutoHeader, &value) != bt_failure) {
	  if (value) {
	    ++e;
	    SKIP_BLANKS(e);
	    *(char **)value = allocStr(e, -1);
	  }
	}
	else if (btri_fast_ci_search_mem(ti->ptr, e - ti->ptr, tab, &value) == bt_failure) {
	  btri_search_mem(&btri_string_ci_tab_desc, BTRI_OP_ADD | BTRI_OP_WR,
			  ti->ptr, e - ti->ptr, tab, (void **)&ti);
	  pushText(&HTTPRequestHeaderList, ti->ptr);
	}
      }
    }

    ct = UserSpecifiedContentType;
    UserSpecifiedContentType = NULL;

#ifdef USE_COOKIE
    if (!use_cookie) {
      if (UserSpecifiedCookie) {
	cookie_in_init = Strnew_m_charp("Cookie: ", UserSpecifiedCookie, NULL)->ptr;
	UserSpecifiedCookie = NULL;
      }

      if (UserSpecifiedCookie2) {
	cookie2_in_init = Strnew_m_charp("Cookie2: ", UserSpecifiedCookie2, NULL)->ptr;
	UserSpecifiedCookie2 = NULL;
      }
    }
#endif

    for (ti = ExtraHTTPRequestHeaderList.first ; ti ; ti = ti->next) {
      if ((e = strchr(ti->ptr, ':'))) {
	if (btri_fast_ci_search_mem(ti->ptr, e - ti->ptr, HTTPAutoHeader, &value) != bt_failure) {
	  if (value) {
	    ++e;
	    SKIP_BLANKS(e);
	    *(char **)value = allocStr(e, -1);
	  }
	}
	else if (btri_fast_ci_search_mem(ti->ptr, e - ti->ptr, tab, &value) == bt_failure) {
	  btri_search_mem(&btri_string_ci_tab_desc, BTRI_OP_ADD | BTRI_OP_WR,
			  ti->ptr, e - ti->ptr, tab, (void **)&ti);
	  continue;
	}

	if (ti->prev)
	  ti->prev->next = ti->next;
	else
	  ExtraHTTPRequestHeaderList.first = ti->next;

	if (ti->next)
	  ti->next->prev = ti->prev;
	else
	  ExtraHTTPRequestHeaderList.last = ti->prev;
      }
    }

    UserSpecifiedExtraContentType = UserSpecifiedContentType;
    UserSpecifiedContentType = ct;

#ifdef USE_COOKIE
    if (!use_cookie) {
      if (UserSpecifiedCookie) {
	pushValue((GeneralList *)&ExtraHTTPRequestHeaderList,
		  Strnew_m_charp("Cookie: ", UserSpecifiedCookie, NULL)->ptr);
      }
      else if (cookie_in_init)
	pushValue((GeneralList *)&HTTPRequestHeaderList, cookie_in_init);

      if (UserSpecifiedCookie2)
	pushValue((GeneralList *)&ExtraHTTPRequestHeaderList,
		  Strnew_m_charp("Cookie2: ", UserSpecifiedCookie2, NULL)->ptr);
      else if (cookie2_in_init)
	pushValue((GeneralList *)&HTTPRequestHeaderList, cookie2_in_init);
    }

    UserSpecifiedCookie = UserSpecifiedCookie2 = NULL;
#endif

    if (!UserAgent || !*UserAgent)
      UserAgent = w3m_version;

    if (!AcceptMedia || !*AcceptMedia)
      AcceptMedia = acceptableMimeTypes();

    if (!AcceptEncoding || !*AcceptEncoding) {
      AllAcceptEncodings = NULL;
      AcceptEncoding = allAcceptEncodings();
    }

    if (!AcceptLang || !*AcceptLang) {
#if defined(ACCEPT_LANG)
      AcceptLang = ACCEPT_LANG;
#elif defined(JP_CHARSET)
      AcceptLang = "ja;q=1.0, en;q=0.5";
#else
      AcceptLang = "en;q=1.0";
#endif
    }

    if (!HTTPVersion || btri_fast_search_str(HTTPVersion, HTTPVersions, &value) == bt_failure)
      HTTPVersion = HTTP_DEFAULT_VERSION;
}

void
sync_with_option(void)
{
    WrapSearch = WrapDefault;
    freeAllKeptAsyncRWBuffers();
    parse_proxy();
#ifdef USE_COOKIE
    parse_cookie();
#endif
    initMailcap(&UserMailcap, &mailcap_list, mailcap_files, &mailcap_entries, TRUE);
    initMimeTypes();
    initMailcap(&UserBrowsecap, &browsecap_list, browsecap_files, &browsecap_entries, FALSE);
#ifdef USE_ROMAJI
    init_romaji_filter(0);
#endif

    main_p0env.flag &= ~RG_PROC_MASK;

    if (concurrent_per_server > KEPT_SOCK_MAX_PER_SERVER)
      concurrent_per_server = KEPT_SOCK_MAX_PER_SERVER;

    if (concurrent > KEPT_SOCK_MAX ||
	(concurrent == 1 && concurrent_per_server > 1))
      concurrent = KEPT_SOCK_MAX;

    if (concurrent > 0)
      main_p0env.flag |= RG_PROC_FORK;

    if (fmInitialized) {
      initKeymap(FALSE);
#ifdef USE_MENU
      initMenu();
#endif				/* MENU */
    }

    initTryExtensions();
    loadPasswd();
    sync_with_header();
}

static void
clear_stringvs(void)
{
  AcceptEnc.first = AcceptEnc.last =
    InitHTTPRequestHeaderList.first = InitHTTPRequestHeaderList.last =
    mailcap_entries.first = mailcap_entries.last =
    browsecap_entries.first = browsecap_entries.last = NULL;
  AcceptEnc.nitem = InitHTTPRequestHeaderList.nitem =
    mailcap_entries.nitem = browsecap_entries.nitem = 0;
#ifdef MANY_CHARSET
  charset_cnames.first = tty_char_conv_list.first =
    charset_cnames.last = tty_char_conv_list.last = NULL;
  charset_cnames.nitem = tty_char_conv_list.nitem = 0;
#endif
}

void
init_rc(char *config_filename)
{
    struct stat st, tst;
    FILE *f;
#ifdef MANY_CHARSET
    char *lang, *country, *encoding;
#endif
    char *tmpdir;

    if (((tmpdir = getenv("TMP")) == NULL || *tmpdir == '\0')
	&& ((tmpdir = getenv("TEMP")) == NULL || *tmpdir == '\0')
	&& ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0'))
	    tmpdir = "/tmp";

#ifdef MANY_CHARSET
#undef def_rcsect_begin
#define def_rcsect_begin(n)
#undef def_rcsect_end
#define def_rcsect_end(n)
#undef def_rcitem
#define def_rcitem(name, type, inputtype, varptr, comment, select)
#undef def_rcstr
#define def_rcstr(key, mac, ini, eng) RCSTR_ ## mac = _(ini);
#undef def_mbsetup
#define def_mbsetup(key, mac, inputtype, eng)
#include "rc.h"
#endif

    if (stat(rc_dir, &st) < 0) {
	if (errno == ENOENT) {	/* no directory */
	    if (do_mkdir(rc_dir, 0700) < 0) {
		fprintf(stderr, "Can't create config directory (%s)!\n", rc_dir);
		rc_dir = tmpdir;
		rc_dir_is_tmp = TRUE;
		goto find_tmp_dir;
	    }
	    else {
		stat(rc_dir, &st);
	    }
	}
	else {
	    fprintf(stderr, "Can't open config directory (%s)!\n", rc_dir);
	    rc_dir = tmpdir;
	    rc_dir_is_tmp = TRUE;
	    goto find_tmp_dir;
	}
    }
    if (!S_ISDIR(st.st_mode)) {
	/* not a directory */
	fprintf(stderr, "%s is not a directory!\n", rc_dir);
	rc_dir = tmpdir;
	rc_dir_is_tmp = TRUE;
    }
find_tmp_dir:
    if (strcmp(tmp_dir, rc_dir)) {
      if (stat(tmp_dir, &tst) < 0) {
	if (errno == ENOENT) { /* no directory */
	  if (do_mkdir(tmp_dir, 0700) < 0) {
	    fprintf(stderr,
		    "Can't create directory (%s) for temporary files!\n"
		    "Use config directory (%s) instead"
		    , tmp_dir, rc_dir);
	    tmp_dir = rc_dir;
	    goto end;
	  }
	  else
	    stat(tmp_dir, &tst);
	}
	else {
	  fprintf(stderr,
		  "Can't open directory (%s) for temporary files!\n"
		  "Use config directory (%s) instead"
		  , tmp_dir, rc_dir);
	  tmp_dir = rc_dir;
	  goto end;
	}
      }

      if (!S_ISDIR(tst.st_mode)) {
	/* not a directory */
	fprintf(stderr,
		"%s is not a directory!\n"
		"Use config directory (%s) for temporary files"
		, tmp_dir, rc_dir);
	tmp_dir = rc_dir;
      }
      else if (tst.st_uid != getuid() || (tst.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) != S_IRWXU) {
	fprintf(stderr,
		"%s is world readable or writable!\n"
		"Use config directory (%s) for temporary files"
		, tmp_dir, rc_dir);
	tmp_dir = rc_dir;
      }
    end:
      ;
    }

    /* open config file */
    clear_stringvs();
#ifdef MANY_CHARSET
    xe = 0;

    if (parse_lang("W3MLANG", &lang, &country, &encoding) ||
	parse_lang("LANG", &lang, &country, &encoding)) {
      if (*lang) xv[xe++] = Strnew_m_charp(".", lang, NULL)->ptr;
      if (*country) xv[xe++] = country;
      if (*encoding) xv[xe++] = encoding;
    }

    try_cfg_files(W3MCONFIG, interpret_rc, etcFile);
#else
    if ((f = fopen(etcFile(W3MCONFIG), "rt")) != NULL) {
	interpret_rc(f);
	fclose(f);
    }
#endif
    if (!rc_dir_is_tmp) {
      if (!(config_file = config_filename))
	config_filename = rcFile(CONFIG_FILE);

      if ((f = fopen(config_filename, "rt"))) {
	int orig_rcsave_temp = rcsave_temp;

	orig_rcsave_temp = rcsave_temp;
	rcsave_temp = PARAM_FLAG_RCSAVE;
	interpret_rc(f);
	rcsave_temp = orig_rcsave_temp;
	fclose(f);
      }
    }
}


static char optionpanel_src1[] =
"<html><head>"
#ifdef MANY_CHARSET
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=x-moe-internal\">\n"
#endif
"<title>Option Setting Panel</title></head>\
<body><center><b>Option Setting Panel</b><br><b>(w3m version %s)</b></center><p>\n"
"<a href=\"file:///$LIB/" W3MHELPERPANEL_CMDNAME "?mode=panel\">%s</a>\n"
"<form method=internal action=option>";

static Str
to_str(struct param_ptr *p)
{
    switch (p->type) {
    case P_INT:
    case P_NZINT:
#ifdef USE_COLOR
    case P_COLOR:
#endif
    case P_SINT:
	return Sprintf("%d", *(int *) p->varptr);
    case P_SHORT:
	return Sprintf("%d", *(short *) p->varptr);
    case P_CHARINT:
	return Sprintf("%d", *(char *) p->varptr);
    case P_CHAR:
#ifdef JP_CHARSET
    case P_CODE:
#endif
	return Sprintf("%c", *(char *) p->varptr);
    case P_STRING:
#if defined(USE_SSL) && defined(USE_SSL_VERIFY)
    case P_SSLPATH:
#endif
	return Strnew_charp(*(char **) p->varptr);
    case P_PIXELS:
    case P_SCALE:
	return Sprintf("%g", *(double *) p->varptr);
    case P_STRINGV:
	return stringv_to_str(p->varptr);
    case P_SEL:
	return sel_to_str(p);
#ifdef MANY_CHARSET
    case P_MBSETUP:
	return Strnew_charp(p->varptr);
#endif
    }
    /* not reached */
    return NULL;
}

Buffer *
load_option_panel(void)
{
    Str src = Sprintf(optionpanel_src1, w3m_version, CMT_HELPER);
    struct param_ptr *p;
    struct sel_c *s;
    int x, i;
    Str tmp;

#ifdef MANY_CHARSET
    fetch_param_cmts();
#endif
    Strcat_m_charp(src,
		   "<hr width=50%>\n"
		   "<table>"
		   "<tr><td>",
		   html_quote(message_about_config_save),
		   "</td><td><input type=text name=\"config_file\" value=\"",
		   html_quote(config_file),
		   Sprintf("\"></td><td width=%d><input type=radio name=save_to_config_file value=1",
			   (int)((sizeof("(*)Yes (*)No") - 1 + 2) * pixel_per_char))->ptr,
		   (save_to_config_file ? " checked" : ""),
		   ">Yes&nbsp;&nbsp;<input type=radio name=save_to_config_file value=0",
		   (save_to_config_file ? "" : " checked"),
		   ">No"
		   "</td></tr></table><hr width=50%>", NULL);
    for (i = 0; sections[i].name != NULL; i++) {
	Strcat_m_charp(src, "<h1>",
		       html_quote(sections[i].name_set ? sections[i].name : _(sections[i].name)),
		       "</h1>", NULL);
	Strcat_charp(src, "<table>");
	for (p = &paramv[sections[i].beg] ; p - paramv < sections[i].end ; ++p) {
	    if (p->comment && !*p->comment)
		continue;
	    Strcat_m_charp(src, "<tr><td>",
#ifdef MANY_CHARSET
			   p->flag & PARAM_FLAG_CMT_SET ? p->comment : _(p->comment),
#else
			   p->comment,
#endif
			   NULL);
	    Strcat_charp(src, "</td><td>");
	    switch (p->inputtype) {
	    case PI_TEXTA:
		if (p->type == P_STRINGV && ((TextList *)p->varptr)->nitem > 1) {
		  if ((x = ((TextList *)p->varptr)->nitem) > PI_TEXTA_ROWS_MAX)
		    x = PI_TEXTA_ROWS_MAX;
		}
		else
		  x = 1;
		Strcat(src,
		       Sprintf("<textarea name=%s rows=%d>%s</textarea>",
			       p->name, x, html_quote(to_str(p)->ptr)));
		break;
	    case PI_TEXT:
		Strcat_m_charp(src, "<input type=text name=",
			       p->name,
			       " value=\"",
			       html_quote( to_str(p)->ptr ),
			       "\">", NULL);
		break;
	    case PI_ONOFF:
		x = atoi(to_str(p)->ptr);
		Strcat_m_charp(src, "<input type=radio name=",
			       p->name,
			       " value=1",
			       (x ? " checked" : ""),
			       ">YES&nbsp;&nbsp;<input type=radio name=",
			       p->name,
			       " value=0",
			       (x ? "" : " checked"),
			       ">NO", NULL);
		break;
	    case PI_SEL_C:
		tmp = to_str(p);
		Strcat_m_charp(src, "<select name=",
			       p->name,
			       ">", NULL);
		for (s = p->select; s->text != NULL; s++) {
		    Strcat_charp(src, "<option value=");
		    Strcat(src, Sprintf("%s\n", s->cvalue));
		    if ((p->type != P_CHAR &&
#ifdef JP_CHARSET
			 p->type != P_CODE &&
#endif
			 s->value == atoi(tmp->ptr)) ||
			((p->type == P_CHAR
#ifdef JP_CHARSET
			  || p->type == P_CODE
#endif
			    ) && (char) (s->value) == *(tmp->ptr)))
			Strcat_charp(src, " selected");
		    Strcat_char(src, '>');
		    Strcat_charp(src, s->text);
		}
		Strcat_charp(src, "</select>");
	    }
	    Strcat(src, Sprintf("</td><td width=%d>save?&nbsp;"
				"<input type=checkbox name=" RCSAVE_NAME "%s value=\"%s\">"
				"</td></tr>\n",
				(int)((sizeof("[*] save?") - 1 + 2) * pixel_per_char),
				(p->flag & PARAM_FLAG_RCSAVE) ? " checked" : "", p->name));
	}
	Strcat_charp(src, "<tr><td></td><td><p><input type=submit value=\"OK\"></td><td></td></tr>");
	Strcat_charp(src, "</table><hr width=50%>");
    }
    Strcat_charp(src, "</table></form></body></html>");
    return loadHTMLString(src, NULL, &main_p0env);
}

void
panel_set_option(struct parsed_tagarg *arg)
{
    FILE *f = NULL;
    int n;
    struct parsed_tagarg *savearg = arg;

    clear_stringvs();

    for (n = 0 ; n < 2 ; ++n) {
      for (arg = savearg ; arg ; arg = arg->next) {
	if (!strcmp(arg->arg, "save_to_config_file")) {
	  if (!n)
	    save_to_config_file = str_to_bool(arg->value, save_to_config_file);
	}
	else if (!strcmp(arg->arg, "config_file")) {
	  if (!n && arg->value && *arg->value) config_file = expandName(arg->value);
	}
	else if (!strcmp(arg->arg, RCSAVE_NAME)) {
	  if (!n && arg->value) {
	    struct param_ptr *p;

	    if ((p = search_param(arg->value)))
	      p->flag |= PARAM_FLAG_RCSAVE_BY_PANEL;
	  }
	}
	else if (n) {
	  int orig_rcsave_temp_mask, orig_rcsave_temp;

	  orig_rcsave_temp_mask = rcsave_temp_mask;
	  rcsave_temp_mask = ~0;
	  orig_rcsave_temp = rcsave_temp;
	  rcsave_temp = 0;

	  if (set_param(arg->arg, arg->value)) {
	    struct param_ptr *p;

	    if ((p = search_param(arg->arg))) {
	      if (p->flag & PARAM_FLAG_RCSAVE_BY_PANEL) {
		p->flag &= ~PARAM_FLAG_RCSAVE_BY_PANEL;
		p->flag |= PARAM_FLAG_RCSAVE;

		if (f) {
		  size_t eol;
		  char *s;
		  Str temp;

		  for (temp = Strnew(), s = arg->value ;; s += eol + 1) {
		    eol = strcspn(s, "\n");
		    Strcat_charp_n(temp, s, (eol && s[eol - 1] == '\r') ? eol - 1 : eol);
#ifdef MANY_CHARSET
		    mb_fprintf(f, "%s %s\n", arg->arg, temp->ptr);
#else
		    fprintf(f, "%s %s\n", arg->arg, arg->value);
#endif
		    if (!s[eol]) break;
		    Strclear(temp);
		  }
		}
	      }
	      else
		p->flag &= ~PARAM_FLAG_RCSAVE;
	    }
	  }

	  rcsave_temp_mask = orig_rcsave_temp_mask;
	  rcsave_temp = orig_rcsave_temp;
	}
      }

      if (!n && save_to_config_file) {
	if (!rc_dir_is_tmp &&
#ifdef MANY_CHARSET
	    !(f = mb_fopen(config_file, "w@", lookup_process_charset(myname)))
#else
	    !(f = fopen(config_file, "wt"))
#endif
	    ) {
	  char *emsg = Sprintf("Can't write option to \"%s\"!", config_file)->ptr;
	  disp_err_message(emsg, FALSE);
	}
      }
    }
    if (f)
#ifdef MANY_CHARSET
	mb_fclose(f);
    rc_finish();
#else
	fclose(f);
#endif
    sync_with_option();
    backBf();
}

char *
rcFile(char *base)
{
    if (base &&
	(base[0] == '/' ||
	 (base[0] == '.' && (base[1] == '/' || (base[1] == '.' && base[2] == '/'))) ||
	 (base[0] == '~' && (base[1] == '/' || IS_ALPHA(base[1])))))
	return expandName(base);
    else {
	Str file = Strnew_charp(rc_dir);

	if (Strlastchar(file) != '/')
	    Strcat_char(file, '/');
	Strcat_charp(file, base);
	return expandName(file->ptr);
    }
}

char *
auxbinFile(char *base)
{
    Str file = Strnew_charp(w3m_auxbin_dir());
    Strcat_char(file, '/');
    Strcat_charp(file, base);
    return expandName(file->ptr);
}

#if 0				/* not used */
char *
libFile(char *base)
{
    Str file = Strnew_charp(w3m_lib_dir());
    Strcat_char(file, '/');
    Strcat_charp(file, base);
    return expandName(file->ptr);
}
#endif

char *
etcFile(char *base)
{
    Str file = Strnew_charp(w3m_etc_dir());
    Strcat_char(file, '/');
    Strcat_charp(file, base);
    return expandName(file->ptr);
}

char *
helpFile(char *base)
{
    Str file = Strnew_charp(w3m_help_dir());
    Strcat_char(file, '/');
    Strcat_charp(file, base);
    return expandName(file->ptr);
}
